final_realms_fluffos_v1/
final_realms_fluffos_v1/bin/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/ChangeLog.old/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/Win32/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/compat/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/compat/simuls/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/include/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/clone/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/command/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/data/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/etc/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/include/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/inherit/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/inherit/master/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/log/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/single/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/single/tests/compiler/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/single/tests/efuns/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/single/tests/operators/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/testsuite/u/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/tmp/
final_realms_fluffos_v1/fluffos-2.9-ds2.11/windows/
final_realms_fluffos_v1/lib/baseobs/guilds/
final_realms_fluffos_v1/lib/baseobs/misc/
final_realms_fluffos_v1/lib/baseobs/races/shadows/
final_realms_fluffos_v1/lib/cmds/god/
final_realms_fluffos_v1/lib/cmds/handlers/
final_realms_fluffos_v1/lib/cmds/handlers/cmds/
final_realms_fluffos_v1/lib/d/heaven/
final_realms_fluffos_v1/lib/d/heaven/heaven/ave/
final_realms_fluffos_v1/lib/d/mudlib/
final_realms_fluffos_v1/lib/d/newbie/
final_realms_fluffos_v1/lib/d/newbie/docs/
final_realms_fluffos_v1/lib/d/newbie/drow/armour/
final_realms_fluffos_v1/lib/d/newbie/drow/items/
final_realms_fluffos_v1/lib/d/newbie/drow/mobs/
final_realms_fluffos_v1/lib/d/newbie/drow/oldmobs/
final_realms_fluffos_v1/lib/d/newbie/drow/weapons/
final_realms_fluffos_v1/lib/d/newbie/duergar/weapons/
final_realms_fluffos_v1/lib/d/newbie/dwarf/weapons/
final_realms_fluffos_v1/lib/d/newbie/elf/cafe/
final_realms_fluffos_v1/lib/d/newbie/elf/chars/equip/
final_realms_fluffos_v1/lib/d/newbie/elf/items/armours/
final_realms_fluffos_v1/lib/d/newbie/elf/items/obj/
final_realms_fluffos_v1/lib/d/newbie/elf/items/weapons/
final_realms_fluffos_v1/lib/d/newbie/elf/quick_fix/
final_realms_fluffos_v1/lib/d/newbie/gnome/armour/
final_realms_fluffos_v1/lib/d/newbie/gnome/buildings/
final_realms_fluffos_v1/lib/d/newbie/gnome/items/
final_realms_fluffos_v1/lib/d/newbie/gnome/npcs/clones/
final_realms_fluffos_v1/lib/d/newbie/gnome/rooms/northrooms/
final_realms_fluffos_v1/lib/d/newbie/gnome/weapons/
final_realms_fluffos_v1/lib/d/newbie/goblin/armour/
final_realms_fluffos_v1/lib/d/newbie/goblin/items/
final_realms_fluffos_v1/lib/d/newbie/grads/log/
final_realms_fluffos_v1/lib/d/newbie/grads/npcs/
final_realms_fluffos_v1/lib/d/newbie/grads/rooms/
final_realms_fluffos_v1/lib/d/newbie/grads/rooms/cave1/
final_realms_fluffos_v1/lib/d/newbie/grads/temp/
final_realms_fluffos_v1/lib/d/newbie/guests/weapons/
final_realms_fluffos_v1/lib/d/newbie/half-elf/items/
final_realms_fluffos_v1/lib/d/newbie/half-elf/newroomss/
final_realms_fluffos_v1/lib/d/newbie/half-elf/rooms/
final_realms_fluffos_v1/lib/d/newbie/half-elf/rooms/castle/
final_realms_fluffos_v1/lib/d/newbie/half-elf/rooms/drows/
final_realms_fluffos_v1/lib/d/newbie/half-elf/rooms/savannah/
final_realms_fluffos_v1/lib/d/newbie/half-elf/rooms/secret/
final_realms_fluffos_v1/lib/d/newbie/half-elf/rooms/town/
final_realms_fluffos_v1/lib/d/newbie/halfling/
final_realms_fluffos_v1/lib/d/newbie/halfling/misc/
final_realms_fluffos_v1/lib/d/newbie/halfling/rooms/cave/
final_realms_fluffos_v1/lib/d/newbie/human/
final_realms_fluffos_v1/lib/d/newbie/human/armour/
final_realms_fluffos_v1/lib/d/newbie/human/monsters/
final_realms_fluffos_v1/lib/d/newbie/human/obj/
final_realms_fluffos_v1/lib/d/newbie/human/weapons/
final_realms_fluffos_v1/lib/d/newbie/lizard/armour/
final_realms_fluffos_v1/lib/d/newbie/lizard/items/
final_realms_fluffos_v1/lib/d/newbie/lizard/underwater/
final_realms_fluffos_v1/lib/d/newbie/lizard/weapons/
final_realms_fluffos_v1/lib/d/newbie/logs/
final_realms_fluffos_v1/lib/d/newbie/new_halfelf/
final_realms_fluffos_v1/lib/d/newbie/new_halfelf/npcs/
final_realms_fluffos_v1/lib/d/newbie/newdrow/npcs/
final_realms_fluffos_v1/lib/d/newbie/newdrow/rooms/
final_realms_fluffos_v1/lib/d/newbie/newelf/
final_realms_fluffos_v1/lib/d/newbie/newelf/chars/
final_realms_fluffos_v1/lib/d/newbie/newelf/npcs/
final_realms_fluffos_v1/lib/d/newbie/newelf/npcs/recopied/
final_realms_fluffos_v1/lib/d/newbie/newelf/obj/
final_realms_fluffos_v1/lib/d/newbie/newelf/quest_docs./
final_realms_fluffos_v1/lib/d/newbie/newken/
final_realms_fluffos_v1/lib/d/newbie/newken/chars/
final_realms_fluffos_v1/lib/d/newbie/newken/misc/
final_realms_fluffos_v1/lib/d/newbie/newken/npcs/
final_realms_fluffos_v1/lib/d/newbie/newken/obj/
final_realms_fluffos_v1/lib/d/newbie/newliz/
final_realms_fluffos_v1/lib/d/newbie/newliz/cave/
final_realms_fluffos_v1/lib/d/newbie/newliz/npcs/
final_realms_fluffos_v1/lib/d/newbie/orc/items/misc/
final_realms_fluffos_v1/lib/d/newbie/orc/items/weapons/
final_realms_fluffos_v1/lib/d/newbie/orc/tower/
final_realms_fluffos_v1/lib/d/vehicle/
final_realms_fluffos_v1/lib/doc/
final_realms_fluffos_v1/lib/doc/driver/
final_realms_fluffos_v1/lib/doc/driver/concepts/
final_realms_fluffos_v1/lib/doc/driver/driver/
final_realms_fluffos_v1/lib/doc/driver/efuns/arrays/
final_realms_fluffos_v1/lib/doc/driver/efuns/bitstrings/
final_realms_fluffos_v1/lib/doc/driver/efuns/communication/
final_realms_fluffos_v1/lib/doc/driver/efuns/core/
final_realms_fluffos_v1/lib/doc/driver/efuns/debugging/
final_realms_fluffos_v1/lib/doc/driver/efuns/filesystem/
final_realms_fluffos_v1/lib/doc/driver/efuns/interactive/
final_realms_fluffos_v1/lib/doc/driver/efuns/mappings/
final_realms_fluffos_v1/lib/doc/driver/efuns/objects/
final_realms_fluffos_v1/lib/doc/driver/efuns/security/
final_realms_fluffos_v1/lib/doc/driver/efuns/strings/
final_realms_fluffos_v1/lib/doc/driver/efuns/system/
final_realms_fluffos_v1/lib/doc/driver/efuns/types/
final_realms_fluffos_v1/lib/doc/driver/lpc/constructs/
final_realms_fluffos_v1/lib/doc/driver/lpc/types/
final_realms_fluffos_v1/lib/doc/driver/platforms/
final_realms_fluffos_v1/lib/doc/lpc/
final_realms_fluffos_v1/lib/doc/mail/
final_realms_fluffos_v1/lib/doc/man/
final_realms_fluffos_v1/lib/doc/man/html/
final_realms_fluffos_v1/lib/doc/man/html/applies/
final_realms_fluffos_v1/lib/doc/man/html/applies/parsing/
final_realms_fluffos_v1/lib/doc/man/html/driver/
final_realms_fluffos_v1/lib/doc/man/html/efuns/
final_realms_fluffos_v1/lib/doc/man/html/efuns/arrays/
final_realms_fluffos_v1/lib/doc/man/html/efuns/buffers/
final_realms_fluffos_v1/lib/doc/man/html/efuns/compile/
final_realms_fluffos_v1/lib/doc/man/html/efuns/floats/
final_realms_fluffos_v1/lib/doc/man/html/efuns/functions/
final_realms_fluffos_v1/lib/doc/man/html/efuns/general/
final_realms_fluffos_v1/lib/doc/man/html/efuns/numbers/
final_realms_fluffos_v1/lib/doc/man/html/efuns/parsing/
final_realms_fluffos_v1/lib/doc/man/local/
final_realms_fluffos_v1/lib/doc/man/local/applies/
final_realms_fluffos_v1/lib/doc/man/local/applies/interactive/
final_realms_fluffos_v1/lib/doc/man/local/applies/master/
final_realms_fluffos_v1/lib/doc/man/local/concepts/
final_realms_fluffos_v1/lib/doc/man/local/defines/
final_realms_fluffos_v1/lib/doc/man/local/driver/
final_realms_fluffos_v1/lib/doc/man/local/efuns/
final_realms_fluffos_v1/lib/doc/man/local/efuns/arrays/
final_realms_fluffos_v1/lib/doc/man/local/efuns/buffers/
final_realms_fluffos_v1/lib/doc/man/local/efuns/calls/
final_realms_fluffos_v1/lib/doc/man/local/efuns/compile/
final_realms_fluffos_v1/lib/doc/man/local/efuns/filesystem/
final_realms_fluffos_v1/lib/doc/man/local/efuns/floats/
final_realms_fluffos_v1/lib/doc/man/local/efuns/functions/
final_realms_fluffos_v1/lib/doc/man/local/efuns/general/
final_realms_fluffos_v1/lib/doc/man/local/efuns/interactive/
final_realms_fluffos_v1/lib/doc/man/local/efuns/internals/
final_realms_fluffos_v1/lib/doc/man/local/efuns/mappings/
final_realms_fluffos_v1/lib/doc/man/local/efuns/mudlib/
final_realms_fluffos_v1/lib/doc/man/local/efuns/numbers/
final_realms_fluffos_v1/lib/doc/man/local/efuns/objects/
final_realms_fluffos_v1/lib/doc/man/local/efuns/parsing/
final_realms_fluffos_v1/lib/doc/man/local/efuns/sockets/
final_realms_fluffos_v1/lib/doc/man/local/efuns/strings/
final_realms_fluffos_v1/lib/doc/man/local/efuns/system/
final_realms_fluffos_v1/lib/doc/man/local/historical/
final_realms_fluffos_v1/lib/doc/man/local/lfun/QC/
final_realms_fluffos_v1/lib/doc/man/local/lfun/events/
final_realms_fluffos_v1/lib/doc/man/local/lfun/monster/
final_realms_fluffos_v1/lib/doc/man/local/lfun/properties/
final_realms_fluffos_v1/lib/doc/man/local/lpc/
final_realms_fluffos_v1/lib/doc/man/local/lpc/constructs/
final_realms_fluffos_v1/lib/doc/man/local/lpc/types/
final_realms_fluffos_v1/lib/doc/man/local/standards/
final_realms_fluffos_v1/lib/doc/man/local/tutorials/
final_realms_fluffos_v1/lib/doc/man/local/tutorials/basic/
final_realms_fluffos_v1/lib/doc/man/local/tutorials/intermediate/
final_realms_fluffos_v1/lib/doc/man/mudos/applies/
final_realms_fluffos_v1/lib/doc/man/mudos/applies/interactive/
final_realms_fluffos_v1/lib/doc/man/mudos/applies/parsing/
final_realms_fluffos_v1/lib/doc/man/mudos/concepts/
final_realms_fluffos_v1/lib/doc/man/mudos/driver/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/arrays/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/buffers/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/calls/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/compile/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/filesystem/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/floats/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/functions/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/general/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/mappings/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/mixed/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/mudlib/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/numbers/
final_realms_fluffos_v1/lib/doc/man/mudos/efuns/parsing/
final_realms_fluffos_v1/lib/doc/man/mudos/lpc/constructs/
final_realms_fluffos_v1/lib/doc/man/mudos/lpc/types/
final_realms_fluffos_v1/lib/doc/races/
final_realms_fluffos_v1/lib/doc/races/old_race/
final_realms_fluffos_v1/lib/global/virtual/
final_realms_fluffos_v1/lib/global/wiz_backup/
final_realms_fluffos_v1/lib/net/config/
final_realms_fluffos_v1/lib/net/daemon/chars/
final_realms_fluffos_v1/lib/net/inherit/
final_realms_fluffos_v1/lib/net/intermud3/
final_realms_fluffos_v1/lib/net/intermud3/cmds/
final_realms_fluffos_v1/lib/net/intermud3/save/
final_realms_fluffos_v1/lib/net/intermud3/services/
final_realms_fluffos_v1/lib/net/obj/
final_realms_fluffos_v1/lib/net/old/
final_realms_fluffos_v1/lib/net/old/intermud/
final_realms_fluffos_v1/lib/net/old/intermud/adm/
final_realms_fluffos_v1/lib/net/old/intermud/services/
final_realms_fluffos_v1/lib/net/old/intermud/udp/
final_realms_fluffos_v1/lib/net/virtual/
final_realms_fluffos_v1/lib/obj/b_day/
final_realms_fluffos_v1/lib/obj/chars/
final_realms_fluffos_v1/lib/obj/handlers/lists/
final_realms_fluffos_v1/lib/obj/handlers/useless/
final_realms_fluffos_v1/lib/obj/monsters/
final_realms_fluffos_v1/lib/obj/roomgen/
final_realms_fluffos_v1/lib/obj/soul/
final_realms_fluffos_v1/lib/obj/vegetation/
final_realms_fluffos_v1/lib/obj/weapons/oldsys/
final_realms_fluffos_v1/lib/open/
final_realms_fluffos_v1/lib/players/g/
final_realms_fluffos_v1/lib/releasefiles/d/heaven/
final_realms_fluffos_v1/lib/releasefiles/d/mudlib/
final_realms_fluffos_v1/lib/releasefiles/d/newbie/
final_realms_fluffos_v1/lib/releasefiles/doc/
final_realms_fluffos_v1/lib/releasefiles/players/g/
final_realms_fluffos_v1/lib/releasefiles/save/
final_realms_fluffos_v1/lib/releasefiles/save/environ/
final_realms_fluffos_v1/lib/releasefiles/save/roomgen/
final_realms_fluffos_v1/lib/releasefiles/secure/
final_realms_fluffos_v1/lib/releasefiles/w/
final_realms_fluffos_v1/lib/releasefiles/w/god/
final_realms_fluffos_v1/lib/room/
final_realms_fluffos_v1/lib/save/
final_realms_fluffos_v1/lib/save/environ/
final_realms_fluffos_v1/lib/save/roomgen/
final_realms_fluffos_v1/lib/scripts/
final_realms_fluffos_v1/lib/secure/crerem/
final_realms_fluffos_v1/lib/secure/dom/
final_realms_fluffos_v1/lib/secure/log/
final_realms_fluffos_v1/lib/secure/misc/
final_realms_fluffos_v1/lib/std/adnd/
final_realms_fluffos_v1/lib/std/commands/shadows/
final_realms_fluffos_v1/lib/std/creator/
final_realms_fluffos_v1/lib/std/curses/
final_realms_fluffos_v1/lib/std/curses/old_sys/
final_realms_fluffos_v1/lib/std/curses/shadows/
final_realms_fluffos_v1/lib/std/dom/
final_realms_fluffos_v1/lib/std/effects/
final_realms_fluffos_v1/lib/std/effects/healing/
final_realms_fluffos_v1/lib/std/effects/other/
final_realms_fluffos_v1/lib/std/effects/poisons/
final_realms_fluffos_v1/lib/std/environ/
final_realms_fluffos_v1/lib/std/guilds/
final_realms_fluffos_v1/lib/std/guilds/priests/samples/
final_realms_fluffos_v1/lib/std/guilds/wizards/
final_realms_fluffos_v1/lib/std/living/baldy/
final_realms_fluffos_v1/lib/std/living/divstuff/
final_realms_fluffos_v1/lib/std/paran/
final_realms_fluffos_v1/lib/std/poisons/
final_realms_fluffos_v1/lib/std/poisons/shadows/
final_realms_fluffos_v1/lib/std/poisons/weapons/
final_realms_fluffos_v1/lib/std/race_groups/
final_realms_fluffos_v1/lib/std/room/
final_realms_fluffos_v1/lib/std/room/old/
final_realms_fluffos_v1/lib/std/rooms/
final_realms_fluffos_v1/lib/std/shadows/
final_realms_fluffos_v1/lib/std/shadows/test_shad/
final_realms_fluffos_v1/lib/std/socket/
final_realms_fluffos_v1/lib/std/spells/
final_realms_fluffos_v1/lib/std/vaults/
final_realms_fluffos_v1/lib/tmp/
final_realms_fluffos_v1/lib/w/
final_realms_fluffos_v1/lib/w/god/
final_realms_fluffos_v1/old/
final_realms_fluffos_v1/win32/
/* /secure/ftpd... */
/*
 * Originaly written by Pinkfish@Discworld.
 *
 * 93-08-22  Bannor@newmoon
 *           - Added DEBUG and LOGGING flags.
 *           - Fixed a problem with sending files larger then 1k.
 *           - Added support for events.
 *           - Added support for finger ftp.
 *           - Fixed security problems that allowed anyone to read/write
 *             to any directory.
 *           - Improved readability (well, I think so anyway)
 *
 * 13-07-94  Pinkfish@Discworld
 *           - Fixed up the handling of new lines.
 *
 * 28-11-94  Pinkfish@Discworld
 *           - Added in binary mode communications.
 * 
 * 26-6-95   Turrican@Discworld
 *           - Fixed binary mode communications.
 *           - Fixed ABOR handling (control characters), and now the data socket
 *             is closed instead of the control socket ;-)
 *           - Fixed the "CWD ~" bug.
 *           - Fixed the STAT and the SIZE command.
 *           - Added "long" listing of ls.
 *           - Made NLST and LIST take arguments like -C, -l.
 *           - Fixed timeout checking.
 *           - Added commands MKD, RMD, RNFR, RNTO, MDTM, DELE, REST,
 *             HELP, and some SITE commands.
 *
 * 19-8-95   Turrican@Discworld
 *           - Fixed login timeout checking.
 *           - Fixed data_write_callback function, it was quite broken.
 *             Sending of double packets should now belong to the past.
 *           - Fixed data_conn function, it violated the flow control model.
 * 15-02-97  Baldrick@Final Realms.
 *           - How the heck do I make this multi-mud aware..
 */
