/* * RAM $Id: note.c 47 2008-11-07 22:50:47Z quixadhal $ */ /*************************************************************************** * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. * * * * Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * * * In order to use any part of this Merc Diku Mud, you must comply with * * both the original Diku license in 'license.doc' as well the Merc * * license in 'license.txt'. In particular, you may not remove either of * * these copyright notices. * * * * Much time and thought has gone into this software and you are * * benefitting. We hope that you share your changes too. What goes * * around, comes around. * ***************************************************************************/ /*************************************************************************** * ROM 2.4 is copyright 1993-1998 Russ Taylor * * ROM has been brought to you by the ROM consortium * * Russ Taylor (rtaylor@hypercube.org) * * Gabrielle Taylor (gtaylor@hypercube.org) * * Brian Moore (zump@rom.org) * * By using this code, you have agreed to follow the terms of the * * ROM license, in the file Rom24/doc/rom.license * ***************************************************************************/ #include <sys/types.h> #include <sys/time.h> #include <ctype.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <errno.h> #include "merc.h" #include "strings.h" #include "db.h" #include "interp.h" #include "tables.h" NOTE_DATA *note_list = NULL; NOTE_DATA *idea_list = NULL; NOTE_DATA *penalty_list = NULL; NOTE_DATA *news_list = NULL; NOTE_DATA *changes_list = NULL; NOTE_DATA *note_free = NULL; NOTE_DATA *new_note( void ) { NOTE_DATA *note = NULL; if ( note_free == NULL ) note = ( NOTE_DATA * ) alloc_perm( sizeof( *note ) ); else { note = note_free; note_free = note_free->next; } VALIDATE( note ); return note; } void free_note( NOTE_DATA *note ) { if ( !IS_VALID( note ) ) return; free_string( note->text ); free_string( note->subject ); free_string( note->to_list ); free_string( note->date ); free_string( note->sender ); INVALIDATE( note ); note->next = note_free; note_free = note; } int count_spool( CHAR_DATA *ch, NOTE_DATA *spool ) { int count = 0; NOTE_DATA *pnote = NULL; for ( pnote = spool; pnote != NULL; pnote = pnote->next ) if ( !hide_note( ch, pnote ) ) count++; return count; } void do_unread( CHAR_DATA *ch, const char *argument ) { int count = 0; bool found = false; if ( IS_NPC( ch ) ) return; if ( ( count = count_spool( ch, news_list ) ) > 0 ) { found = true; ch_printf( ch, "There %s %d new news article%s waiting.\r\n", count > 1 ? "are" : "is", count, count > 1 ? "s" : "" ); } if ( ( count = count_spool( ch, changes_list ) ) > 0 ) { found = true; ch_printf( ch, "There %s %d change%s waiting to be read.\r\n", count > 1 ? "are" : "is", count, count > 1 ? "s" : "" ); } if ( ( count = count_spool( ch, note_list ) ) > 0 ) { found = true; ch_printf( ch, "You have %d new note%s waiting.\r\n", count, count > 1 ? "s" : "" ); } if ( ( count = count_spool( ch, idea_list ) ) > 0 ) { found = true; ch_printf( ch, "You have %d unread idea%s to peruse.\r\n", count, count > 1 ? "s" : "" ); } if ( IS_TRUSTED( ch, ANGEL ) && ( count = count_spool( ch, penalty_list ) ) > 0 ) { found = true; ch_printf( ch, "%d %s been added.\r\n", count, count > 1 ? "penalties have" : "penalty has" ); } if ( !found ) ch_printf( ch, "You have no unread notes.\r\n" ); } void do_note( CHAR_DATA *ch, const char *argument ) { parse_note( ch, argument, NOTE_NOTE ); } void do_idea( CHAR_DATA *ch, const char *argument ) { parse_note( ch, argument, NOTE_IDEA ); } void do_penalty( CHAR_DATA *ch, const char *argument ) { parse_note( ch, argument, NOTE_PENALTY ); } void do_news( CHAR_DATA *ch, const char *argument ) { parse_note( ch, argument, NOTE_NEWS ); } void do_changes( CHAR_DATA *ch, const char *argument ) { parse_note( ch, argument, NOTE_CHANGES ); } void save_notes( int type ) { FILE *fp = NULL; const char *name = NULL; NOTE_DATA *pnote = NULL; switch ( type ) { default: return; case NOTE_NOTE: name = NOTE_FILE; pnote = note_list; break; case NOTE_IDEA: name = IDEA_FILE; pnote = idea_list; break; case NOTE_PENALTY: name = PENALTY_FILE; pnote = penalty_list; break; case NOTE_NEWS: name = NEWS_FILE; pnote = news_list; break; case NOTE_CHANGES: name = CHANGES_FILE; pnote = changes_list; break; } if ( ( fp = fopen( name, "w" ) ) == NULL ) { char *e = strerror( errno ); log_error( "fopen: %s: %s", name, e ); } else { for ( ; pnote != NULL; pnote = pnote->next ) { fprintf( fp, "Sender %s~\n", pnote->sender ); fprintf( fp, "Date %s~\n", pnote->date ); fprintf( fp, "Stamp %ld\n", pnote->date_stamp ); fprintf( fp, "To %s~\n", pnote->to_list ); fprintf( fp, "Subject %s~\n", pnote->subject ); fprintf( fp, "Text\n%s~\n", pnote->text ); } fclose( fp ); return; } } void load_notes( void ) { load_thread( NOTE_FILE, ¬e_list, NOTE_NOTE, 14 * 24 * 60 * 60 ); load_thread( IDEA_FILE, &idea_list, NOTE_IDEA, 28 * 24 * 60 * 60 ); load_thread( PENALTY_FILE, &penalty_list, NOTE_PENALTY, 0 ); load_thread( NEWS_FILE, &news_list, NOTE_NEWS, 0 ); load_thread( CHANGES_FILE, &changes_list, NOTE_CHANGES, 0 ); } void load_thread( const char *name, NOTE_DATA **list, int type, time_t free_time ) { FILE *fp = NULL; NOTE_DATA *pnotelast = NULL; if ( ( fp = fopen( name, "r" ) ) == NULL ) return; pnotelast = NULL; for ( ;; ) { NOTE_DATA *pnote; char letter; do { letter = getc( fp ); if ( feof( fp ) ) { fclose( fp ); return; } } while ( isspace( letter ) ); ungetc( letter, fp ); pnote = ( NOTE_DATA * ) alloc_perm( sizeof( *pnote ) ); if ( str_cmp( fread_word( fp ), "sender" ) ) break; pnote->sender = fread_string( fp ); if ( str_cmp( fread_word( fp ), "date" ) ) break; pnote->date = fread_string( fp ); if ( str_cmp( fread_word( fp ), "stamp" ) ) break; pnote->date_stamp = fread_number( fp ); if ( str_cmp( fread_word( fp ), "to" ) ) break; pnote->to_list = fread_string( fp ); if ( str_cmp( fread_word( fp ), "subject" ) ) break; pnote->subject = fread_string( fp ); if ( str_cmp( fread_word( fp ), "text" ) ) break; pnote->text = fread_string( fp ); if ( free_time && pnote->date_stamp < current_time - free_time ) { free_note( pnote ); continue; } pnote->type = type; if ( *list == NULL ) *list = pnote; else pnotelast->next = pnote; pnotelast = pnote; } strcpy( strArea, NOTE_FILE ); fpArea = fp; proper_exit( MUD_HALT, "Load_notes: bad key word." ); return; } void append_note( NOTE_DATA *pnote ) { FILE *fp = NULL; const char *name = NULL; NOTE_DATA **list = NULL; NOTE_DATA *last = NULL; switch ( pnote->type ) { default: return; case NOTE_NOTE: name = NOTE_FILE; list = ¬e_list; break; case NOTE_IDEA: name = IDEA_FILE; list = &idea_list; break; case NOTE_PENALTY: name = PENALTY_FILE; list = &penalty_list; break; case NOTE_NEWS: name = NEWS_FILE; list = &news_list; break; case NOTE_CHANGES: name = CHANGES_FILE; list = &changes_list; break; } if ( *list == NULL ) *list = pnote; else { for ( last = *list; last->next != NULL; last = last->next ); last->next = pnote; } if ( ( fp = fopen( name, "a" ) ) == NULL ) { char *e = strerror( errno ); log_error( "fopen: %s: %s", name, e ); } else { fprintf( fp, "Sender %s~\n", pnote->sender ); fprintf( fp, "Date %s~\n", pnote->date ); fprintf( fp, "Stamp %ld\n", pnote->date_stamp ); fprintf( fp, "To %s~\n", pnote->to_list ); fprintf( fp, "Subject %s~\n", pnote->subject ); fprintf( fp, "Text\n%s~\n", pnote->text ); fclose( fp ); } } bool is_note_to( CHAR_DATA *ch, NOTE_DATA *pnote ) { if ( !str_cmp( ch->name, pnote->sender ) ) return true; if ( is_exact_name( "all", pnote->to_list ) ) return true; if ( IS_IMMORTAL( ch ) && is_exact_name( "immortal", pnote->to_list ) ) return true; if ( ch->clan && is_exact_name( clan_table[ch->clan].name, pnote->to_list ) ) return true; if ( is_exact_name( ch->name, pnote->to_list ) ) return true; return false; } void note_attach( CHAR_DATA *ch, int type ) { NOTE_DATA *pnote = NULL; if ( ch->pnote != NULL ) return; pnote = new_note( ); pnote->next = NULL; pnote->sender = str_dup( ch->name ); pnote->date = str_dup( "" ); pnote->to_list = str_dup( "" ); pnote->subject = str_dup( "" ); pnote->text = str_dup( "" ); pnote->type = type; ch->pnote = pnote; return; } void note_remove( CHAR_DATA *ch, NOTE_DATA *pnote, bool idelete ) { char to_new[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0"; char to_one[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0"; NOTE_DATA *prev = NULL; NOTE_DATA **list = NULL; const char *to_list = NULL; if ( !idelete ) { /* * make a new list */ to_new[0] = '\0'; to_list = pnote->to_list; while ( *to_list != '\0' ) { to_list = one_argument( to_list, to_one ); if ( to_one[0] != '\0' && str_cmp( ch->name, to_one ) ) { strcat( to_new, " " ); strcat( to_new, to_one ); } } /* * Just a simple recipient removal? */ if ( str_cmp( ch->name, pnote->sender ) && to_new[0] != '\0' ) { free_string( pnote->to_list ); pnote->to_list = str_dup( to_new + 1 ); return; } } /* * nuke the whole note */ switch ( pnote->type ) { default: return; case NOTE_NOTE: list = ¬e_list; break; case NOTE_IDEA: list = &idea_list; break; case NOTE_PENALTY: list = &penalty_list; break; case NOTE_NEWS: list = &news_list; break; case NOTE_CHANGES: list = &changes_list; break; } /* * Remove note from linked list. */ if ( pnote == *list ) { *list = pnote->next; } else { for ( prev = *list; prev != NULL; prev = prev->next ) { if ( prev->next == pnote ) break; } if ( prev == NULL ) { log_error( "%s", "Pnote not found" ); return; } prev->next = pnote->next; } save_notes( pnote->type ); free_note( pnote ); return; } bool hide_note( CHAR_DATA *ch, NOTE_DATA *pnote ) { time_t last_read = 0; if ( IS_NPC( ch ) ) return true; switch ( pnote->type ) { default: return true; case NOTE_NOTE: last_read = ch->pcdata->last_note; break; case NOTE_IDEA: last_read = ch->pcdata->last_idea; break; case NOTE_PENALTY: last_read = ch->pcdata->last_penalty; break; case NOTE_NEWS: last_read = ch->pcdata->last_news; break; case NOTE_CHANGES: last_read = ch->pcdata->last_changes; break; } if ( pnote->date_stamp <= last_read ) return true; if ( !str_cmp( ch->name, pnote->sender ) ) return true; if ( !is_note_to( ch, pnote ) ) return true; return false; } void update_read( CHAR_DATA *ch, NOTE_DATA *pnote ) { time_t stamp = 0; if ( IS_NPC( ch ) ) return; stamp = pnote->date_stamp; switch ( pnote->type ) { default: return; case NOTE_NOTE: ch->pcdata->last_note = UMAX( ch->pcdata->last_note, stamp ); break; case NOTE_IDEA: ch->pcdata->last_idea = UMAX( ch->pcdata->last_idea, stamp ); break; case NOTE_PENALTY: ch->pcdata->last_penalty = UMAX( ch->pcdata->last_penalty, stamp ); break; case NOTE_NEWS: ch->pcdata->last_news = UMAX( ch->pcdata->last_news, stamp ); break; case NOTE_CHANGES: ch->pcdata->last_changes = UMAX( ch->pcdata->last_changes, stamp ); break; } } void parse_note( CHAR_DATA *ch, const char *argument, int type ) { BUFFER *buffer = NULL; char buf[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0"; char arg[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0"; NOTE_DATA *pnote = NULL; NOTE_DATA **list = NULL; const char *list_name = NULL; int vnum = 0; int anum = 0; char local_argument[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0"; const char *lap = local_argument; if ( IS_NPC( ch ) ) return; switch ( type ) { default: return; case NOTE_NOTE: list = ¬e_list; list_name = "notes"; break; case NOTE_IDEA: list = &idea_list; list_name = "ideas"; break; case NOTE_PENALTY: list = &penalty_list; list_name = "penalties"; break; case NOTE_NEWS: list = &news_list; list_name = "news"; break; case NOTE_CHANGES: list = &changes_list; list_name = "changes"; break; } strcpy( local_argument, argument ); smash_tilde( local_argument ); /* I moved smash_tilde above the * call to one_argument */ lap = one_argument( lap, arg ); if ( arg[0] == '\0' || !str_prefix( arg, "read" ) ) { bool fAll; if ( !str_cmp( lap, "all" ) ) { fAll = true; anum = 0; } else if ( lap[0] == '\0' || !str_prefix( lap, "next" ) ) /* * read next unread note */ { vnum = 0; for ( pnote = *list; pnote != NULL; pnote = pnote->next ) { if ( !hide_note( ch, pnote ) ) { page_printf( ch, "[%3d] %s: %s\r\n%s\r\nTo: %s\r\n%s", vnum, pnote->sender, pnote->subject, pnote->date, pnote->to_list, pnote->text ); update_read( ch, pnote ); return; } else if ( is_note_to( ch, pnote ) ) vnum++; } ch_printf( ch, "You have no unread %s.\r\n", list_name ); return; } else if ( is_number( lap ) ) { fAll = false; anum = atoi( lap ); } else { ch_printf( ch, "Read which number?\r\n" ); return; } vnum = 0; for ( pnote = *list; pnote != NULL; pnote = pnote->next ) { if ( is_note_to( ch, pnote ) && ( vnum++ == anum || fAll ) ) { page_printf( ch, "[%3d] %s: %s\r\n%s\r\nTo: %s\r\n%s", vnum - 1, pnote->sender, pnote->subject, pnote->date, pnote->to_list, pnote->text ); update_read( ch, pnote ); return; } } ch_printf( ch, "There aren't that many %s.\r\n", list_name ); return; } if ( !str_prefix( arg, "list" ) ) { vnum = 0; for ( pnote = *list; pnote != NULL; pnote = pnote->next ) { if ( is_note_to( ch, pnote ) ) { ch_printf( ch, "[%3d%s] %s: %s\r\n", vnum, hide_note( ch, pnote ) ? " " : "N", pnote->sender, pnote->subject ); vnum++; } } if ( !vnum ) { switch ( type ) { case NOTE_NOTE: ch_printf( ch, "There are no notes for you.\r\n" ); break; case NOTE_IDEA: ch_printf( ch, "There are no ideas for you.\r\n" ); break; case NOTE_PENALTY: ch_printf( ch, "There are no penalties for you.\r\n" ); break; case NOTE_NEWS: ch_printf( ch, "There is no news for you.\r\n" ); break; case NOTE_CHANGES: ch_printf( ch, "There are no changes for you.\r\n" ); break; } } return; } if ( !str_prefix( arg, "remove" ) ) { if ( !is_number( lap ) ) { ch_printf( ch, "Note remove which number?\r\n" ); return; } anum = atoi( lap ); vnum = 0; for ( pnote = *list; pnote != NULL; pnote = pnote->next ) { if ( is_note_to( ch, pnote ) && vnum++ == anum ) { note_remove( ch, pnote, false ); ch_printf( ch, "Ok.\r\n" ); return; } } ch_printf( ch, "There aren't that many %s.", list_name ); return; } if ( !str_prefix( arg, "delete" ) && get_trust( ch ) >= MAX_LEVEL - 1 ) { if ( !is_number( lap ) ) { ch_printf( ch, "Note delete which number?\r\n" ); return; } anum = atoi( lap ); vnum = 0; for ( pnote = *list; pnote != NULL; pnote = pnote->next ) { if ( is_note_to( ch, pnote ) && vnum++ == anum ) { note_remove( ch, pnote, true ); ch_printf( ch, "Ok.\r\n" ); return; } } ch_printf( ch, "There aren't that many %s.", list_name ); return; } if ( !str_prefix( arg, "catchup" ) ) { switch ( type ) { case NOTE_NOTE: ch->pcdata->last_note = current_time; break; case NOTE_IDEA: ch->pcdata->last_idea = current_time; break; case NOTE_PENALTY: ch->pcdata->last_penalty = current_time; break; case NOTE_NEWS: ch->pcdata->last_news = current_time; break; case NOTE_CHANGES: ch->pcdata->last_changes = current_time; break; } return; } /* * below this point only certain people can edit notes */ if ( ( type == NOTE_NEWS && !IS_TRUSTED( ch, ANGEL ) ) || ( type == NOTE_CHANGES && !IS_TRUSTED( ch, CREATOR ) ) ) { ch_printf( ch, "You aren't high enough level to write %s.", list_name ); return; } if ( !str_cmp( arg, "+" ) ) { note_attach( ch, type ); if ( ch->pnote->type != type ) { ch_printf( ch, "You already have a different note in progress.\r\n" ); return; } if ( strlen( ch->pnote->text ) + strlen( lap ) >= 4096 ) { ch_printf( ch, "Note too long.\r\n" ); return; } buffer = new_buf( ); add_buf( buffer, ch->pnote->text ); add_buf( buffer, lap ); add_buf( buffer, "\r\n" ); free_string( ch->pnote->text ); ch->pnote->text = str_dup( buf_string( buffer ) ); free_buf( buffer ); ch_printf( ch, "Ok.\r\n" ); return; } if ( !str_cmp( arg, "-" ) ) { int len; bool found = false; note_attach( ch, type ); if ( ch->pnote->type != type ) { ch_printf( ch, "You already have a different note in progress.\r\n" ); return; } if ( ch->pnote->text == NULL || ch->pnote->text[0] == '\0' ) { ch_printf( ch, "No lines left to remove.\r\n" ); return; } strcpy( buf, ch->pnote->text ); for ( len = strlen( buf ); len > 0; len-- ) { if ( buf[len] == '\r' ) { if ( !found ) /* back it up */ { if ( len > 0 ) len--; found = true; } else /* found the second one */ { buf[len + 1] = '\0'; free_string( ch->pnote->text ); ch->pnote->text = str_dup( buf ); return; } } } buf[0] = '\0'; free_string( ch->pnote->text ); ch->pnote->text = str_dup( buf ); return; } if ( !str_prefix( arg, "subject" ) ) { note_attach( ch, type ); if ( ch->pnote->type != type ) { ch_printf( ch, "You already have a different note in progress.\r\n" ); return; } free_string( ch->pnote->subject ); ch->pnote->subject = str_dup( lap ); ch_printf( ch, "Ok.\r\n" ); return; } if ( !str_prefix( arg, "to" ) ) { note_attach( ch, type ); if ( ch->pnote->type != type ) { ch_printf( ch, "You already have a different note in progress.\r\n" ); return; } free_string( ch->pnote->to_list ); ch->pnote->to_list = str_dup( lap ); ch_printf( ch, "Ok.\r\n" ); return; } if ( !str_prefix( arg, "clear" ) ) { if ( ch->pnote != NULL ) { free_note( ch->pnote ); ch->pnote = NULL; } ch_printf( ch, "Ok.\r\n" ); return; } if ( !str_prefix( arg, "show" ) ) { if ( ch->pnote == NULL ) { ch_printf( ch, "You have no note in progress.\r\n" ); return; } if ( ch->pnote->type != type ) { ch_printf( ch, "You aren't working on that kind of note.\r\n" ); return; } ch_printf( ch, "%s: %s\r\nTo: %s\r\n%s", ch->pnote->sender, ch->pnote->subject, ch->pnote->to_list, ch->pnote->text ); return; } if ( !str_prefix( arg, "post" ) || !str_prefix( arg, "send" ) ) { char *strtime; if ( ch->pnote == NULL ) { ch_printf( ch, "You have no note in progress.\r\n" ); return; } if ( ch->pnote->type != type ) { ch_printf( ch, "You aren't working on that kind of note.\r\n" ); return; } if ( !str_cmp( ch->pnote->to_list, "" ) ) { ch_printf( ch, "You need to provide a recipient (name, all, or immortal).\r\n" ); return; } if ( !str_cmp( ch->pnote->subject, "" ) ) { ch_printf( ch, "You need to provide a subject.\r\n" ); return; } ch->pnote->next = NULL; strtime = ctime( ¤t_time ); strtime[strlen( strtime ) - 1] = '\0'; ch->pnote->date = str_dup( strtime ); ch->pnote->date_stamp = current_time; append_note( ch->pnote ); ch->pnote = NULL; return; } ch_printf( ch, "You can't do that.\r\n" ); return; }