BridgeConfig.cpp 7.82 KB
Newer Older
1 2 3
#include "BridgeConfig.h"
#include "Common/Log.h"

4 5
#include <memory>
#include <mutex>
6 7
#include <string>
#include <sstream>
Jacob Gladish's avatar
Jacob Gladish committed
8
#include <string.h>
9 10 11 12 13

#include <strings.h>
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>
jacobgladish's avatar
jacobgladish committed
14
#include <libxml/xmlerror.h>
15 16 17 18 19 20
#include <libxml/xmlsave.h>

namespace
{
  DSB_DECLARE_LOGNAME(BridgeConfig);

jacobgladish's avatar
jacobgladish committed
21 22 23 24 25
  void libXmlGenericErrorFunc(void* /*ctx*/, xmlErrorPtr error)
  {
    if (!error)
      return;

26
    common::LogLevel level = common::LogLevel::Info;
jacobgladish's avatar
jacobgladish committed
27 28
    switch (error->level)
    {
29 30 31 32
      case XML_ERR_NONE: level = common::LogLevel::Debug; break;
      case XML_ERR_WARNING: level = common::LogLevel::Warn; break;
      case XML_ERR_ERROR: level = common::LogLevel::Error; break;
      case XML_ERR_FATAL: level = common::LogLevel::Fatal; break;
jacobgladish's avatar
jacobgladish committed
33 34 35 36 37 38 39 40 41 42 43 44 45 46
    }

    // remove newline. I hate blank lines in logfiles.
    if (error->message)
    {
      char* p = strrchr(error->message, '\n');
      if (p)
        *p = '\0';
    }

    char const* msg = error->message;
    if (!msg)
      msg = "<unknown error>";

47
    common::Logger::Write("libxml", level, error->file, error->line, "%s", msg);
jacobgladish's avatar
jacobgladish committed
48 49 50 51 52 53 54 55 56 57 58 59
  }

  void installXmlLogger()
  {
    static bool libXmlLoggerInstalled = false;
    if (!libXmlLoggerInstalled)
    {
      xmlSetStructuredErrorFunc(NULL, &libXmlGenericErrorFunc);
      libXmlLoggerInstalled = true;
    }
  }

gladish's avatar
gladish committed
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
  inline xmlChar const* toXmlChar(char const* s)
  {
    return reinterpret_cast<xmlChar const *>(s);
  }

  inline char const* toChar(xmlChar const* s)
  {
    return reinterpret_cast<char const *>(s);
  }

  inline xmlChar const* toXmlChar(bool b)
  {
    return b ? toXmlChar("true") : toXmlChar("false");
  }

75
  char const* kAlljoynObjectPathPrefix = "/BridgeConfig/AdapterDevices/Device[@Id=\"";
gladish's avatar
gladish committed
76
  char const* kAdapterDevices = "/BridgeConfig/AdapterDevices";
77
  char const* kAlljoynObjectPathSuffix = "\"]";
gladish's avatar
gladish committed
78
  char const* kDeviceElementName = "Device";
79
  char const* kIdAttribute = "Id";
gladish's avatar
gladish committed
80
  char const* kVisibleAttribute = "Visible";
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

  std::string MakePathId(std::string const& id)
  {
    std::stringstream buff;
    buff << kAlljoynObjectPathPrefix;
    buff << id;
    buff << kAlljoynObjectPathSuffix;
    return buff.str();
  }

  xmlChar* GetNodeAttribute(const xmlNodePtr node, char const* attr)
  {
    if (node)
    {
      xmlAttr* attrs = node->properties;
      while (attrs && attrs->name && attrs->children)
      {
gladish's avatar
gladish committed
98
        if (strcmp(toChar(attrs->name), attr) == 0)
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
          return xmlNodeListGetString(node->doc, attrs->children, 1);
        attrs = attrs->next;
      }
    }
    return NULL;
  }

  bool ToBoolean(std::string const& s)
  {
    return strcasecmp(s.c_str(), "true") == 0 ? true : false;
  }

  xmlNodePtr SelectSingleNode(xmlXPathContextPtr ctx, char const* xpath)
  {
    xmlNodePtr node = NULL;

    DSBLOG_INFO("select: %s", xpath);

gladish's avatar
gladish committed
117
    xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression(toXmlChar(xpath), ctx);
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
    if (xpathObj)
    {
      int n = 0;
      if (xpathObj->nodesetval)
        n = xpathObj->nodesetval->nodeNr;

      if (n == 1)
        node = xpathObj->nodesetval->nodeTab[0];
    }

    return node;
  }