#include <ftp.h>
#include <socket.h>
#include <socket_errors.h>
#include <localtime.h>
 
 
 
/*
-----------------------------------------------
   debugging defines.
-----------------------------------------------
*/
#define TP_CRE "baldrick"
 
#undef DEBUG
 
#ifdef DEBUG
 
#define TPX(STR) if (find_player(TP_CRE)) \
                    tell_object(find_player(TP_CRE), STR)
 
#define DEBUG_SEND   /* define to debug data_write_callback() */
 
#else
 
#define TPX(STR)
#undef DEBUG_SEND
 
#endif
 
 
/*
-----------------------------------------------
   log file defines.
-----------------------------------------------
*/
#define LOGGING
 
#ifdef LOGGING
 
#define LOG_CONNECT /* define to log all connections */
#define LOG_FILE    /* define to log all file xfers */
#undef LOG_CD_SIZE /* define to log cd's and file size commands */
 
#else
 
#undef LOG_CONNECT
#undef LOG_FILE
#undef LOG_CD_SIZE
 
#endif
 
#define BLOCK_SIZE 4096

 
/*
-----------------------------------------------
   driver version defines.
-----------------------------------------------
*/
#define NEWDRIVER
 
#ifdef __VERSION__
#define VERSION __VERSION__
#else
#ifndef VERSION
#define VERSION version()
#endif
#endif
 
 
 
