Anope IRC Services  Version 1.8
news.c
Go to the documentation of this file.
1 
2 /* News functions.
3  *
4  * (C) 2003-2014 Anope Team
5  * Contact us at team@anope.org
6  *
7  * Please read COPYING and README for further details.
8  *
9  * Based on the original code of Epona by Lara.
10  * Based on the original code of Services by Andy Church.
11  *
12  *
13  */
14 
15 #include "services.h"
16 #include "pseudo.h"
17 
18 /*************************************************************************/
19 
22 NewsItem *news = NULL;
23 
24 /*************************************************************************/
25 
26 /* List of messages for each news type. This simplifies message sending. */
27 
28 #define MSG_SYNTAX 0
29 #define MSG_LIST_HEADER 1
30 #define MSG_LIST_ENTRY 2
31 #define MSG_LIST_NONE 3
32 #define MSG_ADD_SYNTAX 4
33 #define MSG_ADD_FULL 5
34 #define MSG_ADDED 6
35 #define MSG_DEL_SYNTAX 7
36 #define MSG_DEL_NOT_FOUND 8
37 #define MSG_DELETED 9
38 #define MSG_DEL_NONE 10
39 #define MSG_DELETED_ALL 11
40 #define MSG_MAX 11
41 
42 struct newsmsgs {
44  char *name;
45  int msgs[MSG_MAX + 1];
46 };
47 
48 struct newsmsgs msgarray[] = {
49  {NEWS_LOGON, "LOGON",
50  {NEWS_LOGON_SYNTAX,
51  NEWS_LOGON_LIST_HEADER,
52  NEWS_LOGON_LIST_ENTRY,
53  NEWS_LOGON_LIST_NONE,
54  NEWS_LOGON_ADD_SYNTAX,
55  NEWS_LOGON_ADD_FULL,
56  NEWS_LOGON_ADDED,
57  NEWS_LOGON_DEL_SYNTAX,
58  NEWS_LOGON_DEL_NOT_FOUND,
59  NEWS_LOGON_DELETED,
60  NEWS_LOGON_DEL_NONE,
61  NEWS_LOGON_DELETED_ALL}
62  },
63  {NEWS_OPER, "OPER",
64  {NEWS_OPER_SYNTAX,
65  NEWS_OPER_LIST_HEADER,
66  NEWS_OPER_LIST_ENTRY,
67  NEWS_OPER_LIST_NONE,
68  NEWS_OPER_ADD_SYNTAX,
69  NEWS_OPER_ADD_FULL,
70  NEWS_OPER_ADDED,
71  NEWS_OPER_DEL_SYNTAX,
72  NEWS_OPER_DEL_NOT_FOUND,
73  NEWS_OPER_DELETED,
74  NEWS_OPER_DEL_NONE,
75  NEWS_OPER_DELETED_ALL}
76  },
77  {NEWS_RANDOM, "RANDOM",
78  {NEWS_RANDOM_SYNTAX,
79  NEWS_RANDOM_LIST_HEADER,
80  NEWS_RANDOM_LIST_ENTRY,
81  NEWS_RANDOM_LIST_NONE,
82  NEWS_RANDOM_ADD_SYNTAX,
83  NEWS_RANDOM_ADD_FULL,
84  NEWS_RANDOM_ADDED,
85  NEWS_RANDOM_DEL_SYNTAX,
86  NEWS_RANDOM_DEL_NOT_FOUND,
87  NEWS_RANDOM_DELETED,
88  NEWS_RANDOM_DEL_NONE,
89  NEWS_RANDOM_DELETED_ALL}
90  }
91 };
92 
93 static int *findmsgs(int16 type, char **typename)
94 {
95  int i;
96  for (i = 0; i < lenof(msgarray); i++) {
97  if (msgarray[i].type == type) {
98  if (typename)
99  *typename = msgarray[i].name;
100  return msgarray[i].msgs;
101  }
102  }
103  return NULL;
104 }
105 
106 /*************************************************************************/
107 
108 /* Called by the main OperServ routine in response to a NEWS command. */
109 static void do_news(User * u, int16 type);
110 
111 /* Lists all a certain type of news. */
112 static void do_news_list(User * u, int16 type, int *msgs);
113 
114 /* Add news items. */
115 static void do_news_add(User * u, int16 type, int *msgs,
116  const char *typename);
117 static int add_newsitem(User * u, const char *text, int16 type);
118 
119 /* Delete news items. */
120 static void do_news_del(User * u, int16 type, int *msgs,
121  const char *typename);
122 static int del_newsitem(int num, int16 type);
123 
124 /*************************************************************************/
125 /****************************** Statistics *******************************/
126 /*************************************************************************/
127 
128 void get_news_stats(long *nrec, long *memuse)
129 {
130  long mem;
131  int i;
132 
133  mem = sizeof(NewsItem) * news_size;
134  for (i = 0; i < nnews; i++)
135  mem += strlen(news[i].text) + 1;
136  *nrec = nnews;
137  *memuse = mem;
138 }
139 
140 /*************************************************************************/
141 /*********************** News item loading/saving ************************/
142 /*************************************************************************/
143 
144 #define SAFE(x) do { \
145  if ((x) < 0) { \
146  if (!forceload) \
147  fatal("Read error on %s", NewsDBName); \
148  nnews = i; \
149  break; \
150  } \
151 } while (0)
152 
153 void load_news()
154 {
155  dbFILE *f;
156  int i;
157  uint16 n;
158  uint32 tmp32;
159 
160  if (!(f = open_db(s_OperServ, NewsDBName, "r", NEWS_VERSION)))
161  return;
162  switch (i = get_file_version(f)) {
163  case 9:
164  case 8:
165  case 7:
166  SAFE(read_int16(&n, f));
167  nnews = n;
168  if (nnews < 8)
169  news_size = 16;
170  else if (nnews >= 16384)
171  news_size = 32767;
172  else
173  news_size = 2 * nnews;
174  news = scalloc(sizeof(*news) * news_size, 1);
175  if (!nnews) {
176  close_db(f);
177  return;
178  }
179  for (i = 0; i < nnews; i++) {
180  SAFE(read_int16(&news[i].type, f));
181  SAFE(read_int32(&news[i].num, f));
182  SAFE(read_string(&news[i].text, f));
183  SAFE(read_buffer(news[i].who, f));
184  SAFE(read_int32(&tmp32, f));
185  news[i].time = tmp32;
186  }
187  break;
188 
189  default:
190  fatal("Unsupported version (%d) on %s", i, NewsDBName);
191  } /* switch (ver) */
192 
193  close_db(f);
194 }
195 
196 #undef SAFE
197 
198 /*************************************************************************/
199 
200 #define SAFE(x) do { \
201  if ((x) < 0) { \
202  restore_db(f); \
203  log_perror("Write error on %s", NewsDBName); \
204  if (time(NULL) - lastwarn > WarningTimeout) { \
205  anope_cmd_global(NULL, "Write error on %s: %s", NewsDBName, \
206  strerror(errno)); \
207  lastwarn = time(NULL); \
208  } \
209  return; \
210  } \
211 } while (0)
212 
213 void save_news()
214 {
215  dbFILE *f;
216  int i;
217  static time_t lastwarn = 0;
218 
219  if (!(f = open_db(s_OperServ, NewsDBName, "w", NEWS_VERSION)))
220  return;
221  SAFE(write_int16(nnews, f));
222  for (i = 0; i < nnews; i++) {
223  SAFE(write_int16(news[i].type, f));
224  SAFE(write_int32(news[i].num, f));
225  SAFE(write_string(news[i].text, f));
226  SAFE(write_buffer(news[i].who, f));
227  SAFE(write_int32(news[i].time, f));
228  }
229  close_db(f);
230 }
231 
232 #undef SAFE
233 
235 {
236 #ifdef USE_RDB
237  int i;
238  NewsItem *ni;
239 
240  if (!rdb_open())
241  return;
242 
243  if (rdb_tag_table("anope_os_news") == 0) {
244  alog("Unable to tag table 'anope_os_news' - News RDB save failed.");
245  return;
246  }
247 
248  for (i = 0; i < nnews; i++) {
249  ni = &news[i];
250  if (rdb_save_news(ni) == 0) {
251  alog("Unable to save NewsItem %d - News RDB save failed.", ni->num);
252  return;
253  }
254  }
255 
256  if (rdb_clean_table("anope_os_news") == 0) {
257  alog("Unable to clean table 'anope_os_news' - News RDB save failed.");
258  return;
259  }
260 
261  rdb_close();
262 #endif
263 }
264 
265 /*************************************************************************/
266 /***************************** News display ******************************/
267 /*************************************************************************/
268 
270 {
271  int msg;
272 
273  if (type == NEWS_LOGON) {
274  msg = NEWS_LOGON_TEXT;
275  } else if (type == NEWS_OPER) {
276  msg = NEWS_OPER_TEXT;
277  } else if (type == NEWS_RANDOM) {
278  msg = NEWS_RANDOM_TEXT;
279  } else {
280  alog("news: Invalid type (%d) to display_news()", type);
281  return;
282  }
283 
284  if (type == NEWS_RANDOM) {
285  static int current_news = -1;
286  int count = 0;
287 
288  if (!nnews)
289  return;
290 
291  while (count++ < nnews) {
292  if (++current_news >= nnews)
293  current_news = 0;
294 
295  if (news[current_news].type == type) {
296  struct tm *tm;
297  char timebuf[64];
298 
299  tm = localtime(&news[current_news].time);
300  strftime_lang(timebuf, sizeof(timebuf), u,
301  STRFTIME_SHORT_DATE_FORMAT, tm);
302  notice_lang(s_GlobalNoticer, u, msg, timebuf,
303  news[current_news].text);
304 
305  return;
306  }
307  }
308  } else {
309  int i, count = 0;
310 
311  for (i = nnews - 1; i >= 0; i--) {
312  if (count >= NewsCount)
313  break;
314  if (news[i].type == type)
315  count++;
316  }
317  while (++i < nnews) {
318  if (news[i].type == type) {
319  struct tm *tm;
320  char timebuf[64];
321 
322  tm = localtime(&news[i].time);
323  strftime_lang(timebuf, sizeof(timebuf), u,
324  STRFTIME_SHORT_DATE_FORMAT, tm);
325  notice_lang(s_GlobalNoticer, u, msg, timebuf,
326  news[i].text);
327  }
328  }
329  }
330 }
331 
332 /*************************************************************************/
333 /***************************** News editing ******************************/
334 /*************************************************************************/
335 /* Declared in extern.h */
337 {
338  do_news(u, NEWS_LOGON);
339  return MOD_CONT;
340 }
341 
342 
343 /* Declared in extern.h */
345 {
346  do_news(u, NEWS_OPER);
347  return MOD_CONT;
348 }
349 
350 /* Declared in extern.h */
352 {
353  do_news(u, NEWS_RANDOM);
354  return MOD_CONT;
355 }
356 
357 /*************************************************************************/
358 
359 /* Main news command handling routine. */
360 void do_news(User * u, short type)
361 {
362  int is_servadmin = is_services_admin(u);
363  char *cmd = strtok(NULL, " ");
364  char *typename;
365  int *msgs;
366 
367  msgs = findmsgs(type, &typename);
368  if (!msgs) {
369  alog("news: Invalid type to do_news()");
370  return;
371  }
372 
373  if (!cmd)
374  cmd = "";
375 
376  if (stricmp(cmd, "LIST") == 0) {
377  do_news_list(u, type, msgs);
378  } else if (stricmp(cmd, "ADD") == 0) {
379  if (is_servadmin)
380  do_news_add(u, type, msgs, typename);
381  else
382  notice_lang(s_OperServ, u, PERMISSION_DENIED);
383 
384  } else if (stricmp(cmd, "DEL") == 0) {
385  if (is_servadmin)
386  do_news_del(u, type, msgs, typename);
387  else
388  notice_lang(s_OperServ, u, PERMISSION_DENIED);
389 
390  } else {
391  char buf[32];
392  snprintf(buf, sizeof(buf), "%sNEWS", typename);
393  syntax_error(s_OperServ, u, buf, msgs[MSG_SYNTAX]);
394  }
395 }
396 
397 /*************************************************************************/
398 
399 /* Handle a {LOGON,OPER}NEWS LIST command. */
400 
401 static void do_news_list(User * u, int16 type, int *msgs)
402 {
403  int i, count = 0;
404  char timebuf[64];
405  struct tm *tm;
406 
407  for (i = 0; i < nnews; i++) {
408  if (news[i].type == type) {
409  if (count == 0)
411  tm = localtime(&news[i].time);
412  strftime_lang(timebuf, sizeof(timebuf),
413  u, STRFTIME_DATE_TIME_FORMAT, tm);
415  news[i].num, timebuf,
416  *news[i].who ? news[i].who : "<unknown>",
417  news[i].text);
418  count++;
419  }
420  }
421  if (count == 0)
423  else {
424  notice_lang(s_OperServ, u, END_OF_ANY_LIST, "News");
425  }
426 }
427 
428 /*************************************************************************/
429 
430 /* Handle a {LOGON,OPER}NEWS ADD command. */
431 
432 static void do_news_add(User * u, int16 type, int *msgs,
433  const char *typename)
434 {
435  char *text = strtok(NULL, "");
436  int n;
437 
438  if (!text) {
439  char buf[32];
440  snprintf(buf, sizeof(buf), "%sNEWS", typename);
441  syntax_error(s_OperServ, u, buf, msgs[MSG_ADD_SYNTAX]);
442  } else {
443  if (readonly) {
444  notice_lang(s_OperServ, u, READ_ONLY_MODE);
445  return;
446  }
447  n = add_newsitem(u, text, type);
448  if (n < 0)
450  else
451  notice_lang(s_OperServ, u, msgs[MSG_ADDED], n);
452  }
453 }
454 
455 
456 /* Actually add a news item. Return the number assigned to the item, or -1
457  * if the news list is full (32767 items).
458  */
459 
460 static int add_newsitem(User * u, const char *text, short type)
461 {
462  int i, num;
463 
464  if (nnews >= 32767)
465  return -1;
466 
467  if (nnews >= news_size) {
468  if (news_size < 8)
469  news_size = 8;
470  else
471  news_size *= 2;
472  news = srealloc(news, sizeof(*news) * news_size);
473  }
474  num = 0;
475  for (i = nnews - 1; i >= 0; i--) {
476  if (news[i].type == type) {
477  num = news[i].num;
478  break;
479  }
480  }
481  news[nnews].type = type;
482  news[nnews].num = num + 1;
483  news[nnews].text = sstrdup(text);
484  news[nnews].time = time(NULL);
485  strscpy(news[nnews].who, u->nick, NICKMAX);
486  nnews++;
487  return num + 1;
488 }
489 
490 /*************************************************************************/
491 
492 /* Handle a {LOGON,OPER}NEWS DEL command. */
493 
494 static void do_news_del(User * u, int16 type, int *msgs,
495  const char *typename)
496 {
497  char *text = strtok(NULL, " ");
498  int i, num;
499 
500  if (!text) {
501  char buf[32];
502  snprintf(buf, sizeof(buf), "%sNEWS", typename);
503  syntax_error(s_OperServ, u, buf, msgs[MSG_DEL_SYNTAX]);
504  } else {
505  if (readonly) {
506  notice_lang(s_OperServ, u, READ_ONLY_MODE);
507  return;
508  }
509  if (stricmp(text, "ALL") != 0) {
510  num = atoi(text);
511  if (num > 0 && del_newsitem(num, type)) {
512  notice_lang(s_OperServ, u, msgs[MSG_DELETED], num);
513  /* Reset the order - #0000397 */
514  for (i = 0; i < nnews; i++) {
515  if ((news[i].type == type) && (news[i].num > num))
516  news[i].num--;
517  }
518  } else
519  notice_lang(s_OperServ, u, msgs[MSG_DEL_NOT_FOUND], num);
520  } else {
521  if (del_newsitem(0, type))
523  else
525  }
526  }
527 }
528 
529 
530 /* Actually delete a news item. If `num' is 0, delete all news items of
531  * the given type. Returns the number of items deleted.
532  */
533 
534 static int del_newsitem(int num, short type)
535 {
536  int i;
537  int count = 0;
538 
539  for (i = 0; i < nnews; i++) {
540  if (news[i].type == type && (num == 0 || news[i].num == num)) {
541  free(news[i].text);
542  count++;
543  nnews--;
544  if (i < nnews)
545  memcpy(news + i, news + i + 1,
546  sizeof(*news) * (nnews - i));
547  i--;
548  }
549  }
550  return count;
551 }
552 
553 /*************************************************************************/
#define NEWS_VERSION
Definition: services.h:464
void save_news()
Definition: news.c:213
NewsItem * news
Definition: news.c:22
E int readonly
Definition: extern.h:776
int32 news_size
Definition: news.c:21
char nick[NICKMAX]
Definition: services.h:875
E int snprintf(char *buf, size_t size, const char *fmt,...)
Definition: compat.c:37
static int * findmsgs(int16 type, char **typename)
Definition: news.c:93
#define lenof(a)
Definition: defs.h:30
#define NEWS_LOGON
Definition: services.h:1049
#define MSG_LIST_ENTRY
Definition: news.c:30
uint32 num
Definition: services.h:1120
#define MSG_ADD_FULL
Definition: news.c:33
void load_news()
Definition: news.c:153
int32 nnews
Definition: news.c:20
E int stricmp(const char *s1, const char *s2)
Definition: compat.c:58
#define read_buffer(buf, f)
Definition: datafiles.h:61
#define NEWS_RANDOM
Definition: services.h:1051
int16 type
Definition: news.c:43
#define MSG_LIST_NONE
Definition: news.c:31
void get_news_stats(long *nrec, long *memuse)
Definition: news.c:128
E char * strscpy(char *d, const char *s, size_t len)
Definition: db-merger.c:1886
#define MSG_DELETED
Definition: news.c:37
E char * NewsDBName
Definition: extern.h:338
E void syntax_error(char *service, User *u, const char *command, int msgnum)
Definition: language.c:295
E void notice_lang(char *source, User *dest, int message,...)
Definition: send.c:169
int rdb_save_news(NewsItem *ni)
Definition: rdb.c:338
#define MSG_DEL_SYNTAX
Definition: news.c:35
E int read_int16(uint16 *ret, dbFILE *f)
Definition: datafiles.c:405
E char * sstrdup(const char *s)
Definition: memory.c:105
int rdb_close()
Definition: rdb.c:47
E void * scalloc(long elsize, long els)
Definition: memory.c:55
E void E void E void fatal(const char *fmt,...) FORMAT(printf
E char * s_OperServ
Definition: extern.h:289
E int read_string(char **ret, dbFILE *f)
Definition: db-merger.c:1806
E int get_file_version(dbFILE *f)
Definition: datafiles.c:30
E int write_int16(uint16 val, dbFILE *f)
Definition: db-merger.c:1737
int do_randomnews(User *u)
Definition: news.c:351
int32_t int32
Definition: db-merger.c:122
E void close_db(dbFILE *f)
Definition: db-merger.c:1706
#define MSG_SYNTAX
Definition: news.c:28
u_int32_t uint32
Definition: db-merger.c:123
E dbFILE * open_db(const char *service, const char *filename, const char *mode, uint32 version)
Definition: datafiles.c:295
time_t time
Definition: services.h:1123
E void alog(const char *fmt,...) FORMAT(printf
int do_logonnews(User *u)
Definition: news.c:336
#define MOD_CONT
Definition: modules.h:54
static void do_news_add(User *u, int16 type, int *msgs, const char *typename)
Definition: news.c:432
int16_t int16
Definition: db-merger.c:120
static void do_news_list(User *u, int16 type, int *msgs)
Definition: news.c:401
#define MSG_ADD_SYNTAX
Definition: news.c:32
E int is_services_admin(User *u)
Definition: operserv.c:591
int do_opernews(User *u)
Definition: news.c:344
void save_rdb_news()
Definition: news.c:234
static time_t lastwarn
Definition: datafiles.c:19
Definition: news.c:42
#define MSG_LIST_HEADER
Definition: news.c:29
#define MSG_MAX
Definition: news.c:40
int rdb_clean_table(char *table)
Definition: rdb.c:113
struct newsitem_ NewsItem
Definition: services.h:237
#define NICKMAX
Definition: config.h:62
#define MSG_DELETED_ALL
Definition: news.c:39
char * text
Definition: services.h:1121
int rdb_open()
Definition: rdb.c:31
E int NewsCount
Definition: extern.h:361
E void * srealloc(void *oldptr, long newsize)
Definition: memory.c:80
#define SAFE(x)
Definition: news.c:200
int msgs[MSG_MAX+1]
Definition: news.c:45
int rdb_tag_table(char *table)
Definition: rdb.c:75
struct newsmsgs msgarray[]
Definition: news.c:48
char * name
Definition: news.c:44
E int read_int32(uint32 *ret, dbFILE *f)
Definition: datafiles.c:444
E int strftime_lang(char *buf, int size, User *u, int format, struct tm *tm)
Definition: language.c:240
E int write_string(const char *s, dbFILE *f)
Definition: db-merger.c:1826
#define write_buffer(buf, f)
Definition: datafiles.h:62
E int write_int32(uint32 val, dbFILE *f)
Definition: db-merger.c:1773
uint16 type
Definition: services.h:1119
#define NEWS_OPER
Definition: services.h:1050
static int add_newsitem(User *u, const char *text, int16 type)
static int del_newsitem(int num, int16 type)
static void do_news(User *u, int16 type)
E char * s_GlobalNoticer
Definition: extern.h:290
void display_news(User *u, int16 type)
Definition: news.c:269
#define MSG_ADDED
Definition: news.c:34
#define MSG_DEL_NONE
Definition: news.c:38
static void do_news_del(User *u, int16 type, int *msgs, const char *typename)
Definition: news.c:494
u_int16_t uint16
Definition: db-merger.c:121
#define MSG_DEL_NOT_FOUND
Definition: news.c:36