]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_xline_db.cpp
Replace hardcoded mode letters, part 3
[user/henk/code/inspircd.git] / src / modules / m_xline_db.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
5  *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
6  *
7  * This file is part of InspIRCd.  InspIRCd is free software: you can
8  * redistribute it and/or modify it under the terms of the GNU General Public
9  * License as published by the Free Software Foundation, version 2.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20
21 #include "inspircd.h"
22 #include "xline.h"
23 #include <fstream>
24
25 class ModuleXLineDB : public Module
26 {
27         bool dirty;
28         std::string xlinedbpath;
29  public:
30         void init() CXX11_OVERRIDE
31         {
32                 /* Load the configuration
33                  * Note:
34                  *              this is on purpose not in the OnRehash() method. It would be non-trivial to change the database on-the-fly.
35                  *              Imagine a scenario where the new file already exists. Merging the current XLines with the existing database is likely a bad idea
36                  *              ...and so is discarding all current in-memory XLines for the ones in the database.
37                  */
38                 ConfigTag* Conf = ServerInstance->Config->ConfValue("xlinedb");
39                 xlinedbpath = Conf->getString("filename", DATA_PATH "/xline.db");
40
41                 // Read xlines before attaching to events
42                 ReadDatabase();
43
44                 Implementation eventlist[] = { I_OnAddLine, I_OnDelLine, I_OnExpireLine, I_OnBackgroundTimer };
45                 ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
46                 dirty = false;
47         }
48
49         /** Called whenever an xline is added by a local user.
50          * This method is triggered after the line is added.
51          * @param source The sender of the line or NULL for local server
52          * @param line The xline being added
53          */
54         void OnAddLine(User* source, XLine* line) CXX11_OVERRIDE
55         {
56                 dirty = true;
57         }
58
59         /** Called whenever an xline is deleted.
60          * This method is triggered after the line is deleted.
61          * @param source The user removing the line or NULL for local server
62          * @param line the line being deleted
63          */
64         void OnDelLine(User* source, XLine* line) CXX11_OVERRIDE
65         {
66                 dirty = true;
67         }
68
69         void OnExpireLine(XLine *line) CXX11_OVERRIDE
70         {
71                 dirty = true;
72         }
73
74         void OnBackgroundTimer(time_t now) CXX11_OVERRIDE
75         {
76                 if (dirty)
77                 {
78                         if (WriteDatabase())
79                                 dirty = false;
80                 }
81         }
82
83         bool WriteDatabase()
84         {
85                 /*
86                  * We need to perform an atomic write so as not to fuck things up.
87                  * So, let's write to a temporary file, flush it, then rename the file..
88                  * Technically, that means that this can block, but I have *never* seen that.
89                  *     -- w00t
90                  */
91                 ServerInstance->Logs->Log("m_xline_db", LOG_DEBUG, "xlinedb: Opening temporary database");
92                 std::string xlinenewdbpath = xlinedbpath + ".new";
93                 std::ofstream stream(xlinenewdbpath.c_str());
94                 if (!stream.is_open())
95                 {
96                         ServerInstance->Logs->Log("m_xline_db", LOG_DEBUG, "xlinedb: Cannot create database! %s (%d)", strerror(errno), errno);
97                         ServerInstance->SNO->WriteToSnoMask('a', "database: cannot create new db: %s (%d)", strerror(errno), errno);
98                         return false;
99                 }
100
101                 ServerInstance->Logs->Log("m_xline_db", LOG_DEBUG, "xlinedb: Opened. Writing..");
102
103                 /*
104                  * Now, much as I hate writing semi-unportable formats, additional
105                  * xline types may not have a conf tag, so let's just write them.
106                  * In addition, let's use a file version, so we can maintain some
107                  * semblance of backwards compatibility for reading on startup..
108                  *              -- w00t
109                  */
110                 stream << "VERSION 1" << std::endl;
111
112                 // Now, let's write.
113                 std::vector<std::string> types = ServerInstance->XLines->GetAllTypes();
114                 for (std::vector<std::string>::const_iterator it = types.begin(); it != types.end(); ++it)
115                 {
116                         XLineLookup* lookup = ServerInstance->XLines->GetAll(*it);
117                         if (!lookup)
118                                 continue; // Not possible as we just obtained the list from XLineManager
119
120                         for (LookupIter i = lookup->begin(); i != lookup->end(); ++i)
121                         {
122                                 XLine* line = i->second;
123                                 stream << "LINE " << line->type << " " << line->Displayable() << " "
124                                         << ServerInstance->Config->ServerName << " " << line->set_time << " "
125                                         << line->duration << " " << line->reason << std::endl;
126                         }
127                 }
128
129                 ServerInstance->Logs->Log("m_xline_db", LOG_DEBUG, "xlinedb: Finished writing XLines. Checking for error..");
130
131                 if (stream.fail())
132                 {
133                         ServerInstance->Logs->Log("m_xline_db", LOG_DEBUG, "xlinedb: Cannot write to new database! %s (%d)", strerror(errno), errno);
134                         ServerInstance->SNO->WriteToSnoMask('a', "database: cannot write to new db: %s (%d)", strerror(errno), errno);
135                         return false;
136                 }
137                 stream.close();
138
139 #ifdef _WIN32
140                 if (remove(xlinedbpath.c_str()))
141                 {
142                         ServerInstance->Logs->Log("m_xline_db", LOG_DEBUG, "xlinedb: Cannot remove old database! %s (%d)", strerror(errno), errno);
143                         ServerInstance->SNO->WriteToSnoMask('a', "database: cannot remove old database: %s (%d)", strerror(errno), errno);
144                         return false;
145                 }
146 #endif
147                 // Use rename to move temporary to new db - this is guarenteed not to fuck up, even in case of a crash.
148                 if (rename(xlinenewdbpath.c_str(), xlinedbpath.c_str()) < 0)
149                 {
150                         ServerInstance->Logs->Log("m_xline_db", LOG_DEBUG, "xlinedb: Cannot move new to old database! %s (%d)", strerror(errno), errno);
151                         ServerInstance->SNO->WriteToSnoMask('a', "database: cannot replace old with new db: %s (%d)", strerror(errno), errno);
152                         return false;
153                 }
154
155                 return true;
156         }
157
158         bool ReadDatabase()
159         {
160                 // If the xline database doesn't exist then we don't need to load it.
161                 if (!ServerConfig::FileExists(xlinedbpath.c_str()))
162                         return true;
163
164                 std::ifstream stream(xlinedbpath.c_str());
165                 if (!stream.is_open())
166                 {
167                         ServerInstance->Logs->Log("m_xline_db", LOG_DEBUG, "xlinedb: Cannot read database! %s (%d)", strerror(errno), errno);
168                         ServerInstance->SNO->WriteToSnoMask('a', "database: cannot read db: %s (%d)", strerror(errno), errno);
169                         return false;
170                 }
171                 
172                 std::string line;
173                 while (std::getline(stream, line))
174                 {
175                         // Inspired by the command parser. :)
176                         irc::tokenstream tokens(line);
177                         int items = 0;
178                         std::string command_p[7];
179                         std::string tmp;
180
181                         while (tokens.GetToken(tmp) && (items < 7))
182                         {
183                                 command_p[items] = tmp;
184                                 items++;
185                         }
186
187                         ServerInstance->Logs->Log("m_xline_db", LOG_DEBUG, "xlinedb: Processing %s", line.c_str());
188
189                         if (command_p[0] == "VERSION")
190                         {
191                                 if (command_p[1] == "1")
192                                 {
193                                         ServerInstance->Logs->Log("m_xline_db", LOG_DEBUG, "xlinedb: Reading db version %s", command_p[1].c_str());
194                                 }
195                                 else
196                                 {
197                                         stream.close();
198                                         ServerInstance->Logs->Log("m_xline_db", LOG_DEBUG, "xlinedb: I got database version %s - I don't understand it", command_p[1].c_str());
199                                         ServerInstance->SNO->WriteToSnoMask('a', "database: I got a database version (%s) I don't understand", command_p[1].c_str());
200                                         return false;
201                                 }
202                         }
203                         else if (command_p[0] == "LINE")
204                         {
205                                 // Mercilessly stolen from spanningtree
206                                 XLineFactory* xlf = ServerInstance->XLines->GetFactory(command_p[1]);
207
208                                 if (!xlf)
209                                 {
210                                         ServerInstance->SNO->WriteToSnoMask('a', "database: Unknown line type (%s).", command_p[1].c_str());
211                                         continue;
212                                 }
213
214                                 XLine* xl = xlf->Generate(ServerInstance->Time(), atoi(command_p[5].c_str()), command_p[3], command_p[6], command_p[2]);
215                                 xl->SetCreateTime(atoi(command_p[4].c_str()));
216
217                                 if (ServerInstance->XLines->AddLine(xl, NULL))
218                                 {
219                                         ServerInstance->SNO->WriteToSnoMask('x', "database: Added a line of type %s", command_p[1].c_str());
220                                 }
221                                 else
222                                         delete xl;
223                         }
224                 }
225                 stream.close();
226                 return true;
227         }
228
229         Version GetVersion() CXX11_OVERRIDE
230         {
231                 return Version("Keeps a dynamic log of all XLines created, and stores them in a separate conf file (xline.db).", VF_VENDOR);
232         }
233 };
234
235 MODULE_INIT(ModuleXLineDB)