#define CHECK_LOGIN() if (!socket_info[fd][LOGGED_IN]) \
                      { \
                        socket_write(fd, "530 Please login with USER and PASS.\r\n"); \
                        break; \
                      }
#define CHECK_CMD(INT) if (sizeof(bits) < INT+1) \
                       { \
                         socket_write(fd, sprintf("500 '%s': command not understood.\r\n", \
                            str)); \
                         break; \
                       }
#define CHECK_PLAYER() if (socket_info[fd][LOGGED_IN] == 2) \
               { \
                         socket_write(fd, "553 Permission denied (you are not a creator)\r\n"); \
                         break; \
                       }
#define CHECK_STRING socket_info[pfd][TYPE] == STRING
#define UNAME   socket_info[fd][USER_NAME]


#define HOME_DIR(NAME) "/w/"+NAME

 
/*
-----------------------------------------------
    Variables
-----------------------------------------------
*/
 
mapping socket_info;
int our_socket, offset;
string rnfr;
 
 
 
/*
-----------------------------------------------
    Prototypes
-----------------------------------------------
*/
void setup_ftp(int port);
void finish_lookup(string host, string number);
string get_path(int fd, string str);
static void do_update(string name, int fd);
 
mixed *query_connections()
/* returns an array of users connected to ftpd */
{
mixed  *vals;
string *list;
int  count;
 
  list = ({ });
  vals = values(socket_info);
 
  for (count = 0; count < sizeof(vals); count++)
  {
    if (vals[count][USER_NAME]) {
      if (vals[count][LOGGED_IN]) {
        list += ({ capitalize((string)((vals[count])[USER_NAME])) });
      } else {
        list += ({ "login" });
      }
    }
  }
  return list;
 
} /* query_connections() */
 
 
void  create()
{
  seteuid("Root");
  socket_info = ([]);
  rnfr = "";
  offset = 0;
  call_out("setup_ftp", 2, FR_FTP_PORT);
  call_out("check_connections", 5 * 60);
}      /* create() */
 
 
void  setup_ftp(int port)
{
  our_socket = socket_create(STREAM, "in_read_callback", "in_close_callback");
  if (our_socket < 0)
  {
    TPX("Failed to create socket.\n");
    return;
  }
  if (socket_bind(our_socket, port) < 0)
  {
    TPX("Failed to bind socket.\n");
    socket_close(our_socket);
    return;
  }
  if (socket_listen(our_socket, "in_listen_callback") < 0)
  {
    TPX("Failed to listen to socket.\n");
    return;
  }
}      /* setup_ftp() */
 
 
void  in_listen_callback(int fd)
{
int   new_fd;

  if ((new_fd = socket_accept(fd, "in_read_callback", "in_write_callback")) < 0)
  {
    return;
  }
  socket_info[new_fd] = ([USER_NAME : "Login", IDLE : 900, LAST_DATA : time()]);
  socket_write(new_fd, sprintf("220 %s FTP server ready.  " +
          "Please login as yourself.\r\n", mud_name()));
}      /* in_listen_callback() */

