]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_xline_db.cpp
Header update: 2007 -> 2008
[user/henk/code/inspircd.git] / src / modules / m_xline_db.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include "xline.h"
16
17 /* $ModDesc: Keeps a dynamic log of all XLines created, and stores them in a seperate conf file (xline.db). */
18
19 class ModuleXLineDB : public Module
20 {
21         std::vector<XLine *> xlines;
22         bool reading_db;                        // If this is true, addlines are as a result of db reading, so don't bother flushing the db to disk.
23                                                 // DO REMEMBER TO SET IT, otherwise it's annoying :P
24  public:
25         ModuleXLineDB(InspIRCd* Me) : Module(Me)
26         {
27                 Implementation eventlist[] = { I_OnAddLine, I_OnDelLine, I_OnExpireLine };
28                 ServerInstance->Modules->Attach(eventlist, this, 3);
29
30                 reading_db = true;
31                 ReadDatabase();
32                 reading_db = false;
33         }
34
35         virtual ~ModuleXLineDB()
36         {
37         }
38
39         /** Called whenever an xline is added by a local user.
40          * This method is triggered after the line is added.
41          * @param source The sender of the line or NULL for local server
42          * @param line The xline being added
43          */
44         void OnAddLine(User* source, XLine* line)
45         {
46                 ServerInstance->Log(DEBUG, "xlinedb: Adding a line");
47                 xlines.push_back(line);
48
49                 if (!reading_db)
50                 {
51                         WriteDatabase();
52                 }
53         }
54
55         /** Called whenever an xline is deleted.
56          * This method is triggered after the line is deleted.
57          * @param source The user removing the line or NULL for local server
58          * @param line the line being deleted
59          */
60         void OnDelLine(User* source, XLine* line)
61         {
62                 RemoveLine(line);
63         }
64
65         void OnExpireLine(XLine *line)
66         {
67                 RemoveLine(line);
68         }
69
70         void RemoveLine(XLine *line)
71         {
72                 ServerInstance->Log(DEBUG, "xlinedb: Removing a line");
73                 for (std::vector<XLine *>::iterator i = xlines.begin(); i != xlines.end(); i++)
74                 {
75                         if ((*i) == line)
76                         {
77                                 xlines.erase(i);
78                                 break;
79                         }
80                 }
81
82                 WriteDatabase();
83         }
84
85         bool WriteDatabase()
86         {
87                 FILE *f;
88
89                 /*
90                  * We need to perform an atomic write so as not to fuck things up.
91                  * So, let's write to a temporary file, flush and sync the FD, then rename the file..
92                  * Technically, that means that this can block, but I have *never* seen that.
93                  *              -- w00t
94                  */
95                 ServerInstance->Log(DEBUG, "xlinedb: Opening temporary database");
96                 f = fopen("xline.db.new", "w");
97                 if (!f)
98                 {
99                         ServerInstance->Log(DEBUG, "xlinedb: Cannot create database! %s (%d)", strerror(errno), errno);
100                         ServerInstance->SNO->WriteToSnoMask('x', "database: cannot create new db: %s (%d)", strerror(errno), errno);
101                         return false;
102                 }
103
104                 ServerInstance->Log(DEBUG, "xlinedb: Opened. Writing..");
105
106                 /*
107                  * Now, much as I hate writing semi-unportable formats, additional
108                  * xline types may not have a conf tag, so let's just write them.
109                  * In addition, let's use a file version, so we can maintain some
110                  * semblance of backwards compatibility for reading on startup..
111                  *              -- w00t
112                  */
113                 fprintf(f, "VERSION 1\n");
114
115                 // Now, let's write.
116                 XLine *line;
117                 for (std::vector<XLine *>::iterator i = xlines.begin(); i != xlines.end(); i++)
118                 {
119                         line = (*i);
120                         fprintf(f, "LINE %s %s %s %lu %lu :%s\n", line->type.c_str(), line->Displayable(),
121                                 ServerInstance->Config->ServerName, line->set_time, line->duration, line->reason);
122                 }
123
124                 ServerInstance->Log(DEBUG, "xlinedb: Finished writing XLines. Checking for error..");
125
126                 int write_error = 0;
127                 write_error = ferror(f);
128                 write_error |= fclose(f);
129                 if (write_error)
130                 {
131                         ServerInstance->Log(DEBUG, "xlinedb: Cannot write to new database! %s (%d)", strerror(errno), errno);
132                         ServerInstance->SNO->WriteToSnoMask('x', "database: cannot write to new db: %s (%d)", strerror(errno), errno);
133                         return false;
134                 }
135
136                 // Use rename to move temporary to new db - this is guarenteed not to fuck up, even in case of a crash.
137                 if (rename("xline.db.new", "xline.db") < 0)
138                 {
139                         ServerInstance->Log(DEBUG, "xlinedb: Cannot move new to old database! %s (%d)", strerror(errno), errno);
140                         ServerInstance->SNO->WriteToSnoMask('x', "database: cannot replace old with new db: %s (%d)", strerror(errno), errno);
141                         return false;
142                 }
143
144                 return true;
145         }
146
147         bool ReadDatabase()
148         {
149                 FILE *f;
150                 char linebuf[MAXBUF];
151                 unsigned int lineno = 0;
152
153                 f = fopen("xline.db", "r");
154                 if (!f)
155                 {
156                         if (errno == ENOENT)
157                         {
158                                 /* xline.db doesn't exist, fake good return value (we don't care about this) */
159                                 return true;
160                         }
161                         else
162                         {
163                                 /* this might be slightly more problematic. */
164                                 ServerInstance->Log(DEBUG, "xlinedb: Cannot read database! %s (%d)", strerror(errno), errno);
165                                 ServerInstance->SNO->WriteToSnoMask('x', "database: cannot read db: %s (%d)", strerror(errno), errno);
166                                 return false;
167                         }
168                 }
169
170                 while (fgets(linebuf, MAXBUF, f))
171                 {
172                         char *c = linebuf;
173
174                         while (c && *c)
175                         {
176                                 if (*c == '\n')
177                                 {
178                                         *c = '\0';
179                                 }
180
181                                 c++;
182                         }
183                         // Smart man might think of initing to 1, and moving this to the bottom. Don't. We use continue in this loop.
184                         lineno++;
185
186                         // Inspired by the command parser. :)
187                         irc::tokenstream tokens(linebuf);
188                         int items = 0;
189                         std::string command_p[MAXPARAMETERS];
190                         std::string tmp;
191
192                         while (tokens.GetToken(tmp) && (items < MAXPARAMETERS))
193                         {
194                                 command_p[items] = tmp.c_str();
195                                 items++;
196                         }
197
198                         ServerInstance->Log(DEBUG, "xlinedb: Processing %s", linebuf);
199
200                         if (command_p[0] == "VERSION")
201                         {
202                                 if (command_p[1] == "1")
203                                 {
204                                         ServerInstance->Log(DEBUG, "xlinedb: Reading db version %s", command_p[1].c_str());
205                                 }
206                                 else
207                                 {
208                                         fclose(f);
209                                         ServerInstance->Log(DEBUG, "xlinedb: I got database version %s - I don't understand it", command_p[1].c_str());
210                                         ServerInstance->SNO->WriteToSnoMask('x', "database: I got a database version (%s) I don't understand", command_p[1].c_str());
211                                         return false;
212                                 }
213                         }
214                         else if (command_p[0] == "LINE")
215                         {
216                                 // Mercilessly stolen from spanningtree
217                                 XLineFactory* xlf = ServerInstance->XLines->GetFactory(command_p[1]);
218
219                                 if (!xlf)
220                                 {
221                                         ServerInstance->SNO->WriteToSnoMask('x', "database: Unknown line type (%s).", command_p[1].c_str());
222                                         continue;
223                                 }
224
225                                 XLine* xl = xlf->Generate(ServerInstance->Time(), atoi(command_p[5].c_str()), command_p[3].c_str(), command_p[6].c_str(), command_p[2].c_str());
226                                 xl->SetCreateTime(atoi(command_p[4].c_str()));
227
228                                 if (ServerInstance->XLines->AddLine(xl, NULL))
229                                 {
230                                         ServerInstance->SNO->WriteToSnoMask('x', "database: Added a line of type %s", command_p[1].c_str());
231                                 }
232                         }
233                 }
234
235                 return true;
236         }
237
238         
239
240         virtual Version GetVersion()
241         {
242                 return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
243         }
244 };
245
246 MODULE_INIT(ModuleXLineDB)
247