635 lines
19 KiB
C
635 lines
19 KiB
C
/* ************************************************************************
|
|
* File: mail.c Part of CircleMUD *
|
|
* Usage: Internal funcs and player spec-procs of mud-mail system *
|
|
* *
|
|
* All rights reserved. See license.doc for complete information. *
|
|
* *
|
|
* Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
|
|
* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. *
|
|
************************************************************************ */
|
|
|
|
/******* MUD MAIL SYSTEM MAIN FILE ***************************************
|
|
|
|
Written by Jeremy Elson (jelson@circlemud.org)
|
|
|
|
*************************************************************************/
|
|
|
|
#include "conf.h"
|
|
#include "sysdep.h"
|
|
|
|
#include "structs.h"
|
|
#include "utils.h"
|
|
#include "comm.h"
|
|
#include "db.h"
|
|
#include "interpreter.h"
|
|
#include "handler.h"
|
|
#include "mail.h"
|
|
|
|
|
|
/* external variables */
|
|
extern int no_mail;
|
|
|
|
/* external functions */
|
|
SPECIAL(postmaster);
|
|
|
|
/* local globals */
|
|
mail_index_type *mail_index = NULL; /* list of recs in the mail file */
|
|
position_list_type *free_list = NULL; /* list of free positions in file */
|
|
long file_end_pos = 0; /* length of file */
|
|
|
|
/* local functions */
|
|
void postmaster_send_mail(struct char_data *ch, struct char_data *mailman, int cmd, char *arg);
|
|
void postmaster_check_mail(struct char_data *ch, struct char_data *mailman, int cmd, char *arg);
|
|
void postmaster_receive_mail(struct char_data *ch, struct char_data *mailman, int cmd, char *arg);
|
|
void push_free_list(long pos);
|
|
long pop_free_list(void);
|
|
void clear_free_list(void);
|
|
mail_index_type *find_char_in_index(long searchee);
|
|
void write_to_file(void *buf, int size, long filepos);
|
|
void read_from_file(void *buf, int size, long filepos);
|
|
void index_mail(long id_to_index, long pos);
|
|
int mail_recip_ok(const char *name);
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
int mail_recip_ok(const char *name)
|
|
{
|
|
struct char_file_u tmp_store;
|
|
struct char_data *victim;
|
|
int ret = FALSE;
|
|
|
|
CREATE(victim, struct char_data, 1);
|
|
clear_char(victim);
|
|
if (load_char(name, &tmp_store) >= 0) {
|
|
store_to_char(&tmp_store, victim);
|
|
char_to_room(victim, 0);
|
|
if (!PLR_FLAGGED(victim, PLR_DELETED))
|
|
ret = TRUE;
|
|
extract_char_final(victim);
|
|
} else
|
|
free(victim);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* void push_free_list(long #1)
|
|
* #1 - What byte offset into the file the block resides.
|
|
*
|
|
* Net effect is to store a list of free blocks in the mail file in a linked
|
|
* list. This is called when people receive their messages and at startup
|
|
* when the list is created.
|
|
*/
|
|
void push_free_list(long pos)
|
|
{
|
|
position_list_type *new_pos;
|
|
|
|
CREATE(new_pos, position_list_type, 1);
|
|
new_pos->position = pos;
|
|
new_pos->next = free_list;
|
|
free_list = new_pos;
|
|
}
|
|
|
|
|
|
/*
|
|
* long pop_free_list(none)
|
|
* Returns the offset of a free block in the mail file.
|
|
*
|
|
* Typically used whenever a person mails a message. The blocks are not
|
|
* guaranteed to be sequential or in any order at all.
|
|
*/
|
|
long pop_free_list(void)
|
|
{
|
|
position_list_type *old_pos;
|
|
long return_value;
|
|
|
|
/*
|
|
* If we don't have any free blocks, we append to the file.
|
|
*/
|
|
if ((old_pos = free_list) == NULL)
|
|
return (file_end_pos);
|
|
|
|
/* Save the offset of the free block. */
|
|
return_value = free_list->position;
|
|
/* Remove this block from the free list. */
|
|
free_list = old_pos->next;
|
|
/* Get rid of the memory the node took. */
|
|
free(old_pos);
|
|
/* Give back the free offset. */
|
|
return (return_value);
|
|
}
|
|
|
|
|
|
void clear_free_list(void)
|
|
{
|
|
while (free_list)
|
|
pop_free_list();
|
|
}
|
|
|
|
|
|
/*
|
|
* main_index_type *find_char_in_index(long #1)
|
|
* #1 - The idnum of the person to look for.
|
|
* Returns a pointer to the mail block found.
|
|
*
|
|
* Finds the first mail block for a specific person based on id number.
|
|
*/
|
|
mail_index_type *find_char_in_index(long searchee)
|
|
{
|
|
mail_index_type *tmp;
|
|
|
|
if (searchee < 0) {
|
|
log("SYSERR: Mail system -- non fatal error #1 (searchee == %ld).", searchee);
|
|
return (NULL);
|
|
}
|
|
for (tmp = mail_index; (tmp && tmp->recipient != searchee); tmp = tmp->next);
|
|
|
|
return (tmp);
|
|
}
|
|
|
|
|
|
/*
|
|
* void write_to_file(void * #1, int #2, long #3)
|
|
* #1 - A pointer to the data to write, usually the 'block' record.
|
|
* #2 - How much to write (because we'll write NUL terminated strings.)
|
|
* #3 - What offset (block position) in the file to write to.
|
|
*
|
|
* Writes a mail block back into the database at the given location.
|
|
*/
|
|
void write_to_file(void *buf, int size, long filepos)
|
|
{
|
|
FILE *mail_file;
|
|
|
|
if (filepos % BLOCK_SIZE) {
|
|
log("SYSERR: Mail system -- fatal error #2!!! (invalid file position %ld)", filepos);
|
|
no_mail = TRUE;
|
|
return;
|
|
}
|
|
if (!(mail_file = fopen(MAIL_FILE, "r+b"))) {
|
|
log("SYSERR: Unable to open mail file '%s'.", MAIL_FILE);
|
|
no_mail = TRUE;
|
|
return;
|
|
}
|
|
fseek(mail_file, filepos, SEEK_SET);
|
|
fwrite(buf, size, 1, mail_file);
|
|
|
|
/* find end of file */
|
|
fseek(mail_file, 0L, SEEK_END);
|
|
file_end_pos = ftell(mail_file);
|
|
fclose(mail_file);
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* void read_from_file(void * #1, int #2, long #3)
|
|
* #1 - A pointer to where we should store the data read.
|
|
* #2 - How large the block we're reading is.
|
|
* #3 - What position in the file to read.
|
|
*
|
|
* This reads a block from the mail database file.
|
|
*/
|
|
void read_from_file(void *buf, int size, long filepos)
|
|
{
|
|
FILE *mail_file;
|
|
|
|
if (filepos % BLOCK_SIZE) {
|
|
log("SYSERR: Mail system -- fatal error #3!!! (invalid filepos read %ld)", filepos);
|
|
no_mail = TRUE;
|
|
return;
|
|
}
|
|
if (!(mail_file = fopen(MAIL_FILE, "r+b"))) {
|
|
log("SYSERR: Unable to open mail file '%s'.", MAIL_FILE);
|
|
no_mail = TRUE;
|
|
return;
|
|
}
|
|
|
|
fseek(mail_file, filepos, SEEK_SET);
|
|
fread(buf, size, 1, mail_file);
|
|
fclose(mail_file);
|
|
return;
|
|
}
|
|
|
|
|
|
void index_mail(long id_to_index, long pos)
|
|
{
|
|
mail_index_type *new_index;
|
|
position_list_type *new_position;
|
|
|
|
if (id_to_index < 0) {
|
|
log("SYSERR: Mail system -- non-fatal error #4. (id_to_index == %ld)", id_to_index);
|
|
return;
|
|
}
|
|
if (!(new_index = find_char_in_index(id_to_index))) {
|
|
/* name not already in index.. add it */
|
|
CREATE(new_index, mail_index_type, 1);
|
|
new_index->recipient = id_to_index;
|
|
new_index->list_start = NULL;
|
|
|
|
/* add to front of list */
|
|
new_index->next = mail_index;
|
|
mail_index = new_index;
|
|
}
|
|
/* now, add this position to front of position list */
|
|
CREATE(new_position, position_list_type, 1);
|
|
new_position->position = pos;
|
|
new_position->next = new_index->list_start;
|
|
new_index->list_start = new_position;
|
|
}
|
|
|
|
|
|
/*
|
|
* int scan_file(none)
|
|
* Returns false if mail file is corrupted or true if everything correct.
|
|
*
|
|
* This is called once during boot-up. It scans through the mail file
|
|
* and indexes all entries currently in the mail file.
|
|
*/
|
|
int scan_file(void)
|
|
{
|
|
FILE *mail_file;
|
|
header_block_type next_block;
|
|
int total_messages = 0, block_num = 0;
|
|
|
|
if (!(mail_file = fopen(MAIL_FILE, "rb"))) {
|
|
log(" Mail file non-existant... creating new file.");
|
|
touch(MAIL_FILE);
|
|
return (1);
|
|
}
|
|
while (fread(&next_block, sizeof(header_block_type), 1, mail_file)) {
|
|
if (next_block.block_type == HEADER_BLOCK) {
|
|
index_mail(next_block.header_data.to, block_num * BLOCK_SIZE);
|
|
total_messages++;
|
|
} else if (next_block.block_type == DELETED_BLOCK)
|
|
push_free_list(block_num * BLOCK_SIZE);
|
|
block_num++;
|
|
}
|
|
|
|
file_end_pos = ftell(mail_file);
|
|
fclose(mail_file);
|
|
log(" %ld bytes read.", file_end_pos);
|
|
if (file_end_pos % BLOCK_SIZE) {
|
|
log("SYSERR: Error booting mail system -- Mail file corrupt!");
|
|
log("SYSERR: Mail disabled!");
|
|
return (0);
|
|
}
|
|
log(" Mail file read -- %d messages.", total_messages);
|
|
return (1);
|
|
} /* end of scan_file */
|
|
|
|
|
|
/*
|
|
* int has_mail(long #1)
|
|
* #1 - id number of the person to check for mail.
|
|
* Returns true or false.
|
|
*
|
|
* A simple little function which tells you if the guy has mail or not.
|
|
*/
|
|
int has_mail(long recipient)
|
|
{
|
|
return (find_char_in_index(recipient) != NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* void store_mail(long #1, long #2, char * #3)
|
|
* #1 - id number of the person to mail to.
|
|
* #2 - id number of the person the mail is from.
|
|
* #3 - The actual message to send.
|
|
*
|
|
* call store_mail to store mail. (hard, huh? :-) ) Pass 3 arguments:
|
|
* who the mail is to (long), who it's from (long), and a pointer to the
|
|
* actual message text (char *).
|
|
*/
|
|
void store_mail(long to, long from, char *message_pointer)
|
|
{
|
|
header_block_type header;
|
|
data_block_type data;
|
|
long last_address, target_address;
|
|
char *msg_txt = message_pointer;
|
|
int bytes_written, total_length = strlen(message_pointer);
|
|
|
|
if ((sizeof(header_block_type) != sizeof(data_block_type)) ||
|
|
(sizeof(header_block_type) != BLOCK_SIZE)) {
|
|
core_dump();
|
|
return;
|
|
}
|
|
|
|
if (from < 0 || to < 0 || !*message_pointer) {
|
|
log("SYSERR: Mail system -- non-fatal error #5. (from == %ld, to == %ld)", from, to);
|
|
return;
|
|
}
|
|
memset((char *) &header, 0, sizeof(header)); /* clear the record */
|
|
header.block_type = HEADER_BLOCK;
|
|
header.header_data.next_block = LAST_BLOCK;
|
|
header.header_data.from = from;
|
|
header.header_data.to = to;
|
|
header.header_data.mail_time = time(0);
|
|
strncpy(header.txt, msg_txt, HEADER_BLOCK_DATASIZE); /* strncpy: OK (h.txt:HEADER_BLOCK_DATASIZE+1) */
|
|
header.txt[HEADER_BLOCK_DATASIZE] = '\0';
|
|
|
|
target_address = pop_free_list(); /* find next free block */
|
|
index_mail(to, target_address); /* add it to mail index in memory */
|
|
write_to_file(&header, BLOCK_SIZE, target_address);
|
|
|
|
if (strlen(msg_txt) <= HEADER_BLOCK_DATASIZE)
|
|
return; /* that was the whole message */
|
|
|
|
bytes_written = HEADER_BLOCK_DATASIZE;
|
|
msg_txt += HEADER_BLOCK_DATASIZE; /* move pointer to next bit of text */
|
|
|
|
/*
|
|
* find the next block address, then rewrite the header to reflect where
|
|
* the next block is.
|
|
*/
|
|
last_address = target_address;
|
|
target_address = pop_free_list();
|
|
header.header_data.next_block = target_address;
|
|
write_to_file(&header, BLOCK_SIZE, last_address);
|
|
|
|
/* now write the current data block */
|
|
memset((char *) &data, 0, sizeof(data)); /* clear the record */
|
|
data.block_type = LAST_BLOCK;
|
|
strncpy(data.txt, msg_txt, DATA_BLOCK_DATASIZE); /* strncpy: OK (d.txt:DATA_BLOCK_DATASIZE+1) */
|
|
data.txt[DATA_BLOCK_DATASIZE] = '\0';
|
|
write_to_file(&data, BLOCK_SIZE, target_address);
|
|
bytes_written += strlen(data.txt);
|
|
msg_txt += strlen(data.txt);
|
|
|
|
/*
|
|
* if, after 1 header block and 1 data block there is STILL part of the
|
|
* message left to write to the file, keep writing the new data blocks and
|
|
* rewriting the old data blocks to reflect where the next block is. Yes,
|
|
* this is kind of a hack, but if the block size is big enough it won't
|
|
* matter anyway. Hopefully, MUD players won't pour their life stories out
|
|
* into the Mud Mail System anyway.
|
|
*
|
|
* Note that the block_type data field in data blocks is either a number >=0,
|
|
* meaning a link to the next block, or LAST_BLOCK flag (-2) meaning the
|
|
* last block in the current message. This works much like DOS' FAT.
|
|
*/
|
|
while (bytes_written < total_length) {
|
|
last_address = target_address;
|
|
target_address = pop_free_list();
|
|
|
|
/* rewrite the previous block to link it to the next */
|
|
data.block_type = target_address;
|
|
write_to_file(&data, BLOCK_SIZE, last_address);
|
|
|
|
/* now write the next block, assuming it's the last. */
|
|
data.block_type = LAST_BLOCK;
|
|
strncpy(data.txt, msg_txt, DATA_BLOCK_DATASIZE); /* strncpy: OK (d.txt:DATA_BLOCK_DATASIZE+1) */
|
|
data.txt[DATA_BLOCK_DATASIZE] = '\0';
|
|
write_to_file(&data, BLOCK_SIZE, target_address);
|
|
|
|
bytes_written += strlen(data.txt);
|
|
msg_txt += strlen(data.txt);
|
|
}
|
|
} /* store mail */
|
|
|
|
|
|
/*
|
|
* char *read_delete(long #1)
|
|
* #1 - The id number of the person we're checking mail for.
|
|
* Returns the message text of the mail received.
|
|
*
|
|
* Retrieves one messsage for a player. The mail is then discarded from
|
|
* the file and the mail index.
|
|
*/
|
|
char *read_delete(long recipient)
|
|
{
|
|
header_block_type header;
|
|
data_block_type data;
|
|
mail_index_type *mail_pointer, *prev_mail;
|
|
position_list_type *position_pointer;
|
|
long mail_address, following_block;
|
|
char *tmstr, buf[MAX_MAIL_SIZE + 256]; /* + header */
|
|
char *from, *to;
|
|
|
|
if (recipient < 0) {
|
|
log("SYSERR: Mail system -- non-fatal error #6. (recipient: %ld)", recipient);
|
|
return (NULL);
|
|
}
|
|
if (!(mail_pointer = find_char_in_index(recipient))) {
|
|
log("SYSERR: Mail system -- post office spec_proc error? Error #7. (invalid character in index)");
|
|
return (NULL);
|
|
}
|
|
if (!(position_pointer = mail_pointer->list_start)) {
|
|
log("SYSERR: Mail system -- non-fatal error #8. (invalid position pointer %p)", position_pointer);
|
|
return (NULL);
|
|
}
|
|
if (!(position_pointer->next)) { /* just 1 entry in list. */
|
|
mail_address = position_pointer->position;
|
|
free(position_pointer);
|
|
|
|
/* now free up the actual name entry */
|
|
if (mail_index == mail_pointer) { /* name is 1st in list */
|
|
mail_index = mail_pointer->next;
|
|
free(mail_pointer);
|
|
} else {
|
|
/* find entry before the one we're going to del */
|
|
for (prev_mail = mail_index;
|
|
prev_mail->next != mail_pointer;
|
|
prev_mail = prev_mail->next);
|
|
prev_mail->next = mail_pointer->next;
|
|
free(mail_pointer);
|
|
}
|
|
} else {
|
|
/* move to next-to-last record */
|
|
while (position_pointer->next->next)
|
|
position_pointer = position_pointer->next;
|
|
mail_address = position_pointer->next->position;
|
|
free(position_pointer->next);
|
|
position_pointer->next = NULL;
|
|
}
|
|
|
|
/* ok, now lets do some readin'! */
|
|
read_from_file(&header, BLOCK_SIZE, mail_address);
|
|
|
|
if (header.block_type != HEADER_BLOCK) {
|
|
log("SYSERR: Oh dear. (Header block %ld != %d)", header.block_type, HEADER_BLOCK);
|
|
no_mail = TRUE;
|
|
log("SYSERR: Mail system disabled! -- Error #9. (Invalid header block.)");
|
|
return (NULL);
|
|
}
|
|
tmstr = asctime(localtime(&header.header_data.mail_time));
|
|
*(tmstr + strlen(tmstr) - 1) = '\0';
|
|
|
|
from = get_name_by_id(header.header_data.from);
|
|
to = get_name_by_id(recipient);
|
|
|
|
snprintf(buf, sizeof(buf),
|
|
" * * * * Midgaard Mail System * * * *\r\n"
|
|
"Date: %s\r\n"
|
|
" To: %s\r\n"
|
|
"From: %s\r\n"
|
|
"\r\n"
|
|
"%s",
|
|
|
|
tmstr,
|
|
to ? to : "Unknown",
|
|
from ? from : "Unknown",
|
|
header.txt
|
|
);
|
|
following_block = header.header_data.next_block;
|
|
|
|
/* mark the block as deleted */
|
|
header.block_type = DELETED_BLOCK;
|
|
write_to_file(&header, BLOCK_SIZE, mail_address);
|
|
push_free_list(mail_address);
|
|
|
|
while (following_block != LAST_BLOCK) {
|
|
read_from_file(&data, BLOCK_SIZE, following_block);
|
|
|
|
strcat(buf, data.txt); /* strcat: OK (data.txt:DATA_BLOCK_DATASIZE < buf:MAX_MAIL_SIZE) */
|
|
mail_address = following_block;
|
|
following_block = data.block_type;
|
|
data.block_type = DELETED_BLOCK;
|
|
write_to_file(&data, BLOCK_SIZE, mail_address);
|
|
push_free_list(mail_address);
|
|
}
|
|
|
|
return strdup(buf);
|
|
}
|
|
|
|
|
|
/****************************************************************
|
|
* Below is the spec_proc for a postmaster using the above *
|
|
* routines. Written by Jeremy Elson (jelson@circlemud.org) *
|
|
****************************************************************/
|
|
|
|
SPECIAL(postmaster)
|
|
{
|
|
if (!ch->desc || IS_NPC(ch))
|
|
return (0); /* so mobs don't get caught here */
|
|
|
|
if (!(CMD_IS("mail") || CMD_IS("check") || CMD_IS("receive")))
|
|
return (0);
|
|
|
|
if (no_mail) {
|
|
send_to_char(ch, "Sorry, the mail system is having technical difficulties.\r\n");
|
|
return (0);
|
|
}
|
|
|
|
if (CMD_IS("mail")) {
|
|
postmaster_send_mail(ch, (struct char_data *)me, cmd, argument);
|
|
return (1);
|
|
} else if (CMD_IS("check")) {
|
|
postmaster_check_mail(ch, (struct char_data *)me, cmd, argument);
|
|
return (1);
|
|
} else if (CMD_IS("receive")) {
|
|
postmaster_receive_mail(ch, (struct char_data *)me, cmd, argument);
|
|
return (1);
|
|
} else
|
|
return (0);
|
|
}
|
|
|
|
|
|
void postmaster_send_mail(struct char_data *ch, struct char_data *mailman,
|
|
int cmd, char *arg)
|
|
{
|
|
long recipient;
|
|
char buf[MAX_INPUT_LENGTH], **mailwrite;
|
|
|
|
if (GET_LEVEL(ch) < MIN_MAIL_LEVEL) {
|
|
snprintf(buf, sizeof(buf), "$n tells you, 'Sorry, you have to be level %d to send mail!'", MIN_MAIL_LEVEL);
|
|
act(buf, FALSE, mailman, 0, ch, TO_VICT);
|
|
return;
|
|
}
|
|
one_argument(arg, buf);
|
|
|
|
if (!*buf) { /* you'll get no argument from me! */
|
|
act("$n tells you, 'You need to specify an addressee!'",
|
|
FALSE, mailman, 0, ch, TO_VICT);
|
|
return;
|
|
}
|
|
if (GET_GOLD(ch) < STAMP_PRICE) {
|
|
snprintf(buf, sizeof(buf), "$n tells you, 'A stamp costs %d coin%s.'\r\n"
|
|
"$n tells you, '...which I see you can't afford.'", STAMP_PRICE,
|
|
STAMP_PRICE == 1 ? "" : "s");
|
|
act(buf, FALSE, mailman, 0, ch, TO_VICT);
|
|
return;
|
|
}
|
|
if ((recipient = get_id_by_name(buf)) < 0 || !mail_recip_ok(buf)) {
|
|
act("$n tells you, 'No one by that name is registered here!'",
|
|
FALSE, mailman, 0, ch, TO_VICT);
|
|
return;
|
|
}
|
|
act("$n starts to write some mail.", TRUE, ch, 0, 0, TO_ROOM);
|
|
snprintf(buf, sizeof(buf), "$n tells you, 'I'll take %d coins for the stamp.'\r\n"
|
|
"$n tells you, 'Write your message, use @ on a new line when done.'",
|
|
STAMP_PRICE);
|
|
|
|
act(buf, FALSE, mailman, 0, ch, TO_VICT);
|
|
GET_GOLD(ch) -= STAMP_PRICE;
|
|
SET_BIT(PLR_FLAGS(ch), PLR_MAILING); /* string_write() sets writing. */
|
|
|
|
/* Start writing! */
|
|
CREATE(mailwrite, char *, 1);
|
|
string_write(ch->desc, mailwrite, MAX_MAIL_SIZE, recipient, NULL);
|
|
}
|
|
|
|
|
|
void postmaster_check_mail(struct char_data *ch, struct char_data *mailman,
|
|
int cmd, char *arg)
|
|
{
|
|
if (has_mail(GET_IDNUM(ch)))
|
|
act("$n tells you, 'You have mail waiting.'", FALSE, mailman, 0, ch, TO_VICT);
|
|
else
|
|
act("$n tells you, 'Sorry, you don't have any mail waiting.'", FALSE, mailman, 0, ch, TO_VICT);
|
|
}
|
|
|
|
|
|
void postmaster_receive_mail(struct char_data *ch, struct char_data *mailman,
|
|
int cmd, char *arg)
|
|
{
|
|
char buf[256];
|
|
struct obj_data *obj;
|
|
|
|
if (!has_mail(GET_IDNUM(ch))) {
|
|
snprintf(buf, sizeof(buf), "$n tells you, 'Sorry, you don't have any mail waiting.'");
|
|
act(buf, FALSE, mailman, 0, ch, TO_VICT);
|
|
return;
|
|
}
|
|
while (has_mail(GET_IDNUM(ch))) {
|
|
obj = create_obj();
|
|
obj->item_number = NOTHING;
|
|
obj->name = strdup("mail paper letter");
|
|
obj->short_description = strdup("a piece of mail");
|
|
obj->description = strdup("Someone has left a piece of mail here.");
|
|
|
|
GET_OBJ_TYPE(obj) = ITEM_NOTE;
|
|
GET_OBJ_WEAR(obj) = ITEM_WEAR_TAKE | ITEM_WEAR_HOLD;
|
|
GET_OBJ_WEIGHT(obj) = 1;
|
|
GET_OBJ_COST(obj) = 30;
|
|
GET_OBJ_RENT(obj) = 10;
|
|
obj->action_description = read_delete(GET_IDNUM(ch));
|
|
|
|
if (obj->action_description == NULL)
|
|
obj->action_description =
|
|
strdup("Mail system error - please report. Error #11.\r\n");
|
|
|
|
obj_to_char(obj, ch);
|
|
|
|
act("$n gives you a piece of mail.", FALSE, mailman, 0, ch, TO_VICT);
|
|
act("$N gives $n a piece of mail.", FALSE, ch, 0, mailman, TO_ROOM);
|
|
}
|
|
}
|
|
|
|
|
|
void free_mail_index(void)
|
|
{
|
|
while (mail_index) {
|
|
mail_index_type *tmp_index = mail_index;
|
|
mail_index = mail_index->next;
|
|
|
|
while (tmp_index->list_start) {
|
|
position_list_type *tmp_list;
|
|
|
|
tmp_list = tmp_index->list_start;
|
|
tmp_index->list_start = tmp_index->list_start->next;
|
|
free(tmp_list);
|
|
}
|
|
free(tmp_index);
|
|
}
|
|
}
|