string ls( string path, int mask) {
    string *files;
    int i, j, s;
    mixed *xfiles;
    mixed *stats;
    string tmp, tmp2, creator, domain;

    /* if path is a directory get contents */
    if ( file_size( path ) == -2) {
        if ( path[ strlen( path ) - 1 ] == '/' )
            path += "*";
        else
            path += "/*";
    }

    /* begin narrow columnar "nlst" */
    if (!(MASK_L & mask)) {
        files = get_dir( path );

        /* can only happen if permissions are messed up at account level */
        if (!files)
            return "";
        if (!(MASK_A & mask))
          files -= ({ ".", ".." });

        if (!(i = sizeof( files )))
            return "";

        /* no wild cards...must have been the exact pathname to a file */
        if (strsrch(path, '*') == -1 && strsrch(path, '?') == -1) {
            return files[0] + "\n";
        }

        /* remove globber at end of path, leave a trailing slash */
        j = strsrch(path, '/', -1);
        path = path[0..j];

        while ( i-- ) {
            /* scan next level down for files */
            tmp = sprintf("%s%s/", path, files[i]);
            if (MASK_F & mask) {
              if (strsrch(tmp, "/./") != -1 || strsrch(tmp, "/../") != -1) {
                files[i] += "/";
                continue;
              }
              if (file_size(tmp) == -2)
                files[i] += "/";
           //   else if (stat(tmp[0..-2])[2])
              else if (virtual_find_object(tmp[0..(strlen(tmp)-2)]))
                files[i] += "*";
            }
        }
        if (MASK_C & mask)
          return sprintf("%-#70s\n", implode(files, "\n"));
        else
          return implode( files, "\n" ) + "\n";
    }

    /* begin long "list" */
    xfiles = get_dir( path, -1 );
    if (!(mask & MASK_A))
      xfiles = filter_array(xfiles, "check_dots", this_object());
    if (!xfiles || !(s = sizeof( xfiles )))
        return "";

    files = allocate(s);

    // the Unix-like file permissions are mainly for effect...hopefully it
    // isn't too much, since anything more would likely be too cpu intensive
    // and cause it to max eval...
    creator = (string)MASTER->creator_file(path);
    if (!creator)  creator = "Root";

    domain = (string)MASTER->domain_file(path);
    if (!domain)  domain = "Root";

    i = strsrch(path, '/', -1);
    if (i >= 0)
        path = path[0..i];

    for (i = 0; i < s; i++) {
        /* process timestamp */
        tmp2 = ctime((xfiles[i])[2]); /* get last modified timestamp */
        if ((xfiles[i])[2] + (365 * 24 * 60 * 60) < time()) {
            /* MMM DD  YYYY */
            tmp = sprintf("%s  %s", tmp2[4..9], tmp2[20..23]);
        } else {
            /* MMM DD hh:mm */
            tmp = tmp2[4..15];
        }

        j = (xfiles[i])[1];   /* get filesize */
        if (j == -2) {
            /* directory */
            files[i] = sprintf("drwxrwsr-x %3d %-12s %-12s      0 %12s %s",
                  sizeof(get_dir(sprintf((path == "/"?"%s%s":"%s/%s/"),path,
                  xfiles[i][0]))),creator,
                  domain, tmp, (xfiles[i])[0]+((MASK_F & mask)?"/":""));
        } else {
            /* file */
            stats = stat(path + (xfiles[i])[0]);
            files[i] = sprintf("-rw%crw-r--   1 %-12s %-12s %6d %12s %s",
                 sizeof(stats) > 1 && stats[2] ? 'x' : '-',
                  creator, domain, j, tmp, (xfiles[i])[0]+
                 (sizeof(stats) > 1 && stats[2] && (MASK_F & mask)?"*":""));
        }
    }

    return sprintf( "%-#70s\n", implode( files, "\n" ) );
}

void data_write_callback(int fd) {
  int pfd, pos, length, ret_val;
  mixed tmp;

  if (socket_info[fd][TYPE] == DOWNLOAD) return;

  pos = socket_info[fd][POS];

  pfd = socket_info[fd][PARENT_FD];
  if (undefinedp(pfd) || undefinedp(socket_info[pfd]))
    return; // not a data connection, or was orphaned
  socket_info[pfd][LAST_DATA] = time();

  if (pos > socket_info[fd][LEN]) {
    TPX("pos > len\n");
    socket_write(pfd, "226 Transfer complete.\r\n");
    socket_close(fd);
    map_delete(socket_info[pfd], DATA_FD);
    map_delete(socket_info[pfd], DATA_ADDR);
    offset = 0;
#ifdef DEBUG_SEND
    TPX("dwc() complete, exiting.\n");
#endif
    return;
  }
  length = ((pos+BLOCK_SIZE) >= socket_info[fd][LEN]) ?
       (socket_info[fd][LEN] - pos) : BLOCK_SIZE;

#ifdef DEBUG_SEND
  TPX("Entering dwc(), pos: " + pos + " length should be: " + length + ".\n")
;
#endif

  if (socket_info[fd][TYPE] == STRING) {
#ifdef DEBUG_SEND
    TPX("type == STRING\n");
#endif
    while ((ret_val = socket_write(fd,
        socket_info[fd][DATA][pos..(pos+BLOCK_SIZE-1)])) == EESUCCESS) {

      pos += BLOCK_SIZE;
      socket_info[fd][POS] = pos;
      if (pos > socket_info[fd][LEN]) {
        socket_write(pfd, "226 Transfer complete.\r\n");
        socket_close(fd);
        map_delete(socket_info[pfd], DATA_FD);
        map_delete(socket_info[pfd], DATA_ADDR);
        offset = 0;
#ifdef DEBUG_SEND
        TPX("dwc() complete, exiting.\n");
#endif
        return;
      }
     }
  } else {
#ifdef DEBUG_SEND
    TPX("type is other then STRING\n");
#endif
    if (CHECK_STRING) {
      tmp = "";
      if (catch(tmp = read_bytes(socket_info[fd][DATA], pos,
          length)))
        socket_write(pfd, "551 Error on input file.\r\n");
      tmp = replace_string(tmp, "\n", "\r\n");
    }
    else {
      tmp = allocate_buffer(0);  
      if (catch(tmp = read_buffer(socket_info[fd][DATA], pos,
          length)))
        socket_write(pfd, "551 Error on input file.\r\n");
    }

    while ((ret_val = socket_write(fd, tmp)) == EESUCCESS) {
#ifdef DEBUG_SEND
      TPX("sent from " + pos + " to " + (pos + length) + ".\n");
      TPX("ret_val was: " + ret_val + ".\n");
#endif

      pos += BLOCK_SIZE;
      socket_info[fd][POS] = pos;
      if (pos >= socket_info[fd][LEN]) {
        socket_write(pfd, "226 Transfer complete.\r\n");
        socket_close(fd);
        map_delete(socket_info[pfd], DATA_FD);
        map_delete(socket_info[pfd], DATA_ADDR);
        offset = 0;
#ifdef DEBUG_SEND
        TPX("dwc() complete, exiting.\n");
#endif
        return;
      }
      if (CHECK_STRING) {
        tmp = "";
        tmp = read_bytes(socket_info[fd][DATA], pos, BLOCK_SIZE);
        tmp = replace_string(tmp, "\n", "\r\n");
      } else {
        tmp = allocate_buffer(0);
        tmp = read_buffer(socket_info[fd][DATA], pos, BLOCK_SIZE);
      }
    }
  }
#ifdef DEBUG_SEND
  TPX("ret_val was: " + ret_val + ".\n");
  TPX("leaving dwc(), pos: " + pos + ".\n");
#endif

  if (ret_val == EEWOULDBLOCK) {
    /* it would block, so it's up to us to try again */
#ifdef DEBUG_SEND
    TPX("Adding call_out\n");
#endif
    call_out("data_write_callback", 2, fd);
  } else if (ret_val == EECALLBACK) {
    /* Buffer full, wait untill we are called back again. Do increase the
     * position, since the previous block WAS sent. 
     * We are now flow controlled. */
    socket_info[fd][POS] += BLOCK_SIZE;
  } else {
    /* not going to be called again by driver */
    while (remove_call_out("data_write_callback") != -1) {
#ifdef DEBUG_SEND
        TPX("Killing callout.\n");
#endif
    }
  }
} /* data_write_callback() */

static void data_conn(int fd, string mess, string name, int type) {
  int new_fd, ret, data_mode;
  string data_mode_name;

  if (type == STRING ||
      (type == FILE && socket_info[fd][TYPE] == STRING)) {
    data_mode_name = "ASCII";
    data_mode = STREAM;
  } else {
    data_mode_name = "BINARY";
    data_mode = STREAM_BINARY;
  }

  if (socket_info[fd][DATA_FD]) {
    socket_write(fd, "425 Can't open data connection.\r\n");
    return;
  }
  new_fd = socket_create(data_mode, "data_read_callback",
      "data_close_callback"); 

  if (new_fd < 0) {
    socket_write(fd, "425 Can't create data socket.\r\n");
    return;
  }

  if (!stringp(socket_info[fd][DATA_ADDR])) {
    socket_write(fd, "425 Can't open data connection.\r\n");
    return;
  }

  socket_info[new_fd] = ([
    DATA    : (type == STRING ? replace_string(mess, "\n", "\r\n") : mess),
    POS     : offset,
    PARENT_FD : fd,
    TYPE    : type,
    LEN     : (type == STRING ? strlen(mess) : file_size(mess))
   ]);

  socket_info[fd][DATA_FD] = new_fd;
  if ((ret = socket_connect(new_fd, sprintf("%s %d",
      socket_info[fd][DATA_ADDR], socket_info[fd][DATA_PORT]),
      "data_read_callback", "data_write_callback")) < 0) {
    TPX("Error: " + socket_info[fd][DATA_ADDR] + " " +
      socket_info[fd][DATA_PORT] + "\n");
    TPX(socket_error(ret) + "\n");
    socket_write(fd, "425 Can't build data connection.\r\n");
    socket_close(new_fd);
    return;
  }
  socket_write(fd, sprintf("150 Opening %s mode data connection for %s "
      "(%d bytes).\r\n", data_mode_name, name, socket_info[new_fd][LEN]));
}  /* data_conn() */ 

