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