/* * Playground+ - news.c * NuNews system, enhanced news with groups and more (c) phypor 1998 * --------------------------------------------------------------------------- * * Modifications to original release: * Include paths * varible argument functions * changed ADC to LOWER_ADMIN * changed pager calls to be pg+ conformant * cleaned up presentation (used LINE and pstack_mid) * added number of times read to "news check" * added news next command to read next unread article */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <fcntl.h> #include <unistd.h> #include <signal.h> #include <memory.h> #include <dirent.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> #include <errno.h> #include "include/config.h" #include "include/player.h" #include "include/proto.h" /* for sync streamlining, so we dont sync unless there is some change */ #define NEWS_CHANGED newschanged = 1 #define NEWS_CHANGE_RESET newschanged = 0 #define NEWS_HAS_CHANGED newschanged == 1 extern command_func news_command; extern int news_sync; extern void help(player *, char *); int newschanged = 0; newsgroup NewsGroups[] = { {"main", 0, 7, "Default news group", "files/news/main", 0}, {"sus", PSU, 10, "Staff news group", "files/news/sus", 0}, {"admin", LOWER_ADMIN, 15, "Admin news groups", "files/news/admin", 0}, {"flames", 0, 20, "Flames. keep em to This Board Only", "files/news/flames", 0}, {"songs", 0, 20, "Songs, Poems, Things of that nature", "files/news/songs", 0}, {"humor", 0, 20, "Humor, jokes, funny stories.", "files/news/humor", 0}, {"", 0, 0, "", "", 0} }; /*** io functs ***/ char *load_file_to_string(char *filename) { int fd, len; char *loaded; fd = open(filename, O_RDONLY); if (fd < 0) { LOGF("error", "failed to load file to string [%s]", filename); return (char *) NULL; } len = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); loaded = (char *) MALLOC(len + 1); if (!loaded) { close(fd); LOGF("error", "failed to malloc in load_file_to_string %d.", len); return (char *) NULL; } memset(loaded, 0, len + 1); if (read(fd, loaded, len) < 0) { close(fd); LOGF("error", "failed to read, load_file_to_string [%s]", filename); return (char *) NULL; } close(fd); if (sys_flags & VERBOSE) LOGF("verbose", "load_file_to_string [%s]", filename); return loaded; } void sync_news(newsgroup * group, news_header * nh, char *body) { int fd; FILE *of; /* allows us to just sync header, if no body is passed */ if (*body) { sprintf(stack, "%s/%d.body", group->path, nh->id); #ifdef BSDISH fd = open(stack, O_CREAT | O_WRONLY | S_IRUSR | S_IWUSR); #else fd = open(stack, O_CREAT | O_WRONLY | O_SYNC, S_IRUSR | S_IWUSR); #endif /* BSDISH */ if (fd < 0) { LOGF("error", "sync_news() failed to open " "body file, %s", strerror(errno)); return; } write(fd, body, strlen(body)); close(fd); } sprintf(stack, "%s/%d.head", group->path, nh->id); of = fopen(stack, "w"); if (!of) { LOGF("error", "sync_news() failed to open " "head file, %s", strerror(errno)); return; } fwrite(nh, sizeof(news_header), 1, of); fclose(of); } void sync_group_news_headers(newsgroup * group) { news_header *scan; int fd; char *oldstack = stack; char path[160]; *stack = '\0'; for (scan = group->top; scan; scan = scan->next) sync_news(group, scan, ""); /* update the .newslist file */ for (scan = group->top; scan; scan = scan->next) stack += sprintf(stack, "%d.head\n", scan->id); stack = end_string(stack); memset(path, 0, 160); sprintf(path, "%s/.newslist", group->path); #ifdef BSDISH fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR); #else fd = open(path, O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, S_IRUSR | S_IWUSR); #endif /* BSDISH */ if (fd < 0) { LOGF("error", "failed to open .newslist in " "sync_group_news_headers() [%s], %s", group->name, strerror(errno)); return; } write(fd, oldstack, strlen(oldstack)); close(fd); stack = oldstack; } void sync_all_news_headers(void) { int i; if (!(NEWS_HAS_CHANGED)) return; for (i = 0; NewsGroups[i].name[0]; i++) sync_group_news_headers(&NewsGroups[i]); NEWS_CHANGE_RESET; } news_header *load_news_header(newsgroup * group, char *fname) { FILE *ni; news_header *nh; sprintf(stack, "%s/%s", group->path, fname); ni = fopen(stack, "r"); if (!ni) { LOGF("error", "failed to fopen() in load_news_article() [%s:%s], %s", group->name, fname, strerror(errno)); return (news_header *) NULL; } nh = (news_header *) MALLOC(sizeof(news_header)); if (!nh) { LOGF("error", "failed to malloc() in load_news_article() [%s], %s", fname, strerror(errno)); return (news_header *) NULL; } fread(nh, sizeof(news_header), 1, ni); fclose(ni); return nh; } void load_all_news_for_group(newsgroup * group) { news_header *nh = 0; news_header *pe = 0; FILE *lf; char li[160]; /* open the file with news list */ memset(li, 0, 160); sprintf(li, "%s/.newslist", group->path); lf = fopen(li, "r"); if (!lf) { LOGF("error", "failed to fopen() in load_all_news_for_group()[%s], %s", group->name, strerror(errno)); return; } /* read through each line of file and load that header */ while (fgets(li, 159, lf)) { if (li[strlen(li) - 1] == '\n') li[strlen(li) - 1] = '\0'; /* spank off the newlines */ nh = load_news_header(group, li); if (nh) { if (!(group->top)) group->top = nh; else pe->next = nh; pe = nh; /* set this one to the previous article */ } memset(li, 0, 160); } } void init_news(void) { struct stat sbuf; char *oldaction = action; char *oldstack = stack; int i, fd; action = "News Initation"; for (i = 0; NewsGroups[i].name[0]; i++) { /* make sure the directory for the group exists */ if (stat(NewsGroups[i].path, &sbuf) < 0) { if (mkdir(NewsGroups[i].path, S_IRUSR | S_IWUSR | S_IXUSR) < 0) { LOGF("error", "failed to create diretory for news [%s], %s", NewsGroups[i].path, strerror(errno)); continue; } sprintf(stack, "%s/.newslist", NewsGroups[i].path); stack = end_string(stack); fd = open(oldstack, O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (fd < 0) LOGF("error", "failed to create dummy newslist file [%s], %s", NewsGroups[i].name, strerror(errno)); else close(fd); stack = oldstack; LOGF("boot", "Created new directory for newsgroup '%s'", NewsGroups[i].name); } load_all_news_for_group(&NewsGroups[i]); } action = oldaction; } /*** misc functions ***/ void destroy_news(newsgroup * group, news_header * nh) { char path[160]; NEWS_CHANGED; memset(path, 0, 160); sprintf(path, "%s/%d.body", group->path, nh->id); unlink(path); memset(path, 0, 160); sprintf(path, "%s/%d.head", group->path, nh->id); unlink(path); memset(nh, 0, sizeof(news_header)); FREE(nh); } void scan_news(void) { news_header *scan, *current; int t, i; int d = 0; /* for sanity, only delete 5 articles per loop */ t = time(0); for (i = 0; NewsGroups[i].name[0]; i++) { while (NewsGroups[i].top && NewsGroups[i].top->flags & DELETE_ME && d < 5) { current = NewsGroups[i].top; NewsGroups[i].top = NewsGroups[i].top->next; destroy_news(&NewsGroups[i], current); d++; } for (scan = NewsGroups[i].top; (d < 5 && scan); scan = scan->next) { if (((t - (scan->date)) > NEWS_TIMEOUT) && !(scan->flags & STICKY_ARTICLE)) { LOGF("news", "Timeout of %s %s posting, %s", scan->name, NewsGroups[i].name, scan->header); scan->flags |= DELETE_ME; } /* keep this the last thing done, so we can have other things in the loop set the DELETE_ME flag and handle it promptly */ if (scan->next && scan->next->flags & DELETE_ME) { NEWS_CHANGED; current = scan->next; scan->next = scan->next->next; destroy_news(&NewsGroups[i], current); d++; } } } } int remove_all_news(player * p, char *rm_name) { news_header *scan; int rmd = 0, i; for (i = 0; NewsGroups[i].name[0]; i++) for (scan = NewsGroups[i].top; scan; scan = scan->next) if (!strcasecmp(scan->name, rm_name)) { scan->flags |= DELETE_ME; rmd++; } return rmd; } newsgroup *find_news_group(char *str) { int i; for (i = 0; NewsGroups[i].name[0]; i++) if (!strcasecmp(str, NewsGroups[i].name)) if (!NewsGroups[i].required_priv || (current_player && current_player->residency & NewsGroups[i].required_priv)) return &NewsGroups[i]; return (newsgroup *) NULL; } int find_news_group_number(newsgroup * group) { int i; for (i = 0; NewsGroups[i].name[0]; i++) if (&NewsGroups[i] == group) return i; LOGF("error", "news group not in NewsGroups array?!? [%s]", group->name); return -1; } int count_player_postings(player * p, newsgroup * group) { int i = 0; news_header *scan; for (scan = group->top; scan; scan = scan->next) if (!strcasecmp(scan->name, p->name)) i++; return i; } int get_next_news_id(newsgroup * group) { int lowest = 1; news_header *scan; scan = group->top; while (scan) { for (scan = group->top; scan; scan = scan->next) if (scan->id == lowest) { lowest++; break; } } return lowest; } int count_news_articles(newsgroup * group) { news_header *nh = group->top; int i = 0; while (nh) { i++; nh = nh->next; } return i; } news_header *find_news_article(newsgroup * group, int i) { news_header *scan = group->top; int ret = 1; for (; (scan && ret < i); ret++, scan = scan->next); return scan; } char *id_or_not(player * p, news_header * nh) { static char fawn[40]; memset(fawn, 0, 40); if (p->residency & ADMIN) sprintf(fawn, "(%d)", nh->id); else return ""; return fawn; } char *spaces10(int i) { if (i < 10) return " "; return ""; } char *spaces_followups(news_header * nh) { switch (nh->followups) { case 0: return ""; case 1: return " "; case 2: return " "; case 3: return " "; case 4: return " "; case 5: return " "; case 6: return " "; case 7: return " "; case 8: return " "; case 9: return " "; } return " "; } char *sender_str(player * p, news_header * nh) { static char isat[80]; if (nh->flags & ANONYMOUS) { if (p && (p->residency & ADMIN || !strcasecmp(nh->name, p->name))) sprintf(isat, "?%s?", nh->name); else sprintf(isat, "(?????)"); } else sprintf(isat, "(%s)", nh->name); return isat; } char *read_count_string(int i) { static char s[16]; memset(s, 0, 16); sprintf(s, "<%d>", i); for (i = strlen(s); i < 5; i++) strcat(s, " "); return s; } int next_unread(player * p, newsgroup * g) { news_header *nh = g->top; int i = 0, gn = find_news_group_number(g); if (gn < 0) return 0; while (nh && nh->date > p->news_last[gn]) { nh = nh->next; i++; } return i; } void save_str_public(char *filename, char *str) { int fd; #ifdef BSDISH fd = open(filename, O_CREAT | O_WRONLY | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); #else fd = open(filename, O_CREAT | O_WRONLY | O_SYNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); #endif /* BSDISH */ if (fd < 0) { LOGF("error", "save_str() failed to open [%s], %s", filename, strerror(errno)); return; } write(fd, str, strlen(str)); /* make sure we get the right privs ..no matter whut umask */ fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); close(fd); } /* new version for walling to news inform And enough privs */ void news_wall_priv_but(player * p, char *str, int priv) { player *scan; for (scan = flatlist_start; scan; scan = scan->flat_next) { if (scan->custom_flags & NEWS_INFORM && scan != p && (!priv || scan->residency & priv)) { command_type |= HIGHLIGHT; tell_player(scan, str); command_type &= ~HIGHLIGHT; } } } int count_new_news(newsgroup * group, int cutoff) { news_header *scan; int newcnt = 0; for (scan = group->top; scan; scan = scan->next) if (cutoff < scan->date) newcnt++; return newcnt; } void new_news_inform(player * p) { int i, h = 0; char *oldstack = stack; stack += sprintf(stack, " Unread news : "); for (i = 0; (NewsGroups[i].name[0] && i < MAX_LAST_NEWS_INTS); i++) if (!(NewsGroups[i].required_priv) || p->residency & NewsGroups[i].required_priv) if (NewsGroups[i].top && NewsGroups[i].top->date > p->news_last[i]) { h++; stack += sprintf(stack, "%s (%d) ", NewsGroups[i].name, count_new_news(&NewsGroups[i], p->news_last[i])); } stack += sprintf(stack, "\n"); stack = end_string(stack); if (h) tell_player(p, oldstack); stack = oldstack; } /*** final functs ***/ void quit_news_posting(player * p) { tell_player(p, " Article NOT posted.\n"); FREE(p->edit_info->misc); p->mode &= ~NEWSEDIT; } void end_news_posting(player * p) { news_header *nh; char *oldstack = stack; int ngn; if (!*(p->edit_info->buffer)) { quit_news_posting(p); return; } NEWS_CHANGED; nh = (news_header *) p->edit_info->misc; nh->id = get_next_news_id(nh->group); nh->date = time(0); /* add it to the linked news list */ if (nh->group->top) nh->next = nh->group->top; nh->group->top = nh; /* save it to disk */ sync_news(nh->group, nh, p->edit_info->buffer); /* sync the news to disk */ sync_group_news_headers(nh->group); /* inform the player */ tell_player(p, " Article posted ...\n"); /* inform the masses */ if (nh->group != &NewsGroups[0]) stack += sprintf(stack, " -=*> A new news article has been posted in %s", nh->group->name); else stack += sprintf(stack, " -=*> A new news article has been posted"); if (nh->flags & ANONYMOUS) stack += sprintf(stack, " anonymously"); else stack += sprintf(stack, " by %s", p->name); stack += sprintf(stack, " entitled ...\n %s^N\n", nh->header); stack = end_string(oldstack); news_wall_priv_but(p, oldstack, nh->group->required_priv); stack = oldstack; /* update the players read count, so they dont get it as unread */ ngn = find_news_group_number(nh->group); if (ngn > -1 && ngn < MAX_LAST_NEWS_INTS) p->news_last[ngn] = nh->date; /* just make a log for whutever */ LOGF("news", "%s posts in %s [%s]", nh->name, nh->group->name, nh->header); /* preserve (or not) the players mode */ if (p->edit_info->input_copy == news_command) { do_prompt(p, "News Mode >"); p->mode |= NEWSEDIT; } else p->mode &= ~NEWSEDIT; } /*** command functs ***/ /* the news command */ void news_command(player * p, char *str) { if (p->edit_info) { tell_player(p, " Can't do news commands whilst in the editor or pager.\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; } } /* exit news mode */ void exit_news_mode(player * p, char *str) { if (p->input_to_fn != news_command) { tell_player(p, " Idle git! ;)\n"); return; } tell_player(p, " Leaving news mode.\n"); p->input_to_fn = 0; p->flags |= PROMPT; p->mode &= ~NEWSEDIT; } void view_news_commands(player * p, char *str) { view_sub_commands(p, news_list); } /* toggle whether someone gets informed of news */ void toggle_news_inform(player * p, char *str) { if (!strcasecmp("off", str)) p->custom_flags &= ~NEWS_INFORM; else if (!strcasecmp("on", str)) p->custom_flags |= NEWS_INFORM; else p->custom_flags ^= NEWS_INFORM; if (p->custom_flags & NEWS_INFORM) tell_player(p, " You will be informed of new news when posted.\n"); else tell_player(p, " You will not be informed of new news when posted.\n"); } /* toggle seeing unread news postings on login */ void toggle_news_login(player * p, char *str) { if (!strcasecmp("off", str)) p->custom_flags |= NO_NEW_NEWS_INFORM; else if (!strcasecmp("on", str)) p->custom_flags &= ~NO_NEW_NEWS_INFORM; else p->custom_flags ^= NO_NEW_NEWS_INFORM; if (p->custom_flags & NO_NEW_NEWS_INFORM) tell_player(p, " You will not see unread news on login.\n"); else tell_player(p, " On login, you will be informed of unread news.\n"); } /* new news post */ void post_news(player * p, char *str) { newsgroup *group; news_header *nh; char *scan, *first, word[80]; if (!*str) { tell_player(p, " Format : post [group] <header>\n"); return; } strncpy(word, str, 79); /* get the first word, for checking group */ for (scan = word; (*scan && *scan != ' '); scan++); *scan = '\0'; if ((group = find_news_group(word))) { while (*str && *str != ' ') str++; /* cut off the first word */ if (!*str) /* they tried to trick us */ { tell_player(p, " Format : news post [group] <header>\n"); return; } str++; /* get past the space we stopped on */ } else group = &NewsGroups[0]; /* use the first group, by default for default */ if ((count_player_postings(p, group) >= group->max) && !(p->residency & ADMIN)) { tell_player(p, " You have posted your maximum amount of articles in that group.\n"); return; } /* get a new news_header */ nh = (news_header *) MALLOC(sizeof(news_header)); memset(nh, 0, sizeof(news_header)); /* setup its stuffs */ strncpy(nh->name, p->name, MAX_NAME - 1); strncpy(nh->header, str, MAX_TITLE - 1); nh->flags |= NEWS_ARTICLE; nh->read_count = 0; nh->group = group; first = first_char(p); scan = strstr(first, "post"); if (scan && (scan != first_char(p)) && (*(scan - 1) == 'a')) nh->flags |= ANONYMOUS; tell_player(p, " Now enter the body for the article.\n"); *stack = 0; start_edit(p, MAX_ARTICLE_SIZE, end_news_posting, quit_news_posting, stack, 1); if (p->edit_info) p->edit_info->misc = (void *) nh; else { LOGF("error", "failed to enter the editor " "in post_news() for %s", p->name); FREE(nh); } } /* follow up an article */ void followup(player * p, char *str) { newsgroup *group; news_header *scan, *nh; char *oldstack = stack; char *newbody, *body, *indent, *ptr, *first, *word; int which; if (!*str) { tell_player(p, " Format : news followup [group] #\n"); return; } if (isalpha(*str)) /* they want a news group */ { word = next_space(str); *word++ = 0; group = find_news_group(str); if (!group) { TELLPLAYER(p, " There doesn't appear to be a group '%s'\n", str); return; } which = atoi(word); } else { which = atoi(str); group = &NewsGroups[0]; } if (which < 1) { tell_player(p, " Format : news followup [group] #\n"); return; } if ((count_player_postings(p, group) >= group->max) && !(p->residency & ADMIN)) { tell_player(p, " You have posted your maximum amount of articles in that group.\n"); return; } scan = find_news_article(group, which); if (!scan) { if (group != &NewsGroups[0]) TELLPLAYER(p, " No such news posting '%d' in the %s group\n", which, group->name); else TELLPLAYER(p, " No such news posting '%d'\n", which); return; } sprintf(stack, "%s/%d.body", group->path, scan->id); stack = end_string(stack); body = load_file_to_string(oldstack); stack = oldstack; if (!body) { tell_player(p, " Ergs, no body file found for that news posting.\n"); LOGF("error", "No news body for header, id %d, group %s", scan->id, group->name); return; } body[strlen(body)] = 0; nh = (news_header *) MALLOC(sizeof(news_header)); if (!nh) { tell_player(p, " Erg, malloc fails, try again ...\n"); log("error", "malloc failed in news followup"); return; } memset(nh, 0, sizeof(news_header)); /* setup its stuffs */ strncpy(nh->name, p->name, MAX_NAME - 1); nh->flags |= NEWS_ARTICLE; nh->read_count = 0; nh->group = group; nh->followups = scan->followups + 1; strncpy(nh->header, str, MAX_TITLE - 1); if (strstr(scan->header, "Re: ") == scan->header) strncpy(nh->header, scan->header, MAX_TITLE - 1); else { sprintf(stack, "Re: %s", scan->header); strncpy(nh->header, stack, MAX_TITLE - 1); } nh->flags |= NEWS_ARTICLE; first = first_char(p); ptr = strstr(first, "followup"); if (ptr && (ptr != first_char(p)) && (*(ptr - 1) == 'a')) nh->flags |= ANONYMOUS; indent = body; newbody = stack; if (scan->flags & ANONYMOUS) stack += sprintf(stack, "\nFrom anonymous article written on %s ...\n", convert_time(scan->date)); else stack += sprintf(stack, "\nOn %s, %s wrote ...\n", convert_time(scan->date), scan->name); 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 possible ...\n"); start_edit(p, MAX_ARTICLE_SIZE, end_news_posting, quit_news_posting, newbody, 1); if (p->edit_info) p->edit_info->misc = (void *) nh; else { tell_player(p, " Erg, failed to enter the editor ...\n"); FREE(nh); } stack = oldstack; FREE(body); } /* list news articles */ void list_news(player * p, char *str) { newsgroup *group; news_header *scan; char *oldstack = stack; char middle[80], *which; int amt = 0; int page, pages; int count, ncount = 1; if (*str && isalpha(*str)) /* they want a specfic group */ { which = next_space(str); /* maybe they want a page too */ *which++ = 0; group = find_news_group(str); if (!group) { TELLPLAYER(p, " There doesn't appear to be a group '%s'\n", str); return; } } else { which = str; group = &NewsGroups[0]; } amt = count_news_articles(group); if (!amt) { if (group != &NewsGroups[0]) TELLPLAYER(p, " There appears to be no news in the %s group.\n", group->name); else tell_player(p, " There doesn't seem to be any news at all.\n"); return; } page = atoi(which); if (page <= 0) page = 1; page--; pages = (amt - 1) / (TERM_LINES - 2); if (page > pages) page = pages; /* setup info thinger */ if (group != &NewsGroups[0]) { if (amt == 1) sprintf(middle, "1 article in group %s", group->name); else sprintf(middle, "%d articles in group %s", amt, group->name); } else { if (amt == 1) strcpy(middle, "There is one news article"); else sprintf(middle, "There are %s articles", number2string(amt)); } pstack_mid(middle); /* set our first article to be the top if the page to be viewed */ count = page * (TERM_LINES - 2); for (scan = group->top; count; count--, ncount++) scan = scan->next; for (count = 0; ((count < (TERM_LINES - 1)) && scan); count++, ncount++) { stack += sprintf(stack, "%s [%d] %s%s%s%s^N %s\n", id_or_not(p, scan), ncount, read_count_string(scan->read_count), spaces10(ncount), spaces_followups(scan), scan->header, sender_str(p, scan)); scan = scan->next; } sprintf(middle, "Page %d of %d", page + 1, pages + 1); pstack_mid(middle); *stack++ = 0; tell_player(p, oldstack); stack = oldstack; } void sync_news_command(player * p, char *str) { if (!(NEWS_HAS_CHANGED)) { tell_player(p, " No news changes since last sync ...\n"); return; } tell_player(p, "Syncing news ...\n"); sync_all_news_headers(); tell_player(p, "Done ...\n"); } void read_article(player * p, char *str) { newsgroup *group; news_header *scan; int which, t, ngn; char *oldstack = stack; char *body, lastreader[MAX_INET_ADDR], head[MAX_TITLE], *word; if (!*str) { tell_player(p, " Format : news read [group] #\n"); return; } if (isalpha(*str)) /* they want a news group */ { word = next_space(str); *word++ = 0; group = find_news_group(str); if (!group) { TELLPLAYER(p, " There doesn't appear to be a group '%s'\n", str); return; } which = atoi(word); } else { which = atoi(str); group = &NewsGroups[0]; } if (which < 1) { tell_player(p, " Format : news read [group] #\n"); return; } scan = find_news_article(group, which); if (!scan) { if (group != &NewsGroups[0]) TELLPLAYER(p, " No such news posting '%d' in the %s group\n", which, group->name); else TELLPLAYER(p, " No such news posting '%d'\n", which); return; } sprintf(stack, "%s/%d.body", group->path, scan->id); stack = end_string(stack); body = load_file_to_string(oldstack); stack = oldstack; if (!body) { tell_player(p, " Ergs, no body file found for that news posting.\n"); LOGF("error", "No news body for header, id %d, group %s", scan->id, group->name); return; } NEWS_CHANGED; ngn = find_news_group_number(group); if (ngn > -1 && ngn < MAX_LAST_NEWS_INTS) { if (p->news_last[ngn] < scan->date) p->news_last[ngn] = scan->date; } strncpy(lastreader, scan->lastreader, MAX_INET_ADDR - 1); if (!*scan->lastreader || strcasecmp(p->inet_addr, scan->lastreader)) { scan->read_count++; strncpy(scan->lastreader, p->inet_addr, MAX_INET_ADDR - 1); } t = time(0); memset(head, 0, MAX_TITLE); if (group != &NewsGroups[0]) /* is it the the defaault ? */ { strncpy(head, group->name, MAX_TITLE - 1); /* put the group up top */ pstack_mid(head); } else /* just a line */ stack += sprintf(stack, LINE); stack += sprintf(stack, " Subject: %s^N\n", scan->header); if (scan->flags & ANONYMOUS) { if (!strcasecmp(scan->name, p->name)) stack += sprintf(stack, " Posted anonymously by you on %s\n", convert_time(scan->date)); else if (p->residency & ADMIN) stack += sprintf(stack, " Posted anonymously by %s on %s\n", scan->name, convert_time(scan->date)); else stack += sprintf(stack, " Posted anonymously on %s\n", convert_time(scan->date)); } else stack += sprintf(stack, " Posted by %s on %s\n", scan->name, convert_time(scan->date)); if (scan->read_count == 1) stack += sprintf(stack, " Article has been read one time.\n"); else { stack += sprintf(stack, " Article has been read %s times.\n", number2string(scan->read_count)); if (p->residency & (LOWER_ADMIN | ADMIN)) stack += sprintf(stack, " Last read by someone from %s.\n", lastreader); } if (p->residency & (LOWER_ADMIN | ADMIN)) { if (!(scan->flags & STICKY_ARTICLE)) stack += sprintf(stack, " Times out in %s.\n", word_time(NEWS_TIMEOUT + (scan->date - t))); else stack += sprintf(stack, " Article will never timeout.\n"); } stack += sprintf(stack, LINE); sprintf(stack, "%s\n", body); stack = end_string(stack); pager(p, oldstack); stack = oldstack; FREE(body); } void remove_article(player * p, char *str) { newsgroup *group; news_header *nh; char *word; int which; if (!*str) { tell_player(p, " Format : news remove #\n"); return; } if (isalpha(*str)) /* they want a news group */ { word = next_space(str); *word++ = 0; group = find_news_group(str); if (!group) { TELLPLAYER(p, " There doesn't appear to be a group '%s'\n", str); return; } which = atoi(word); } else { which = atoi(str); group = &NewsGroups[0]; } if (which < 1) { tell_player(p, " Format : news remove [group] #\n"); return; } nh = find_news_article(group, which); if (!nh) { TELLPLAYER(p, " No such news posting '%d'\n", which); return; } if (strcasecmp(nh->name, p->name) && !(p->residency & ADMIN)) { tell_player(p, " You may only remove posts you yourself have made.\n"); return; } NEWS_CHANGED; nh->flags |= DELETE_ME; if (strcasecmp(nh->name, p->name)) LOGF("remove", "%s removed news in %s from %s entitled %s", p->name, group->name, nh->name, nh->header); tell_player(p, " Article removed ...\n"); } void news_setsticky_command(player * p, char *str) { newsgroup *group; news_header *nh; char *word; int which; if (!*str) { tell_player(p, " Format : news sticky #\n"); return; } if (isalpha(*str)) /* they want a news group */ { word = next_space(str); *word++ = 0; group = find_news_group(str); if (!group) { TELLPLAYER(p, " There doesn't appear to be a group '%s'\n", str); return; } which = atoi(word); } else { which = atoi(str); group = &NewsGroups[0]; } if (which < 1) { tell_player(p, " Format : news sticky [group] #\n"); return; } nh = find_news_article(group, which); if (!nh) { TELLPLAYER(p, " No such news posting '%d'\n", which); return; } nh->flags ^= STICKY_ARTICLE; if (nh->flags & STICKY_ARTICLE) tell_player(p, " Sticky bit set ...\n"); else tell_player(p, " Sticky bit removed ...\n"); } void list_news_groups(player * p, char *str) { int i; char *oldstack = stack; char temp[70]; sprintf(temp, "%s News Groups", get_config_msg("talker_name")); pstack_mid(temp); for (i = 1; NewsGroups[i].name[0]; i++) { if (NewsGroups[i].required_priv && !(p->residency & NewsGroups[i].required_priv)) continue; stack += sprintf(stack, "%-20s %s\n", NewsGroups[i].name, NewsGroups[i].desc); } stack += sprintf(stack, LINE "\n"); stack = end_string(stack); pager(p, oldstack); stack = oldstack; } void news_checkown_command(player * p, char *str) { newsgroup *group; news_header *scan; int cnt, i, tot = 0; char *oldstack = stack; if (!*str || (*str && strcasecmp(str, "all"))) { if (*str) group = find_news_group(str); else group = &NewsGroups[0]; if (!group) { TELLPLAYER(p, " There doesn't appear to be a newsgroup '%s'.\n", str); return; } for (scan = group->top, cnt = 1; scan; scan = scan->next, cnt++) if (!strcasecmp(scan->name, p->name)) { stack += sprintf(stack, "[%d] %s\n", cnt, scan->header); tot++; } stack = end_string(stack); if (tot) tell_player(p, oldstack); else tell_player(p, " You have posted no news ...\n"); stack = oldstack; return; } for (i = 0; NewsGroups[i].name[0]; i++) { if (NewsGroups[i].required_priv && !(p->residency & NewsGroups[i].required_priv)) continue; stack += sprintf(stack, "---- %s group\n", NewsGroups[i].name); for (scan = NewsGroups[i].top, cnt = 1; scan; scan = scan->next, cnt++) if (!strcasecmp(scan->name, p->name)) { stack += sprintf(stack, "[%d] %s\n", cnt, scan->header); tot++; } } stack = end_string(stack); if (tot) pager(p, oldstack); else tell_player(p, " You have posted no news ...\n"); stack = oldstack; } void remove_all_news_command(player * p, char *str) { int rmd; if (!*str) { tell_player(p, " Format : remove_all_news <player>\n"); return; } rmd = remove_all_news(p, str); if (rmd) { NEWS_CHANGED; TELLPLAYER(p, " Removed %d articles ...\n", rmd); LOGF("remove", "%s removed %d postings of %s", p->name, rmd, str); } else TELLPLAYER(p, " No articles found posted by %s to be removed.\n", str); } void news_help(player * p, char *str) { char holder[15]; /* use instead of taking a chance with a constant */ memset(holder, 0, 15); strcpy(holder, "news"); help(p, holder); } /* lil stuffs for sus shortcutting */ void sus_news_post(player * p, char *str) { char *oldstack = stack; if (!*str) { tell_player(p, " Format : spost <subject>\n"); return; } sprintf(stack, "sus %s", str); stack = end_string(stack); post_news(p, oldstack); stack = oldstack; } void sus_news_list(player * p, char *str) { char *oldstack = stack; if (!*str) { sprintf(stack, "sus"); /* use stack so we dont muck constants */ stack = end_string(stack); list_news(p, oldstack); stack = oldstack; return; } sprintf(stack, "sus %s", str); stack = end_string(stack); list_news(p, oldstack); stack = oldstack; } void sus_news_read(player * p, char *str) { char *oldstack = stack; if (!*str) { tell_player(p, " Format : sread #\n"); return; } sprintf(stack, "sus %s", str); stack = end_string(stack); read_article(p, oldstack); stack = oldstack; } /* and for admin ... */ void ad_news_post(player * p, char *str) { char *oldstack = stack; if (!*str) { tell_player(p, " Format : adpost <subject>\n"); return; } sprintf(stack, "admin %s", str); stack = end_string(stack); post_news(p, oldstack); stack = oldstack; } void ad_news_list(player * p, char *str) { char *oldstack = stack; if (!*str) { sprintf(stack, "admin"); stack = end_string(stack); list_news(p, oldstack); stack = oldstack; return; } sprintf(stack, "admin %s", str); stack = end_string(stack); list_news(p, oldstack); stack = oldstack; } void ad_news_read(player * p, char *str) { char *oldstack = stack; if (!*str) { tell_player(p, " Format : adread #\n"); return; } sprintf(stack, "admin %s", str); stack = end_string(stack); read_article(p, oldstack); stack = oldstack; } void news_stats(player * p, char *str) { char *oldstack = stack; int curcnt, totalcnt = 0, i; stack += sprintf(stack, " The NewsGroups array is %d bytes in size.\n", sizeof(NewsGroups)); for (i = 0; NewsGroups[i].name[0]; i++) { curcnt = count_news_articles(&NewsGroups[i]); stack += sprintf(stack, " Newsgroup %-20s - %6d posting/s, %d bytes\n", NewsGroups[i].name, curcnt, curcnt * sizeof(news_header)); totalcnt += sizeof(news_header) * curcnt; } stack += sprintf(stack, " %d bytes is total resident memory used" " by all news headers\n", totalcnt); stack += sprintf(stack, " %s til the next news sync", word_time(news_sync)); stack += sprintf(stack, ", %s interval.\n", word_time(NEWS_SYNC_INTERVAL)); stack = end_string(stack); pager(p, oldstack); stack = oldstack; } void news_read_next(player * p, char *str) { newsgroup *ng; int n; char poppy[16]; if (*str) { ng = find_news_group(str); if (!ng) { TELLPLAYER(p, " No such news group '%s' ...\n", str); return; } } else ng = &NewsGroups[0]; n = next_unread(p, ng); if (!n) { if (ng == &NewsGroups[0]) tell_player(p, " No more unread articles.\n"); else TELLPLAYER(p, " No more unread articles in '%s' group.\n", str); return; } sprintf(poppy, "%d", n); read_article(p, poppy); } void nunews_version(void) { stack += sprintf(stack, " -=*> NuNews v0.5 (by phypor) enabled.\n"); }