static void read_connection(int fd, string path, int append) {
  int new_fd, ret, data_mode;
  string data_mode_name;

  if (socket_info[fd][TYPE] == BINARY) {
    data_mode_name = "BINARY";
    data_mode = STREAM_BINARY;
  } else {
    data_mode_name = "ASCII";
    data_mode = STREAM;
  }

  if (socket_info[fd][DATA_FD]) {
    socket_write(fd, "425 Can't open data connection.\r\n");
    return;
  }

  new_fd = socket_create(data_mode, "data_read_callback",
              "data_close_callback");

  if (new_fd < 0) {
    socket_write(fd, "425 Can't create data socket.\r\n");
    return;
  }

  socket_info[new_fd] = ([
    PATH    : path,
    PARENT_FD : fd,
    POS     : (!append?0:(file_size(path)==-1?0:file_size(path))),
    TYPE    : DOWNLOAD,
    APPEND  : append
   ]);

  if ((ret = socket_connect(new_fd, sprintf("%s %d",
      socket_info[fd][DATA_ADDR], socket_info[fd][DATA_PORT]),
      "data_read_callback", "data_write_callback")) < 0) {
    TPX("Error: " + socket_info[fd][DATA_ADDR] + " " +
      socket_info[fd][DATA_PORT] + "\n");
    TPX(socket_error(ret) + "\n");
    socket_write(fd, "425 Can't build data connection.\r\n");
    socket_close(new_fd);
    return;
  }

  if (append != 1)
    catch(rm(path));
  socket_info[fd][DATA_FD] = new_fd;

  socket_write(fd, sprintf("150 Opening %s mode data connection for %s.\r\n",
      data_mode_name, path));
}  /* read_connection() */ 
 
void data_read_callback(int fd, mixed mess) {
  int pfd;

  if (socket_info[fd][TYPE] != DOWNLOAD)
    return;

  pfd = socket_info[fd][PARENT_FD];
  if (undefinedp(pfd) || undefinedp(socket_info[pfd]))
    return; // not a data connection, or was orphaned
  socket_info[pfd][LAST_DATA] = time();
   
  if (stringp(mess))
    mess = replace_string(mess, "\r", "");

  write_buffer(socket_info[fd][PATH], socket_info[fd][POS], mess);
  socket_info[fd][POS] += (stringp(mess)?strlen(mess):sizeof(mess));
}  /* data_read_callback() */

void data_close_callback(int fd) {
  int pfd;

  pfd = socket_info[fd][PARENT_FD];

  if (socket_info[fd][TYPE] == DOWNLOAD) {
    if (socket_info[fd][APPEND] == -1)
      socket_write(pfd,
          sprintf("226 Transfer complete (unique file name:%s).\r\n",
          socket_info[fd][PATH]));
    else
      socket_write(pfd, "226 Transfer complete.\r\n");
  }

  /*
   * only close data connections here
   */
  if (!undefinedp(pfd)) {
    socket_close(fd);
    map_delete(socket_info, fd);
    map_delete(socket_info[pfd], DATA_FD);
    map_delete(socket_info[pfd], DATA_ADDR);
  }
  offset = 0;
}  /* data_close_callback() */