  std::string GetElementText(const xmlDocPtr doc, char const* xpath)
  {
    xmlXPathContextPtr ctx = xmlXPathNewContext(doc);
    if (!ctx)
      return NULL;

    std::string content;

    xmlNodePtr node = SelectSingleNode(ctx, xpath);
    if (node)
    {
      xmlChar* s = xmlNodeGetContent(node);
gladish's avatar
gladish committed
143
      content = std::string(toChar(s));
144 145
    }

gladish's avatar
gladish committed
146
    xmlXPathFreeContext(ctx);
147 148 149
    DSBLOG_INFO("%s == %s", xpath, content.c_str());
    return content;
  }
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166

  class LibXmlInitializer
  {
  public:
    LibXmlInitializer()
    {
      xmlInitParser();
    }

    ~LibXmlInitializer()
    {
      xmlCleanupParser();
    }
  };

  std::once_flag libXmlInit;
  std::shared_ptr<LibXmlInitializer> libXmlInitializer;
167 168
}

gladish's avatar
gladish committed
169
bridge::BridgeConfig::BridgeConfig()
170 171
  : m_doc(NULL)
{
jacobgladish's avatar
jacobgladish committed
172
  installXmlLogger();
173 174
}

gladish's avatar
gladish committed
175
bridge::BridgeConfig::~BridgeConfig()
176 177 178 179 180
{
  if (m_doc)
    xmlFreeDoc(m_doc);
}

gladish's avatar
gladish committed
181 182
QStatus
bridge::BridgeConfig::FromFile(std::string const& fileName)
183 184 185 186
{
  QStatus st = ER_OK;

  m_fileName = fileName;
jacobgladish's avatar
jacobgladish committed
187
  installXmlLogger();
188 189 190 191

  std::call_once(libXmlInit, [] {
    if (!libXmlInitializer) libXmlInitializer.reset(new LibXmlInitializer());
  });
192 193 194 195 196 197 198 199 200 201 202

  xmlDocPtr doc = xmlParseFile(m_fileName.c_str());
  if (!doc)
  {
    DSBLOG_WARN("failed to parse: %s", m_fileName.c_str());
    st = ER_FAIL;
  }
  if (m_doc)
    xmlFreeDoc(m_doc);

  m_doc = doc;
203
  return st;
204 205
}

gladish's avatar
gladish committed
206 207
QStatus
bridge::BridgeConfig::ToFile(std::string const& fileName)
208 209 210 211 212 213 214 215
{
  if (!m_doc)
    return ER_FAIL;

  char const* outfile = m_fileName.c_str();
  if (!fileName.empty())
    outfile = fileName.c_str();

gladish's avatar
gladish committed
216 217 218 219 220 221
  // TODO: use TextWriter to actually indent this stuff
//  xmlSaveCtxtPtr ctx = xmlSaveToFilename(outfile, NULL, XML_SAVE_AS_HTML);
//  xmlSaveDoc(ctx, m_doc);
//  xmlSaveClose(ctx);
  xmlKeepBlanksDefault(1);
  xmlSaveFormatFile(outfile, m_doc, 1);
222 223 224 225

  return ER_OK;
}

gladish's avatar
gladish committed
226 227
QStatus
bridge::BridgeConfig::ToString(std::string& out)
228 229 230
{
  xmlBufferPtr buff = xmlBufferCreate();

gladish's avatar
gladish committed
231
  // TODO: use TextWriter to actually indent this stuff
232 233 234 235
  xmlSaveCtxtPtr ctx = xmlSaveToBuffer(buff, NULL, XML_SAVE_FORMAT);
  xmlSaveDoc(ctx, m_doc);
  xmlSaveClose(ctx);

gladish's avatar
gladish committed
236
  out = std::string(toChar(buff->content));
237 238 239 240 241
  xmlBufferFree(buff);

  return ER_OK;
}

