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