void  logout(int fd)
{
string name;

  name = socket_info[fd][USER_NAME];
  event(users(), "inform", sprintf("%s logged out of ftpd", "name"), "ftp");
 
#ifdef LOG_CONNECT
  log_file("ftpd.log", sprintf("%s logged out at %s.\n", name, ctime(time())));
#endif
 
  map_delete(socket_info[fd], USER_NAME);
  map_delete(socket_info[fd], LOGGED_IN);
  map_delete(socket_info[fd], CWD);
}      /* logout() */
 
 
void  parse_comm(int fd, string str)
{
  string *bits, tmp;
  mixed *misc;
  int   port, i, mask;

  if (strsrch(str, "PASS") == -1) 
    TPX("Parseing " + str + ".\n");

  bits = explode(str, " ");
  socket_info[fd][LAST_DATA] = time();
  switch (lower_case(bits[0]))
  {
    case "port":
        bits = explode(implode(bits[1..1000], " "), ",");
        if (sizeof(bits) < 6)
          socket_write(fd, "550 Failed command.\r\n");
        else
        {
          socket_info[fd][DATA_ADDR] = implode(bits[0..3], ".");
          sscanf(bits[4], "%d", i);
          port = i << 8;
          sscanf(bits[5], "%d", i);
          port += i;
          socket_info[fd][DATA_PORT] = port;
          socket_write(fd, "200 PORT command successful.\r\n");
        }
        break;
    case "user":
        CHECK_CMD(1);
        if ((bits[1] == "mabel") &&
          socket_info[fd][LOGGED_IN])
          if (MASTER->query_lord(socket_info[fd][USER_NAME])) {
          "/obj/shut"->shut(10);
          if (find_object("/obj/shut"))
            socket_write(fd, "530 Mabelrode loaded.\r\n");
          else
            socket_write(fd, "530 Mabelrode failed to load.\r\n");
          break;
          }
        if (socket_info[fd][LOGGED_IN])
          logout(fd);
        if (!LOGIN_OB->test_user(bits[1]))
            socket_write(fd, sprintf("530 User %s access denied...\r\n", bits[1]));
        else if (!LOGIN_OB->test_creator(bits[1]))
            socket_write(fd, sprintf("530 User %s access denied...\r\n", bits[1]));
        else {
          socket_write(fd, 
            sprintf("331 Password required for %s.\r\n", bits[1]));
          socket_info[fd][USER_NAME] = bits[1];
        }
        break;
    case "pass":
        if (socket_info[fd][LOGGED_IN] ||
           !socket_info[fd][USER_NAME])
        {
          socket_write(fd, "503 Login with USER first.\r\n");
          break;
        }
        if (!LOGIN_OB->test_password(socket_info[fd][USER_NAME],
               bits[1]))
        {
          socket_write(fd, "530 Login incorrect.\r\n");
          map_delete(socket_info[fd], USER_NAME);
        }
        else if (!LOGIN_OB->test_creator(socket_info[fd][USER_NAME]))
        {
          socket_info[fd][LOGGED_IN] = 2;
          socket_info[fd][CWD] = "/open";
          socket_info[fd][TYPE] = STRING;
          event(users(), "inform", sprintf("%s(player) connected to ftpd", 
               socket_info[fd][USER_NAME]), "ftp");
#ifdef LOG_CONNECT
          log_file("ftpd.log", sprintf("%s(player) (%s) connected at %s.\n", 
               UNAME, explode(socket_address(fd)," ")[0], ctime(time())));
#endif
    } else {
          socket_info[fd][LOGGED_IN] = 1;
          socket_info[fd][CWD] = HOME_DIR(socket_info[fd][USER_NAME]);
          socket_info[fd][TYPE] = STRING;
          event(users(), "inform", sprintf("%s connected to ftpd",
               socket_info[fd][USER_NAME]), "ftp");
#ifdef LOG_CONNECT
          log_file("ftpd.log", sprintf("%s (%s) connected at %s.\n", UNAME,
               explode(socket_address(fd)," ")[0], ctime(time())));
#endif
        }
        if (file_size(socket_info[fd][CWD]) != -2)
        {
          socket_write(fd, 
              "230 Cannot cd to home.  Logging in with dir=/\r\n");
           socket_info[fd][CWD] = "/";
        }
        else
          socket_write(fd, sprintf("230 User %s logged in.\r\n",
              socket_info[fd][USER_NAME]));
        break;
    case "allo":
        socket_write(fd, "201 ALLO command ignored.\r\n");
        break;
    case "noop":
        socket_write(fd, "200 NOOP operation successful.\r\n");
        break;
    case "rnfr":
        CHECK_LOGIN();
        CHECK_CMD(1);
    CHECK_PLAYER();
        tmp = get_path(fd, bits[1]);
        if (MASTER->valid_read(tmp, socket_info[fd][USER_NAME],
                "file_size")) {
          socket_write(fd, sprintf("550 Permission denied reading %s.\r\n",
            tmp));
          break;
        } 
        if (file_size(tmp) != -1) {
          rnfr = tmp;
          socket_write(fd, "350 File exists, ready for destination name\r\n");
        }
        else
          socket_write(fd, sprintf("550 %s: No such file or directory.\r\n",
                 bits[1]));
        break;
    case "rnto":
        CHECK_LOGIN();
        CHECK_CMD(1);
    CHECK_PLAYER();
        if (rnfr == "") {
          socket_write(fd, "503 Bad sequence of commands.\r\n");
          break;
        }
        tmp = get_path(fd, bits[1]);
        if (MASTER->valid_write(rnfr, socket_info[fd][USER_NAME],
                "rename") &&
            MASTER->valid_write(tmp, socket_info[fd][USER_NAME],
                "rename")) {
          if (!catch(rename(rnfr, tmp)))
            socket_write(fd, "250 RNTO command successful.\r\n");
          else
            socket_write(fd, "550 rename: No such file or directory.\r\n");
        }
        else
          socket_write(fd, "550 rename: Operation not permitted.\r\n");
        rnfr = "";
        break;
    case "rest":
        CHECK_LOGIN();
        CHECK_CMD(1);
        sscanf(bits[1], "%d", offset);
        socket_write(fd, sprintf("350 Restarting at %d. %s\r\n", offset,
                "Send STORE or RETRIEVE to initiate transfer."));
        break;
    case "retr":
        CHECK_LOGIN();
        CHECK_CMD(1);
        tmp = get_path(fd, bits[1]);
        switch(file_size(tmp)) {
          case -2:
            socket_write(fd, sprintf("550 %s: Not a plain file.\r\n", 
                tmp));
            break;
          case -1:
            socket_write(fd, sprintf("550 %s: No such file or directory.\r\n",
                tmp));
            break;
          default:
            if (!MASTER->valid_read(tmp, socket_info[fd][USER_NAME],
                     "read_file"))
              socket_write(fd, sprintf("550 Permison denied reading %s.\r\n", 
                tmp));
            else
            {
#ifdef LOG_FILE
              log_file("ftpd.log", sprintf("%s RETR %s at %s.\n", UNAME, tmp, 
                ctime(time())));
#endif
              data_conn(fd, tmp, bits[1], FILE);
            }
            break;
        }
        break;
    case "stor":
        CHECK_LOGIN();
        CHECK_CMD(1);
        tmp = get_path(fd, bits[1]);
        if (MASTER->valid_write(tmp, socket_info[fd][USER_NAME],
          "write_file"))
        {
#ifdef LOG_FILE
          log_file("ftpd.log", sprintf("%s STOR %s at %s.\n", UNAME, tmp, 
                ctime(time())));
#endif
          if (offset)
            read_connection(fd, tmp, 1);
          else
            read_connection(fd, tmp, 0);
        }
        else
          socket_write(fd, sprintf("553 Permision denied to %s.\r\n", tmp));
        break;
    case "dele":
        /* delete a file */
        CHECK_LOGIN();
        CHECK_CMD(1);
    CHECK_PLAYER();
        tmp = get_path(fd, bits[1]);
        if (MASTER->valid_write(tmp, socket_info[fd][USER_NAME],
          "rm"))
        {
#ifdef LOG_FILE
          log_file("ftpd.log", sprintf("%s DELE %s at %s.\n", UNAME, tmp, 
                ctime(time())));
#endif
          if (!rm(tmp))
            socket_write(fd, sprintf("550 %s: Directory not empty.\r\n",
                tmp));
          else 
            socket_write(fd, "250 DELE command successful.\r\n");
        }
        else
          socket_write(fd, sprintf("550 Permission denied to %s.\r\n", tmp));
        break;
    case "mkd":
    case "xmkd":
        /* make a dir */
        CHECK_LOGIN();
        CHECK_CMD(1);
    CHECK_PLAYER();
        tmp = get_path(fd, bits[1]);
        if (MASTER->valid_write(tmp, socket_info[fd][USER_NAME], "mkdir"))
        {
#ifdef LOG_FILE
          log_file("ftpd.log", sprintf("%s MKD %s at %s.\n", UNAME, tmp, 
                ctime(time())));
#endif
          if (!mkdir(tmp))
            socket_write(fd, sprintf("550 %s: File exits.\r\n",
                tmp));
          else 
            socket_write(fd, "257 MKD command successful.\r\n");
        }
        else
          socket_write(fd, sprintf("550 Permission denied to %s.\r\n", tmp));
        break;
    case "rmd":
    case "xrmd":
        /* remove a dir */
        CHECK_LOGIN();
        CHECK_CMD(1);
    CHECK_PLAYER();
        tmp = get_path(fd, bits[1]);
        if (MASTER->valid_write(tmp, socket_info[fd][USER_NAME],
          "rmdir"))
        {
          if (file_size(tmp) == -1) {
            socket_write(fd, sprintf("550 %s: No such file or directory.\r\n",
                tmp));
            break;
          }
          if (file_size(tmp) != -2) {
            socket_write(fd, sprintf("550 %s: Not a directory.\r\n", tmp));
            break;
          }
          if (!rmdir(tmp))
            socket_write(fd, sprintf("550 %s: Directory not empty.\r\n",
                tmp));
          else {
#ifdef LOG_FILE
            log_file("ftpd.log", sprintf("%s RMD %s at %s.\n", UNAME, tmp, 
                ctime(time())));
#endif
            socket_write(fd, "250 RMD command successful.\r\n");
          }
        }
        else
          socket_write(fd, sprintf("550 Permission denied to %s.\r\n", tmp));
        break;
    case "appe":
        /* append... */
        CHECK_LOGIN();
        CHECK_CMD(1);
    CHECK_PLAYER();
        tmp = get_path(fd, bits[1]);
        if (MASTER->valid_write(tmp, socket_info[fd][USER_NAME],
                     "write_file"))
        {
#ifdef LOG_FILE
          log_file("ftpd.log", sprintf("%s APPE %s at %s.\n", UNAME, tmp, 
                ctime(time())));
#endif
          read_connection(fd, tmp, 1);
        }
        else
          socket_write(fd, sprintf("553 Permision denied to %s.\r\n", tmp));
        break;
    case "help": // give help information
        if (sizeof(bits) > 1) {
          tmp = lower_case(bits[1]);
          if (bits[1] == "site")
            bits[1] = "HELP";
          if (!undefinedp(cmdtab[ tmp ]) && tmp != "site") {
            misc = cmdtab[ tmp ];
            if (misc[1])
              socket_write(fd, sprintf("214 Syntax: %s %s.\r\n",
                misc[0], misc[2]) );
            else
              socket_write(fd, sprintf("214 %s %s; unimplemented.\r\n",
                              misc[0], misc[2]) );
            break;
          } else if (bits[1] != "HELP") {
            socket_write(fd, sprintf("502 Unknown command %s.\r\n",
                  bits[ 1 ]) );
            break;
          }
        } else {
          int s;
          socket_write(fd, "214-The following commands are recognized "
              "(* =>'s unimplemented).\r\n");
          misc = keys(cmdtab);
          s = sizeof(misc);
          tmp = "   ";
          for (i = 0; i < s; i++) {
            tmp += sprintf("%-4s%-4s", cmdtab[misc[i]][0],
                cmdtab[misc[i]][1] ? " " : "*");
            if (i % 8 == 7) {
              socket_write(fd, tmp + "\r\n");
              tmp = "   ";
            }
          }
          if (i % 8)
            socket_write(fd, tmp + "\r\n");
          socket_write(fd, sprintf("214 Direct comments to %s.\r\n",
              "Dyraen@RotD"));
          break;
        }
    case "site": // non-standard commands
        CHECK_LOGIN();
        switch (lower_case(bits[ 1 ])) {
            case "idle":
                if (sizeof(bits) < 3) {
                        socket_write(fd,
                        sprintf("200 Current IDLE time limit is %d seconds; max 7200\r\n",
                        socket_info[fd][IDLE]));
                        break;
                }

                if (!sscanf(bits[2], "%d", i)) {
                        socket_write(fd, "550 SITE IDLE command failed.\r\n");
                        break;
                    }

                    i = (i<300?300:(i>7200?7200:i));
                    socket_info[fd][IDLE] = i;
                    socket_write(fd, sprintf("200 Maximum IDLE time set to %d seconds\r\n", i));
                    break;
                case "time":
                socket_write(fd,
                  sprintf("200 Local TIME is %s.\r\n", ctime(time())[4..15] ) );
                break;
            case "upd":
                /* remote updating of files */
                CHECK_CMD(2);
                tmp = get_path(fd, bits[2]);
                do_update(tmp, fd);
#ifdef LOG_FILE
                log_file("ftpd.log", sprintf("%s UPD %s at %s.\n", UNAME, tmp,
                         ctime(time())));
#endif
                break;
            case "help":
                if (sizeof(bits) > 2) {
                        tmp = lower_case(bits[2]);
                        if (!undefinedp(sitecmdtab[ tmp ])) {
                    misc = sitecmdtab[ tmp ];
                      if (misc[1])
                      socket_write(fd, sprintf("214 Syntax: SITE %s %s.\r\n",
                        misc[0], misc[2]) );
                      else
                      socket_write(fd, sprintf("214 SITE %s %s; unimplemented.\r\n",
                              misc[0], misc[2]) );
                  } else {
                      socket_write(fd, sprintf("502 Unknown command %s.\r\n",
                        bits[ 2 ]) );
                        }
                } else {
             int s;
                  socket_write(fd, "214-The following SITE commands are recognized "
                            "(* =>'s unimplemented).\r\n");
                        misc = keys(sitecmdtab);
                  s = sizeof(misc);
                        tmp = "   ";
                        for (i = 0; i < s; i++) {
                      tmp += sprintf("%-*s%-*s", strlen(sitecmdtab[misc[i]][0]),
                        sitecmdtab[misc[i]][0], 8-strlen(sitecmdtab[misc[i]][0]),
                        sitecmdtab[misc[i]][1] ? " " : "*");
                    if (i % 8 == 7) {
                      socket_write(fd, tmp + "\r\n");
                      tmp = "   ";
                      }
                        }
                      if (i % 8)
                    socket_write(fd, tmp + "\r\n");
                      socket_write(fd, sprintf("214 Direct comments to %s.\r\n",
                      "Dyraen@RotD"));
                }
                break;
            case "newer":
            case "minfo":
                socket_write( fd, sprintf("502 %s command not implemented.\r\n",
                  bits[ 0 ]) );
                break;
            default:
                socket_write( fd, sprintf("500 '%s %s': command "
                  "not understood.\r\n", bits[ 0 ],
                  bits[ 1 ]) );
                break;
              }
        break;
    case "mdtm":
  /* Supposed to return modified time in the format: YYYYMMDDHHMMSS */
        CHECK_LOGIN();
        CHECK_CMD(1);
        tmp = get_path(fd, bits[1]);
        if (MASTER->valid_read(tmp, socket_info[fd][USER_NAME], "file_size"))
        {
          if (file_size(tmp) == -2)
            socket_write(fd, sprintf("550 %s not a plain file.\r\n", tmp));
          else
            if (file_size(tmp) == -1)
              socket_write(fd, sprintf("550 %s does not exist.\r\n", tmp));
            else
            {
              mixed *tm;
 
              tm = localtime(stat(tmp)[1]+localtime(0)[LT_GMTOFF]);
              socket_write(fd, sprintf("213 %d%02d%02d%02d%02d%02d\r\n", 
                    tm[LT_YEAR], tm[LT_MON]+1, tm[LT_MDAY], tm[LT_HOUR], tm[LT_MIN], 
                    tm[LT_SEC]));
            }
        }
        else
              socket_write(fd, sprintf("550 Permission denied to %s.\r\n", tmp));
        break;
    case "size":
        CHECK_LOGIN();
        CHECK_CMD(1);
        tmp = get_path(fd, bits[1]) ;
        if (MASTER->valid_read(tmp, socket_info[fd][USER_NAME], "file_size"))
        {
          if (file_size(tmp) == -2)
            socket_write(fd, sprintf("550 %s not a plain file.\r\n", tmp));
          else
            if (file_size(tmp) == -1)
              socket_write(fd, sprintf("550 %s does not exist.\r\n", tmp));
            else
            {
#ifdef LOG_CD_SIZE
              log_file("ftpd.log", sprintf("%s SIZE %s at %s.\n", UNAME, tmp, 
                    ctime(time())));
#endif
              socket_write(fd, sprintf("213 %d\r\n", file_size(tmp)));
            }
        }
        else
          socket_write(fd, sprintf("550 Permission denied to %s.\r\n", tmp));
        break;
    case "stat":
        if (sizeof(bits) > 1) {
          CHECK_LOGIN();
          tmp = get_path(fd, bits[1]);
          if (MASTER->valid_read(tmp, socket_info[fd][USER_NAME],
                 "get_dir")) {
            if (file_size(tmp) != -1)
              socket_write(fd, sprintf("211-status of %s:\r\n%s"
                "211 End of status\r\n", bits[1], ls(tmp, MASK_L)));
            else
              socket_write(fd, sprintf("211 %s: No such file or directory.\r\n",
                tmp));
          }
          else 
            socket_write(fd, sprintf("211 Permission denied to %s.\r\n",
              tmp));
          break;
        }
        else {
          socket_write(fd, sprintf("211-%s FTP server status:\r\n",
                  mud_name()));
          socket_write(fd, sprintf("     %s %s\r\n", FTP_VERSION,
            ctime(stat(file_name(this_object())+".c")[1])));
          sscanf(socket_address(fd), "%s %*d", tmp);
          socket_write(fd, sprintf("     Connected to %s\r\n", tmp));
          if (socket_info[fd][LOGGED_IN])
            socket_write(fd, "     Logged in as " +
                         socket_info[fd][USER_NAME] + "\r\n");
          else if (socket_info[fd][USER_NAME])
            socket_write(fd, "     Waiting for password\r\n");
          else
            socket_write(fd, "     Waiting for user name\r\n");
          socket_write(fd, sprintf("     TYPE: %s",
                       (socket_info[fd][TYPE] == STRING?"ASCII":"BINARY"))+
                       ", FORM: Nonprint; STRUcture: " +
                       "File; transfer MODE: Stream\r\n");
          if (socket_info[fd][DATA_FD])
            socket_write(fd, "     Data connection open\r\n");
          else if (socket_info[fd][DATA_ADDR])
            socket_write(fd, sprintf("     PORT (%s,%d,%d)\r\n",
                  replace_string(socket_info[fd][DATA_ADDR], ".", ","),
                  socket_info[fd][DATA_PORT]>>8, socket_info[fd][DATA_PORT]
                  & 0xff));
          else
            socket_write(fd, "     No data connection\r\n");
          socket_write(fd, "211 End of status\r\n");
          break;
        }
    case "list":
        if ((sizeof(bits) > 1 && bits[1][0] != '-') || sizeof(bits) == 1) {
          mask |= MASK_L;
          mask |= MASK_A;
        }
    case "nlst":
        CHECK_LOGIN();
        /* Send name list. */
        if ((i = sizeof(bits)) > 1 && bits[1][0] == '-') {
          int j = strlen(bits[1]);
          while (j--) {
            if (bits[1][j] == '-') continue;
                  switch(bits[1][j]) {
                case 'l':
                    mask |= MASK_L;
                    break;
                case 'C':
                    mask |= MASK_C;
                    break;
                case 'F':
                    mask |= MASK_F;
                    break;
                case 'a':
                    mask |= MASK_A;
            }
          }
          if (i == 2)
            bits[1] = ".";
          else
            bits = ({ bits[0] }) + bits[2..i-1];
          }
          if (sizeof(bits) > 1)
          tmp = get_path(fd, bits[1]);
        else
          tmp = socket_info[fd][CWD];
        if (MASTER->valid_read(tmp, socket_info[fd][USER_NAME],
               "read_file"))
          data_conn(fd, ls(tmp, mask), "ls", STRING);
        else
          socket_write(fd, sprintf("550 Permision denied to %s.\r\n", tmp));
        break;
    case "pwd":
    case "xpwd":
        CHECK_LOGIN();
        socket_write(fd, sprintf("257 \"%s\" is the current directory.\r\n",
            socket_info[fd][CWD]));
        break;
    case "cdup":
    case "xcup":
        bits[1] = "..";
    case "cwd":
    case "xcwd":
        CHECK_LOGIN();
    CHECK_PLAYER();
        if (sizeof(bits) > 1)
          tmp = get_path(fd, bits[1]);
        else
          tmp = socket_info[fd][CWD];
        if (MASTER->valid_read(tmp, socket_info[fd][USER_NAME],
          "cwd") || tmp == "/")
        {
          switch(file_size(tmp)) {
            case -2:
#ifdef LOG_CD_SIZE
              log_file("ftpd.log", sprintf("%s CWD %s at %s.\n", UNAME, tmp, 
                ctime(time())));
#endif
              socket_info[fd][CWD] = get_path(fd, tmp);
              socket_write(fd, "250 CWD command successful.\r\n");
              break;
            case -1:
              socket_write(fd, sprintf("550 %s: No such file or directory.\r\n",
                tmp));
              break;
            default:
              socket_write(fd, sprintf("550 %s: Not a directory.\r\n", tmp));
              break;
          }
        } else
          socket_write(fd, sprintf("550 Permission denied to %s.\r\n", tmp));
        break;
    case "quit":
        socket_write(fd, "221 Goodbye.\r\n");
        event(users(), "inform", sprintf("%s quit ftpd", 
            socket_info[fd][USER_NAME]), "ftp");
#ifdef LOG_CONNECT
        log_file("ftpd.log", sprintf("%s logged out at %s.\n", UNAME,
            ctime(time())));
#endif
        socket_close(fd);
        map_delete(socket_info, fd);
        break;
    case "type":
        CHECK_LOGIN();
        CHECK_CMD(1);
        if (bits[1] == "I" || bits [1] == "B") {
          socket_info[fd][TYPE] = BINARY;
          socket_write(fd, "200 Type set to I.\r\n");
        } else
        if (bits[1] == "A") {
          socket_info[fd][TYPE] = STRING;
            socket_write(fd, "200 Type set to A.\r\n");
        } else
            socket_write(fd, 
                    sprintf("504 Type %s not implemented.\r\n",
                    bits[1]));
        break;
    case "abor":
        /* Abort...  Handle this with blue stuff,
         * stops recvs and stors. I guess thats
         * what it is supposed to do. */
        if (socket_info[fd][DATA_FD]) {
          socket_close(socket_info[fd][DATA_FD]);
          map_delete(socket_info, socket_info[fd][DATA_FD]);
          map_delete(socket_info[fd], DATA_FD);
          map_delete(socket_info[fd], DATA_ADDR);
          offset = 0;
          }
        socket_write(fd, "225 ABOR command successful.\r\n");
        break;
    case "syst":
        socket_write(fd, "215 MUD Type: Realms of the Dragon\r\n");
        break;
    case "acct":
    case "smnt":
    case "rein":
    case "pasv":
    case "stru":
    case "mode":
    case "mlfl":
    case "mail":
    case "msnd":
    case "msom":
    case "msam":
    case "mrsq":
    case "mrcp":
    case "stou":
            socket_write(fd, sprintf("502 %s command not implemented.\r\n",
          bits[0]));
        break;
    default:
        socket_write(fd, sprintf("500 '%s': command not understood.\r\n", 
          str));
        break;
    }
}      /* parse_comm */
 
