+struct ListModeData
+{
+ std::string modes;
+ std::string params;
+};
+
+// Not in a class due to circular dependancy hell.
+static std::string permchannelsconf;
+static bool WriteDatabase(Module* mod, bool save_listmodes)
+{
+ FILE *f;
+
+ if (permchannelsconf.empty())
+ {
+ // Fake success.
+ return true;
+ }
+
+ std::string tempname = permchannelsconf + ".tmp";
+
+ /*
+ * We need to perform an atomic write so as not to fuck things up.
+ * So, let's write to a temporary file, flush and sync the FD, then rename the file..
+ * -- w00t
+ */
+ f = fopen(tempname.c_str(), "w");
+ if (!f)
+ {
+ ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: Cannot create database! %s (%d)", strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot create new db: %s (%d)", strerror(errno), errno);
+ return false;
+ }
+
+ fputs("# Permchannels DB\n# This file is autogenerated; any changes will be overwritten!\n<config format=\"compat\">\n", f);
+ // Now, let's write.
+ std::string line;
+ for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++)
+ {
+ Channel* chan = i->second;
+ if (!chan->IsModeSet('P'))
+ continue;
+
+ std::string chanmodes = chan->ChanModes(true);
+ if (save_listmodes)
+ {
+ ListModeData lm;
+
+ // Bans are managed by the core, so we have to process them separately
+ lm.modes = std::string(chan->bans.size(), 'b');
+ for (BanList::const_iterator j = chan->bans.begin(); j != chan->bans.end(); ++j)
+ {
+ lm.params += j->data;
+ lm.params += ' ';
+ }
+
+ // All other listmodes are managed by modules, so we need to ask them (call their
+ // OnSyncChannel() handler) to give our ProtoSendMode() a list of modes that are
+ // set on the channel. The ListModeData struct is passed as an opaque pointer
+ // that will be passed back to us by the module handling the mode.
+ FOREACH_MOD(I_OnSyncChannel, OnSyncChannel(chan, mod, &lm));
+
+ if (!lm.modes.empty())
+ {
+ // Remove the last space
+ lm.params.erase(lm.params.end()-1);
+
+ // If there is at least a space in chanmodes (that is, a non-listmode has a parameter)
+ // insert the listmode mode letters before the space. Otherwise just append them.
+ std::string::size_type p = chanmodes.find(' ');
+ if (p == std::string::npos)
+ chanmodes += lm.modes;
+ else
+ chanmodes.insert(p, lm.modes);
+
+ // Append the listmode parameters (the masks themselves)
+ chanmodes += ' ';
+ chanmodes += lm.params;
+ }
+ }
+
+ std::string chants = ConvToStr(chan->age);
+ std::string topicts = ConvToStr(chan->topicset);
+ const char* items[] =
+ {
+ "<permchannels channel=",
+ chan->name.c_str(),
+ " ts=",
+ chants.c_str(),
+ " topic=",
+ chan->topic.c_str(),
+ " topicts=",
+ topicts.c_str(),
+ " topicsetby=",
+ chan->setby.c_str(),
+ " modes=",
+ chanmodes.c_str(),
+ ">\n"
+ };
+
+ line.clear();
+ int item = 0, ipos = 0;
+ while (item < 13)
+ {
+ char c = items[item][ipos++];
+ if (c == 0)
+ {
+ // end of this string; hop to next string, insert a quote
+ item++;
+ ipos = 0;
+ c = '"';
+ }
+ else if (c == '\\' || c == '"')
+ {
+ line += '\\';
+ }
+ line += c;
+ }
+
+ // Erase last '"'
+ line.erase(line.end()-1);
+ fputs(line.c_str(), f);
+ }
+
+ int write_error = 0;
+ write_error = ferror(f);
+ write_error |= fclose(f);
+ if (write_error)
+ {
+ ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: Cannot write to new database! %s (%d)", strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot write to new db: %s (%d)", strerror(errno), errno);
+ return false;
+ }
+
+#ifdef _WIN32
+ remove(permchannelsconf.c_str());
+#endif
+ // Use rename to move temporary to new db - this is guarenteed not to fuck up, even in case of a crash.
+ if (rename(tempname.c_str(), permchannelsconf.c_str()) < 0)
+ {
+ ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: Cannot move new to old database! %s (%d)", strerror(errno), errno);
+ ServerInstance->SNO->WriteToSnoMask('a', "database: cannot replace old with new db: %s (%d)", strerror(errno), errno);
+ return false;
+ }
+
+ return true;
+}
+
+