/* * mail.c */ #include <stdlib.h> #include <ctype.h> #include <sys/time.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <signal.h> #include <memory.h> #include <sys/types.h> #include <sys/time.h> #include "fix.h" #include "config.h" #include "player.h" /* externs */ extern char *first_char(), *next_space(); extern char *store_string(), *store_int(), *store_word(); extern char *get_string(), *get_int(), *get_word(), *end_string(); extern saved_player *find_saved_player(); extern struct command mail_list[], news_list[]; extern player *find_player_global(), *find_player_absolute_quiet(); extern void pager(player *, char *, int); extern void lower_case(char *); /* interns */ note *n_hash[NOTE_HASH_SIZE]; int nhash_update[NOTE_HASH_SIZE]; int unique = 1; int news_start = 0, news_count = 0; void unlink_mail(); /* find note in hash */ note *find_note(int id) { note *scan; if (!id) return 0; for (scan = n_hash[id % NOTE_HASH_SIZE]; scan; scan = scan->hash_next) if (scan->id == id) return scan; return 0; } /* create a new note */ note *create_note() { note *n; int num; n = (note *) MALLOC(sizeof(note)); memset(n, 0, sizeof(note)); n->flags = NOT_READY; n->date = time(0); n->next_sent = 0; n->text.where = 0; n->text.length = 0; strcpy(n->header, "DEFUNCT"); strcpy(n->name, "system"); n->id = unique++; unique &= 65535; num = (n->id) % NOTE_HASH_SIZE; n->hash_next = n_hash[num]; n_hash[num] = n; nhash_update[num] = 1; return n; } /* remove a note */ void remove_note(note * n) { note *scan, *prev; int num; if (n->text.where) FREE(n->text.where); num = (n->id) % NOTE_HASH_SIZE; scan = n_hash[num]; if (scan == n) n_hash[num] = n->hash_next; else { do { prev = scan; scan = scan->hash_next; } while (scan != n); prev->hash_next = n->hash_next; } FREE(n); nhash_update[num] = 1; } /* remove various types of note */ void remove_any_note(note * n) { saved_player *sp; note *scan = 0; char *oldstack; int *change; oldstack = stack; if (n->flags & NEWS_ARTICLE) { scan = find_note(news_start); change = &news_start; while (scan && scan != n) { change = &(scan->next_sent); scan = find_note(scan->next_sent); } if (scan == n) news_count--; } else { strcpy(stack, n->name); lower_case(stack); stack = end_string(stack); sp = find_saved_player(oldstack); if (!sp) { log("error", "Bad owner name in mail(1)"); log("error", oldstack); unlink_mail(find_note(n->next_sent)); scan = 0; } else { change = &(sp->mail_sent); scan = find_note(sp->mail_sent); while (scan && scan != n) { change = &(scan->next_sent); scan = find_note(scan->next_sent); } } } if (scan == n) (*change) = n->next_sent; remove_note(n); stack = oldstack; } /* dump one note onto the stack */ int save_note(note * d) { if (d->flags & NOT_READY) return 0; stack = store_int(stack, d->id); stack = store_int(stack, d->flags); stack = store_int(stack, d->date); stack = store_int(stack, d->read_count); stack = store_int(stack, d->next_sent); stack = store_string(stack, d->header); stack = store_int(stack, d->text.length); memcpy(stack, d->text.where, d->text.length); stack += d->text.length; stack = store_string(stack, d->name); return 1; } /* sync one hash bucket to disk */ void sync_note_hash(int number) { char *oldstack, name[MAX_NAME + 2]; note *scan, *check; int length, count = 0, fd, t; saved_player *sp; oldstack = stack; if (sys_flags & VERBOSE) { sprintf(stack, "Syncing note hash %d.", number); stack = end_string(stack); log("sync", oldstack); stack = oldstack; } t = time(0); for (scan = n_hash[number]; scan; scan = check) { if (scan->flags & NEWS_ARTICLE) { strcpy(name, scan->name); lower_case(name); sp = find_saved_player(name); if ((t - (scan->date)) > NEWS_TIMEOUT) { if (!(sp && sp->residency & ADMIN)) { check = scan->hash_next; remove_any_note(scan); /* scan=n_hash[number]; */ } } } else if ((t - (scan->date)) > MAIL_TIMEOUT) { check = scan->hash_next; remove_any_note(scan); /* scan=n_hash[number]; */ } check = scan->hash_next; } stack = store_int(stack, 0); for (scan = n_hash[number]; scan; scan = scan->hash_next) count+=save_note(scan); store_int(oldstack, count); length = (int) stack - (int) oldstack; #ifdef PC sprintf(stack, "files\\notes\\hash%d", number); fd = open(stack, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY); #else sprintf(stack, "files/notes/hash%d", number); fd = open(stack, O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, S_IRUSR | S_IWUSR); #endif if (fd < 0) handle_error("Failed to open note file."); if (write(fd, oldstack, length) < 0) handle_error("Failed to write note file."); close(fd); nhash_update[number] = 0; stack = oldstack; } /* throw all the notes to disk */ void sync_notes(int background) { int n, fd; struct itimerval new; char *oldstack; oldstack = stack; if (background && fork()) return; if (sys_flags & VERBOSE || sys_flags & PANIC) log("sync", "Dumping notes to disk"); for (n = 0; n < NOTE_HASH_SIZE; n++) sync_note_hash(n); if (sys_flags & VERBOSE || sys_flags & PANIC) log("sync", "Note dump completed"); #ifdef PC fd = open("files\\notes\\track", O_CREAT | O_WRONLY | O_TRUNC | O_BINARY); #else fd = open("files/notes/track", O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, S_IRUSR | S_IWUSR); #endif if (fd < 0) handle_error("Failed to open track file."); stack = store_int(stack, unique); stack = store_int(stack, news_start); stack = store_int(stack, news_count); if (write(fd, oldstack, 12) < 0) handle_error("Failed to write track file."); close(fd); stack = oldstack; if (background) exit(0); } /* su command to dump all or single notes to disk */ void dump_com(player * p, char *str) { int num; char *oldstack; oldstack = stack; if (!isdigit(*str)) { if (p!=NULL) tell_player(p, " Dumping all notes to disk.\n"); sync_notes(1); return; } num = atoi(str); if (num < 0 || num >= NOTE_HASH_SIZE) { if (p!=NULL) tell_player(p, " Argument not in hash range !\n"); return; } sprintf(stack, " Dumping hash %d to disk.\n", num); stack = end_string(stack); if (p!=NULL) tell_player(p, oldstack); sync_note_hash(num); stack = oldstack; } /* throw away a hash of notes */ void discard_hash(int num) { note *scan, *next; for (scan = n_hash[num]; scan; scan = next) { next = scan->hash_next; if (scan->text.where) FREE(scan->text.where); FREE(scan); } } /* extracts one note into the hash list */ char *extract_note(char *where) { int num; note *d; d = (note *) MALLOC(sizeof(note)); memset(d, 0, sizeof(note)); where = get_int(&d->id, where); where = get_int(&d->flags, where); where = get_int(&d->date, where); where = get_int(&d->read_count, where); where = get_int(&d->next_sent, where); where = get_string(d->header, where); where = get_int(&d->text.length, where); d->text.where = (char *) MALLOC(d->text.length); memcpy(d->text.where, where, d->text.length); where += d->text.length; where = get_string(d->name, where); num = (d->id) % NOTE_HASH_SIZE; if (n_hash[num]) d->hash_next = n_hash[num]; else d->hash_next = 0; n_hash[num] = d; return where; } /* * load all notes from disk this should be changed for arbitary hashes */ void init_notes() { int n, length, fd, count; char *oldstack, *where, *scan; oldstack = stack; if (sys_flags & VERBOSE || sys_flags & PANIC) log("boot", "Loading notes from disk"); #ifdef PC fd = open("files\\notes\\track", O_RDONLY | O_BINARY); #else fd = open("files/notes/track", O_RDONLY | O_NDELAY); #endif if (fd < 0) { sprintf(oldstack, "Failed to load track file"); stack = end_string(oldstack); log("error", oldstack); unique = 1; news_start = 0; stack = oldstack; } else { if (read(fd, oldstack, 12) < 0) handle_error("Can't read track file."); stack = get_int(&unique, stack); stack = get_int(&news_start, stack); stack = get_int(&news_count, stack); close(fd); stack = oldstack; } for (n = 0; n < NOTE_HASH_SIZE; n++) { discard_hash(n); nhash_update[n] = 0; #ifdef PC sprintf(oldstack, "files\\notes\\hash%d", n); fd = open(oldstack, O_RDONLY | O_BINARY); #else sprintf(oldstack, "files/notes/hash%d", n); fd = open(oldstack, O_RDONLY | O_NDELAY); #endif if (fd < 0) { sprintf(oldstack, "Failed to load note hash%d", n); stack = end_string(oldstack); log("error", oldstack); stack = oldstack; } else { length = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); if (length) { where = (char *) MALLOC(length); if (read(fd, where, length) < 0) handle_error("Can't read note file."); scan = get_int(&count, where); for (; count; count--) scan = extract_note(scan); FREE(where); } close(fd); } } stack = oldstack; } /* store info for a player save */ void construct_mail_save(saved_player * sp) { int count = 0, *scan; char *oldstack; stack = store_int(stack, sp->mail_sent); if (!(sp->mail_received)) stack = store_int(stack, 0); else { oldstack = stack; stack = store_int(oldstack, 0); for (scan = sp->mail_received; *scan; scan++, count++) stack = store_int(stack, *scan); store_int(oldstack, count); } } /* get info back from a player save */ char *retrieve_mail_data(saved_player * sp, char *where) { int count = 0, *fill; where = get_int(&sp->mail_sent, where); where = get_int(&count, where); if (count) { fill = (int *) MALLOC((count + 1) * sizeof(int)); sp->mail_received = fill; for (; count; count--, fill++) where = get_int(fill, where); *fill++ = 0; } else sp->mail_received = 0; return where; } /* su command to check out note hashes */ void list_notes(player * p, char *str) { int num; note *scan; char *oldstack; oldstack = stack; num = atoi(str); if (num < 0 || num >= NOTE_HASH_SIZE) { tell_player(p, " Number not in range.\n"); return; } strcpy(stack, " Notes:\n"); stack = strchr(stack, 0); for (scan = n_hash[num]; scan; scan = scan->hash_next) { if (scan->flags & NEWS_ARTICLE) sprintf(stack, " [%d] %s + %s", scan->id, scan->name, scan->header); else sprintf(stack, " [%d] %s - %s", scan->id, scan->name, bit_string(scan->flags)); stack = strchr(stack, 0); if (scan->flags & NOT_READY) { strcpy(stack, "(DEFUNCT)\n"); stack = strchr(stack, 0); } else *stack++ = '\n'; } strcpy(stack, " --\n"); stack = strchr(stack, 0); stack++; tell_player(p, oldstack); stack = oldstack; } void list_all_notes(player * p, char *str) { char *oldstack; int count = 0, hash; note *scan; oldstack = stack; strcpy(stack, " All notes --\n"); stack = strchr(stack, 0); for (hash = 0; hash <= NOTE_HASH_SIZE; count = 0, hash++) { for (scan = n_hash[hash]; scan; scan = scan->hash_next) count++; sprintf(stack, " %3d notes in bucket %2d", count, hash); stack = strchr(stack, 0); if (hash & 1) strcpy(stack, " -- "); else strcpy(stack, "\n"); stack = strchr(stack, 0); } strcpy(stack, " --\n"); stack = strchr(stack, 0); stack++; tell_player(p, oldstack); stack = oldstack; } /* the news command */ void news_command(player * p, char *str) { if (p->edit_info) { tell_player(p, " Can't do news commands whilst in editor.\n"); return; } if ((*str == '/') && (p->input_to_fn == news_command)) { match_commands(p, str + 1); if (!(p->flags & PANIC) && (p->input_to_fn == news_command)) { do_prompt(p, "News Mode >"); p->mode |= NEWSEDIT; } return; } if (!*str) if (p->input_to_fn == news_command) { tell_player(p, " Format: news <action>\n"); if (!(p->flags & PANIC) && (p->input_to_fn == news_command)) { do_prompt(p, "News Mode >"); p->mode |= NEWSEDIT; } return; } else { tell_player(p, " Entering news mode. Use 'end' to leave.\n" " '/<command>' does normal commands.\n"); p->flags &= ~PROMPT; p->input_to_fn = news_command; } else sub_command(p, str, news_list); if (!(p->flags & PANIC) && (p->input_to_fn == news_command)) { do_prompt(p, "News Mode >"); p->mode |= NEWSEDIT; } } /* the mail command */ void mail_command(player * p, char *str) { if (p->edit_info) { tell_player(p, " Can't do mail commands whilst in editor.\n"); return; } if (!p->saved) { tell_player(p, " You have no save information, and so can't use " "mail.\n"); return; } if ((*str == '/') && (p->input_to_fn == mail_command)) { match_commands(p, str + 1); if (!(p->flags & PANIC) && (p->input_to_fn == mail_command)) { do_prompt(p, "Mail Mode >"); p->mode |= MAILEDIT; } return; } if (!*str) if (p->input_to_fn == mail_command) { tell_player(p, " Format: mail <action>\n"); if (!(p->flags & PANIC) && (p->input_to_fn == mail_command)) { do_prompt(p, "Mail Mode >"); p->mode |= MAILEDIT; } return; } else { tell_player(p, " Entering mail mode. Use 'end' to leave.\n" " '/<command>' does normal commands.\n"); p->flags &= ~PROMPT; p->input_to_fn = mail_command; } else sub_command(p, str, mail_list); if (!(p->flags & PANIC) && (p->input_to_fn == mail_command)) { do_prompt(p, "Mail Mode >"); p->mode |= MAILEDIT; } } /* view news commands */ void view_news_commands(player * p, char *str) { view_sub_commands(p, news_list); } /* view mail commands */ void view_mail_commands(player * p, char *str) { view_sub_commands(p, mail_list); } /* exit news mode */ void exit_news_mode(player * p, char *str) { if (p->input_to_fn != news_command) return; tell_player(p, " Leaving news mode.\n"); p->input_to_fn = 0; p->flags |= PROMPT; p->mode &= ~NEWSEDIT; } /* exit mail mode */ void exit_mail_mode(player * p, char *str) { if (p->input_to_fn != mail_command) return; tell_player(p, " Leaving mail mode.\n"); p->input_to_fn = 0; p->flags |= PROMPT; p->mode &= ~MAILEDIT; } /* finds news article number x */ note *find_news_article(int n) { int integrity = 0; note *scan; if (n < 1 || n > news_count) return 0; for (n--, scan = find_note(news_start); n; n--, integrity++) if (scan) scan = find_note(scan->next_sent); else { news_count = integrity; return 0; } return scan; } /* list news articles */ void list_news(player * p, char *str) { char *oldstack, middle[80]; int page, pages, count, article, ncount = 1; note *scan; oldstack = stack; if (!news_count) { tell_player(p, " No news articles to view.\n"); return; } page = atoi(str); if (page <= 0) page = 1; page--; pages = (news_count - 1) / (TERM_LINES - 2); if (page > pages) page = pages; if (news_count == 1) strcpy(middle, "There is one news article"); else sprintf(middle, "There are %s articles", number2string(news_count)); pstack_mid(middle); count = page * (TERM_LINES - 2); for (article = news_start; count; count--, ncount++) { scan = find_note(article); if (!scan) { tell_player(p, " Bad news listing, aborted.\n"); log("error", "Bad news list"); stack = oldstack; return; } article = scan->next_sent; } for (count = 0; (count < (TERM_LINES - 1)); count++, ncount++) { scan = find_note(article); if (!scan) break; if (p->residency & ADMIN) sprintf(stack, "(%d) [%d] ", scan->id, ncount); else sprintf(stack, "[%d] ", ncount); while (*stack) *stack++; if (ncount < 10) *stack++ = ' '; strcpy(stack, scan->header); while (*stack) *stack++; if (scan->flags & ANONYMOUS) { if (p->residency & ADMIN) sprintf(stack, " <%s>\n", scan->name); else strcpy(stack, "\n"); } else sprintf(stack, " (%s)\n", scan->name); stack = strchr(stack, 0); article = scan->next_sent; } sprintf(middle, "Page %d of %d", page + 1, pages + 1); pstack_mid(middle); *stack++ = 0; tell_player(p, oldstack); stack = oldstack; } /* post a news article */ void quit_post(player * p) { tell_player(p, " Article NOT posted.\n"); remove_note((note *) p->edit_info->misc); p->mode &= ~NEWSEDIT; } void end_post(player * p, char *str) { note *article; char *oldstack; oldstack = stack; article = (note *) p->edit_info->misc; stack = store_string(oldstack, p->edit_info->buffer); article->text.length = (int) stack - (int) oldstack; article->text.where = (char *) MALLOC(article->text.length); memcpy(article->text.where, oldstack, article->text.length); stack = oldstack; article->next_sent = news_start; news_start = article->id; news_count++; article->flags &= ~NOT_READY; article->read_count = 0; tell_player(p, " Article posted....\n"); p->mode &= ~NEWSEDIT; if (p->edit_info->input_copy == news_command) { do_prompt(p, "News Mode >"); p->mode |= NEWSEDIT; } } void post_news(player * p, char *str) { note *article; char *scan, *first, *secure; if (!*str) { tell_player(p, " Format: post <header>\n"); return; } secure=strchr(str,')'); if (secure) if (*(++secure)=='\0') if (strchr(str,'(')) { tell_player(p," You may not have bracketed comments at the end " "of news titles\n"); return; } secure=strchr(str,'>'); if (secure) if (*(++secure)=='\0') if (strchr(str,'>')) { tell_player(p," You may not have bracketed comments at the end " "of news titles\n"); return; } article = create_note(); strncpy(article->header, str, MAX_TITLE - 2); article->flags |= NEWS_ARTICLE; first = first_char(p); scan = strstr(first, "post"); if (scan && (scan != first_char(p)) && (*(scan - 1) == 'a')) article->flags |= ANONYMOUS; strcpy(article->name, p->name); tell_player(p, " Now enter the main body text for the article.\n"); *stack = 0; start_edit(p, MAX_ARTICLE_SIZE, end_post, quit_post, stack); if (p->edit_info) p->edit_info->misc = (void *) article; else FREE(article); } /* follow up an article */ void followup(player * p, char *str) { char *oldstack, *body, *indent, *scan, *first; note *article, *old; oldstack = stack; old = find_news_article(atoi(str)); if (!old) { sprintf(stack, " No such news article '%s'\n", str); stack = end_string(stack); tell_player(p, oldstack); stack = oldstack; return; } article = create_note(); if (strstr(old->header, "Re: ") == old->header) strcpy(article->header, old->header); else { sprintf(stack, "Re: %s", old->header); strncpy(article->header, stack, MAX_TITLE - 2); } article->flags |= NEWS_ARTICLE; first = first_char(p); scan = strstr(first, "followup"); if (scan && (scan != first_char(p)) && (*(scan - 1) == 'a')) article->flags |= ANONYMOUS; strcpy(article->name, p->name); indent = stack; get_string(stack, old->text.where); stack = end_string(stack); body = stack; if (old->flags & ANONYMOUS) sprintf(stack, "From anonymous article written on %s ...\n", convert_time(old->date)); else sprintf(stack, "On %s, %s wrote ...\n", convert_time(old->date), old->name); stack = strchr(stack, 0); while (*indent) { *stack++ = '>'; *stack++ = ' '; while (*indent && *indent != '\n') *stack++ = *indent++; *stack++ = '\n'; indent++; } *stack++ = '\n'; *stack++ = 0; tell_player(p, " Please trim article as much as is possible ....\n"); start_edit(p, MAX_ARTICLE_SIZE, end_post, quit_post, body); if (p->edit_info) p->edit_info->misc = (void *) article; else FREE(article); stack = oldstack; } /* wipe a news article */ void remove_article(player * p, char *str) { note *article, *prev, *scan; char *oldstack; oldstack = stack; article = find_news_article(atoi(str)); if (!article) { sprintf(stack, " No such news article '%s'\n", str); stack = end_string(stack); tell_player(p, oldstack); stack = oldstack; return; } strcpy(stack, article->name); lower_case(stack); if (!(p->residency & ADMIN) && strcmp(stack, p->lower_name)) { tell_player(p, " You can't remove an article that isn't yours.\n"); return; } scan = find_note(news_start); if (scan == article) news_start = article->next_sent; else { do { prev = scan; scan = find_note(scan->next_sent); } while (scan != article); prev->next_sent = article->next_sent; } news_count--; remove_note(article); tell_player(p, " Article removed.\n"); stack = oldstack; } /* read an article */ void read_article(player * p, char *str) { char *oldstack; note *article; oldstack = stack; if (!*str) { tell_player(p, " Format: read <article-number>\n"); return; } article = find_news_article(atoi(str)); if (!article) { sprintf(stack, " No such news article '%s'\n", str); stack = end_string(stack); tell_player(p, oldstack); stack = oldstack; return; } article->read_count++; if (article->flags & ANONYMOUS) { if (p->residency & ADMIN) sprintf(stack, "Subject: %s\nPosted anonymously on %s by %s.\n", article->header, convert_time(article->date), article->name); else sprintf(stack, "Subject: %s\nPosted anonymously on %s.\n", article->header, convert_time(article->date)); } else sprintf(stack, "Subject: %s\nPosted by %s on %s.\n", article->header, article->name, convert_time(article->date)); stack = strchr(stack, 0); if (article->read_count == 1) strcpy(stack, "Article read once only.\n\n"); else sprintf(stack, "Article has been read %s times.\n\n", number2string(article->read_count)); stack = strchr(stack, 0); get_string(stack, article->text.where); stack = end_string(stack); pager(p, oldstack, 0); stack = oldstack; } /* mail stuff */ /* count how many mails have been posted */ int posted_count(player * p) { int scan, count = 0; note *mail, *next; if (!p->saved) return 0; scan = p->saved->mail_sent; mail = find_note(scan); if (!mail) { p->saved->mail_sent = 0; return 0; } while (mail) { count++; scan = mail->next_sent; next = find_note(scan); if (!next && scan) { mail->next_sent = 0; mail = 0; } else mail = next; } return count; } /* view mail that has been sent */ void view_sent(player * p, char *str) { char *oldstack; note *mail, *next; int count = 1, scan; saved_player *sp; oldstack = stack; if (*str && p->residency & ADMIN) { sp = find_saved_player(str); if (!sp) { tell_player(p, " No such player in save files.\n"); return; } else { scan = sp->mail_sent; mail = find_note(scan); if (!mail) { sp->mail_sent = 0; tell_player(p, " They have sent no mail.\n"); return; } } } else { if (!p->saved) { tell_player(p, " You have no save file, and therefore no mail " "either.\n"); return; } scan = p->saved->mail_sent; mail = find_note(scan); if (!mail) { p->saved->mail_sent = 0; tell_player(p, " You have sent no mail.\n"); return; } } strcpy(stack, "Listing mail sent...\n"); stack = strchr(stack, 0); while (mail) { if (p->residency & ADMIN) { sprintf(stack, "(%d) [%d] %d - %s\n", mail->id, count, mail->read_count, mail->header); } else { sprintf(stack, "[%d] %d - %s\n", count, mail->read_count, mail->header); } stack = strchr(stack, 0); scan = mail->next_sent; count++; next = find_note(scan); if (!next && scan) { mail->next_sent = 0; mail = 0; } else { mail = next; } } sprintf(stack, " You have sent %d out of your maximum of %d mails.\n", count - 1, p->max_mail); stack = end_string(stack); tell_player(p, oldstack); stack = oldstack; } /* this fn goes through the received list and removes dud note pointers */ void reconfigure_received_list(saved_player * sp) { int count = 0; int *scan, *fill; char *oldstack; oldstack = stack; align(stack); fill = (int *) stack; scan = sp->mail_received; if (!scan) return; for (; *scan; scan++) if (((*scan) < 0) || ((*scan) > 65536)) { count = 0; break; } else if (find_note(*scan)) { *(int *) stack = *scan; stack += sizeof(int); count++; } FREE(sp->mail_received); if (count) { *(int *) stack = 0; stack += sizeof(int); count++; sp->mail_received = (int *) MALLOC(count * sizeof(int)); memcpy(sp->mail_received, oldstack, count * sizeof(int)); } else sp->mail_received = 0; stack = oldstack; } /* view mail that has been received */ void view_received(player * p, char *str) { char *oldstack, middle[80], *page_input; int page, pages, *scan, *scan_count, mcount = 0, ncount = 1, n; note *mail; saved_player *sp; oldstack = stack; if (*str && !isdigit(*str) && p->residency & ADMIN) { page_input = next_space(str); if (*page_input) *page_input++ = 0; sp = find_saved_player(str); if (!sp) { tell_player(p, " No such player in save files.\n"); return; } else scan = sp->mail_received; str = page_input; } else { if (!p->saved) { tell_player(p, " You have no save file, " "and therefore no mail either.\n"); return; } sp = p->saved; scan = sp->mail_received; } if (!scan) { tell_player(p, " You have received no mail.\n"); return; } p->saved_flags &= ~NEW_MAIL; for (scan_count = scan; *scan_count; scan_count++) mcount++; page = atoi(str); if (page <= 0) page = 1; page--; pages = (mcount - 1) / (TERM_LINES - 2); if (page > pages) page = pages; if (mcount == 1) strcpy(middle, "You have received one letter"); else sprintf(middle, "You have received %s letters", number2string(mcount)); pstack_mid(middle); ncount = page * (TERM_LINES - 2); scan += ncount; for (n = 0, ncount++; n < (TERM_LINES - 1); n++, ncount++, scan++) { if (!*scan) break; mail = find_note(*scan); if (!mail) { stack = oldstack; tell_player(p, " Found mail that owner had deleted ...\n"); reconfigure_received_list(sp); if (sp != p->saved) { tell_player(p, " Reconfigured ... try again\n"); return; } view_received(p, str); return; } if (p->residency & ADMIN) sprintf(stack, "(%d) [%d] ", mail->id, ncount); else sprintf(stack, "[%d] ", ncount); while (*stack) *stack++; if (ncount < 10) *stack++ = ' '; strcpy(stack, mail->header); while (*stack) *stack++; if (mail->flags & ANONYMOUS) { if (p->residency & ADMIN) sprintf(stack, " <%s>\n", mail->name); else strcpy(stack, "\n"); } else sprintf(stack, " (%s)\n", mail->name); while (*stack) *stack++; } sprintf(middle, "Page %d of %d", page + 1, pages + 1); pstack_mid(middle); *stack++ = 0; tell_player(p, oldstack); stack = oldstack; } /* send a letter */ void quit_mail(player * p) { tell_player(p, " Letter NOT posted.\n"); remove_note((note *) p->edit_info->misc); } void end_mail(player * p, char *str) { note *mail; char *oldstack, *name_list, *body, *tcpy, *comp, *text; saved_player **player_list, **pscan, **pfill; player *on; int receipt_count, n, m, *received_lists, *r; oldstack = stack; mail = (note *) p->edit_info->misc; align(stack); player_list = (saved_player **) stack; receipt_count = saved_tag(p, mail->text.where); if (mail->text.where) FREE(mail->text.where); mail->text.where = 0; if (!receipt_count) { tell_player(p, " No one to send the letter to !\n"); remove_note(mail); stack = oldstack; if (p->edit_info->input_copy == mail_command) { do_prompt(p, "Mail Mode >"); p->mode |= MAILEDIT; } return; } if (!p->saved) { tell_player(p, " Eeek, no save file !\n"); remove_note(mail); stack = oldstack; if (p->edit_info->input_copy == mail_command) { do_prompt(p, "Mail Mode >"); p->mode |= MAILEDIT; } return; } pscan = player_list; pfill = player_list; m = receipt_count; for (n = 0; n < m; n++) { if (((*pscan)->residency == SYSTEM_ROOM) || ((*pscan)->residency & BANISHD)) { tell_player(p, " You can't send mail to a room, " "or someone who is banished.\n"); receipt_count--; pscan++; } else { if ((mail->flags & ANONYMOUS) && ((*pscan)->saved_flags & NO_ANONYMOUS)) { text = stack; sprintf(stack, " %s is not receiving anonymous mail.\n", (*pscan)->lower_name); stack = end_string(stack); tell_player(p, text); stack = text; receipt_count--; pscan++; } else *pfill++ = *pscan++; } } if (receipt_count > 0) { pscan = player_list; name_list = stack; if (mail->flags & SUPRESS_NAME) { strcpy(stack, "Anonymous"); stack = end_string(stack); } else { strcpy(stack, (*pscan)->lower_name); stack = strchr(stack, 0); if (receipt_count > 1) { pscan++; for (n = 2; n < receipt_count; n++, pscan++) { sprintf(stack, ", %s", (*pscan)->lower_name); stack = strchr(stack, 0); } sprintf(stack, " and %s", (*pscan)->lower_name); stack = strchr(stack, 0); } *stack++ = 0; } body = stack; sprintf(stack, " Sending mail to %s.\n", name_list); stack = end_string(stack); tell_player(p, body); stack = body; if (mail->flags & ANONYMOUS) sprintf(stack, " Anonymous mail dated %s.\nSubject: %s\nTo: %s\n\n", convert_time(mail->date), mail->header, name_list); else sprintf(stack, " Mail dated %s.\nSubject: %s\nTo: %s\nFrom: %s\n\n", convert_time(mail->date), mail->header, name_list, mail->name); stack = strchr(stack, 0); tcpy = p->edit_info->buffer; for (n = 0; n < p->edit_info->size; n++) *stack++ = *tcpy++; comp = stack; stack = store_string(stack, body); mail->text.length = (int) stack - (int) comp; mail->text.where = (char *) MALLOC(mail->text.length); memcpy(mail->text.where, comp, mail->text.length); mail->next_sent = p->saved->mail_sent; p->saved->mail_sent = mail->id; text = stack; command_type |= HIGHLIGHT; if (mail->flags & ANONYMOUS) sprintf(stack, " -=> New mail, '%s' sent anonymously\n\n", mail->header); else sprintf(stack, " -=> New mail, '%s' from %s.\n\n", mail->header, mail->name); stack = end_string(stack); align(stack); received_lists = (int *) stack; pscan = player_list; for (n = 0; n < receipt_count; n++, pscan++) { stack = (char *) received_lists; r = (*pscan)->mail_received; if (!r) m = 2 * sizeof(int); else { for (m = 2 * sizeof(int); *r; r++, m += sizeof(int)) { *(int *) stack = *r; stack += sizeof(int); } } *(int *) stack = mail->id; stack += sizeof(int); *(int *) stack = 0; if ((*pscan)->mail_received) FREE((*pscan)->mail_received); r = (int *) MALLOC(m); memcpy(r, received_lists, m); (*pscan)->mail_received = r; on = find_player_absolute_quiet((*pscan)->lower_name); if (on && on->saved_flags & MAIL_INFORM) { tell_player(on, "\007\n"); tell_player(on, text); } else (*pscan)->saved_flags |= NEW_MAIL; } command_type &= ~HIGHLIGHT; mail->read_count = receipt_count; mail->flags &= ~NOT_READY; tell_player(p, " Mail posted....\n"); } else tell_player(p, " No mail posted.\n"); p->mode &= ~MAILEDIT; if (p->edit_info->input_copy == mail_command) { do_prompt(p, "Mail Mode >"); p->mode |= MAILEDIT; } stack = oldstack; } void send_letter(player * p, char *str) { note *mail; char *subject, *scan, *first; int length; if (posted_count(p) >= p->max_mail) { tell_player(p, " Sorry, you have reached your mail limit.\n"); return; } subject = next_space(str); if (!*subject) { tell_player(p, " Format: post <character(s)> <subject>\n"); return; } *subject++ = 0; mail = create_note(); first = first_char(p); scan = strstr(first, "post"); if (scan && (scan != first_char(p)) && (*(scan - 1) == 'a')) mail->flags |= ANONYMOUS; strcpy(mail->name, p->name); strncpy(mail->header, subject, MAX_TITLE - 2); tell_player(p, " Enter main text of the letter...\n"); *stack = 0; start_edit(p, MAX_ARTICLE_SIZE, end_mail, quit_mail, stack); if (p->edit_info) { p->edit_info->misc = (void *) mail; length = strlen(str) + 1; mail->text.where = (char *) MALLOC(length); memcpy(mail->text.where, str, length); } else FREE(mail); } /* find the note corresponing to letter number */ note *find_received(saved_player * sp, int n) { int *scan, count = 1; note *mail; scan = sp->mail_received; if (!scan) return 0; for (; *scan; count++, scan++) if (count == n) { mail = find_note(*scan); if (!mail) { reconfigure_received_list(sp); return find_received(sp, n); } return mail; } return 0; } /* view a letter */ void read_letter(player * p, char *str) { char *oldstack, *which; saved_player *sp; note *mail; oldstack = stack; which = str; sp = p->saved; if (!sp) { tell_player(p, " You have no save info.\n"); return; } mail = find_received(sp, atoi(which)); if (!mail) { sprintf(stack, " No such letter '%s'\n", which); stack = end_string(stack); tell_player(p, oldstack); stack = oldstack; return; } get_string(stack, mail->text.where); stack = end_string(stack); pager(p, oldstack, 0); p->saved_flags &= ~NEW_MAIL; stack = oldstack; } /* reply to a letter */ void reply_letter(player * p, char *str) { note *mail, *old; char *indent, *body, *oldstack, *scan, *first; int length; oldstack = stack; if (!*str) { tell_player(p, " Format: reply <number>\n"); return; } if (posted_count(p) >= p->max_mail) { tell_player(p, " Sorry, you have reached your mail limit.\n"); return; } if (!p->saved) { tell_player(p, " Eeek, no saved bits.\n"); return; } old = find_received(p->saved, atoi(str)); if (!old) { sprintf(stack, " Can't find letter '%s'\n", str); stack = end_string(stack); tell_player(p, oldstack); stack = oldstack; return; } mail = create_note(); first = first_char(p); scan = strstr(first, "reply"); if (scan && (scan != first_char(p)) && (*(scan - 1) == 'a')) mail->flags |= ANONYMOUS; if (old->flags & ANONYMOUS) mail->flags |= SUPRESS_NAME; strcpy(mail->name, p->name); if (strstr(old->header, "Re: ") == old->header) strcpy(mail->header, old->header); else { sprintf(stack, "Re: %s", old->header); strncpy(mail->header, stack, MAX_TITLE - 2); } indent = stack; get_string(stack, old->text.where); stack = end_string(stack); body = stack; sprintf(stack, "In reply to '%s'\n", old->header); stack = strchr(stack, 0); while (*indent) { *stack++ = '>'; *stack++ = ' '; while (*indent && *indent != '\n') *stack++ = *indent++; *stack++ = '\n'; indent++; } *stack++ = '\n'; *stack++ = 0; tell_player(p, " Please trim letter as much as possible...\n"); start_edit(p, MAX_ARTICLE_SIZE, end_mail, quit_mail, body); if (p->edit_info) { p->edit_info->misc = (void *) mail; length = strlen(old->name) + 1; mail->text.where = (char *) MALLOC(length); memcpy(mail->text.where, old->name, length); } else FREE(mail); stack = oldstack; } /* reply to an article */ void reply_article(player * p, char *str) { note *mail, *old; char *indent, *body, *oldstack, *scan, *first; int length; oldstack = stack; if (!*str) { tell_player(p, " Format: reply <no>\n"); return; } if (posted_count(p) >= p->max_mail) { tell_player(p, " Sorry, you have reached your mail limit.\n"); return; } if (!p->saved) { tell_player(p, " Eeek, no saved bits.\n"); return; } old = find_news_article(atoi(str)); if (!old) { sprintf(stack, " Can't find letter '%s'\n", str); stack = end_string(stack); tell_player(p, oldstack); stack = oldstack; return; } mail = create_note(); first = first_char(p); scan = strstr(first, "reply"); if (scan && (scan != first_char(p)) && (*(scan - 1) == 'a')) mail->flags |= ANONYMOUS; if (old->flags & ANONYMOUS) mail->flags |= SUPRESS_NAME; strcpy(mail->name, p->name); if (strstr(old->header, "Re: ") == old->header) strcpy(mail->header, old->header); else { sprintf(stack, "Re: %s", old->header); strncpy(mail->header, stack, MAX_TITLE - 2); } indent = stack; get_string(stack, old->text.where); stack = end_string(stack); body = stack; sprintf(stack, "In your article '%s' you wrote ...\n", old->header); stack = strchr(stack, 0); while (*indent) { *stack++ = '>'; *stack++ = ' '; while (*indent && *indent != '\n') *stack++ = *indent++; *stack++ = '\n'; indent++; } *stack++ = '\n'; *stack++ = 0; tell_player(p, " Please trim letter as much as possible...\n"); start_edit(p, MAX_ARTICLE_SIZE, end_mail, quit_mail, body); if (p->edit_info) { p->edit_info->misc = (void *) mail; length = strlen(old->name) + 1; mail->text.where = (char *) MALLOC(length); memcpy(mail->text.where, old->name, length); } else FREE(mail); stack = oldstack; } /* unlink and remove a mail note */ void unlink_mail(note * m) { char *oldstack, *temp; saved_player *sp; int *change; note *scan, *tmp; if (!m) return; oldstack = stack; strcpy(stack, m->name); lower_case(stack); stack = end_string(stack); sp = find_saved_player(oldstack); if (!sp) { temp = stack; if (current_player) { sprintf(stack, "(2) mail: %s - current: %s", m->name, current_player->name); } else { sprintf(stack, "(2) mail, %s - no current", m->name); } stack = end_string(stack); log("error", temp); stack = temp; unlink_mail(find_note(m->next_sent)); } else { change = &(sp->mail_sent); scan = find_note(sp->mail_sent); while (scan && scan != m) { change = &(scan->next_sent); scan = find_note(scan->next_sent); } tmp = find_note(m->next_sent); if (tmp) *change = tmp->id; else *change = 0; } remove_note(m); stack = oldstack; } /* remove sent mail */ void delete_sent(player * p, char *str) { int number; note *scan; number = atoi(str); if (number <= 0) { tell_player(p, " Format: remove <mail number>\n"); return; } if (!(p->saved)) { tell_player(p, " You have no save information, " "and therefore no mail either.\n"); return; } scan = find_note(p->saved->mail_sent); for (number--; number; number--) if (scan) scan = find_note(scan->next_sent); else break; if (!scan) { tell_player(p, " You haven't sent that many mails.\n"); return; } unlink_mail(scan); tell_player(p, " Removed mail ...\n"); } /* remove received mail */ void delete_received(player * p, char *str) { int number, count = 0; int *make, *scan; char *oldstack; note *deleted; number = atoi(str); if (number <= 0) { tell_player(p, " Format: delete <mail number>\n"); return; } if (!(p->saved)) { tell_player(p, " You have no save information, " "and therefore no mail either.\n"); return; } scan = p->saved->mail_received; if (!scan) { tell_player(p, " You have recieved no mail to delete.\n"); return; } oldstack = stack; align(stack); make = (int *) stack; for (number--; number; number--) if (*scan) { *make++ = *scan++; count++; } else break; if (!*scan) { tell_player(p, " Not that many pieces of mail.\n"); stack = oldstack; return; } deleted = find_note(*scan++); while (*scan) { *make++ = *scan++; count++; } *make++ = 0; count++; if (p->saved->mail_received) FREE(p->saved->mail_received); if (count != 1) { p->saved->mail_received = (int *) MALLOC(sizeof(int) * count); memcpy(p->saved->mail_received, stack, sizeof(int) * count); } else p->saved->mail_received = 0; if (deleted) { deleted->read_count--; if (!(deleted->read_count)) unlink_mail(deleted); } tell_player(p, " Mail deleted....\n"); stack = oldstack; } /* admin ability to view notes */ void view_note(player * p, char *str) { note *n; char *oldstack; oldstack = stack; if (!*str) { tell_player(p, " Format: view_note <number>\n"); return; } n = find_note(atoi(str)); if (!n) { tell_player(p, " Can't find note with that number.\n"); return; } if (n->flags & NEWS_ARTICLE) strcpy(stack, " News Article.\n"); else strcpy(stack, " Mail (?).\n"); stack = strchr(stack, 0); sprintf(stack, " Posted on %s, by %s.\n Read count: %d\n", convert_time(n->date), n->name, n->read_count); stack = strchr(stack, 0); if (n->flags & ANONYMOUS) { strcpy(stack, " Posted anonymously.\n"); stack = strchr(stack, 0); } if (n->flags & NOT_READY) { strcpy(stack, " Not ready flag set.\n"); stack = strchr(stack, 0); } if (n->flags & SUPRESS_NAME) { strcpy(stack, " Name suppressed.\n"); stack = strchr(stack, 0); } sprintf(stack, " Next link -> %d\n", n->next_sent); stack = end_string(stack); pager(p, oldstack, 0); stack = oldstack; } /* remove a note from */ void dest_note(player * p, char *str) { note *n; if (!*str) { tell_player(p, " Format: dest_note <number>\n"); return; } n = find_note(atoi(str)); if (!n) { tell_player(p, " Can't find note with that number.\n"); return; } remove_any_note(n); tell_player(p, " Note removed\n"); } /* relink emergancy command */ void relink_note(player * p, char *str) { char *to; int id1, id2; note *n; to = next_space(str); *to++ = 0; id1 = atoi(str); id2 = atoi(to); /* * if (!id1 || !id2) { tell_player(p,"Format : relink <number> * <number>\n"); return; } */ n = find_note(id1); if (!n) { tell_player(p, " Can't find first note\n"); return; } n->next_sent = id2; tell_player(p, " next_sent pointer twiddled.\n"); return; } /* recount emergancy command */ void recount_news(player * p, char *str) { int article; note *scan; news_count = 0; scan = find_note(news_start); if (scan) { article = scan->next_sent; for (; scan; article = scan->next_sent, news_count++) { scan = find_note(article); if (!scan) break; } news_count++; } tell_player(p, " Tis Done ...\n"); }