news.c

Go to the documentation of this file.
00001 
00002 /* News functions.
00003  *
00004  * (C) 2003-2007 Anope Team
00005  * Contact us at info@anope.org
00006  *
00007  * Please read COPYING and README for further details.
00008  *
00009  * Based on the original code of Epona by Lara.
00010  * Based on the original code of Services by Andy Church. 
00011  * 
00012  * $Id: news.c 1265 2007-08-26 15:33:06Z geniusdex $ 
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 /* List of messages for each news type.  This simplifies message sending. */
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 /* Called by the main OperServ routine in response to a NEWS command. */
00110 static void do_news(User * u, int16 type);
00111 
00112 /* Lists all a certain type of news. */
00113 static void do_news_list(User * u, int16 type, int *msgs);
00114 
00115 /* Add news items. */
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 /* Delete news items. */
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 /****************************** Statistics *******************************/
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 /*********************** News item loading/saving ************************/
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     }                           /* switch (ver) */
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 /***************************** News display ******************************/
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 /***************************** News editing ******************************/
00335 /*************************************************************************/
00336 /* Declared in extern.h */
00337 int do_logonnews(User * u)
00338 {
00339     do_news(u, NEWS_LOGON);
00340     return MOD_CONT;
00341 }
00342 
00343 
00344 /* Declared in extern.h */
00345 int do_opernews(User * u)
00346 {
00347     do_news(u, NEWS_OPER);
00348     return MOD_CONT;
00349 }
00350 
00351 /* Declared in extern.h */
00352 int do_randomnews(User * u)
00353 {
00354     do_news(u, NEWS_RANDOM);
00355     return MOD_CONT;
00356 }
00357 
00358 /*************************************************************************/
00359 
00360 /* Main news command handling routine. */
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 /* Handle a {LOGON,OPER}NEWS LIST command. */
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 /* Handle a {LOGON,OPER}NEWS ADD command. */
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 /* Actually add a news item.  Return the number assigned to the item, or -1
00458  * if the news list is full (32767 items).
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 /* Handle a {LOGON,OPER}NEWS DEL command. */
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                 /* Reset the order - #0000397 */
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 /* Actually delete a news item.  If `num' is 0, delete all news items of
00532  * the given type.  Returns the number of items deleted.
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 /*************************************************************************/

Generated on Sun Dec 30 09:26:50 2007 for Anope by  doxygen 1.5.1-20070107