00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "services.h"
00017 #include "pseudo.h"
00018
00019
00020
00021 int32 nnews = 0;
00022 int32 news_size = 0;
00023 NewsItem *news = NULL;
00024
00025
00026
00027
00028
00029 #define MSG_SYNTAX 0
00030 #define MSG_LIST_HEADER 1
00031 #define MSG_LIST_ENTRY 2
00032 #define MSG_LIST_NONE 3
00033 #define MSG_ADD_SYNTAX 4
00034 #define MSG_ADD_FULL 5
00035 #define MSG_ADDED 6
00036 #define MSG_DEL_SYNTAX 7
00037 #define MSG_DEL_NOT_FOUND 8
00038 #define MSG_DELETED 9
00039 #define MSG_DEL_NONE 10
00040 #define MSG_DELETED_ALL 11
00041 #define MSG_MAX 11
00042
00043 struct newsmsgs {
00044 int16 type;
00045 char *name;
00046 int msgs[MSG_MAX + 1];
00047 };
00048
00049 struct newsmsgs msgarray[] = {
00050 {NEWS_LOGON, "LOGON",
00051 {NEWS_LOGON_SYNTAX,
00052 NEWS_LOGON_LIST_HEADER,
00053 NEWS_LOGON_LIST_ENTRY,
00054 NEWS_LOGON_LIST_NONE,
00055 NEWS_LOGON_ADD_SYNTAX,
00056 NEWS_LOGON_ADD_FULL,
00057 NEWS_LOGON_ADDED,
00058 NEWS_LOGON_DEL_SYNTAX,
00059 NEWS_LOGON_DEL_NOT_FOUND,
00060 NEWS_LOGON_DELETED,
00061 NEWS_LOGON_DEL_NONE,
00062 NEWS_LOGON_DELETED_ALL}
00063 },
00064 {NEWS_OPER, "OPER",
00065 {NEWS_OPER_SYNTAX,
00066 NEWS_OPER_LIST_HEADER,
00067 NEWS_OPER_LIST_ENTRY,
00068 NEWS_OPER_LIST_NONE,
00069 NEWS_OPER_ADD_SYNTAX,
00070 NEWS_OPER_ADD_FULL,
00071 NEWS_OPER_ADDED,
00072 NEWS_OPER_DEL_SYNTAX,
00073 NEWS_OPER_DEL_NOT_FOUND,
00074 NEWS_OPER_DELETED,
00075 NEWS_OPER_DEL_NONE,
00076 NEWS_OPER_DELETED_ALL}
00077 },
00078 {NEWS_RANDOM, "RANDOM",
00079 {NEWS_RANDOM_SYNTAX,
00080 NEWS_RANDOM_LIST_HEADER,
00081 NEWS_RANDOM_LIST_ENTRY,
00082 NEWS_RANDOM_LIST_NONE,
00083 NEWS_RANDOM_ADD_SYNTAX,
00084 NEWS_RANDOM_ADD_FULL,
00085 NEWS_RANDOM_ADDED,
00086 NEWS_RANDOM_DEL_SYNTAX,
00087 NEWS_RANDOM_DEL_NOT_FOUND,
00088 NEWS_RANDOM_DELETED,
00089 NEWS_RANDOM_DEL_NONE,
00090 NEWS_RANDOM_DELETED_ALL}
00091 }
00092 };
00093
00094 static int *findmsgs(int16 type, char **typename)
00095 {
00096 int i;
00097 for (i = 0; i < lenof(msgarray); i++) {
00098 if (msgarray[i].type == type) {
00099 if (typename)
00100 *typename = msgarray[i].name;
00101 return msgarray[i].msgs;
00102 }
00103 }
00104 return NULL;
00105 }
00106
00107
00108
00109
00110 static void do_news(User * u, int16 type);
00111
00112
00113 static void do_news_list(User * u, int16 type, int *msgs);
00114
00115
00116 static void do_news_add(User * u, int16 type, int *msgs,
00117 const char *typename);
00118 static int add_newsitem(User * u, const char *text, int16 type);
00119
00120
00121 static void do_news_del(User * u, int16 type, int *msgs,
00122 const char *typename);
00123 static int del_newsitem(int num, int16 type);
00124
00125
00126
00127
00128
00129 void get_news_stats(long *nrec, long *memuse)
00130 {
00131 long mem;
00132 int i;
00133
00134 mem = sizeof(NewsItem) * news_size;
00135 for (i = 0; i < nnews; i++)
00136 mem += strlen(news[i].text) + 1;
00137 *nrec = nnews;
00138 *memuse = mem;
00139 }
00140
00141
00142
00143
00144
00145 #define SAFE(x) do { \
00146 if ((x) < 0) { \
00147 if (!forceload) \
00148 fatal("Read error on %s", NewsDBName); \
00149 nnews = i; \
00150 break; \
00151 } \
00152 } while (0)
00153
00154 void load_news()
00155 {
00156 dbFILE *f;
00157 int i;
00158 uint16 n;
00159 uint32 tmp32;
00160
00161 if (!(f = open_db(s_OperServ, NewsDBName, "r", NEWS_VERSION)))
00162 return;
00163 switch (i = get_file_version(f)) {
00164 case 9:
00165 case 8:
00166 case 7:
00167 SAFE(read_int16(&n, f));
00168 nnews = n;
00169 if (nnews < 8)
00170 news_size = 16;
00171 else if (nnews >= 16384)
00172 news_size = 32767;
00173 else
00174 news_size = 2 * nnews;
00175 news = scalloc(sizeof(*news) * news_size, 1);
00176 if (!nnews) {
00177 close_db(f);
00178 return;
00179 }
00180 for (i = 0; i < nnews; i++) {
00181 SAFE(read_int16(&news[i].type, f));
00182 SAFE(read_int32(&news[i].num, f));
00183 SAFE(read_string(&news[i].text, f));
00184 SAFE(read_buffer(news[i].who, f));
00185 SAFE(read_int32(&tmp32, f));
00186 news[i].time = tmp32;
00187 }
00188 break;
00189
00190 default:
00191 fatal("Unsupported version (%d) on %s", i, NewsDBName);
00192 }
00193
00194 close_db(f);
00195 }
00196
00197 #undef SAFE
00198
00199
00200
00201 #define SAFE(x) do { \
00202 if ((x) < 0) { \
00203 restore_db(f); \
00204 log_perror("Write error on %s", NewsDBName); \
00205 if (time(NULL) - lastwarn > WarningTimeout) { \
00206 anope_cmd_global(NULL, "Write error on %s: %s", NewsDBName, \
00207 strerror(errno)); \
00208 lastwarn = time(NULL); \
00209 } \
00210 return; \
00211 } \
00212 } while (0)
00213
00214 void save_news()
00215 {
00216 dbFILE *f;
00217 int i;
00218 static time_t lastwarn = 0;
00219
00220 if (!(f = open_db(s_OperServ, NewsDBName, "w", NEWS_VERSION)))
00221 return;
00222 SAFE(write_int16(nnews, f));
00223 for (i = 0; i < nnews; i++) {
00224 SAFE(write_int16(news[i].type, f));
00225 SAFE(write_int32(news[i].num, f));
00226 SAFE(write_string(news[i].text, f));
00227 SAFE(write_buffer(news[i].who, f));
00228 SAFE(write_int32(news[i].time, f));
00229 }
00230 close_db(f);
00231 }
00232
00233 #undef SAFE
00234
00235 void save_rdb_news()
00236 {
00237 #ifdef USE_RDB
00238 int i;
00239 NewsItem *ni;
00240
00241 if (!rdb_open())
00242 return;
00243
00244 if (rdb_tag_table("anope_os_news") == 0) {
00245 alog("Unable to tag table 'anope_os_news' - News RDB save failed.");
00246 return;
00247 }
00248
00249 for (i = 0; i < nnews; i++) {
00250 ni = &news[i];
00251 if (rdb_save_news(ni) == 0) {
00252 alog("Unable to save NewsItem %d - News RDB save failed.", ni->num);
00253 return;
00254 }
00255 }
00256
00257 if (rdb_clean_table("anope_os_news") == 0) {
00258 alog("Unable to clean table 'anope_os_news' - News RDB save failed.");
00259 return;
00260 }
00261
00262 rdb_close();
00263 #endif
00264 }
00265
00266
00267
00268
00269
00270 void display_news(User * u, int16 type)
00271 {
00272 int msg;
00273
00274 if (type == NEWS_LOGON) {
00275 msg = NEWS_LOGON_TEXT;
00276 } else if (type == NEWS_OPER) {
00277 msg = NEWS_OPER_TEXT;
00278 } else if (type == NEWS_RANDOM) {
00279 msg = NEWS_RANDOM_TEXT;
00280 } else {
00281 alog("news: Invalid type (%d) to display_news()", type);
00282 return;
00283 }
00284
00285 if (type == NEWS_RANDOM) {
00286 static int current_news = -1;
00287 int count = 0;
00288
00289 if (!nnews)
00290 return;
00291
00292 while (count++ < nnews) {
00293 if (++current_news >= nnews)
00294 current_news = 0;
00295
00296 if (news[current_news].type == type) {
00297 struct tm *tm;
00298 char timebuf[64];
00299
00300 tm = localtime(&news[current_news].time);
00301 strftime_lang(timebuf, sizeof(timebuf), u,
00302 STRFTIME_SHORT_DATE_FORMAT, tm);
00303 notice_lang(s_GlobalNoticer, u, msg, timebuf,
00304 news[current_news].text);
00305
00306 return;
00307 }
00308 }
00309 } else {
00310 int i, count = 0;
00311
00312 for (i = nnews - 1; i >= 0; i--) {
00313 if (count >= NewsCount)
00314 break;
00315 if (news[i].type == type)
00316 count++;
00317 }
00318 while (++i < nnews) {
00319 if (news[i].type == type) {
00320 struct tm *tm;
00321 char timebuf[64];
00322
00323 tm = localtime(&news[i].time);
00324 strftime_lang(timebuf, sizeof(timebuf), u,
00325 STRFTIME_SHORT_DATE_FORMAT, tm);
00326 notice_lang(s_GlobalNoticer, u, msg, timebuf,
00327 news[i].text);
00328 }
00329 }
00330 }
00331 }
00332
00333
00334
00335
00336
00337 int do_logonnews(User * u)
00338 {
00339 do_news(u, NEWS_LOGON);
00340 return MOD_CONT;
00341 }
00342
00343
00344
00345 int do_opernews(User * u)
00346 {
00347 do_news(u, NEWS_OPER);
00348 return MOD_CONT;
00349 }
00350
00351
00352 int do_randomnews(User * u)
00353 {
00354 do_news(u, NEWS_RANDOM);
00355 return MOD_CONT;
00356 }
00357
00358
00359
00360
00361 void do_news(User * u, short type)
00362 {
00363 int is_servadmin = is_services_admin(u);
00364 char *cmd = strtok(NULL, " ");
00365 char *typename;
00366 int *msgs;
00367
00368 msgs = findmsgs(type, &typename);
00369 if (!msgs) {
00370 alog("news: Invalid type to do_news()");
00371 return;
00372 }
00373
00374 if (!cmd)
00375 cmd = "";
00376
00377 if (stricmp(cmd, "LIST") == 0) {
00378 do_news_list(u, type, msgs);
00379 } else if (stricmp(cmd, "ADD") == 0) {
00380 if (is_servadmin)
00381 do_news_add(u, type, msgs, typename);
00382 else
00383 notice_lang(s_OperServ, u, PERMISSION_DENIED);
00384
00385 } else if (stricmp(cmd, "DEL") == 0) {
00386 if (is_servadmin)
00387 do_news_del(u, type, msgs, typename);
00388 else
00389 notice_lang(s_OperServ, u, PERMISSION_DENIED);
00390
00391 } else {
00392 char buf[32];
00393 snprintf(buf, sizeof(buf), "%sNEWS", typename);
00394 syntax_error(s_OperServ, u, buf, msgs[MSG_SYNTAX]);
00395 }
00396 }
00397
00398
00399
00400
00401
00402 static void do_news_list(User * u, int16 type, int *msgs)
00403 {
00404 int i, count = 0;
00405 char timebuf[64];
00406 struct tm *tm;
00407
00408 for (i = 0; i < nnews; i++) {
00409 if (news[i].type == type) {
00410 if (count == 0)
00411 notice_lang(s_OperServ, u, msgs[MSG_LIST_HEADER]);
00412 tm = localtime(&news[i].time);
00413 strftime_lang(timebuf, sizeof(timebuf),
00414 u, STRFTIME_DATE_TIME_FORMAT, tm);
00415 notice_lang(s_OperServ, u, msgs[MSG_LIST_ENTRY],
00416 news[i].num, timebuf,
00417 *news[i].who ? news[i].who : "<unknown>",
00418 news[i].text);
00419 count++;
00420 }
00421 }
00422 if (count == 0)
00423 notice_lang(s_OperServ, u, msgs[MSG_LIST_NONE]);
00424 else {
00425 notice_lang(s_OperServ, u, END_OF_ANY_LIST, "News");
00426 }
00427 }
00428
00429
00430
00431
00432
00433 static void do_news_add(User * u, int16 type, int *msgs,
00434 const char *typename)
00435 {
00436 char *text = strtok(NULL, "");
00437 int n;
00438
00439 if (!text) {
00440 char buf[32];
00441 snprintf(buf, sizeof(buf), "%sNEWS", typename);
00442 syntax_error(s_OperServ, u, buf, msgs[MSG_ADD_SYNTAX]);
00443 } else {
00444 if (readonly) {
00445 notice_lang(s_OperServ, u, READ_ONLY_MODE);
00446 return;
00447 }
00448 n = add_newsitem(u, text, type);
00449 if (n < 0)
00450 notice_lang(s_OperServ, u, msgs[MSG_ADD_FULL]);
00451 else
00452 notice_lang(s_OperServ, u, msgs[MSG_ADDED], n);
00453 }
00454 }
00455
00456
00457
00458
00459
00460
00461 static int add_newsitem(User * u, const char *text, short type)
00462 {
00463 int i, num;
00464
00465 if (nnews >= 32767)
00466 return -1;
00467
00468 if (nnews >= news_size) {
00469 if (news_size < 8)
00470 news_size = 8;
00471 else
00472 news_size *= 2;
00473 news = srealloc(news, sizeof(*news) * news_size);
00474 }
00475 num = 0;
00476 for (i = nnews - 1; i >= 0; i--) {
00477 if (news[i].type == type) {
00478 num = news[i].num;
00479 break;
00480 }
00481 }
00482 news[nnews].type = type;
00483 news[nnews].num = num + 1;
00484 news[nnews].text = sstrdup(text);
00485 news[nnews].time = time(NULL);
00486 strscpy(news[nnews].who, u->nick, NICKMAX);
00487 nnews++;
00488 return num + 1;
00489 }
00490
00491
00492
00493
00494
00495 static void do_news_del(User * u, int16 type, int *msgs,
00496 const char *typename)
00497 {
00498 char *text = strtok(NULL, " ");
00499 int i, num;
00500
00501 if (!text) {
00502 char buf[32];
00503 snprintf(buf, sizeof(buf), "%sNEWS", typename);
00504 syntax_error(s_OperServ, u, buf, msgs[MSG_DEL_SYNTAX]);
00505 } else {
00506 if (readonly) {
00507 notice_lang(s_OperServ, u, READ_ONLY_MODE);
00508 return;
00509 }
00510 if (stricmp(text, "ALL") != 0) {
00511 num = atoi(text);
00512 if (num > 0 && del_newsitem(num, type)) {
00513 notice_lang(s_OperServ, u, msgs[MSG_DELETED], num);
00514
00515 for (i = 0; i < nnews; i++) {
00516 if ((news[i].type == type) && (news[i].num > num))
00517 news[i].num--;
00518 }
00519 } else
00520 notice_lang(s_OperServ, u, msgs[MSG_DEL_NOT_FOUND], num);
00521 } else {
00522 if (del_newsitem(0, type))
00523 notice_lang(s_OperServ, u, msgs[MSG_DELETED_ALL]);
00524 else
00525 notice_lang(s_OperServ, u, msgs[MSG_DEL_NONE]);
00526 }
00527 }
00528 }
00529
00530
00531
00532
00533
00534
00535 static int del_newsitem(int num, short type)
00536 {
00537 int i;
00538 int count = 0;
00539
00540 for (i = 0; i < nnews; i++) {
00541 if (news[i].type == type && (num == 0 || news[i].num == num)) {
00542 free(news[i].text);
00543 count++;
00544 nnews--;
00545 if (i < nnews)
00546 memcpy(news + i, news + i + 1,
00547 sizeof(*news) * (nnews - i));
00548 i--;
00549 }
00550 }
00551 return count;
00552 }
00553
00554