gladish's avatar
gladish committed
242 243
QStatus
bridge::BridgeConfig::AddObject(DsbObjectConfig const& obj)
gladish's avatar
gladish committed
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
{
  xmlXPathContextPtr ctx = xmlXPathNewContext(m_doc);
  if (!ctx)
    return ER_FAIL;

  xmlNodePtr parent = SelectSingleNode(ctx, kAdapterDevices);
  if (parent)
  {
    xmlNodePtr node = xmlNewTextChild(parent, NULL,  toXmlChar(kDeviceElementName),
      toXmlChar(obj.GetDescription().c_str()));
    xmlNewProp(node, toXmlChar(kIdAttribute), toXmlChar(obj.GetId().c_str()));
    xmlNewProp(node, toXmlChar(kVisibleAttribute), toXmlChar(obj.IsVisible()));
  }

  xmlXPathFreeContext(ctx);
  return ER_OK;
}

gladish's avatar
gladish committed
262 263
QStatus
bridge::BridgeConfig::RemoveObject(std::string const& id)
gladish's avatar
gladish committed
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
{
  xmlXPathContextPtr ctx = xmlXPathNewContext(m_doc);
  if (!ctx)
    return ER_FAIL;

  xmlNodePtr node = SelectSingleNode(ctx, MakePathId(id).c_str());
  if (node)
  {
    xmlUnlinkNode(node);
    xmlFreeNode(node);
  }

  xmlXPathFreeContext(ctx);
  return ER_OK;
}

gladish's avatar
gladish committed
280 281
QStatus
bridge::BridgeConfig::FindObject(std::string const& id, DsbObjectConfig& obj)
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
{
  if (!m_doc)
    return ER_FAIL;

  xmlXPathContextPtr ctx = xmlXPathNewContext(m_doc);
  if (!ctx)
    return ER_FAIL;

  std::string path = MakePathId(id);
  xmlNodePtr node = SelectSingleNode(ctx, path.c_str());
  if (!node)
    return ER_BUS_OBJ_NOT_FOUND;

  obj.SetId(path);

  xmlChar* attr = GetNodeAttribute(node, kVisibleAttribute);
  if (attr)
  {
gladish's avatar
gladish committed
300
    bool b = strcasecmp(toChar(attr), "true") == 0
301 302 303 304 305 306 307
      ? true
      : false;
    obj.SetIsVisible(b);
    xmlFree(attr);
  }

  xmlChar* desc = xmlNodeGetContent(node);
gladish's avatar
gladish committed
308
  obj.SetDescription(std::string(toChar(desc)));
309 310 311 312 313

  xmlXPathFreeContext(ctx);
  return ER_OK;
}

gladish's avatar
gladish committed
314 315
std::string
bridge::BridgeConfig::GetBridgeKeyX() const
316 317 318 319
{
  return GetElementText(m_doc, "/BridgeConfig/Settings/Bridge/KEYX");
}

gladish's avatar
gladish committed
320 321
std::string
bridge::BridgeConfig::GetDeviceKeyX() const
322 323 324 325
{
  return GetElementText(m_doc, "/BridgeConfig/Settings/Device/KEYX");
}

gladish's avatar
gladish committed
326 327
std::string
bridge::BridgeConfig::GetDeviceUsername() const
328 329 330 331
{
  return GetElementText(m_doc, "/BridgeConfig/Settings/Device/USERNAME");
}

gladish's avatar
gladish committed
332 333
std::string
bridge::BridgeConfig::GetDevicePassword() const
334 335 336 337
{
  return GetElementText(m_doc, "/BridgeConfig/Settings/Device/PASSWORD");
}

gladish's avatar
gladish committed
338 339
std::string
bridge::BridgeConfig::GetDeviceEcdheEcdsaPrivateKey() const
340 341 342 343
{
  return GetElementText(m_doc, "/BridgeConfig/Settings/Device/ECDHEECDSAPRIVATEKEY");
}

gladish's avatar
gladish committed
344 345
std::string
bridge::BridgeConfig::GetDeviceEcdheEcdsaCertChain() const
346 347 348 349
{
  return GetElementText(m_doc, "/BridgeConfig/Settings/Device/ECDHEECDSACERTCHAIN");
}

gladish's avatar
gladish committed
350 351
bool
bridge::BridgeConfig::GetDefaultVisibility() const
352 353 354 355
{
  std::string s = GetElementText(m_doc, "/BridgeConfig/Settings/Device/DefaultVisibility");
  return ToBoolean(s);
}