void  in_read_callback(int fd, string str)
{
string *bits;
int   i;

  str = replace(str, 
        ({sprintf("%c", 242), "", "\r", "", sprintf("%c", 255), "",
          sprintf("%c", 244), ""}));
  bits = explode(str, "\n");
  for (i = 0; i < sizeof(bits); i++)
      parse_comm(fd, bits[i]);
}      /* in_read_callback() */
 
void  in_close_callback(int fd)
{
  map_delete(socket_info, fd);
  socket_close(fd);
}      /* in_close_callback() */
 
string  get_path(int fd, string str)
{
  string *arry, *arry1, temp;
  int   i;
 
  if (!str || str == "")
  {
    /* no change of dir */
    return socket_info[fd][CWD];
  }
 
  if (str == "~")
  {
    /* change to home dir */
    return HOME_DIR(socket_info[fd][USER_NAME]);
  }
  else
  {
    if (str[0] == '~')
    {
      if (str[1] == '/')
      {
        sscanf(str, "~%s", temp);
        str = HOME_DIR(socket_info[fd][USER_NAME]) + temp;
      }
      else
      {
      string  name;
 
        if (sscanf(str, "~%s/%s", name, str) != 2)
        {
          name = extract(str, 1);
          str = HOME_DIR(name);
        }
        else {
          /* cheat at this point and just assume they are a wizard. sigh
           * i know i know */
          str = HOME_DIR(name) + "/" + str;
        }
      }
    }
    else
      if (str[0] != '/')
        str = socket_info[fd][CWD] + "/" + str + "/";
  }
  if (str == "/")
    return "/";
  arry = explode(str, "/") - ({ "" });
  for (i = 0; i < sizeof(arry); i++) {
    if (arry[i] == "..")
    {
      if (i < 1)
        return "/";
      if (i == 1)
        arry1 = ({ "." });
      else
        arry1 = arry[0..i - 2];
      if (i + 1 <= sizeof(arry) - 1)
        arry1 += arry[i + 1..sizeof(arry) - 1];
      arry = arry1;
      i -= 2;
    }
    else
      if (arry[i] == ".")
        arry[i] = 0;
  }
  if (arry)
    str = implode(arry, "/");
  else
    str = "";
  return "/" + str;
}      /* get_path() */

