/* * $Id: note.c,v 1.52 1999/04/16 15:52:20 fjoe Exp $ */ /*************************************************************************** * ANATOLIA 2.1 is copyright 1996-1997 Serdar BULUT, Ibrahim CANPUNAR * * ANATOLIA has been brought to you by ANATOLIA consortium * * Serdar BULUT {Chronos} bulut@rorqual.cc.metu.edu.tr * * Ibrahim Canpunar {Asena} canpunar@rorqual.cc.metu.edu.tr * * Murat BICER {KIO} mbicer@rorqual.cc.metu.edu.tr * * D.Baris ACAR {Powerman} dbacar@rorqual.cc.metu.edu.tr * * By using this code, you have agreed to follow the terms of the * * ANATOLIA license, in the file Anatolia/anatolia.licence * ***************************************************************************/ /*************************************************************************** * 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-1995 Russ Taylor * * ROM has been brought to you by the ROM consortium * * Russ Taylor (rtaylor@pacinfo.com) * * Gabrielle Taylor (gtaylor@pacinfo.com) * * Brian Moore (rom@rom.efn.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 <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <ctype.h> #include "merc.h" #include "db/db.h" /* local procedures */ note_t * new_note (void); void free_note (note_t *note); void load_thread(char *name, note_t **list, int type, time_t free_time); void parse_note(CHAR_DATA *ch, const char *argument, int type); bool hide_note(CHAR_DATA *ch, note_t *pnote); void fwrite_note(FILE *fp, note_t *pnote); note_t *note_list; note_t *idea_list; note_t *penalty_list; note_t *news_list; note_t *changes_list; /* stuff for recyling notes */ note_t *note_free; note_t *new_note() { note_t *note; if (note_free == NULL) note = alloc_perm(sizeof(*note)); else { note = note_free; note_free = note_free->next; } return note; } void free_note(note_t *note) { free_string(note->text ); free_string(note->subject); free_string(note->to_list); free_string(note->date ); free_string(note->sender); note->next = note_free; note_free = note; } int count_spool(CHAR_DATA *ch, note_t *spool) { int count = 0; note_t *pnote; 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; bool found = FALSE; if (IS_NPC(ch)) return; if ((count = count_spool(ch,news_list)) > 0) { found = TRUE; char_printf(ch,"There %s %d new news article%s waiting.\n", count > 1 ? "are" : "is",count, count > 1 ? "s" : str_empty); } if ((count = count_spool(ch,changes_list)) > 0) { found = TRUE; char_printf(ch,"There %s %d change%s waiting to be read.\n", count > 1 ? "are" : "is", count, count > 1 ? "s" : str_empty); } if ((count = count_spool(ch,note_list)) > 0) { found = TRUE; char_printf(ch,"You have %d new note%s waiting.\n", count, count > 1 ? "s" : str_empty); } if ((count = count_spool(ch,idea_list)) > 0) { found = TRUE; char_printf(ch,"You have %d unread idea%s to peruse.\n", count, count > 1 ? "s" : str_empty); } if (IS_TRUSTED(ch,ANGEL) && (count = count_spool(ch,penalty_list)) > 0) { found = TRUE; char_printf(ch,"%d %s been added.\n", count, count > 1 ? "penalties have" : "penalty has"); } if (!found && str_cmp(argument, "login")) char_puts("You have no unread messages.\n", ch); } 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; char *name; note_t *pnote; 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 = dfopen(NOTES_PATH, name, "w")) == NULL) return; for (; pnote; pnote = pnote->next) fwrite_note(fp, pnote); fclose(fp); } 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(char *name, note_t **list, int type, time_t free_time) { FILE *fp; note_t *pnotelast; const char *p; if (!dfexist(NOTES_PATH, name)) return; if ((fp = dfopen(NOTES_PATH, name, "r")) == NULL) return; pnotelast = NULL; for (; ;) { note_t *pnote; char letter; do { letter = getc(fp); if (feof(fp)) { fclose(fp); return; } } while (isspace(letter)); ungetc(letter, fp); pnote = alloc_perm(sizeof(*pnote)); if (str_cmp(p = fread_word(fp), "sender")) break; pnote->sender = fread_string(fp); if (str_cmp(p = fread_word(fp), "date")) break; pnote->date = fread_string(fp); if (str_cmp(p = fread_word(fp), "stamp")) break; pnote->date_stamp = fread_number(fp); if (str_cmp(p = fread_word(fp), "to")) break; pnote->to_list = fread_string(fp); if (str_cmp(p = fread_word(fp), "subject")) break; pnote->subject = fread_string(fp); if (str_cmp(p = 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; } db_error("load_notes", "%s: bad keyword '%s'", name, p); } void append_note(note_t *pnote) { FILE *fp; char *name; note_t **list; note_t *last; 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; last = last->next); last->next = pnote; } if ((fp = dfopen(NOTES_PATH, name, "a")) == NULL) return; fwrite_note(fp, pnote); fclose(fp); } bool is_note_to(CHAR_DATA *ch, note_t *pnote) { clan_t *clan; if (!str_cmp(ch->name, pnote->sender)) return TRUE; if (!str_cmp("all", pnote->to_list)) return TRUE; if (IS_IMMORTAL(ch) && is_name("imm", pnote->to_list)) return TRUE; if ((IS_IMMORTAL(ch) || ch->clan) && is_name("clan", pnote->to_list)) return TRUE; if (is_name_raw(ch->name, pnote->to_list, str_cmp)) return TRUE; if ((clan = clan_lookup(ch->clan)) && is_name_raw(clan->name, pnote->to_list, str_cmp)) return TRUE; return FALSE; } /* * note attach - create note * Returns: TRUE - everything is ok, note attached * FALSE - char is working on different kind of note right now */ bool note_attach(CHAR_DATA *ch, int type) { note_t *pnote; if (ch->pnote) { if (ch->pnote->type != type) { act_puts("You have an unfinished $t in progress.", ch, flag_string(note_types, ch->pnote->type), NULL, TO_CHAR, POS_DEAD); return FALSE; } return TRUE; } pnote = new_note(); pnote->next = NULL; pnote->sender = str_qdup(ch->name); pnote->date = str_empty; pnote->to_list = str_empty; pnote->subject = str_empty; pnote->text = str_empty; pnote->type = type; ch->pnote = pnote; return TRUE; } void note_remove(CHAR_DATA *ch, note_t *pnote, bool delete) { char to_new[MAX_INPUT_LENGTH]; char to_one[MAX_INPUT_LENGTH]; note_t *prev; note_t **list; const char *to_list; if (!delete) { /* 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, sizeof(to_one)); if (to_one[0] != '\0' && str_cmp(ch->name, to_one)) { strnzcat(to_new, sizeof(to_new), " "); strnzcat(to_new, sizeof(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) { bug("Note_remove: pnote not found.", 0); return; } prev->next = pnote->next; } save_notes(pnote->type); free_note(pnote); return; } bool hide_note(CHAR_DATA *ch, note_t *pnote) { time_t last_read; 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_t *pnote) { time_t stamp; 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 print_note(BUFFER *buf, note_t *pnote, int vnum) { buf_printf(buf, "{x[%3d] From: %s, {x%s\n" "{x To : %s\n" "{x Subj: %s\n" "{x%s\n{x", vnum, pnote->sender, pnote->date, pnote->to_list, pnote->subject, pnote->text); } const char * quote_note(note_t *pnote) { const char *p; char *q; char buf[MAX_STRING_LENGTH]; bool need_quote; if (IS_NULLSTR(pnote->text)) return str_dup(str_empty); snprintf(buf, sizeof(buf), "On %s %s {xwrote to %s:\n" "{x\n", pnote->date, pnote->sender, pnote->to_list); q = strchr(buf, '\0'); need_quote = TRUE; for (p = pnote->text; *p && q-buf < sizeof(buf); p++) { if (need_quote) { *q++ = '>'; *q++ = ' '; need_quote = FALSE; } *q++ = *p; if (*p == '\n') need_quote = TRUE; } *q = '\0'; return str_dup(buf); } void parse_note(CHAR_DATA *ch, const char *argument, int type) { char arg[MAX_INPUT_LENGTH]; note_t *pnote; note_t **list; char *list_name; int vnum; int anum; DESCRIPTOR_DATA *d; 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; } argument = one_argument(argument, arg, sizeof(arg)); if (arg[0] == '\0' || !str_prefix(arg, "read")) { bool fAll; BUFFER *output; if (!str_cmp(argument, "all")) { fAll = TRUE; anum = 0; } else if (argument[0] == '\0' || !str_prefix(argument, "next")) { /* read next unread note */ vnum = 0; for (pnote = *list; pnote; pnote = pnote->next) { if (!hide_note(ch, pnote)) { output = buf_new(-1); print_note(output, pnote, vnum); page_to_char(buf_string(output), ch); buf_free(output); update_read(ch, pnote); return; } else if (is_note_to(ch, pnote)) vnum++; } char_puts("You have no unread messages.\n", ch); return; } else if (is_number(argument)) { fAll = FALSE; anum = atoi(argument); } else { char_puts("Read which number?\n", ch); return; } vnum = 0; for (pnote = *list; pnote != NULL; pnote = pnote->next) { if (is_note_to(ch, pnote) && (vnum++ == anum || fAll)) { output = buf_new(-1); print_note(output, pnote, vnum-1); page_to_char(buf_string(output), ch); buf_free(output); if (!fAll) return; } } char_printf(ch,"There aren't that many %s.\n",list_name); return; } if (!str_prefix(arg, "list")) { char buf[MAX_INPUT_LENGTH]; char from[MAX_INPUT_LENGTH]; char to[MAX_INPUT_LENGTH]; # define CHECK_TO (A) # define CHECK_FROM (B) int flags = 0; for (;;) { argument = one_argument(argument, buf, sizeof(buf)); if (!str_cmp(buf, "from")) { argument = one_argument(argument, from, sizeof(from)); if (from[0] == '\0') break; SET_BIT(flags, CHECK_FROM); continue; } if (!str_cmp(buf, "to")) { argument = one_argument(argument, to, sizeof(to)); if (to[0] == '\0') break; SET_BIT(flags, CHECK_TO); continue; } break; } vnum = 0; for (pnote = *list; pnote != NULL; pnote = pnote->next) { if (is_note_to(ch, pnote)) { vnum++; if (IS_SET(flags, CHECK_TO) && (!str_cmp("all", pnote->to_list) || !is_name(to, pnote->to_list))) continue; if (IS_SET(flags, CHECK_FROM) && str_prefix(from, pnote->sender)) continue; char_printf(ch, "[%3d%c] %s: %s\n{x", vnum-1, hide_note(ch, pnote) ? ' ' : 'N', pnote->sender, pnote->subject); } } return; } if (!str_prefix(arg, "remove")) { if (!is_number(argument)) { char_puts("Note remove which number?\n", ch); return; } anum = atoi(argument); vnum = 0; for (pnote = *list; pnote != NULL; pnote = pnote->next) { if (is_note_to(ch, pnote) && vnum++ == anum) { note_remove(ch, pnote, FALSE); char_puts("Ok.\n", ch); return; } } char_printf(ch, "There aren't that many %s.", list_name); return; } if (!str_prefix(arg, "delete") && ch->level >= MAX_LEVEL - 1) { if (!is_number(argument)) { char_puts("Note delete which number?\n", ch); return; } anum = atoi(argument); vnum = 0; for (pnote = *list; pnote != NULL; pnote = pnote->next) { if (is_note_to(ch, pnote) && vnum++ == anum) { note_remove(ch, pnote, TRUE); char_puts("Ok.\n", ch); return; } } char_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))) { char_printf(ch, "You aren't high enough level to write %s.", list_name); return; } if (!str_prefix(arg, "edit")) { if (!note_attach(ch, type)) return; string_append(ch, &ch->pnote->text); return; } if (!str_prefix(arg, "subject")) { if (!note_attach(ch, type)) return; free_string(ch->pnote->subject); ch->pnote->subject = str_dup(argument); char_puts("Ok.\n", ch); return; } if (!str_prefix(arg, "to")) { if (!note_attach(ch, type)) return; free_string(ch->pnote->to_list); ch->pnote->to_list = str_dup(argument); char_puts("Ok.\n", ch); return; } if (!str_prefix(arg, "forward")) { char buf[MAX_INPUT_LENGTH]; argument = one_argument(argument, buf, sizeof(buf)); if (!is_number(buf)) { char_puts("Forward which number?\n", ch); return; } anum = atoi(buf); vnum = 0; for (pnote = *list; pnote; pnote = pnote->next) if (is_note_to(ch, pnote) && vnum++ == anum) break; if (!pnote) { char_printf(ch, "There aren't that many %s.\n", list_name); return; } if (!note_attach(ch, type)) return; free_string(ch->pnote->text); ch->pnote->text = str_printf( "* Forwarded by: %s\n" "* Originally to: %s\n" "* Originally by: %s, %s\n" "\n" "---------- Forwarded message ----------\n" "%s", ch->name, pnote->to_list, pnote->sender, pnote->date, pnote->text); free_string(ch->pnote->subject); ch->pnote->subject = str_qdup(pnote->subject); string_append(ch, &ch->pnote->text); return; } if (!str_prefix(arg, "quote") || !str_prefix(arg, "reply")) { char buf[MAX_INPUT_LENGTH]; argument = one_argument(argument, buf, sizeof(buf)); if (!is_number(buf)) { char_puts("Quote which number?\n", ch); return; } anum = atoi(buf); vnum = 0; for (pnote = *list; pnote; pnote = pnote->next) if (is_note_to(ch, pnote) && vnum++ == anum) break; if (!pnote) { char_printf(ch, "There aren't that many %s.\n", list_name); return; } if (!note_attach(ch, type)) return; free_string(ch->pnote->text); ch->pnote->text = quote_note(pnote); free_string(ch->pnote->subject); ch->pnote->subject = str_qdup(pnote->subject); string_append(ch, &ch->pnote->text); return; } if (!str_prefix(arg, "clear") || !str_prefix(arg, "cancel")) { if (ch->pnote) { free_note(ch->pnote); ch->pnote = NULL; } char_puts("Ok.\n", ch); return; } if (!str_prefix(arg, "show")) { BUFFER *output; if (!ch->pnote) { char_puts("You have no note in progress.\n", ch); return; } if (ch->pnote->type != type) { char_puts("You aren't working on that kind of note.\n",ch); return; } output = buf_new(-1); buf_printf(output, "{xFrom: %s\n" "{xTo : %s\n" "{xSubj: %s\n" "{x%s\n" "{x", ch->pnote->sender, ch->pnote->to_list, ch->pnote->subject, ch->pnote->text); page_to_char(buf_string(output), ch); buf_free(output); return; } if (!str_prefix(arg, "post") || !str_prefix(arg, "send")) { if (ch->pnote == NULL) { char_puts("You have no note in progress.\n", ch); return; } if (ch->pnote->type != type) { char_puts("You aren't working on that kind of note.\n", ch); return; } if (IS_NULLSTR(ch->pnote->to_list)) { char_puts("You need to provide a recipient " "(name, clan name, all, or immortal).\n", ch); return; } if (IS_NULLSTR(ch->pnote->subject)) { char_puts("You need to provide a subject.\n", ch); return; } if (IS_NULLSTR(ch->pnote->text)) { char_puts("You need to provide a text.\n", ch); return; } ch->pnote->next = NULL; ch->pnote->date = str_dup(strtime(current_time)); ch->pnote->date_stamp = current_time; append_note(ch->pnote); /* Show new note message */ for (d = descriptor_list; d; d = d->next) { CHAR_DATA *fch = d->character; if (fch != NULL && fch != ch && is_note_to(fch, ch->pnote) && d->connected == CON_PLAYING) do_unread(fch, "login"); } ch->pnote = NULL; return; } char_puts("You can't do that.\n", ch); } void fwrite_note(FILE *fp, note_t *pnote) { fprintf(fp, "Sender %s~\n", fix_string(pnote->sender)); fprintf(fp, "Date %s~\n", fix_string(pnote->date)); fprintf(fp, "Stamp %ld\n", pnote->date_stamp); fprintf(fp, "To %s~\n", fix_string(pnote->to_list)); fprintf(fp, "Subject %s~\n", fix_string(pnote->subject)); fprintf(fp, "Text\n%s~\n", fix_string(pnote->text)); }