Anope IRC Services  Version 1.8
os_ignore_db.c
Go to the documentation of this file.
1 /* os_ignore_db.c - Provides a database backend for OS IGNORE.
2  *
3  * (C) 2003-2014 Anope Team
4  * Contact us at team@anope.org
5  *
6  * Included in the Anope module pack since Anope 1.7.23
7  * Anope Coder: Viper <viper@anope.org>
8  *
9  * Please read COPYING and README for further details.
10  *
11  */
12 /* ------------------------------------------------------------------------------- */
13 
14 #include "module.h"
15 
16 #define AUTHOR "Viper"
17 #define VERSION VERSION_STRING
18 
19 /* Default database name */
20 #define DefIgnoreDB "os_ignore.db"
21 #define IGNOREDBVERSION 1
22 
23 /* Database seperators */
24 #define SEPARATOR '^' /* End of a key, seperates keys from values */
25 #define BLOCKEND '\n' /* End of a block, e.g. a whole ignore */
26 #define VALUEEND '\000' /* End of a value */
27 #define SUBSTART '\010' /* Beginning of a new subblock, closed by a BLOCKEND */
28 
29 /* Database reading return values */
30 #define DB_READ_SUCCESS 0
31 #define DB_READ_ERROR 1
32 #define DB_EOF_ERROR 2
33 #define DB_VERSION_ERROR 3
34 #define DB_READ_BLOCKEND 4
35 #define DB_READ_SUBSTART 5
36 
37 #define DB_WRITE_SUCCESS 0
38 #define DB_WRITE_ERROR 1
39 #define DB_WRITE_NOVAL 2
40 
41 /* Database Key, Value max length */
42 #define MAXKEYLEN 128
43 #define MAXVALLEN 1024
44 
45 /* Structs */
46 typedef struct db_file_ DBFile;
47 
48 struct db_file_ {
49  FILE *fptr; /* Pointer to the opened file */
50  int db_version; /* The db version of the datafiles (only needed for reading) */
51  int core_db_version; /* The current db version of this anope source */
52  char service[256]; /* StatServ/etc. */
53  char filename[256]; /* Filename of the database */
54  char temp_name[262]; /* Temp filename of the database */
55 };
56 
57 
58 /* Variables */
59 char *IgnoreDB;
60 
61 /* Functions */
62 static int new_open_db_read(DBFile *dbptr, char **key, char **value);
63 static int new_open_db_write(DBFile *dbptr);
64 static void new_close_db(FILE *fptr, char **key, char **value);
65 static int new_read_db_entry(char **key, char **value, FILE * fptr);
66 static int new_write_db_entry(const char *key, DBFile *dbptr, const char *fmt, ...);
67 static int new_write_db_endofblock(DBFile *dbptr);
68 static void fill_db_ptr(DBFile *dbptr, int version, int core_version, char service[256], char filename[256]);
69 
70 static int save_ignoredb(int argc, char **argv);
71 static int backup_ignoredb(int argc, char **argv);
72 static void load_ignore_db(void);
73 static void save_ignore_db(void);
74 static void load_config(void);
75 static int reload_config(int argc, char **argv);
76 
77 /* ------------------------------------------------------------------------------- */
78 
85 int AnopeInit(int argc, char **argv) {
86  EvtHook *hook;
87  IgnoreDB = NULL;
88 
92 
94  if (moduleAddEventHook(hook) != MOD_ERR_OK) {
95  alog("[\002os_ignore_db\002] Can't hook to EVENT_RELOAD event");
96  return MOD_STOP;
97  }
98 
100  if (moduleAddEventHook(hook) != MOD_ERR_OK) {
101  alog("[\002os_ignore_db\002] Can't hook to EVENT_DB_SAVING event");
102  return MOD_STOP;
103  }
104 
106  if (moduleAddEventHook(hook) != MOD_ERR_OK) {
107  alog("[\002os_ignore_db\002] Can't hook to EVENT_DB_BACKUP event");
108  return MOD_STOP;
109  }
110 
111  load_config();
112  /* Load the ignore database and re-add them to anopes ignorelist. */
113  load_ignore_db();
114 
115  return MOD_CONT;
116 }
117 
121 void AnopeFini(void) {
122  /* Save the ignore database before bailing out.. */
123  save_ignore_db();
124 
125  if (IgnoreDB)
126  free(IgnoreDB);
127 }
128 
129 /* ------------------------------------------------------------------------------- */
130 
131 static void load_config(void) {
132  int i;
133 
134  Directive confvalues[][1] = {
135  {{"OSIgnoreDBName", {{PARAM_STRING, PARAM_RELOAD, &IgnoreDB}}}},
136  };
137 
138  if (IgnoreDB)
139  free(IgnoreDB);
140  IgnoreDB = NULL;
141 
142  for (i = 0; i < 1; i++)
143  moduleGetConfigDirective(confvalues[i]);
144 
145  if (!IgnoreDB)
147 
148  if (debug)
149  alog("[os_ignore_db] debug: Set config vars: OSIgnoreDBName='%s'", IgnoreDB);
150 }
151 
155 static int reload_config(int argc, char **argv) {
156  if (argc >= 1) {
157  if (!stricmp(argv[0], EVENT_START)) {
158  load_config();
159  }
160  }
161  return MOD_CONT;
162 }
163 
167 static int save_ignoredb(int argc, char **argv) {
168  if ((argc >= 1) && (!stricmp(argv[0], EVENT_STOP)))
169  save_ignore_db();
170 
171  return MOD_CONT;
172 }
173 
174 
178 static int backup_ignoredb(int argc, char **argv) {
179  if ((argc >= 1) && (!stricmp(argv[0], EVENT_STOP))) {
180  if (debug)
181  alog("[os_ignore_db] debug: Backing up %s database...", IgnoreDB);
183  }
184  return MOD_CONT;
185 }
186 
187 /* ------------------------------------------------------------------------------- */
188 
189 /**************************************************************************
190  * DataBase Handling
191  **************************************************************************/
192 
193 static void load_ignore_db(void) {
194  DBFile *dbptr = scalloc(1, sizeof(DBFile));
195  char *key, *value, *mask = NULL;
196  int retval = 0;
197  time_t expiry_time;
198  IgnoreData *ign;
199 
200  expiry_time = time(NULL);
202 
203  /* let's remove existing temp files here, because we only load dbs on startup */
204  remove(dbptr->temp_name);
205 
206  /* Open the db, fill the rest of dbptr and allocate memory for key and value */
207  if (new_open_db_read(dbptr, &key, &value)) {
208  free(dbptr);
209  return; /* Bang, an error occurred */
210  }
211 
212  while (1) {
213  /* read a new entry and fill key and value with it -Certus */
214  retval = new_read_db_entry(&key, &value, dbptr->fptr);
215 
216  if (retval == DB_READ_ERROR) {
217  new_close_db(dbptr->fptr, &key, &value);
218  free(dbptr);
219  return;
220 
221  } else if (retval == DB_EOF_ERROR) {
222  new_close_db(dbptr->fptr, &key, &value);
223  free(dbptr);
224  return;
225  } else if (retval == DB_READ_BLOCKEND) { /* DB_READ_BLOCKEND */
226  /* Check if we have everything to add the ignore..
227  * We shouldn't bother with already expired ignores either.. */
228  if (mask && (expiry_time > time(NULL) || expiry_time == 0)) {
229  /* We should check for double entries.. */
230  for (ign = ignore; ign; ign = ign->next)
231  if (!stricmp(ign->mask, mask))
232  break;
233 
234  if (!ign) {
235  /* Create a fresh entry.. */
236  ign = scalloc(sizeof(*ign), 1);
237  ign->mask = sstrdup(mask);
238  ign->time = expiry_time;
239  ign->prev = NULL;
240  ign->next = ignore;
241  if (ignore)
242  ignore->prev = ign;
243  ignore = ign;
244  if (debug)
245  alog("[os_ignore_db] debug: Added new ignore entry for %s", mask);
246  } else {
247  /* Update time on existing entry.
248  * The longest expiry time survives.. */
249  if (expiry_time == 0 || ign->time == 0)
250  ign->time = 0;
251  else if (expiry_time > ign->time)
252  ign->time = expiry_time;
253  }
254  }
255 
256  if (mask) free(mask);
257  mask = NULL;
258  expiry_time = time(NULL);
259  } else { /* DB_READ_SUCCESS */
260  if (!*key || !*value)
261  continue;
262 
263  /* mask */
264  if (!stricmp(key, "m")) {
265  if (mask)
266  free(mask);
267  mask = sstrdup(value);
268 
269  /* expiry time */
270  } else if (!stricmp(key, "t")) {
271  expiry_time = atoi(value);
272 
273  } else if (!stricmp(key, "IGNORE_DB_VERSION")) {
274  if ((int)atoi(value) != IGNOREDBVERSION) {
275  alog("[\002os_ignore_db\002] Database version does not match any database versions supported by this module.");
276  alog("[\002os_ignore_db\002] Continuing with clean database...");
277  break;
278  }
279  }
280  } /* else */
281  } /* while */
282 
283  free(dbptr);
284 }
285 
286 
287 static void save_ignore_db(void) {
288  DBFile *dbptr = scalloc(1, sizeof(DBFile));
289  time_t now;
290  IgnoreData *ign, *next;
291 
292  now = time(NULL);
294 
295  /* time to backup the old db */
296  rename(IgnoreDB, dbptr->temp_name);
297 
298  if (new_open_db_write(dbptr)) {
299  rename(dbptr->temp_name, IgnoreDB);
300  free(dbptr);
301  return; /* Bang, an error occurred */
302  }
303 
304  /* Store the version of the DB in the DB as well...
305  * This will make stuff a lot easier if the database scheme needs to modified. */
306  new_write_db_entry("IGNORE_DB_VERSION", dbptr, "%d", IGNOREDBVERSION);
308 
309  /* Go over the entire ignorelist, check whether each entry is still valid
310  * and write it to the database if it is.*/
311  for (ign = ignore; ign; ign = next) {
312  next = ign->next;
313 
314  if (ign->time != 0 && ign->time <= now) {
315  if (debug)
316  alog("[os_ignore_db] debug: Expiring ignore entry %s", ign->mask);
317  if (ign->prev)
318  ign->prev->next = ign->next;
319  else if (ignore == ign)
320  ignore = ign->next;
321  if (ign->next)
322  ign->next->prev = ign->prev;
323  free(ign->mask);
324  free(ign);
325  ign = NULL;
326  } else {
327  new_write_db_entry("m", dbptr, "%s", ign->mask);
328  new_write_db_entry("t", dbptr, "%d", ign->time);
330  }
331  }
332 
333  if (dbptr) {
334  new_close_db(dbptr->fptr, NULL, NULL); /* close file */
335  remove(dbptr->temp_name); /* saved successfully, no need to keep the old one */
336  free(dbptr); /* free the db struct */
337  }
338 }
339 
340 
341 /* ------------------------------------------------------------------------------- */
342 
343 /**************************************************************************
344  * Generic DataBase Functions (Borrowed this from Trystan :-) )
345  **************************************************************************/
346 
347 
348 static int new_open_db_read(DBFile *dbptr, char **key, char **value) {
349  *key = malloc(MAXKEYLEN);
350  *value = malloc(MAXVALLEN);
351 
352  if (!(dbptr->fptr = fopen(dbptr->filename, "rb"))) {
353  if (debug) {
354  alog("debug: Can't read %s database %s : errno(%d)", dbptr->service,
355  dbptr->filename, errno);
356  }
357  free(*key);
358  *key = NULL;
359  free(*value);
360  *value = NULL;
361  return DB_READ_ERROR;
362  }
363  dbptr->db_version = fgetc(dbptr->fptr) << 24 | fgetc(dbptr->fptr) << 16
364  | fgetc(dbptr->fptr) << 8 | fgetc(dbptr->fptr);
365 
366  if (ferror(dbptr->fptr)) {
367  if (debug) {
368  alog("debug: Error reading version number on %s", dbptr->filename);
369  }
370  free(*key);
371  *key = NULL;
372  free(*value);
373  *value = NULL;
374  return DB_READ_ERROR;
375  } else if (feof(dbptr->fptr)) {
376  if (debug) {
377  alog("debug: Error reading version number on %s: End of file detected",
378  dbptr->filename);
379  }
380  free(*key);
381  *key = NULL;
382  free(*value);
383  *value = NULL;
384  return DB_EOF_ERROR;
385  } else if (dbptr->db_version < 1) {
386  if (debug) {
387  alog("debug: Invalid version number (%d) on %s", dbptr->db_version, dbptr->filename);
388  }
389  free(*key);
390  *key = NULL;
391  free(*value);
392  *value = NULL;
393  return DB_VERSION_ERROR;
394  }
395  return DB_READ_SUCCESS;
396 }
397 
398 
399 static int new_open_db_write(DBFile *dbptr) {
400  if (!(dbptr->fptr = fopen(dbptr->filename, "wb"))) {
401  if (debug) {
402  alog("debug: %s Can't open %s database for writing", dbptr->service, dbptr->filename);
403  }
404  return DB_WRITE_ERROR;
405  }
406 
407  if (fputc(dbptr->core_db_version >> 24 & 0xFF, dbptr->fptr) < 0 ||
408  fputc(dbptr->core_db_version >> 16 & 0xFF, dbptr->fptr) < 0 ||
409  fputc(dbptr->core_db_version >> 8 & 0xFF, dbptr->fptr) < 0 ||
410  fputc(dbptr->core_db_version & 0xFF, dbptr->fptr) < 0) {
411  if (debug) {
412  alog("debug: Error writing version number on %s", dbptr->filename);
413  }
414  return DB_WRITE_ERROR;
415  }
416  return DB_WRITE_SUCCESS;
417 }
418 
419 
420 static void new_close_db(FILE *fptr, char **key, char **value) {
421  if (key && *key) {
422  free(*key);
423  *key = NULL;
424  }
425  if (value && *value) {
426  free(*value);
427  *value = NULL;
428  }
429 
430  if (fptr) {
431  fclose(fptr);
432  }
433 }
434 
435 
436 static int new_read_db_entry(char **key, char **value, FILE *fptr) {
437  char *string = *key;
438  int character;
439  int i = 0;
440 
441  **key = '\0';
442  **value = '\0';
443 
444  while (1) {
445  if ((character = fgetc(fptr)) == EOF) { /* a problem occurred reading the file */
446  if (ferror(fptr)) {
447  return DB_READ_ERROR; /* error! */
448  }
449  return DB_EOF_ERROR; /* end of file */
450  } else if (character == BLOCKEND) { /* END OF BLOCK */
451  return DB_READ_BLOCKEND;
452  } else if (character == VALUEEND) { /* END OF VALUE */
453  string[i] = '\0'; /* end of value */
454  return DB_READ_SUCCESS;
455  } else if (character == SEPARATOR) { /* END OF KEY */
456  string[i] = '\0'; /* end of key */
457  string = *value; /* beginning of value */
458  i = 0; /* start with the first character of our value */
459  } else {
460  if ((i == (MAXKEYLEN - 1)) && (string == *key)) { /* max key length reached, continuing with value */
461  string[i] = '\0'; /* end of key */
462  string = *value; /* beginning of value */
463  i = 0; /* start with the first character of our value */
464  } else if ((i == (MAXVALLEN - 1)) && (string == *value)) { /* max value length reached, returning */
465  string[i] = '\0';
466  return DB_READ_SUCCESS;
467  } else {
468  string[i] = character; /* read string (key or value) */
469  i++;
470  }
471  }
472  }
473 }
474 
475 
476 static int new_write_db_entry(const char *key, DBFile *dbptr, const char *fmt, ...) {
477  char string[MAXKEYLEN + MAXVALLEN + 2], value[MAXVALLEN]; /* safety byte :P */
478  va_list ap;
479  unsigned int length;
480 
481  if (!dbptr) {
482  return DB_WRITE_ERROR;
483  }
484 
485  va_start(ap, fmt);
486  vsnprintf(value, MAXVALLEN, fmt, ap);
487  va_end(ap);
488 
489  if (!stricmp(value, "(null)")) {
490  return DB_WRITE_NOVAL;
491  }
492  snprintf(string, MAXKEYLEN + MAXVALLEN + 1, "%s%c%s", key, SEPARATOR, value);
493  length = strlen(string);
494  string[length] = VALUEEND;
495  length++;
496 
497  if (fwrite(string, 1, length, dbptr->fptr) < length) {
498  if (debug) {
499  alog("debug: Error writing to %s", dbptr->filename);
500  }
501  new_close_db(dbptr->fptr, NULL, NULL);
502  if (debug) {
503  alog("debug: Restoring backup.");
504  }
505  remove(dbptr->filename);
506  rename(dbptr->temp_name, dbptr->filename);
507  free(dbptr);
508  dbptr = NULL;
509  return DB_WRITE_ERROR;
510  }
511  return DB_WRITE_SUCCESS;
512 }
513 
514 
515 static int new_write_db_endofblock(DBFile *dbptr) {
516  if (!dbptr) {
517  return DB_WRITE_ERROR;
518  }
519  if (fputc(BLOCKEND, dbptr->fptr) == EOF) {
520  if (debug) {
521  alog("debug: Error writing to %s", dbptr->filename);
522  }
523  new_close_db(dbptr->fptr, NULL, NULL);
524  return DB_WRITE_ERROR;
525  }
526  return DB_WRITE_SUCCESS;
527 }
528 
529 
530 
531 static void fill_db_ptr(DBFile *dbptr, int version, int core_version,
532  char service[256], char filename[256]) {
533  dbptr->db_version = version;
534  dbptr->core_db_version = core_version;
535  if (!service)
536  strcpy(dbptr->service, service);
537  else
538  strcpy(dbptr->service, "");
539 
540  strcpy(dbptr->filename, filename);
541  snprintf(dbptr->temp_name, 261, "%s.temp", filename);
542  return;
543 }
544 
545 /* EOF */
#define DB_READ_SUCCESS
Definition: os_ignore_db.c:30
static int new_open_db_read(DBFile *dbptr, char **key, char **value)
Definition: os_ignore_db.c:348
#define MAXKEYLEN
Definition: os_ignore_db.c:42
E void ModuleDatabaseBackup(char *dbname)
Definition: datafiles.c:732
E int snprintf(char *buf, size_t size, const char *fmt,...)
Definition: compat.c:37
#define EVENT_STOP
Definition: events.h:15
#define EVENT_DB_SAVING
Definition: events.h:17
#define BLOCKEND
Definition: os_ignore_db.c:25
struct ignore_data * prev
Definition: services.h:1058
#define DB_READ_BLOCKEND
Definition: os_ignore_db.c:34
char filename[256]
Definition: os_ignore_db.c:53
E int stricmp(const char *s1, const char *s2)
Definition: compat.c:58
#define EVENT_RELOAD
Definition: events.h:40
void AnopeFini(void)
Definition: os_ignore_db.c:121
#define DB_VERSION_ERROR
Definition: os_ignore_db.c:33
#define IGNOREDBVERSION
Definition: os_ignore_db.c:21
static void save_ignore_db(void)
Definition: os_ignore_db.c:287
static void load_ignore_db(void)
Definition: os_ignore_db.c:193
#define PARAM_RELOAD
Definition: services.h:454
static void fill_db_ptr(DBFile *dbptr, int version, int core_version, char service[256], char filename[256])
Definition: os_ignore_db.c:531
MDE void moduleAddAuthor(const char *author)
Definition: modules.c:1772
time_t time
Definition: services.h:1060
E IgnoreData * ignore
Definition: extern.h:1022
static int reload_config(int argc, char **argv)
Definition: os_ignore_db.c:155
int db_version
Definition: os_ignore_db.c:50
MDE void moduleSetType(MODType type)
Definition: modules.c:818
FILE * fptr
Definition: os_ignore_db.c:49
struct ignore_data * next
Definition: services.h:1058
E char * sstrdup(const char *s)
Definition: memory.c:105
E void * scalloc(long elsize, long els)
Definition: memory.c:55
#define VALUEEND
Definition: os_ignore_db.c:26
E char * s_OperServ
Definition: extern.h:289
#define MOD_STOP
Definition: modules.h:53
#define DefIgnoreDB
Definition: os_ignore_db.c:20
#define DB_WRITE_NOVAL
Definition: os_ignore_db.c:39
static void load_config(void)
Definition: os_ignore_db.c:131
static int new_open_db_write(DBFile *dbptr)
Definition: os_ignore_db.c:399
MDE void moduleAddVersion(const char *version)
Definition: modules.c:1760
int AnopeInit(int argc, char **argv)
Definition: os_ignore_db.c:85
#define VERSION
Definition: os_ignore_db.c:17
#define AUTHOR
Definition: os_ignore_db.c:16
MDE EvtHook * createEventHook(char *name, int(*func)(int argc, char **argv))
Definition: events.c:305
static void new_close_db(FILE *fptr, char **key, char **value)
Definition: os_ignore_db.c:420
char * mask
Definition: services.h:1059
#define DB_READ_ERROR
Definition: os_ignore_db.c:31
E void alog(const char *fmt,...) FORMAT(printf
#define MOD_ERR_OK
Definition: modules.h:71
#define SEPARATOR
Definition: os_ignore_db.c:24
#define MOD_CONT
Definition: modules.h:54
static int save_ignoredb(int argc, char **argv)
Definition: os_ignore_db.c:167
#define EVENT_START
Definition: events.h:14
static int new_write_db_entry(const char *key, DBFile *dbptr, const char *fmt,...)
Definition: os_ignore_db.c:476
E int debug
Definition: extern.h:775
char service[256]
Definition: os_ignore_db.c:52
#define DB_WRITE_SUCCESS
Definition: os_ignore_db.c:37
E int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
static int new_write_db_endofblock(DBFile *dbptr)
Definition: os_ignore_db.c:515
int core_db_version
Definition: os_ignore_db.c:51
char * IgnoreDB
Definition: os_ignore_db.c:59
#define MAXVALLEN
Definition: os_ignore_db.c:43
MDE int moduleGetConfigDirective(Directive *h)
Definition: modules.c:2553
#define PARAM_STRING
Definition: services.h:443
char version[1024]
Definition: version.sh.c:24
#define DB_WRITE_ERROR
Definition: os_ignore_db.c:38
#define EVENT_DB_BACKUP
Definition: events.h:18
static int new_read_db_entry(char **key, char **value, FILE *fptr)
Definition: os_ignore_db.c:436
char temp_name[262]
Definition: os_ignore_db.c:54
static int backup_ignoredb(int argc, char **argv)
Definition: os_ignore_db.c:178
MDE int moduleAddEventHook(EvtHook *evh)
Definition: events.c:528
#define DB_EOF_ERROR
Definition: os_ignore_db.c:32