string desc_object(mixed o){
  string str;

  if (!o) return "** Null-space **";
  if (!catch(str = (string)o->short()) && str) return str;
  if (!catch(str = (string)o->query_name()) && str) return str;
  return file_name(o);
} /* desc_object() */

string desc_f_object(object o) {
  string str, tmp;

  str = desc_object(o);
  if (o && str != file_name(o)) {
    if (tmp)
      str += " (" + tmp + ")";
    else
      str += " (" + file_name(o) + ")";
  }
  return str;
} /* desc_f_object() */

string get_cfile(string str) {
  if (sscanf(str, "%*s.%*s") != 2)
    str += ".c";
  return str;
} /* get_cfile() */

static void do_update(string name, int fd) {
   string pname, err;
   int i, j;
   object *invent, rsv, env, dup, loaded, ov;
   mixed static_arg, dynamic_arg;

   "room/void"->bingle_bingle();

   rsv = find_object("room/void");  /* RSV = Room Slash Void */
   if (!rsv) { /* Die in horror */
      socket_write(fd, "530 The void is lost!\r\n");
      return;
   }
   name = get_cfile(name);
   ov = find_object(name);
   if (!ov) {
     if(file_size(name) >= 0) {
       if (!(err = catch(name->bing_with_me())))
         socket_write(fd, sprintf("530 Loaded %s.\r\n", name));
       else
         socket_write(fd, sprintf("530 Failed to load %s, error: %s\r\n",
    name, err));
     }
     else
       socket_write(fd, sprintf("530 File %s does not exist.\r\n", name));
     return;
   }
   env = environment(ov);
   invent = all_inventory(ov);

   for (j = 0; j < sizeof(invent); j++) 
     invent[j]->move(rsv);

   pname = file_name(ov);
   if (sscanf(pname, "%s#%*d", pname) != 2) { /* a room ? */
     ov -> dest_me();
     if (ov) ov->dwep();
     if (ov) destruct(ov);
     file_size("/secure/master");
     if (!ov)
       ov = find_object(pname);
     call_other(pname, "??");
     ov = find_object(pname);
   } else {
     loaded = find_object(pname);
     static_arg = ov->query_static_auto_load();
     dynamic_arg = ov->query_dynamic_auto_load();
     if (loaded) loaded->dest_me();
     if (loaded) loaded->dwep();
     if (loaded) destruct(loaded);

     dup = clone_object(pname);
     if (dup && ov) {
       ov -> dest_me();
       if (ov) ov->dwep();
       if (ov) destruct(ov);
       ov = dup;
       if (static_arg)
         ov->init_static_arg(static_arg);
       if (dynamic_arg)
         ov->init_dynamic_arg(dynamic_arg);
     }
   }

   if (!ov) {
     socket_write(fd, "530 I seem to have lost your object.\r\n");
     return;
   }

   for (j = 0; j < sizeof(invent); j++)
     if (invent[j]) invent[j]->move(ov);

   if (env) ov->move(env);
   socket_write(fd, sprintf("530 Updated %s.\r\n", desc_f_object(ov)));
} /* do_update() */ 
 
void  check_connections()
{
  int *bits, i;

  bits = keys(socket_info);
  i = sizeof(bits);
  while (i--)
    if ((socket_info[bits[i]][LAST_DATA]+socket_info[bits[i]][IDLE]) <= time())
    {
      socket_write(bits[i], 
  sprintf("421 Timeout (%d seconds): closing control connection.\r\n",
  socket_info[bits[i]][IDLE]));
      socket_close(bits[i]);
      map_delete(socket_info, bits[i]);
    }
  call_out("check_connections", 5 * 60);
}      /* check_connections() */

int check_dots(mixed arg) {
  return (arg[0] != ".." && arg[0] != ".");
}