--- a
+++ b/src/protocolinfo.cxx
@@ -0,0 +1,163 @@
+/* Copyright (C) 2016 J.F.Dockes
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include "protocolinfo.hxx"
+
+#include "libupnpp/log.hxx"
+#include "libupnpp/upnpavutils.hxx"
+
+#include "main.hxx"
+#include "smallut.h"
+#include "pathut.h"
+#include "upmpdutils.hxx"
+
+#include <string>
+
+#include <stdlib.h>
+#include <time.h>
+#include <sys/stat.h>
+
+using namespace std;
+
+class Protocolinfo::Internal {
+public:
+ string prototext;
+ unordered_set<string> supportedformats;
+ bool ok{false};
+ time_t mtime{0};
+ int64_t sz{0};
+
+ bool maybeUpdate();
+};
+
+static Protocolinfo *theProto;
+
+Protocolinfo *Protocolinfo::the()
+{
+ if (theProto == 0)
+ theProto = new Protocolinfo();
+ if (theProto && !theProto->ok()) {
+ delete theProto;
+ theProto = 0;
+ return 0;
+ }
+ return theProto;
+}
+
+Protocolinfo::Protocolinfo()
+{
+ m = new Internal();
+ if (!m) {
+ LOGFAT("Protocolinfo: out of mem\n");
+ abort();
+ }
+ m->maybeUpdate();
+}
+
+bool Protocolinfo::ok()
+{
+ return m && m->ok;
+}
+
+const string& Protocolinfo::gettext()
+{
+ m->maybeUpdate();
+ return m->prototext;
+}
+
+const std::unordered_set<std::string>& Protocolinfo::getsupportedformats()
+{
+ m->maybeUpdate();
+ return m->supportedformats;
+}
+
+/**
+ * Read protocol info file. This contains the connection manager
+ * protocol info data
+ *
+ * We strip white-space from beginning/ends of lines, and allow
+ * #-started comments (on a line alone only, comments after data not allowed).
+ */
+static bool read_protocolinfo(const string& fn, bool enableL16, string& out)
+{
+ LOGDEB1("read_protocolinfo: fn " << fn << "\n");
+ out.clear();
+ ifstream input;
+ input.open(fn, ios::in);
+ if (!input.is_open()) {
+ LOGERR("read_protocolinfo: open failed: " << fn << "\n");
+ return false;
+ }
+ bool eof = false;
+ for (;;) {
+ string line;
+ getline(input, line);
+ if (!input.good()) {
+ if (input.bad()) {
+ LOGERR("read_protocolinfo: read error: " << fn << "\n");
+ return false;
+ }
+ // Must be eof ? But maybe we have a partial line which
+ // must be processed. This happens if the last line before
+ // eof ends with a backslash, or there is no final \n
+ eof = true;
+ }
+ trimstring(line, " \t\n\r,");
+ if (!line.empty()) {
+ if (enableL16 && line[0] == '@') {
+ line = regsub1("@ENABLEL16@", line, "");
+ } else {
+ line = regsub1("@ENABLEL16@", line, "#");
+ }
+ if (line[0] == '#')
+ continue;
+
+ out += line + ',';
+ }
+ if (eof)
+ break;
+ }
+ trimstring(out, ",");
+ LOGDEB0("read_protocolinfo data: [" << out << "]\n");
+ return true;
+}
+
+bool Protocolinfo::Internal::maybeUpdate()
+{
+ string protofile(path_cat(g_datadir, "protocolinfo.txt"));
+ struct stat st;
+ if (::stat(protofile.c_str(), &st) != 0) {
+ LOGFAT("protocolinfo: stat() failed for " << protofile << endl);
+ return ok=false;
+ }
+ if (mtime == st.st_mtime && sz == st.st_size) {
+ return true;
+ }
+ if (!read_protocolinfo(protofile, g_enableL16, prototext)) {
+ LOGFAT("Failed reading protocol info from " << protofile << endl);
+ return ok=false;
+ }
+
+ vector<UPnPP::ProtocolinfoEntry> vpe;
+ parseProtocolInfo(prototext, vpe);
+ for (const auto& it : vpe) {
+ supportedformats.insert(it.contentFormat);
+ }
+ mtime = st.st_mtime;
+ sz = st.st_size;
+ return ok=true;
+}
+