Commit c887a0f0dcc104c68e4004c400c7c95c07742be8

Authored by Hu Chunming
0 parents

提交初成版代码

.vscode/launch.json 0 → 100644
  1 +++ a/.vscode/launch.json
  1 +{
  2 + // 使用 IntelliSense 了解相关属性。
  3 + // 悬停以查看现有属性的描述。
  4 + // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
  5 + "version": "0.2.0",
  6 + "configurations": [
  7 + {
  8 + "name": "gb28181",
  9 + "type": "cppdbg",
  10 + "request": "launch",
  11 + "program": "${workspaceFolder}/bin/sip_server",
  12 + "args": ["34020000001310000001"],
  13 + "stopAtEntry": false,
  14 + "cwd": "${workspaceFolder}/bin",
  15 + "environment": [],
  16 + "externalConsole": false,
  17 + "MIMode": "gdb",
  18 + "setupCommands": [
  19 + {
  20 + "description": "Enable pretty-printing for gdb",
  21 + "text": "-enable-pretty-printing",
  22 + "ignoreFailures": true
  23 + }
  24 + ]
  25 + }
  26 + ]
  27 +}
0 28 \ No newline at end of file
... ...
sip/ConfigParser.hpp 0 → 100644
  1 +++ a/sip/ConfigParser.hpp
  1 +#ifndef __CONFIG_PARSER_H__
  2 +#define __CONFIG_PARSER_H__
  3 +
  4 +#include <fstream>
  5 +#include "./Message/CatalogParser.h"
  6 +#include "sip_header.h"
  7 +#include "./Utils/logger.hpp"
  8 +
  9 +
  10 +class ConfigParser
  11 +{
  12 +public:
  13 + static ConfigParser* getInstance(){
  14 + static ConfigParser* singleton = nullptr;
  15 + if (singleton == nullptr){
  16 + singleton = new ConfigParser();
  17 + }
  18 + return singleton;
  19 + }
  20 +
  21 +public:
  22 + ConfigParser(/* args */);
  23 + ~ConfigParser();
  24 +
  25 + bool init();
  26 +
  27 + ServerInfo getServerCfg();
  28 +
  29 +private:
  30 + ServerInfo mInfo;
  31 +
  32 +};
  33 +
  34 +ConfigParser::ConfigParser(/* args */)
  35 +{
  36 +}
  37 +
  38 +ConfigParser::~ConfigParser()
  39 +{
  40 +}
  41 +
  42 +bool ConfigParser::init() {
  43 + std::ifstream cfgFile("./sip_server_cfg.xml");
  44 + if(cfgFile.is_open()) {
  45 + string strConfig;
  46 + string str;
  47 + while(cfgFile >> str)
  48 + {
  49 + strConfig += str;
  50 + }
  51 + CCatalogParser catPaser;
  52 + mInfo = catPaser.DecodeServerConfig(strConfig.c_str());
  53 + } else {
  54 + LOG_ERROR("read config file failed!");
  55 + return false;
  56 + }
  57 + cfgFile.close();
  58 +
  59 + return true;
  60 +}
  61 +
  62 +ServerInfo ConfigParser::getServerCfg() {
  63 + return mInfo;
  64 +}
  65 +
  66 +#endif
0 67 \ No newline at end of file
... ...
sip/Message/CatalogParser.cpp 0 → 100644
  1 +++ a/sip/Message/CatalogParser.cpp
  1 +#include "CatalogParser.h"
  2 +#include <sstream>
  3 +#include <list>
  4 +
  5 +#include "../Utils/logger.hpp"
  6 +
  7 +static const char* g_event_desc[] =
  8 +{
  9 + "ON",
  10 + "OFF",
  11 + "VLOST",
  12 + "DEFECT",
  13 + "ADD",
  14 + "DEL",
  15 + "UPDATE"
  16 +};
  17 +
  18 +
  19 +bool CCatalogParser::Encode( std::string &message )
  20 +{
  21 + std::ostringstream content;
  22 + content<<"<?xml version=\"1.0\"?>\r\n";
  23 + content<<"<Notify>\r\n";
  24 + content<<"<CmdType>Catalog</CmdType>\r\n";
  25 + content<<"<SN>"<< m_sn <<"</SN>\r\n";
  26 + content<<"<DeviceID>"<< m_deviceid <<"</DeviceID>\r\n";
  27 + content<<"<Status>OK</Status>\r\n";
  28 + content<<"<SumNum>"<< m_sum <<"</SumNum>\r\n";
  29 + content<<"<DeviceList Num=\"" << m_devices.size() << "\">\r\n";
  30 +
  31 + for( auto it = m_devices.begin(); it != m_devices.end(); ++it )
  32 + {
  33 + // 校验目录项的必选参数
  34 + const DeviceInfo &catalog = (*it);
  35 + content<<"<Item>\r\n";
  36 + content<<"<DeviceID>"<<catalog.id<<"</DeviceID>\r\n";
  37 + content<<"<Event>"<< g_event_desc[catalog.event] <<"</Event>\r\n";
  38 +
  39 + if( catalog.event == EVENT_ADD || catalog.event == EVENT_UPDATE )
  40 + {
  41 + content<<"<Name>"<< catalog.name <<"</Name>\r\n";
  42 + content<<"<Manufacturer>"<< catalog.manufacturer <<"</Manufacturer>\r\n";
  43 + content<<"<Model>"<< catalog.model <<"</Model>\r\n";
  44 + content<<"<Owner>"<< catalog.owner <<"</Owner>\r\n";
  45 +
  46 + if( !catalog.civil.empty() )
  47 + {
  48 + content<<"<CivilCode>"<<catalog.civil<<"</CivilCode>\r\n";
  49 + }
  50 + else
  51 + {
  52 + content<<"<CivilCode>"<<catalog.parentid<<"</CivilCode>\r\n";
  53 + }
  54 +
  55 + if( !catalog.block.empty() )
  56 + {
  57 + content<<"<Block>"<< catalog.block <<"</Block>\r\n";
  58 + }
  59 +
  60 + content<<"<Address>"<< catalog.address <<"</Address>\r\n";
  61 + content<<"<Parental>"<< catalog.parental <<"</Parental>\r\n";
  62 + content<<"<ParentID>"<< catalog.parentid <<"</ParentID>\r\n";
  63 +
  64 + if( !catalog.safetyway.empty() )
  65 + {
  66 + content<<"<SafetyWay>"<< catalog.safetyway <<"</SafetyWay>\r\n";
  67 + }
  68 +
  69 + content<<"<RegisterWay>"<< catalog.registerway <<"</RegisterWay>\r\n";
  70 +
  71 + if( !catalog.certnum.empty() )
  72 + {
  73 + content<<"<CertNum>"<< catalog.certnum <<"</CertNum>\r\n";
  74 + }
  75 +
  76 + if( !catalog.certifiable.empty() )
  77 + {
  78 + content<<"<Certifiable>"<< catalog.certifiable <<"</Certifiable>\r\n";
  79 + }
  80 +
  81 + if( !catalog.errcode.empty() )
  82 + {
  83 + content<<"<ErrCode>"<< catalog.errcode <<"</ErrCode>\r\n";
  84 + }
  85 +
  86 + if( !catalog.endtime.empty() )
  87 + {
  88 + content<<"<EndTime>"<< catalog.endtime <<"</EndTime>\r\n";
  89 + }
  90 +
  91 + content<<"<Secrecy>"<< catalog.secrecy <<"</Secrecy>\r\n";
  92 +
  93 + if( !catalog.ip.empty() )
  94 + {
  95 + content<<"<IPAddress>"<< catalog.ip <<"</IPAddress>\r\n";
  96 + }
  97 +
  98 + if( !catalog.port.empty() )
  99 + {
  100 + content<<"<Port>"<< catalog.port <<"</Port>\r\n";
  101 + }
  102 +
  103 + if( !catalog.password.empty() )
  104 + {
  105 + content<<"<Password>"<< catalog.password <<"</Password>\r\n";
  106 + }
  107 +
  108 + content<<"<Status>"<<catalog.status<<"</Status>\r\n";
  109 +
  110 + if( !catalog.longitude.empty() )
  111 + {
  112 + content<<"<Longitude>"<<catalog.longitude<<"</Longitude>\r\n";
  113 + }
  114 +
  115 + if( !catalog.latitude.empty() )
  116 + {
  117 + content<<"<Latitude>"<<catalog.latitude<<"</Latitude>\r\n";
  118 + }
  119 +
  120 + content<<"<Info>\r\n";
  121 + if( !catalog.ptz.empty() )
  122 + {
  123 + content<<"<PTZType>"<<catalog.ptz<<"</PTZType>\r\n";
  124 + }
  125 +
  126 + if( !catalog.position.empty() )
  127 + {
  128 + content<<"<PositionType>"<<catalog.position<<"</PositionType>\r\n";
  129 + }
  130 +
  131 + if( !catalog.room.empty() )
  132 + {
  133 + content<<"<RoomType>"<<catalog.room<<"</RoomType>\r\n";
  134 + }
  135 +
  136 + if( !catalog.use.empty() )
  137 + {
  138 + content<<"<UseType>"<<catalog.use<<"</UseType>\r\n";
  139 + }
  140 +
  141 + if( !catalog.supplylight.empty() )
  142 + {
  143 + content<<"<SupplyLightType>"<<catalog.supplylight<<"</SupplyLightType>\r\n";
  144 + }
  145 +
  146 + if( !catalog.direction.empty() )
  147 + {
  148 + content<<"<DirectionType>"<<catalog.direction<<"</DirectionType>\r\n";
  149 + }
  150 +
  151 + if( !catalog.resolution.empty() )
  152 + {
  153 + content<<"<Resolution>"<<catalog.resolution<<"</Resolution>\r\n";
  154 + }
  155 +
  156 + if( !catalog.businessgroup.empty() )
  157 + {
  158 + content<<"<BusinessGroupID>"<<catalog.businessgroup<<"</BusinessGroupID>\r\n";
  159 + }
  160 +
  161 + content<<"</Info>\r\n";
  162 + }
  163 +
  164 + content<<"</Item>\r\n";
  165 + }
  166 + content<<"</DeviceList>\r\n";
  167 + content<<"</Notify>\r\n\r\n";
  168 +
  169 + message = content.str();
  170 + return true;
  171 +}
  172 +
  173 +std::vector< DeviceInfo > CCatalogParser::Decode( const std::vector< tinyxml2::XMLNode* > &nodes)
  174 +{
  175 + std::vector< DeviceInfo > cat_list;
  176 + // 必须参数校验
  177 + tinyxml2::XMLNode *pSumNum = NULL;
  178 + size_t size = nodes.size();
  179 + for( size_t i = 0; i < size; ++i )
  180 + {
  181 + tinyxml2::XMLNode *pNode = nodes[i];
  182 + if( pNode == NULL )
  183 + {
  184 + LOG_DEBUG( "参数错误" );
  185 + continue;
  186 + }
  187 +
  188 + const char* type = pNode->Value();
  189 + if( type == NULL )
  190 + {
  191 + LOG_DEBUG( "参数名字为空" );
  192 + continue;
  193 + }
  194 +
  195 + if( CGBMessage::CompareNoCase( type, "SumNum" ) )
  196 + {
  197 + pSumNum = pNode;
  198 + }
  199 + }
  200 +
  201 + // 必选参数必须填
  202 + if( pSumNum == NULL )
  203 + {
  204 + LOG_ERROR( "参数SumNum没有被设置" );
  205 + return cat_list;
  206 + }
  207 +
  208 + // 参数解析
  209 + std::list< tinyxml2::XMLNode* > items;
  210 + for( size_t i = 0; i < size; ++i )
  211 + {
  212 + tinyxml2::XMLNode *pNode = nodes[i];
  213 + if( pNode == NULL )
  214 + {
  215 + LOG_DEBUG( "参数错误" );
  216 + continue;
  217 + }
  218 +
  219 + const char* type = pNode->Value();
  220 + if( type == NULL )
  221 + {
  222 + LOG_DEBUG( "参数名字为空" );
  223 + continue;
  224 + }
  225 +
  226 + if( CGBMessage::CompareNoCase( type, "Status" ) )
  227 + {
  228 + tinyxml2::XMLNode *pChild = pNode->FirstChild();
  229 + if( pChild == NULL )
  230 + {
  231 + LOG_DEBUG( "参数值没有设置" );
  232 + continue;
  233 + }
  234 +
  235 + const char *value = pChild->Value();
  236 + if( value == NULL )
  237 + {
  238 + LOG_DEBUG( "参数值为空" );
  239 + continue;
  240 + }
  241 + else
  242 + {
  243 + m_status = value;
  244 + }
  245 + }
  246 + else if( CGBMessage::CompareNoCase( type, "SumNum" ) )
  247 + {
  248 + tinyxml2::XMLNode *pChild = pNode->FirstChild();
  249 + if( pChild == NULL )
  250 + {
  251 + LOG_DEBUG( "参数值没有设置" );
  252 + continue;
  253 + }
  254 +
  255 + const char *value = pChild->Value();
  256 + if( value == NULL )
  257 + {
  258 + LOG_DEBUG( "参数值为空" );
  259 + continue;
  260 + }
  261 + else
  262 + {
  263 + m_sum = atoi( value );
  264 + }
  265 + }
  266 + else if( CGBMessage::CompareNoCase( type, "DeviceList" ) )
  267 + {
  268 + for(tinyxml2::XMLNode *pItem = pNode->FirstChild(); pItem != NULL; pItem = pItem->NextSibling() )
  269 + {
  270 + const char *type = pItem->Value();
  271 + if( type == NULL )
  272 + {
  273 + LOG_DEBUG( "参数名字为空" );
  274 + continue;
  275 + }
  276 +
  277 + if( CGBMessage::CompareNoCase( type, "Item" ) )
  278 + {
  279 + items.push_back( pItem );
  280 + }
  281 + else
  282 + {
  283 + LOG_DEBUG( "国标未定义的参数:{}" , type );
  284 + continue;
  285 + }
  286 + }
  287 + }
  288 + else
  289 + {
  290 + LOG_DEBUG( "国标未定义的参数:{}" , type );
  291 + continue;
  292 + }
  293 + }
  294 +
  295 + // 目录项
  296 + std::list< tinyxml2::XMLNode* >::iterator it = items.begin();
  297 + std::list< tinyxml2::XMLNode* >::iterator end = items.end();
  298 + for( /*it*/; it != end; ++it )
  299 + {
  300 + DeviceInfo catalog;
  301 + for(tinyxml2::XMLNode *pItem = (*it)->FirstChild(); pItem != NULL; pItem = pItem->NextSibling() )
  302 + {
  303 + const char *type = pItem->Value();
  304 + if( type == NULL )
  305 + {
  306 + LOG_DEBUG( "参数名字为空" );
  307 + continue;
  308 + }
  309 +
  310 + tinyxml2::XMLNode *pValue = pItem->FirstChild();
  311 + if( pValue == NULL )
  312 + {
  313 + LOG_DEBUG( "参数值没有设置" );
  314 + continue;
  315 + }
  316 +
  317 + const char *value = pValue->Value();
  318 + if( value == NULL )
  319 + {
  320 + LOG_DEBUG( "参数值为空" );
  321 + continue;
  322 + }
  323 +
  324 + if( CGBMessage::CompareNoCase( type, "DeviceID" ) )
  325 + {
  326 + catalog.id = value;
  327 + }
  328 + else if( CGBMessage::CompareNoCase( type, "Event" ) )
  329 + {
  330 + if( CGBMessage::CompareNoCase( value, "ON" ) )
  331 + {
  332 + catalog.event = EVENT_ON;
  333 + }
  334 + else if( CGBMessage::CompareNoCase( value, "OFF" ) )
  335 + {
  336 + catalog.event = EVENT_OFF;
  337 + }
  338 + else if( CGBMessage::CompareNoCase( value, "VLOST" ) )
  339 + {
  340 + catalog.event = EVENT_VLOST;
  341 + }
  342 + else if( CGBMessage::CompareNoCase( value, "DEFECT" ) )
  343 + {
  344 + catalog.event = EVENT_DEFECT;
  345 + }
  346 + else if( CGBMessage::CompareNoCase( value, "ADD" ) )
  347 + {
  348 + catalog.event = EVENT_ADD;
  349 + }
  350 + else if( CGBMessage::CompareNoCase( value, "DEL" ) )
  351 + {
  352 + catalog.event = EVENT_DEL;
  353 + }
  354 + else if( CGBMessage::CompareNoCase( value, "UPDATE" ) )
  355 + {
  356 + catalog.event = EVENT_UPDATE;
  357 + }
  358 + else
  359 + {
  360 + LOG_ERROR( "Event参数值\'{}\'无效", value );
  361 + return cat_list;
  362 + }
  363 + }
  364 + else if( CGBMessage::CompareNoCase( type, "Name" ) )
  365 + {
  366 + catalog.name = value;
  367 + }
  368 + else if( CGBMessage::CompareNoCase( type, "Manufacturer" ) )
  369 + {
  370 + catalog.manufacturer = value;
  371 + }
  372 + else if( CGBMessage::CompareNoCase( type, "Model" ) )
  373 + {
  374 + catalog.model = value;
  375 + }
  376 + else if( CGBMessage::CompareNoCase( type, "Owner" ) )
  377 + {
  378 + catalog.owner = value;
  379 + }
  380 + else if( CGBMessage::CompareNoCase( type, "CivilCode" ) )
  381 + {
  382 + catalog.civil = value;
  383 + }
  384 + else if( CGBMessage::CompareNoCase( type, "Block" ) )
  385 + {
  386 + catalog.block = value;
  387 + }
  388 + else if( CGBMessage::CompareNoCase( type, "Address" ) )
  389 + {
  390 + catalog.address = value;
  391 + }
  392 + else if( CGBMessage::CompareNoCase( type, "Parental" ) )
  393 + {
  394 + catalog.parental = value;
  395 + }
  396 + else if( CGBMessage::CompareNoCase( type, "ParentID" ) )
  397 + {
  398 + catalog.parentid = value;
  399 + }
  400 + else if( CGBMessage::CompareNoCase( type, "SafetyWay" ) )
  401 + {
  402 + catalog.safetyway = value;
  403 + }
  404 + else if( CGBMessage::CompareNoCase( type, "RegisterWay" ) )
  405 + {
  406 + catalog.registerway = value;
  407 + }
  408 + else if( CGBMessage::CompareNoCase( type, "CertNum" ) )
  409 + {
  410 + catalog.certnum = value;
  411 + }
  412 + else if( CGBMessage::CompareNoCase( type, "Certifiable" ) )
  413 + {
  414 + catalog.certifiable = value;
  415 + }
  416 + else if( CGBMessage::CompareNoCase( type, "ErrCode" ) )
  417 + {
  418 + catalog.errcode = value;
  419 + }
  420 + else if( CGBMessage::CompareNoCase( type, "EndTime" ) )
  421 + {
  422 + catalog.endtime = value;
  423 + }
  424 + else if( CGBMessage::CompareNoCase( type, "Secrecy" ) )
  425 + {
  426 + catalog.secrecy = value;
  427 + }
  428 + else if( CGBMessage::CompareNoCase( type, "IPAddress" ) )
  429 + {
  430 + catalog.ip = value;
  431 + }
  432 + else if( CGBMessage::CompareNoCase( type, "Port" ) )
  433 + {
  434 + catalog.port = value;
  435 + }
  436 + else if( CGBMessage::CompareNoCase( type, "Password" ) )
  437 + {
  438 + catalog.password = value;
  439 + }
  440 + else if( CGBMessage::CompareNoCase( type, "Status" ) )
  441 + {
  442 + catalog.status = value;
  443 + }
  444 + else if( CGBMessage::CompareNoCase( type, "Longitude" ) )
  445 + {
  446 + catalog.longitude = value;
  447 + }
  448 + else if( CGBMessage::CompareNoCase( type, "Latitude" ) )
  449 + {
  450 + catalog.latitude = value;
  451 + }
  452 + else if( CGBMessage::CompareNoCase( type, "PTZType" ) )
  453 + {
  454 + catalog.ptz = value;
  455 + }
  456 + else if( CGBMessage::CompareNoCase( type, "PositionType" ) )
  457 + {
  458 + catalog.position = value;
  459 + }
  460 + else if( CGBMessage::CompareNoCase( type, "RoomType" ) )
  461 + {
  462 + catalog.room = value;
  463 + }
  464 + else if( CGBMessage::CompareNoCase( type, "UseType" ) )
  465 + {
  466 + catalog.use = value;
  467 + }
  468 + else if( CGBMessage::CompareNoCase( type, "SupplyLightType" ) )
  469 + {
  470 + catalog.supplylight = value;
  471 + }
  472 + else if( CGBMessage::CompareNoCase( type, "DirectionType" ) )
  473 + {
  474 + catalog.direction = value;
  475 + }
  476 + else if( CGBMessage::CompareNoCase( type, "Resolution" ) )
  477 + {
  478 + catalog.resolution = value;
  479 + }
  480 + else if( CGBMessage::CompareNoCase( type, "BusinessGroupID" ) )
  481 + {
  482 + catalog.businessgroup = value;
  483 + }
  484 + else if( CGBMessage::CompareNoCase( type, "Info" ) )
  485 + {
  486 + for(tinyxml2::XMLNode *pInfo = pItem->FirstChild(); pInfo != NULL; pInfo = pInfo->NextSibling() )
  487 + {
  488 + pValue = pInfo->FirstChild();
  489 + if( pValue == NULL )
  490 + {
  491 + LOG_DEBUG( "参数值没有设置" );
  492 + continue;
  493 + }
  494 +
  495 + const char *type = pInfo->Value();
  496 + if( type == NULL )
  497 + {
  498 + LOG_DEBUG( "参数名字为空" );
  499 + continue;
  500 + }
  501 +
  502 + const char *value = pValue->Value();
  503 + if( value == NULL )
  504 + {
  505 + LOG_DEBUG( "参数值为空" );
  506 + continue;
  507 + }
  508 +
  509 + if( CGBMessage::CompareNoCase( type, "PTZType" ) )
  510 + {
  511 + catalog.ptz = value;
  512 + }
  513 + else if( CGBMessage::CompareNoCase( type, "PositionType" ) )
  514 + {
  515 + catalog.position = value;
  516 + }
  517 + else if( CGBMessage::CompareNoCase( type, "RoomType" ) )
  518 + {
  519 + catalog.room = value;
  520 + }
  521 + else if( CGBMessage::CompareNoCase( type, "UseType" ) )
  522 + {
  523 + catalog.use = value;
  524 + }
  525 + else if( CGBMessage::CompareNoCase( type, "SupplyLightType" ) )
  526 + {
  527 + catalog.supplylight = value;
  528 + }
  529 + else if( CGBMessage::CompareNoCase( type, "DirectionType" ) )
  530 + {
  531 + catalog.direction = value;
  532 + }
  533 + else if( CGBMessage::CompareNoCase( type, "Resolution" ) )
  534 + {
  535 + catalog.resolution = value;
  536 + }
  537 + else if( CGBMessage::CompareNoCase( type, "BusinessGroupID" ) )
  538 + {
  539 + catalog.businessgroup = value;
  540 + }
  541 + else
  542 + {
  543 + LOG_DEBUG( "国标未定义的参数:{}" , type );
  544 + }
  545 + }
  546 + }
  547 + else
  548 + {
  549 + LOG_DEBUG( "国标未定义的参数:{}" , type );
  550 + }
  551 + }
  552 +
  553 + // add to devices
  554 + m_devices.push_back( catalog );
  555 + }
  556 +
  557 + return m_devices;
  558 +}
  559 +
  560 +std::vector< DeviceInfo > CCatalogParser::DecodeCatlog(const char* body)
  561 +{
  562 + std::vector< DeviceInfo > cat_list;
  563 + if (body == NULL)
  564 + {
  565 + return cat_list;
  566 + }
  567 +
  568 + tinyxml2::XMLDocument doc;
  569 + doc.Parse(body);
  570 + tinyxml2::XMLElement* pRoot = doc.RootElement();
  571 + if (pRoot == NULL)
  572 + {
  573 + return cat_list;
  574 + }
  575 +
  576 + tinyxml2::XMLNode* pCmd = 0;
  577 + tinyxml2::XMLNode* pSN = 0;
  578 + tinyxml2::XMLNode* pDeviceID = 0;
  579 + std::vector< tinyxml2::XMLNode* > nodes;
  580 + for (tinyxml2::XMLNode* pNode = pRoot->FirstChild(); pNode != 0; pNode = pNode->NextSibling())
  581 + {
  582 + std::string value = pNode->Value();
  583 + if (value == "CmdType")
  584 + {
  585 + pCmd = pNode->FirstChild();
  586 + }
  587 + else if (value == "SN")
  588 + {
  589 + pSN = pNode->FirstChild();
  590 + }
  591 + else if (value == "DeviceID")
  592 + {
  593 + pDeviceID = pNode->FirstChild();
  594 + }
  595 + else
  596 + {
  597 + nodes.push_back(pNode);
  598 + }
  599 + }
  600 +
  601 + if (pCmd == NULL || pSN == NULL || pDeviceID == NULL)
  602 + {
  603 + return cat_list;
  604 + }
  605 +
  606 + std::string sn = pSN->Value();
  607 + if (sn.empty()) {
  608 + return cat_list;
  609 + }
  610 +
  611 + std::string deviceid = pDeviceID->Value();
  612 + if (deviceid.empty()) {
  613 + return cat_list;
  614 + }
  615 +
  616 + std::string msgType = pRoot->Value();
  617 + std::string cmdType = pCmd->Value();
  618 + if (msgType == "Response" && cmdType == "Catalog")
  619 + {
  620 + cat_list = Decode(nodes);
  621 + if (cat_list.empty())
  622 + {
  623 + LOG_WARN("消息体未解析出设备!");
  624 + } else {
  625 + // 设置
  626 + for (size_t i = 0; i < cat_list.size(); i++) {
  627 + cat_list[i].parentid = deviceid;
  628 + }
  629 + }
  630 + }
  631 +
  632 + return cat_list;
  633 +}
  634 +
  635 +string CCatalogParser::getItemValue(tinyxml2::XMLNode *pItem) {
  636 +
  637 + tinyxml2::XMLNode *pValue = pItem->FirstChild();
  638 + if( pValue == NULL ) {
  639 + LOG_DEBUG( "参数值没有设置" );
  640 + return "";
  641 + }
  642 +
  643 + const char *value = pValue->Value();
  644 + if( value == NULL ) {
  645 + LOG_DEBUG( "参数值为空" );
  646 + return "";
  647 + }
  648 +
  649 + return value;
  650 +}
  651 +
  652 +ServerInfo CCatalogParser::DecodeServerConfig(const char* body)
  653 +{
  654 + ServerInfo info;
  655 + if (body == NULL) {
  656 + return info;
  657 + }
  658 +
  659 + tinyxml2::XMLDocument doc;
  660 + doc.Parse(body);
  661 + tinyxml2::XMLElement* pRoot = doc.RootElement();
  662 + if (pRoot == NULL) {
  663 + return info;
  664 + }
  665 +
  666 + for (tinyxml2::XMLNode* pNode = pRoot->FirstChild(); pNode != 0; pNode = pNode->NextSibling())
  667 + {
  668 + std::string type = pNode->Value();
  669 + if (CGBMessage::CompareNoCase( type, "Ua" ))
  670 + {
  671 + info.setUa(getItemValue(pNode));
  672 + }
  673 + else if (CGBMessage::CompareNoCase( type, "Nonce" ))
  674 + {
  675 + info.setNonce(getItemValue(pNode));
  676 + }
  677 + else if (CGBMessage::CompareNoCase( type, "Ip" ))
  678 + {
  679 + info.setIp(getItemValue(pNode));
  680 + }
  681 + else if (CGBMessage::CompareNoCase( type, "Port" ))
  682 + {
  683 + string str = getItemValue(pNode);
  684 + int i = atoi(str.c_str());
  685 + info.setPort(i);
  686 + }
  687 + else if (CGBMessage::CompareNoCase( type, "SipId" ))
  688 + {
  689 + info.setSipId(getItemValue(pNode));
  690 + }
  691 + else if (CGBMessage::CompareNoCase( type, "SipRealm" )) {
  692 + info.setSipRealm(getItemValue(pNode));
  693 + }
  694 + else if (CGBMessage::CompareNoCase( type, "Password" )) {
  695 + info.setSipPass(getItemValue(pNode));
  696 + }
  697 + else if (CGBMessage::CompareNoCase( type, "Timeout" )) {
  698 + string str = getItemValue(pNode);
  699 + int i = atoi(str.c_str());
  700 + info.setTimeout(i);
  701 + } else if (CGBMessage::CompareNoCase( type, "Expiry" )) {
  702 + string str = getItemValue(pNode);
  703 + int i = atoi(str.c_str());
  704 + info.setExpiry(i);
  705 + } else if (CGBMessage::CompareNoCase( type, "MinRtpPort" )) {
  706 + string str = getItemValue(pNode);
  707 + int i = atoi(str.c_str());
  708 + info.setMinRtpPort(i);
  709 + } else if (CGBMessage::CompareNoCase( type, "MaxRtpPort" )) {
  710 + string str = getItemValue(pNode);
  711 + int i = atoi(str.c_str());
  712 + info.setMaxRtpPort(i);
  713 + } else if (CGBMessage::CompareNoCase( type, "WsPort" )) {
  714 + string str = getItemValue(pNode);
  715 + int i = atoi(str.c_str());
  716 + info.setWsPort(i);
  717 + }
  718 + }
  719 +
  720 + return info;
  721 +}
0 722 \ No newline at end of file
... ...
sip/Message/CatalogParser.h 0 → 100644
  1 +++ a/sip/Message/CatalogParser.h
  1 +#ifndef MSG_NOTIFY_MSG_HPP_
  2 +#define MSG_NOTIFY_MSG_HPP_
  3 +
  4 +#include "GBMessage.h"
  5 +#include <vector>
  6 +
  7 +#include "../sip_header.h"
  8 +
  9 +class CCatalogParser : public CGBMessage
  10 +{
  11 +public:
  12 + CCatalogParser()
  13 + {
  14 + }
  15 +
  16 +public:
  17 + bool Encode( std::string &message );
  18 +
  19 + std::vector<DeviceInfo> DecodeCatlog(const char* body);
  20 +
  21 + ServerInfo DecodeServerConfig(const char* body);
  22 +
  23 + static std::string GetStrName(EEventType eType)
  24 + {
  25 + switch (eType)
  26 + {
  27 + case EVENT_ON:
  28 + return "ON";
  29 + case EVENT_OFF:
  30 + return "OFF";
  31 + case EVENT_VLOST:
  32 + return "VLOST";
  33 + case EVENT_DEFECT:
  34 + return "DEFECT";
  35 + case EVENT_ADD:
  36 + return "ADD";
  37 + case EVENT_DEL:
  38 + return "DEL";
  39 + case EVENT_UPDATE:
  40 + return "UPDATE";
  41 + default: //EVENT_UNKNOW
  42 + return "UNKNOW";
  43 + }
  44 +
  45 + return "UNKNOW";
  46 + }
  47 +
  48 + EEventType GetEnumName(const std::string &sType)
  49 + {
  50 + if (sType == "ON")
  51 + return EVENT_ON;
  52 + if (sType == "OFF")
  53 + return EVENT_OFF;
  54 + if (sType == "VLOSE")
  55 + return EVENT_VLOST;
  56 + if (sType == "DEFECT")
  57 + return EVENT_DEFECT;
  58 + if (sType == "ADD")
  59 + return EVENT_ADD;
  60 + if (sType == "DEL")
  61 + return EVENT_DEL;
  62 + if (sType == "UPDATE")
  63 + return EVENT_UPDATE;
  64 +
  65 + return EVENT_UNKNOW;
  66 +
  67 + }
  68 +
  69 +
  70 +public:
  71 +
  72 + inline const std::string& GetSN() const
  73 + {
  74 + return m_sn;
  75 + }
  76 +
  77 + inline const std::string& GetDeviceID() const
  78 + {
  79 + return m_deviceid;
  80 + }
  81 +
  82 + inline void SetDeviceID( const std::string &deviceid )
  83 + {
  84 + m_deviceid = deviceid;
  85 + }
  86 +
  87 + inline void SetSN( const std::string &sn )
  88 + {
  89 + m_sn = sn;
  90 + }
  91 +
  92 + inline const std::string& GetStatus() const
  93 + {
  94 + return m_status;
  95 + }
  96 +
  97 + inline void SetStatus( const std::string &status )
  98 + {
  99 + m_status = status;
  100 + }
  101 +
  102 + inline int GetSum() const
  103 + {
  104 + return m_sum;
  105 + }
  106 +
  107 + inline void SetSum( int sum )
  108 + {
  109 + m_sum = sum;
  110 + }
  111 +
  112 + inline const std::vector< DeviceInfo >& GetDevices() const
  113 + {
  114 + return m_devices;
  115 + }
  116 +
  117 + inline void AddEvent(const std::string &id, EEventType eventType)
  118 + {
  119 + DeviceInfo catalog;
  120 + catalog.id = id;
  121 + catalog.event = eventType;
  122 + m_devices.push_back( catalog );
  123 + }
  124 +
  125 + inline void AddOnEvent( const std::string &id )
  126 + {
  127 + DeviceInfo catalog;
  128 + catalog.id = id;
  129 + catalog.event = EVENT_ON;
  130 + m_devices.push_back( catalog );
  131 + }
  132 +
  133 + inline void AddOffEvent( const std::string &id )
  134 + {
  135 + DeviceInfo catalog;
  136 + catalog.id = id;
  137 + catalog.event = EVENT_OFF;
  138 + m_devices.push_back( catalog );
  139 + }
  140 +
  141 + inline void AddVLostEvent( const std::string &id )
  142 + {
  143 + DeviceInfo catalog;
  144 + catalog.id = id;
  145 + catalog.event = EVENT_VLOST;
  146 + m_devices.push_back( catalog );
  147 + }
  148 +
  149 + inline void AddDefectEvent( const std::string &id )
  150 + {
  151 + DeviceInfo catalog;
  152 + catalog.id = id;
  153 + catalog.event = EVENT_DEFECT;
  154 + m_devices.push_back( catalog );
  155 + }
  156 +
  157 + inline void AddDelEvent( const std::string &id )
  158 + {
  159 + DeviceInfo catalog;
  160 + catalog.id = id;
  161 + catalog.event = EVENT_DEL;
  162 + m_devices.push_back( catalog );
  163 + }
  164 +
  165 + inline void AddAddEvent( const DeviceInfo &catalog )
  166 + {
  167 + if( catalog.event == EVENT_ADD )
  168 + {
  169 + m_devices.push_back( catalog );
  170 + }
  171 + }
  172 +
  173 + inline void AddUpdateEvent( const DeviceInfo &catalog )
  174 + {
  175 + if( catalog.event == EVENT_UPDATE )
  176 + {
  177 + m_devices.push_back( catalog );
  178 + }
  179 + }
  180 +
  181 +private:
  182 + std::vector< DeviceInfo > Decode(const std::vector< tinyxml2::XMLNode* >& nodes);
  183 + string getItemValue(tinyxml2::XMLNode *pItem);
  184 +
  185 +private:
  186 + std::string m_sn;
  187 + std::string m_deviceid;
  188 + std::string m_status;
  189 + int m_sum;
  190 + std::vector< DeviceInfo > m_devices;
  191 +};
  192 +
  193 +#endif // MSG_NOTIFY_MSG_HPP_
0 194 \ No newline at end of file
... ...
sip/Message/GBMessage.cpp 0 → 100644
  1 +++ a/sip/Message/GBMessage.cpp
  1 +#include "GBMessage.h"
  2 +//#include "ILocker.h"
  3 +#include <atomic>
  4 +static std::atomic<unsigned int> g_msgsn(0);
  5 +//static CLocker g_msg_lock;
  6 +
  7 +bool CGBMessage::CompareNoCase( const char *type, const char *label )
  8 +{
  9 + // 参数校验
  10 + if( type == NULL || label == NULL )
  11 + {
  12 + return false;
  13 + }
  14 +
  15 + // 比较是否相等
  16 + char ch1 = type[0];
  17 + char ch2 = label[0];
  18 + size_t i = 0;
  19 + while( ch1 != 0 && ch2 != 0 )
  20 + {
  21 + if( ch1 >= 'a' && ch1 <= 'z' )
  22 + {
  23 + ch1 -= 'a';
  24 + ch1 += 'A';
  25 + }
  26 +
  27 + if( ch2 >= 'a' && ch2 <= 'z' )
  28 + {
  29 + ch2 -= 'a';
  30 + ch2 += 'A';
  31 + }
  32 +
  33 + if( ch1 != ch2 )
  34 + {
  35 + return false;
  36 + }
  37 + else
  38 + {
  39 + ++i;
  40 + ch1 = type[i];
  41 + ch2 = label[i];
  42 + }
  43 + }
  44 +
  45 + return( ch1 == 0 && ch2 == 0 );
  46 +}
  47 +
  48 +
  49 +bool CGBMessage::CompareNoCase( const std::string &str, const char *label )
  50 +{
  51 + // 参数校验
  52 + const char* type = str.c_str();
  53 + if( type == NULL || label == NULL )
  54 + {
  55 + return false;
  56 + }
  57 +
  58 + // 比较是否相等
  59 + char ch1 = type[0];
  60 + char ch2 = label[0];
  61 + size_t i = 0;
  62 + while( ch1 != 0 && ch2 != 0 )
  63 + {
  64 + if( ch1 >= 'a' && ch1 <= 'z' )
  65 + {
  66 + ch1 -= 'a';
  67 + ch1 += 'A';
  68 + }
  69 +
  70 + if( ch2 >= 'a' && ch2 <= 'z' )
  71 + {
  72 + ch2 -= 'a';
  73 + ch2 += 'A';
  74 + }
  75 +
  76 + if( ch1 != ch2 )
  77 + {
  78 + return false;
  79 + }
  80 + else
  81 + {
  82 + ++i;
  83 + ch1 = type[i];
  84 + ch2 = label[i];
  85 + }
  86 + }
  87 +
  88 + return( ch1 == 0 && ch2 == 0 );
  89 +}
  90 +
  91 +unsigned int CGBMessage::GetNextSN()
  92 +{
  93 + //CNPAutoLock locker( g_msg_lock );
  94 + g_msgsn++;
  95 + return g_msgsn;
  96 +}
  97 +
  98 +bool CGBMessage::DecodeSubject()
  99 +{
  100 + std::vector< std::string > vec;
  101 + size_t b = 0;
  102 + size_t e = 0;
  103 + while( b != std::string::npos )
  104 + {
  105 + e = m_subject.find_first_of( ',', b );
  106 + vec.push_back( m_subject.substr( b, e ) );
  107 + if( e != std::string::npos )
  108 + {
  109 + b = e + 1;
  110 + }
  111 + else
  112 + {
  113 + break;
  114 + }
  115 + }
  116 +
  117 + if( vec.size() != 2 )
  118 + {
  119 + return false;
  120 + }
  121 +
  122 + //
  123 + std::string send = vec[0];
  124 + std::string recv = vec[1];
  125 +
  126 + // 发送者ID和序列号
  127 + vec.clear();
  128 + b = 0;
  129 + while( b != std::string::npos )
  130 + {
  131 + e = send.find_first_of( ':', b );
  132 + vec.push_back( send.substr( b, e ) );
  133 + if( e != std::string::npos )
  134 + {
  135 + b = e + 1;
  136 + }
  137 + else
  138 + {
  139 + break;
  140 + }
  141 + }
  142 +
  143 + if( vec.size() != 2 )
  144 + {
  145 + return false;
  146 + }
  147 + else
  148 + {
  149 + m_sender.id = vec[0];
  150 + m_sender.seq = vec[1];
  151 + }
  152 +
  153 + // 接收者ID和序列号
  154 + vec.clear();
  155 + b = 0;
  156 + while( b != std::string::npos )
  157 + {
  158 + e = recv.find_first_of( ':', b );
  159 + vec.push_back( recv.substr( b, e ) );
  160 + if( e != std::string::npos )
  161 + {
  162 + b = e + 1;
  163 + }
  164 + else
  165 + {
  166 + break;
  167 + }
  168 + }
  169 +
  170 + if( vec.size() != 2 )
  171 + {
  172 + return false;
  173 + }
  174 + else
  175 + {
  176 + m_recver.id = vec[0];
  177 + m_recver.seq = vec[1];
  178 + }
  179 +
  180 + return true;
  181 +}
  182 +
... ...
sip/Message/GBMessage.h 0 → 100644
  1 +++ a/sip/Message/GBMessage.h
  1 +#ifndef GBMESSAGE_HPP_
  2 +#define GBMESSAGE_HPP_
  3 +
  4 +#include <sstream>
  5 +#include <iostream>
  6 +#include <string>
  7 +#include <vector>
  8 +#include "../tinyxml2/tinyxml2.h"
  9 +
  10 +
  11 +#if defined(CATCH_RESIP_EXCEPTION)
  12 +#undef __DUM_TRY
  13 +#undef __DUM_CATCH
  14 +#define __DUM_TRY try {
  15 +#define __DUM_CATCH \
  16 +} catch(resip::DumException* be) { \
  17 + resip::Data ex; \
  18 + ex = "RESIP �����쳣��name = "; \
  19 + ex += be->name(); \
  20 + ex += "��what = "; \
  21 + ex += be->what(); \
  22 + throw new std::exception(ex.c_str()); \
  23 +}
  24 +#else
  25 +#undef __DUM_TRY
  26 +#undef __DUM_CATCH
  27 +#define __DUM_TRY
  28 +#define __DUM_CATCH
  29 +#endif
  30 +
  31 +
  32 +class CGBMessage
  33 +{
  34 +public:
  35 + enum EGBResultType
  36 + {
  37 + RESULT_OK = 0,
  38 + RESULT_ERROR,
  39 + };
  40 +
  41 + struct SDate
  42 + {
  43 + int year;
  44 + int month;
  45 + int day;
  46 + int hour;
  47 + int minute;
  48 + int second;
  49 + int millisecond;
  50 + };
  51 +
  52 + struct SSubject
  53 + {
  54 + std::string id;
  55 + std::string seq;
  56 + };
  57 +
  58 + struct SUserIdentity
  59 + {
  60 + std::string user;
  61 + std::string deviceid;
  62 + std::string organization;
  63 + std::string policetype;
  64 + std::string duty;
  65 + };
  66 +
  67 +public:
  68 + CGBMessage() : m_adjusttime( false ), m_port( 0 ), m_cseq( 0 ), m_forwards( 0 ), m_expires( 0 ), m_statusCode( 0 )
  69 + {
  70 + }
  71 +
  72 + virtual ~CGBMessage()
  73 + {
  74 + }
  75 +
  76 +private:
  77 + CGBMessage( const CGBMessage &rhs );
  78 + CGBMessage& operator=( const CGBMessage &rhs );
  79 +
  80 +public:
  81 + inline bool Ok() const
  82 + {
  83 + return( m_statusCode == 200 );
  84 + }
  85 +
  86 + inline bool AdjustTime() const
  87 + {
  88 + return m_adjusttime;
  89 + }
  90 +
  91 + inline const std::string& GetSubjectSendID() const
  92 + {
  93 + return m_sender.id;
  94 + }
  95 +
  96 + inline const std::string& GetSubjectSendSeq() const
  97 + {
  98 + return m_sender.seq;
  99 + }
  100 +
  101 + inline const std::string& GetSubjectRecvID() const
  102 + {
  103 + return m_recver.id;
  104 + }
  105 +
  106 + inline const std::string& GetSubjectRecvSeq() const
  107 + {
  108 + return m_recver.seq;
  109 + }
  110 +
  111 + inline const std::string& GetUser() const
  112 + {
  113 + return m_request;
  114 + }
  115 +
  116 + inline const std::string& GetSubject() const
  117 + {
  118 + return m_subject;
  119 + }
  120 +
  121 + inline const std::string& GetFrom() const
  122 + {
  123 + return m_from;
  124 + }
  125 +
  126 + inline const std::string& GetFromTag() const
  127 + {
  128 + return m_tagfrom;
  129 + }
  130 +
  131 + inline const std::string& GetTo() const
  132 + {
  133 + return m_to;
  134 + }
  135 +
  136 + inline const std::string& GetToTag() const
  137 + {
  138 + return m_tagto;
  139 + }
  140 +
  141 + inline unsigned long GetCSeq() const
  142 + {
  143 + return m_cseq;
  144 + }
  145 +
  146 + inline int GetStatusCode() const
  147 + {
  148 + return m_statusCode;
  149 + }
  150 +
  151 + inline int GetMaxFrowards() const
  152 + {
  153 + return m_forwards;
  154 + }
  155 +
  156 + inline int GetExpires() const
  157 + {
  158 + return m_expires;
  159 + }
  160 +
  161 + inline const std::string& GetIP() const
  162 + {
  163 + return m_ip;
  164 + }
  165 +
  166 + inline int GetPort() const
  167 + {
  168 + return m_port;
  169 + }
  170 +
  171 + inline const std::string& GetCallID() const
  172 + {
  173 + return m_callid;
  174 + }
  175 +
  176 + inline const SDate& GetDate() const
  177 + {
  178 + return m_date;
  179 + }
  180 +
  181 + inline const SUserIdentity& GetUserIdentity() const
  182 + {
  183 + return m_useridentity;
  184 + }
  185 +
  186 + inline const std::vector< std::string >& GetContacts() const
  187 + {
  188 + return m_contacts;
  189 + }
  190 +
  191 + inline const std::vector< std::string >& GetVias() const
  192 + {
  193 + return m_vias;
  194 + }
  195 +
  196 + inline void SetSubjectSendID( const std::string &id )
  197 + {
  198 + m_sender.id = id;
  199 + }
  200 +
  201 + inline void SetSubjectSendSeq( const std::string &seq )
  202 + {
  203 + m_sender.seq = seq;
  204 + }
  205 +
  206 + inline void SetSubjectRecvID( const std::string &id )
  207 + {
  208 + m_recver.id = id;
  209 + }
  210 +
  211 + inline void SetSubjectRecvSeq( const std::string &seq )
  212 + {
  213 + m_recver.seq = seq;
  214 + }
  215 +
  216 + inline void SetUser( const char *user )
  217 + {
  218 + if( user != NULL )
  219 + {
  220 + m_request = user;
  221 + }
  222 + }
  223 +
  224 + inline void SetUser( const std::string &user )
  225 + {
  226 + m_request = user;
  227 + }
  228 +
  229 + inline void SetSubject( const char *uri )
  230 + {
  231 + if( uri != NULL )
  232 + {
  233 + m_subject = uri;
  234 + DecodeSubject();
  235 + }
  236 + }
  237 +
  238 + inline void SetSubject( const std::string &uri )
  239 + {
  240 + m_subject = uri;
  241 + DecodeSubject();
  242 + }
  243 +
  244 + inline void SetFrom( const char *code )
  245 + {
  246 + if( code != NULL )
  247 + {
  248 + m_from = code;
  249 + }
  250 + }
  251 +
  252 + inline void SetFrom( const std::string &code )
  253 + {
  254 + m_from = code;
  255 + }
  256 +
  257 + inline void SetFromTag( const char *tag )
  258 + {
  259 + if( tag != NULL )
  260 + {
  261 + m_tagfrom = tag;
  262 + }
  263 + }
  264 +
  265 + inline void SetFromTag( const std::string &code )
  266 + {
  267 + m_tagfrom = code;
  268 + }
  269 +
  270 + inline void SetTo( const char *code )
  271 + {
  272 + if( code != NULL )
  273 + {
  274 + m_to = code;
  275 + }
  276 + }
  277 +
  278 + inline void SetTo( const std::string &code )
  279 + {
  280 + m_to = code;
  281 + }
  282 +
  283 + inline void SetToTag( const char *tag )
  284 + {
  285 + if( tag != NULL )
  286 + {
  287 + m_tagto = tag;
  288 + }
  289 + }
  290 +
  291 + inline void SetToTag( const std::string &tag )
  292 + {
  293 + m_tagto = tag;
  294 + }
  295 +
  296 + inline void SetCSeq( unsigned long cseq )
  297 + {
  298 + m_cseq = cseq;
  299 + }
  300 +
  301 + inline void SetExpires( int expires )
  302 + {
  303 + m_expires = expires;
  304 + }
  305 +
  306 + inline void SetStatusCode( int code )
  307 + {
  308 + m_statusCode = code;
  309 + }
  310 +
  311 + inline void SetMaxFrowards( int forwards )
  312 + {
  313 + m_forwards = forwards;
  314 + }
  315 +
  316 + inline void SetCallID( const char *callid )
  317 + {
  318 + if( callid != NULL )
  319 + {
  320 + m_callid = callid;
  321 + }
  322 + }
  323 +
  324 + inline void SetCallID( const std::string &callid )
  325 + {
  326 + m_callid = callid;
  327 + }
  328 +
  329 + inline void SetIP( const std::string &ip )
  330 + {
  331 + m_ip = ip;
  332 + }
  333 +
  334 + inline void SetIP( const char *ip )
  335 + {
  336 + if( ip != NULL )
  337 + {
  338 + m_ip = ip;
  339 + }
  340 + }
  341 +
  342 + inline void SetPort( int port )
  343 + {
  344 + m_port = port;
  345 + }
  346 +
  347 + inline void SetDate( int year, int month, int day, int hour, int minute, int second, int millisecond )
  348 + {
  349 + m_adjusttime = true;
  350 + m_date.year = year;
  351 + m_date.month = month;
  352 + m_date.day = day;
  353 + m_date.hour = hour;
  354 + m_date.minute = minute;
  355 + m_date.second = second;
  356 + m_date.millisecond = millisecond;
  357 + }
  358 +
  359 + inline void AddContacts( const char *contacts )
  360 + {
  361 + if( contacts != NULL )
  362 + {
  363 + m_contacts.push_back( contacts );
  364 + }
  365 + }
  366 +
  367 + inline void AddContacts( const std::string &contacts )
  368 + {
  369 + m_contacts.push_back( contacts );
  370 + }
  371 +
  372 + inline void AddVias( const char *host, long port )
  373 + {
  374 + if( host != NULL )
  375 + {
  376 + char buf[16] = { 0 };
  377 + sprintf( buf, "%ld", port );
  378 + std::string via = host;
  379 + via += ":";
  380 + via += buf;
  381 + m_vias.push_back( via );
  382 + }
  383 + }
  384 +
  385 + inline void AddVias( const std::string &host, long port )
  386 + {
  387 + char buf[16] = { 0 };
  388 + sprintf( buf, "%ld", port );
  389 + std::string via = host;
  390 + via += ":";
  391 + via += buf;
  392 + m_vias.push_back( via );
  393 + }
  394 +
  395 + inline void SetUserIdentity( const std::string &user, const std::string& deviceid, const std::string& organization, const std::string& policetype, const std::string& duty )
  396 + {
  397 + m_useridentity.user = user;
  398 + m_useridentity.deviceid = deviceid;
  399 + m_useridentity.organization = organization;
  400 + m_useridentity.policetype = policetype;
  401 + m_useridentity.duty = duty;
  402 + }
  403 +
  404 +public:
  405 +
  406 + virtual bool Response( int status )
  407 + {
  408 + return false;
  409 + }
  410 +
  411 +public:
  412 + static bool CompareNoCase( const char *type, const char *label );
  413 + static bool CompareNoCase( const std::string &type, const char *label );
  414 + static unsigned int GetNextSN();
  415 + bool DecodeSubject();
  416 +
  417 +protected:
  418 + SSubject m_sender; // subject
  419 + SSubject m_recver; // subject
  420 + std::string m_request;
  421 + std::string m_subject;
  422 + std::string m_callid;
  423 + std::string m_from;
  424 + std::string m_tagfrom;
  425 + std::string m_to;
  426 + std::string m_tagto;
  427 + SDate m_date;
  428 + std::vector< std::string > m_contacts;
  429 + std::vector< std::string > m_vias;
  430 + std::string m_ip;
  431 + bool m_adjusttime; // message has date header
  432 + int m_port;
  433 + unsigned long m_cseq;
  434 + int m_forwards;
  435 + int m_expires;
  436 + int m_statusCode;
  437 + SUserIdentity m_useridentity;
  438 +};
  439 +
  440 +//////////////////////////////////////////////////////////////////////////
  441 +#endif // GBMESSAGE_HPP_
... ...
sip/SipServer.cpp 0 → 100644
  1 +++ a/sip/SipServer.cpp
  1 +//
  2 +// Created bxc on 2022/11/25.
  3 +//
  4 +
  5 +#include "SipServer.h"
  6 +
  7 +#ifndef WIN32
  8 +// Linux系统
  9 +#include <arpa/inet.h>
  10 +#else
  11 +#include <WinSock2.h>
  12 +#pragma comment(lib, "ws2_32.lib")
  13 +#endif // !WIN32
  14 +
  15 +#include <cstring>
  16 +#include "./Utils/HTTPDigest.h"
  17 +#include "./Utils/Utools.hpp"
  18 +#include "./Utils/logger.hpp"
  19 +#include "./Utils/StringTools.hpp"
  20 +
  21 +
  22 +#include <sstream>
  23 +#include <algorithm>
  24 +
  25 +
  26 +using namespace std;
  27 +
  28 +static void event_loop_thread(void* arg) {
  29 + SipServer* _this = (SipServer*)arg;
  30 + if (_this != nullptr) {
  31 + _this->event_loop();
  32 + }
  33 + else {
  34 + LOG_ERROR("event_loop线程启动失败 !");
  35 + }
  36 +}
  37 +
  38 +static void timing_getcatlog_thread(void* arg) {
  39 + SipServer* _this = (SipServer*)arg;
  40 + if (_this != nullptr) {
  41 + _this->timing_getcatlog();
  42 + }
  43 + else {
  44 + LOG_ERROR("timing_getcatlog 线程启动失败 !");
  45 + }
  46 +}
  47 +
  48 +static void dt_printSipMsg(osip_message_t* msg) {
  49 + osip_message_t* clone_event = NULL;
  50 + size_t length = 0;
  51 + char* message = NULL;
  52 + osip_message_clone(msg, &clone_event);
  53 + osip_message_to_str(clone_event, &message, &length);
  54 + LOG_INFO("{}", message);
  55 +}
  56 +
  57 +SipServer::SipServer():
  58 + mQuit(false),
  59 + mSipCtx(nullptr){
  60 +#ifdef WIN32
  61 + WSADATA wsaData;
  62 + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
  63 + {
  64 + LOG_ERROR("WSAStartup Error");
  65 + return;
  66 + }
  67 +#endif // WIN32
  68 +
  69 +}
  70 +SipServer::~SipServer() {
  71 + LOG_INFO("~SipServer");
  72 +
  73 + if (m_event_loop_thread)
  74 + {
  75 + mQuit = true;
  76 + m_event_loop_thread->join();
  77 +
  78 + delete m_event_loop_thread;
  79 + m_event_loop_thread = nullptr;
  80 + }
  81 +
  82 + this->clearClientMap();
  83 +#ifdef WIN32
  84 + WSACleanup();
  85 +#endif // WIN32
  86 +}
  87 +
  88 +bool SipServer::Init(ServerInfo* pInfo) {
  89 +
  90 + if (pInfo == nullptr) {
  91 + return false;
  92 + }
  93 +
  94 + mInfo = *pInfo;
  95 +
  96 + m_event_loop_thread = new std::thread(event_loop_thread, this);
  97 +
  98 + return true;
  99 +}
  100 +
  101 +int SipServer::sip_event_handle(eXosip_event_t *evtp) {
  102 +
  103 + switch(evtp->type) {
  104 + case EXOSIP_CALL_MESSAGE_NEW://14
  105 + // LOG_INFO("EXOSIP_CALL_MESSAGE_NEW type={}", evtp->type);
  106 + this->dump_request(evtp);
  107 + this->dump_response(evtp);
  108 + break;
  109 +
  110 + case EXOSIP_CALL_CLOSED://21
  111 + LOG_INFO("EXOSIP_CALL_CLOSED type={}",evtp->type);
  112 + this->dump_request(evtp);
  113 + this->dump_response(evtp);
  114 + break;
  115 +
  116 + case EXOSIP_CALL_RELEASED://22
  117 + LOG_INFO("EXOSIP_CALL_RELEASED type={}", evtp->type);
  118 + this->dump_request(evtp);
  119 + this->dump_response(evtp);
  120 +
  121 + // this->clearClientMap();
  122 + break;
  123 + case EXOSIP_MESSAGE_NEW://23
  124 + // LOG_INFO("EXOSIP_MESSAGE_NEW type={}",evtp->type);
  125 +
  126 + if (MSG_IS_REGISTER(evtp->request)) {
  127 + this->response_register(evtp);
  128 + }
  129 + else if (MSG_IS_MESSAGE(evtp->request)) {
  130 + this->response_message(evtp);
  131 + }
  132 + else if(MSG_IS_BYE(evtp->request)){
  133 + LOG_ERROR("BYE");
  134 + }
  135 + else{
  136 + LOG_ERROR("unknown2");
  137 + }
  138 + break;
  139 + case EXOSIP_MESSAGE_ANSWERED:
  140 + this->dump_request(evtp);
  141 + break;
  142 + case EXOSIP_MESSAGE_REQUESTFAILURE:
  143 + LOG_INFO("EXOSIP_MESSAGE_REQUESTFAILURE type={}: Receive feedback on sending failure after actively sending a message", evtp->type);
  144 + this->dump_request(evtp);
  145 + this->dump_response(evtp);
  146 + break;
  147 + case EXOSIP_CALL_INVITE:
  148 + LOG_INFO("EXOSIP_CALL_INVITE type={}: The server receives the Invite request actively sent by the client", evtp->type);
  149 + break;
  150 + case EXOSIP_CALL_PROCEEDING://5
  151 + LOG_INFO("EXOSIP_CALL_PROCEEDING type={}: When the server receives the Invite (SDP) confirmation reply from the client", evtp->type);
  152 + this->dump_request(evtp);
  153 + this->dump_response(evtp);
  154 + break;
  155 + case EXOSIP_CALL_ANSWERED:// 7
  156 + LOG_INFO("EXOSIP_CALL_ANSWERED type={}: The server receives an invite (SDP) confirmation reply from the client", evtp->type);
  157 + this->dump_request(evtp);
  158 + this->dump_response(evtp);
  159 +
  160 + this->response_invite_ack(evtp);
  161 + cache_invite_callinfo(evtp);
  162 + break;
  163 + case EXOSIP_CALL_SERVERFAILURE:
  164 + LOG_INFO("EXOSIP_CALL_SERVERFAILURE type={}", evtp->type);
  165 + break;
  166 + case EXOSIP_IN_SUBSCRIPTION_NEW:
  167 + LOG_INFO("EXOSIP_IN_SUBSCRIPTION_NEW type={}", evtp->type);
  168 + break;
  169 + default:
  170 + LOG_INFO("type={} unknown", evtp->type);
  171 + break;
  172 + }
  173 +
  174 + return 0;
  175 +}
  176 +
  177 +string contract_ip_from_message(string str_msg) {
  178 +
  179 + str_msg = StringTools::to_lower(str_msg);
  180 +
  181 + string c = str_msg.substr(str_msg.find("c=")+2);
  182 + string c1 = c.substr(0, c.find_first_of("=") - 1);
  183 + string c2 = c1.substr(c1.find_first_of("4")+1);
  184 +
  185 + return StringTools::trim(c2);
  186 +}
  187 +
  188 +string contract_port_from_message(string str_msg) {
  189 +
  190 + str_msg = StringTools::to_lower(str_msg);
  191 +
  192 + string c = str_msg.substr(str_msg.find("video")+6);
  193 + string c1 = c.substr(0, c.find_first_of(" "));
  194 +
  195 + return StringTools::trim(c1);
  196 +}
  197 +
  198 +void SipServer::cache_invite_callinfo(eXosip_event_t *evtp) {
  199 + CallInfo call_info;
  200 + call_info.cid = evtp->cid;
  201 + call_info.did = evtp->did;
  202 +
  203 + try
  204 + {
  205 + char *s;
  206 + size_t len;
  207 + osip_message_to_str(evtp->request, &s, &len);
  208 +
  209 + string ip = contract_ip_from_message(s);
  210 + string port = contract_port_from_message(s);
  211 +
  212 + std::cout << "ip:" << ip << " port:" << port << std::endl;
  213 +
  214 + string channel_id = evtp->response->to->url->username;
  215 +
  216 + auto key = std::make_tuple(channel_id, ip, atoi(port.c_str()));
  217 + m_invite_callinfo_map[key] = call_info;
  218 + }
  219 + catch(const std::exception& e)
  220 + {
  221 + std::cerr << e.what() << '\n';
  222 + }
  223 +}
  224 +
  225 +int SipServer::init_sip_server() {
  226 + mSipCtx = eXosip_malloc();
  227 + if (!mSipCtx) {
  228 + LOG_ERROR("eXosip_malloc error");
  229 + return -1;
  230 + }
  231 + if (eXosip_init(mSipCtx)) {
  232 + LOG_ERROR("eXosip_init error");
  233 + return -1;
  234 + }
  235 + if (eXosip_listen_addr(mSipCtx, IPPROTO_UDP, nullptr, mInfo.getPort(), AF_INET, 0)) {
  236 + LOG_ERROR("eXosip_listen_addr error");
  237 + return -1;
  238 + }
  239 + eXosip_set_user_agent(mSipCtx, mInfo.getUa().c_str());
  240 + if (eXosip_add_authentication_info(mSipCtx, mInfo.getSipId().c_str(), mInfo.getSipId().c_str(), mInfo.getSipPass().c_str(), NULL, mInfo.getSipRealm().c_str())) {
  241 + LOG_ERROR("eXosip_add_authentication_info error");
  242 + return -1;
  243 + }
  244 +
  245 + return 0;
  246 +}
  247 +
  248 +void SipServer::event_loop() {
  249 +
  250 + if(this->init_sip_server() !=0 ){
  251 + return;
  252 + }
  253 +
  254 + LOG_INFO("sip server init succeed: {}:{}", mInfo.getIp(), mInfo.getPort());
  255 +
  256 + // thread* timing_getcatlog_threadptr = new std::thread(timing_getcatlog_thread, this);
  257 +
  258 + while(!mQuit) {
  259 + eXosip_event_t *evtp = eXosip_event_wait(mSipCtx, 0, 20);
  260 + if (!evtp){
  261 + eXosip_automatic_action(mSipCtx);
  262 + osip_usleep(100000);
  263 + continue;
  264 + }
  265 + eXosip_automatic_action(mSipCtx);
  266 + this->sip_event_handle(evtp);
  267 + eXosip_event_free(evtp);
  268 + }
  269 +
  270 + mQuit = true;
  271 + // if (timing_getcatlog_threadptr) {
  272 + // timing_getcatlog_threadptr->join();
  273 + // delete timing_getcatlog_threadptr;
  274 + // timing_getcatlog_threadptr = nullptr;
  275 + // }
  276 +}
  277 +
  278 +void SipServer::Close() {
  279 + mQuit = true;
  280 +
  281 + if (m_event_loop_thread) {
  282 + m_event_loop_thread->join();
  283 + delete m_event_loop_thread;
  284 + m_event_loop_thread = nullptr;
  285 + }
  286 +
  287 +}
  288 +
  289 +void SipServer::timing_getcatlog() {
  290 + while(!mQuit) {
  291 + // 5分钟更新一次
  292 + std::this_thread::sleep_for(std::chrono::minutes(5));
  293 + cacheCatalog();
  294 + }
  295 +}
  296 +
  297 +void SipServer::response_message_answer(eXosip_event_t *evtp,int code){
  298 +
  299 + int returnCode = 0 ;
  300 + osip_message_t * pRegister = nullptr;
  301 + returnCode = eXosip_message_build_answer (mSipCtx,evtp->tid,code,&pRegister);
  302 + bool bRegister = false;
  303 + if(pRegister){
  304 + bRegister = true;
  305 + }
  306 + if (returnCode == 0 && bRegister)
  307 + {
  308 + eXosip_lock(mSipCtx);
  309 + eXosip_message_send_answer (mSipCtx,evtp->tid,code,pRegister);
  310 + eXosip_unlock(mSipCtx);
  311 + }
  312 + else{
  313 + LOG_ERROR("code={},returnCode={},bRegister={}",code,returnCode,bRegister);
  314 + }
  315 +
  316 +}
  317 +void SipServer::response_register(eXosip_event_t *evtp) {
  318 +
  319 + osip_authorization_t * auth = nullptr;
  320 + osip_message_get_authorization(evtp->request, 0, &auth);
  321 +
  322 + if(auth && auth->username){
  323 +
  324 + char *method = NULL, // REGISTER
  325 + *algorithm = NULL, // MD5
  326 + *username = NULL,// 340200000013200000024
  327 + *realm = NULL, // sip服务器传给客户端,客户端携带并提交上来的sip服务域
  328 + *nonce = NULL, //sip服务器传给客户端,客户端携带并提交上来的nonce
  329 + *nonce_count = NULL,
  330 + *uri = NULL; // sip:34020000002000000001@3402000000
  331 +
  332 + osip_contact_t *contact = nullptr;
  333 + osip_message_get_contact (evtp->request, 0, &contact);
  334 +
  335 + method = evtp->request->sip_method;
  336 + char calc_response[HASHHEXLEN];
  337 + HASHHEX HA1, HA2 = "", Response;
  338 +
  339 +#define SIP_STRDUP(field) if (auth->field) (field) = osip_strdup_without_quote(auth->field)
  340 +
  341 + SIP_STRDUP(algorithm);
  342 + SIP_STRDUP(username);
  343 + SIP_STRDUP(realm);
  344 + SIP_STRDUP(nonce);
  345 + SIP_STRDUP(nonce_count);
  346 + SIP_STRDUP(uri);
  347 +
  348 + DigestCalcHA1(algorithm, username, realm, mInfo.getSipPass().c_str(), nonce, nonce_count, HA1);
  349 + DigestCalcResponse(HA1, nonce, nonce_count, auth->cnonce, auth->message_qop, 0, method, uri, HA2, Response);
  350 +
  351 + HASHHEX temp_HA1;
  352 + HASHHEX temp_response;
  353 + DigestCalcHA1("REGISTER", username, mInfo.getSipRealm().c_str(), mInfo.getSipPass().c_str(), mInfo.getNonce().c_str(), NULL, temp_HA1);
  354 + DigestCalcResponse(temp_HA1, mInfo.getNonce().c_str(), NULL, NULL, NULL, 0, method, uri, NULL, temp_response);
  355 + memcpy(calc_response, temp_response, HASHHEXLEN);
  356 +
  357 +
  358 +
  359 + if (!memcmp(calc_response, Response, HASHHEXLEN)) {
  360 + this->response_message_answer(evtp,200);
  361 +
  362 + Client* client = new Client(strdup(contact->url->host),
  363 + atoi(contact->url->port),
  364 + strdup(username));
  365 +
  366 + LOG_INFO("Camera registration succee,ip={},port={},device={}",client->getIp(),client->getPort(),client->getDevice());
  367 +
  368 + deleteClientByDevice(client->getDevice());
  369 +
  370 + std::lock_guard<std::mutex> l(m_client_map_mtx);
  371 + mClientMap.insert(std::make_pair(client->getDevice(),client));
  372 + // NVR注册成功,立即请求设备目录
  373 + RequestCatalog(client);
  374 +
  375 + } else {
  376 + this->response_message_answer(evtp,401);
  377 + LOG_INFO("Camera registration error, p={},port={},device={}", strdup(contact->url->host), atoi(contact->url->port), strdup(username));
  378 + }
  379 +
  380 + osip_free(algorithm);
  381 + osip_free(username);
  382 + osip_free(realm);
  383 + osip_free(nonce);
  384 + osip_free(nonce_count);
  385 + osip_free(uri);
  386 + } else {
  387 + response_register_401unauthorized(evtp);
  388 + }
  389 +
  390 +}
  391 +
  392 +void SipServer::response_register_401unauthorized(eXosip_event_t *evtp) {
  393 +
  394 + char *dest = nullptr;
  395 + osip_message_t * reg = nullptr;
  396 + osip_www_authenticate_t * header = nullptr;
  397 +
  398 + osip_www_authenticate_init(&header);
  399 + osip_www_authenticate_set_auth_type (header, osip_strdup("Digest"));
  400 + osip_www_authenticate_set_realm(header,osip_enquote(mInfo.getSipRealm().c_str()));
  401 + osip_www_authenticate_set_nonce(header,osip_enquote(mInfo.getNonce().c_str()));
  402 + osip_www_authenticate_to_str(header, &dest);
  403 + int ret = eXosip_message_build_answer (mSipCtx, evtp->tid, 401, &reg);
  404 + if ( ret == 0 && reg != nullptr ) {
  405 + osip_message_set_www_authenticate(reg, dest);
  406 + osip_message_set_content_type(reg, "Application/MANSCDP+xml");
  407 + eXosip_lock(mSipCtx);
  408 + eXosip_message_send_answer (mSipCtx, evtp->tid,401, reg);
  409 + eXosip_unlock(mSipCtx);
  410 + LOG_INFO("response_register_401unauthorized success");
  411 + }else {
  412 + LOG_INFO("response_register_401unauthorized error");
  413 + }
  414 +
  415 + osip_www_authenticate_free(header);
  416 + osip_free(dest);
  417 +
  418 +}
  419 +
  420 +void printDevice(std::vector<DeviceInfo> vec_device) {
  421 + LOG_INFO("-----start print----");
  422 + for (size_t i = 0; i < vec_device.size(); i++) {
  423 + LOG_INFO("{} {}", vec_device[i].id, vec_device[i].parentid);
  424 + }
  425 + LOG_INFO("-----end print----");
  426 +}
  427 +
  428 +void SipServer::response_message(eXosip_event_t *evtp) {
  429 +
  430 + osip_body_t* body = nullptr;
  431 + char CmdType[64] = {0};
  432 + char DeviceID[64] = {0};
  433 + osip_message_get_body(evtp->request, 0, &body);
  434 + if(body){
  435 + parse_xml(body->body, "<CmdType>", false, "</CmdType>", false, CmdType);
  436 + parse_xml(body->body, "<DeviceID>", false, "</DeviceID>", false, DeviceID);
  437 + }
  438 +
  439 + if(!strcmp(CmdType, "Catalog")) {
  440 + this->response_message_answer(evtp,200);
  441 + // 需要根据对方的Catelog请求,做一些相应的应答请求
  442 + CCatalogParser catPaser;
  443 + std::vector<DeviceInfo> vec_device = catPaser.DecodeCatlog(body->body);
  444 + printDevice(vec_device);
  445 +
  446 + std::lock_guard<std::mutex> l(m_device_map_mtx);
  447 + for (size_t i = 0; i < vec_device.size(); i++) {
  448 + DeviceInfo info = vec_device[i];
  449 + m_device_map[info.id] = info;
  450 + }
  451 + }
  452 + else if(!strcmp(CmdType, "Keepalive")){
  453 + this->response_message_answer(evtp,200);
  454 + // LOG_INFO("CmdType={},DeviceID={}", CmdType, DeviceID);
  455 + std::lock_guard<std::mutex> l_c(m_client_map_mtx);
  456 + Client* client = mClientMap[DeviceID];
  457 + if (client != nullptr) {
  458 + client->updateHeartBeat(Utools::get_cur_time_ms());
  459 + }
  460 + }else{
  461 + this->response_message_answer(evtp,200);
  462 + }
  463 +}
  464 +
  465 +bool SipServer::check_device_status(string id) {
  466 + std::lock_guard<std::mutex> l(m_device_map_mtx);
  467 + auto it_info = m_device_map.find(id);
  468 + if (it_info == m_device_map.end()) {
  469 + return false;
  470 + }
  471 +
  472 + string status = StringTools::trim(it_info->second.status);
  473 + transform(status.begin(), status.end(), status.begin(),::tolower);
  474 + if (status == "on"){
  475 + return true;
  476 + }
  477 +
  478 + return false;
  479 +}
  480 +
  481 +Client* SipServer::get_parent_by_id(string id) {
  482 + std::lock_guard<std::mutex> l(m_device_map_mtx);
  483 + auto it_info = m_device_map.find(id);
  484 + if (it_info == m_device_map.end()) {
  485 + return nullptr;
  486 + }
  487 + string parent_id = it_info->second.parentid;
  488 +
  489 + std::lock_guard<std::mutex> l_c(m_client_map_mtx);
  490 + auto it_client = mClientMap.find(parent_id);
  491 + if (it_client == mClientMap.end()) {
  492 + return nullptr;
  493 + }
  494 +
  495 + return mClientMap[parent_id];
  496 +}
  497 +
  498 +void SipServer::response_invite_ack(eXosip_event_t *evtp){
  499 +
  500 + osip_message_t* msg = nullptr;
  501 + int ret = eXosip_call_build_ack(mSipCtx, evtp->did, &msg);
  502 + if (!ret && msg) {
  503 + eXosip_call_send_ack(mSipCtx, evtp->did, msg);
  504 + } else {
  505 + LOG_ERROR("eXosip_call_send_ack error={}", ret);
  506 + }
  507 +
  508 +}
  509 +int SipServer::request_bye(eXosip_event_t* evtp) {
  510 +
  511 + eXosip_lock(mSipCtx);
  512 + int ret = eXosip_call_terminate(mSipCtx, evtp->cid, evtp->did);
  513 + eXosip_unlock(mSipCtx);
  514 +
  515 + return ret;
  516 +}
  517 +
  518 +int SipServer::ByeInvite(std::string channel_id, string ip, int rtpPort) {
  519 +
  520 + auto key = std::make_tuple(channel_id, ip, rtpPort);
  521 +
  522 + auto it = m_invite_callinfo_map.find(key);
  523 + if (it == m_invite_callinfo_map.end()) {
  524 + return -1;
  525 + }
  526 +
  527 + CallInfo info = it->second;
  528 +
  529 + eXosip_lock(mSipCtx);
  530 + int ret = eXosip_call_terminate(mSipCtx, info.cid, info.did);
  531 + eXosip_unlock(mSipCtx);
  532 +
  533 + m_invite_callinfo_map.erase(it);
  534 +
  535 + return 0;
  536 +}
  537 +
  538 +int SipServer::RequestInvite_UDP(const char* dst_channel, const char* rtpIp, int rtpPort) {
  539 +
  540 + // 检查设备是否在线
  541 + if (!check_device_status(dst_channel)) {
  542 + LOG_ERROR("{} is not online!", dst_channel);
  543 + return -2;
  544 + }
  545 +
  546 + Client* client = get_parent_by_id(dst_channel);
  547 + if (client == nullptr) {
  548 + LOG_ERROR("do not get parent device:{}", dst_channel);
  549 + return -1;
  550 + }
  551 +
  552 + LOG_INFO("INVITE UDP: {} {}:{}", dst_channel, rtpIp, rtpPort);
  553 +
  554 + char session_exp[1024] = { 0 };
  555 + osip_message_t* msg = nullptr;
  556 + char from[1024] = { 0 };
  557 + char to[1024] = { 0 };
  558 + char sdp[2048] = { 0 };
  559 +
  560 + sprintf(from, "sip:%s@%s:%d", mInfo.getSipId().c_str(), mInfo.getIp().c_str(), mInfo.getPort());
  561 + sprintf(to, "sip:%s@%s:%d", dst_channel, client->getIp().c_str(), client->getPort());
  562 + snprintf(sdp, 2048,
  563 + "v=0\r\n"
  564 + "o=%s 0 0 IN IP4 %s\r\n"
  565 + "s=Play\r\n"
  566 + "c=IN IP4 %s\r\n"
  567 + "t=0 0\r\n"
  568 + "m=video %d RTP/AVP 96 98 97\r\n"
  569 + "a=recvonly\r\n"
  570 + "a=rtpmap:96 PS/90000\r\n"
  571 + "a=rtpmap:98 H264/90000\r\n"
  572 + "a=rtpmap:97 MPEG4/90000\r\n"
  573 + "a=setup:passive\r\n"
  574 + "a=connection:new\r\n"
  575 + "y=0100000001\r\n"
  576 + "f=\r\n", mInfo.getSipId().c_str(), rtpIp, rtpIp, rtpPort);
  577 +
  578 + int ret = eXosip_call_build_initial_invite(mSipCtx, &msg, to, from, nullptr, nullptr);
  579 + if (ret) {
  580 + LOG_ERROR("eXosip_call_build_initial_invite error: {} {} ret:{}", from, to, ret);
  581 + return -1;
  582 + }
  583 +
  584 + osip_message_set_body(msg, sdp, strlen(sdp));
  585 + osip_message_set_content_type(msg, "application/sdp");
  586 + snprintf(session_exp, sizeof(session_exp) - 1, "%i;refresher=uac", mInfo.getTimeout());
  587 + osip_message_set_header(msg, "Session-Expires", session_exp);
  588 + osip_message_set_supported(msg, "timer");
  589 +
  590 + int call_id = eXosip_call_send_initial_invite(mSipCtx, msg);
  591 + if (call_id > 0) {
  592 + LOG_INFO("eXosip_call_send_initial_invite success: call_id={}", call_id);
  593 + }
  594 + else {
  595 + LOG_ERROR("eXosip_call_send_initial_invite error: call_id={}", call_id);
  596 + }
  597 + return call_id;
  598 +}
  599 +
  600 +int SipServer::RequestInvite_TCP_a(const char* dst_channel, int rtpPort) {
  601 + // 检查设备是否在线
  602 + if (!check_device_status(dst_channel)) {
  603 + return -2;
  604 + }
  605 +
  606 + Client* client = get_parent_by_id(dst_channel);
  607 + if (client == nullptr) {
  608 + return -1;
  609 + }
  610 +
  611 + LOG_INFO("INVITE TCP active");
  612 +
  613 + char session_exp[1024] = { 0 };
  614 + osip_message_t* msg = nullptr;
  615 + char from[1024] = { 0 };
  616 + char to[1024] = { 0 };
  617 + char sdp[2048] = { 0 };
  618 +
  619 + // const char* dst_channel = "34020000001320000001";
  620 +
  621 + sprintf(from, "sip:%s@%s:%d", mInfo.getSipId().c_str(), mInfo.getIp().c_str(), mInfo.getPort());
  622 + sprintf(to, "sip:%s@%s:%d", dst_channel, client->getIp().c_str(), client->getPort());
  623 + snprintf(sdp, 2048,
  624 + "v=0\r\n"
  625 + "o=%s 0 0 IN IP4 %s\r\n"
  626 + "s=Play\r\n"
  627 + "c=IN IP4 %s\r\n"
  628 + "t=0 0\r\n"
  629 + "m=video %d TCP/RTP/AVP 96 98 97\r\n"
  630 + "a=recvonly\r\n"
  631 + "a=rtpmap:96 PS/90000\r\n"
  632 + "a=rtpmap:98 H264/90000\r\n"
  633 + "a=rtpmap:97 MPEG4/90000\r\n"
  634 + "a=setup:active\r\n"
  635 + "a=connection:new\r\n"
  636 + "y=0100000001\r\n"
  637 + "f=\r\n", mInfo.getSipId().c_str(), mInfo.getIp().c_str(), mInfo.getIp().c_str(), rtpPort);
  638 +
  639 + int ret = eXosip_call_build_initial_invite(mSipCtx, &msg, to, from, nullptr, nullptr);
  640 + if (ret) {
  641 + LOG_ERROR("eXosip_call_build_initial_invite error: {} {} ret:{}", from, to, ret);
  642 + return -1;
  643 + }
  644 +
  645 + osip_message_set_body(msg, sdp, strlen(sdp));
  646 + osip_message_set_content_type(msg, "application/sdp");
  647 + snprintf(session_exp, sizeof(session_exp) - 1, "%i;refresher=uac", mInfo.getTimeout());
  648 + osip_message_set_header(msg, "Session-Expires", session_exp);
  649 + osip_message_set_supported(msg, "timer");
  650 +
  651 + int call_id = eXosip_call_send_initial_invite(mSipCtx, msg);
  652 + if (call_id > 0) {
  653 + LOG_INFO("eXosip_call_send_initial_invite success: call_id={}", call_id);
  654 + }
  655 + else {
  656 + LOG_ERROR("eXosip_call_send_initial_invite error: call_id={}", call_id);
  657 + }
  658 + return call_id;
  659 +}
  660 +
  661 +void SipServer::cacheCatalog() {
  662 +
  663 + std::lock_guard<std::mutex> l(m_client_map_mtx);
  664 +
  665 + if (mClientMap.size() <= 0){
  666 + cout << "no IPC" << endl;
  667 + return ;
  668 + }
  669 +
  670 + cout << "client size:" << mClientMap.size() << endl;
  671 + for (auto it = mClientMap.begin(); it != mClientMap.end(); it++) {
  672 + RequestCatalog(it->second);
  673 + }
  674 +}
  675 +
  676 +void SipServer::RequestCatalog(Client* client) {
  677 +
  678 + eXosip_lock(mSipCtx);
  679 +
  680 + osip_message_t* catlog_msg = NULL;
  681 + char to[100];/*sip:主叫用户名@被叫IP地址*/
  682 + char from[100];/*sip:被叫IP地址:被叫IP端口*/
  683 + char xml_body[4096];
  684 +
  685 + memset(to, 0, 100);
  686 + memset(from, 0, 100);
  687 + memset(xml_body, 0, 4096);
  688 +
  689 + sprintf(from, "sip:%s@%s:%d", mInfo.getSipId().c_str(), mInfo.getIp().c_str(), mInfo.getPort());
  690 + sprintf(to, "sip:%s@%s:%d", client->getDevice().c_str(), client->getIp().c_str(), client->getPort());
  691 + eXosip_message_build_request(mSipCtx, &catlog_msg, "MESSAGE", to, from, NULL);/*构建"MESSAGE"请求*/
  692 +
  693 + snprintf(xml_body, 4096,
  694 + "<?xml version=\"1.0\"?>"
  695 + "<Query>"
  696 + "<CmdType>Catalog</CmdType>"
  697 + "<SN>%d</SN>"
  698 + "<DeviceID>%s</DeviceID>"
  699 + "</Query>", rand() % (99999 - 10000 + 1) + 10000, client->getDevice().c_str());
  700 +
  701 + osip_message_set_body(catlog_msg, xml_body, strlen(xml_body));
  702 + osip_message_set_content_type(catlog_msg, "Application/MANSCDP+xml");
  703 + eXosip_message_send_request(mSipCtx, catlog_msg);
  704 +
  705 + eXosip_unlock(mSipCtx);
  706 +}
  707 +
  708 +int SipServer::clearClientMap(){
  709 + std::lock_guard<std::mutex> l(m_client_map_mtx);
  710 + for (auto iter=mClientMap.begin(); iter!=mClientMap.end(); iter++) {
  711 + delete iter->second;
  712 + iter->second = nullptr;
  713 + }
  714 + mClientMap.clear();
  715 +
  716 + return 0;
  717 +}
  718 +
  719 +void SipServer::deleteClientByDevice(string device) {
  720 + std::lock_guard<std::mutex> l(m_client_map_mtx);
  721 + auto it = mClientMap.find(device);
  722 + if (it == mClientMap.end()) {
  723 + return ;
  724 + }
  725 + delete it->second;
  726 + it->second = nullptr;
  727 + mClientMap.erase(it);
  728 +}
  729 +
  730 +int SipServer::parse_xml(const char *data, const char *s_mark, bool with_s_make, const char *e_mark, bool with_e_make, char *dest) {
  731 + const char* satrt = strstr( data, s_mark );
  732 +
  733 + if(satrt != NULL) {
  734 + const char* end = strstr(satrt, e_mark);
  735 +
  736 + if(end != NULL){
  737 + int s_pos = with_s_make ? 0 : strlen(s_mark);
  738 + int e_pos = with_e_make ? strlen(e_mark) : 0;
  739 +
  740 + strncpy( dest, satrt+s_pos, (end+e_pos) - (satrt+s_pos) );
  741 + }
  742 + return 0;
  743 + }
  744 + return -1;
  745 +
  746 +}
  747 +void SipServer::dump_request(eXosip_event_t *evtp) {
  748 + char *s = nullptr;
  749 + size_t len;
  750 + osip_message_to_str(evtp->request, &s, &len);
  751 + if (s) {
  752 + LOG_INFO("\nprint request start\ntype={}\n{}\nprint request end\n",evtp->type,s);
  753 + }
  754 +
  755 + LOG_INFO("REQUEST cid:{} did: {}",evtp->cid, evtp->did);
  756 +}
  757 +void SipServer::dump_response(eXosip_event_t *evtp) {
  758 + char *s = nullptr;
  759 + size_t len;
  760 + osip_message_to_str(evtp->response, &s, &len);
  761 + if (s)
  762 + {
  763 + LOG_INFO("\nprint response start\ntype={}\n{}\nprint response end\n",evtp->type,s);
  764 + }
  765 +
  766 +
  767 + LOG_INFO("RESPONSE cid:{} did: {}",evtp->cid, evtp->did);
  768 +}
0 769 \ No newline at end of file
... ...
sip/SipServer.h 0 → 100644
  1 +++ a/sip/SipServer.h
  1 +#ifndef __SIPSERVER_H__
  2 +#define __SIPSERVER_H__
  3 +
  4 +extern "C" {
  5 +#include <osip2/osip_mt.h>
  6 +#include <eXosip2/eXosip.h>
  7 +}
  8 +
  9 +#include <map>
  10 +#include <string>
  11 +#include <thread>
  12 +#include <chrono>
  13 +#include <mutex>
  14 +#include <tuple>
  15 +
  16 +#include "./Message/CatalogParser.h"
  17 +#include "sip_header.h"
  18 +
  19 +using namespace std;
  20 +
  21 +class Client {
  22 +public:
  23 + Client(string ip, int port, string device) :
  24 + mIp(ip),
  25 + mPort(port),
  26 + mRtpPort(0),
  27 + mDevice(device),
  28 + mIsReg(false){
  29 + }
  30 + ~Client() = default;
  31 +public:
  32 +
  33 + void setRtpPort(int rtpPort) {
  34 + mRtpPort = rtpPort;
  35 + }
  36 +
  37 + void setReg(bool isReg) {
  38 + mIsReg = isReg;
  39 + }
  40 + string getDevice() const{
  41 + return mDevice;
  42 + }
  43 + string getIp() const{
  44 + return mIp;
  45 + }
  46 + int getPort() const{
  47 + return mPort;
  48 + }
  49 +
  50 + unsigned long getHeartBeat() {
  51 + return mHeartBeatTime;
  52 + }
  53 +
  54 + void updateHeartBeat(unsigned long ts) {
  55 + mHeartBeatTime = ts;
  56 + }
  57 +
  58 +private:
  59 + string mIp; // client ip
  60 + int mPort; // client port
  61 + string mDevice;// 340200000013200000024
  62 + bool mIsReg;
  63 + int mRtpPort;
  64 + unsigned long mHeartBeatTime{0};
  65 +
  66 +};
  67 +
  68 +
  69 +class SipServer {
  70 +public:
  71 + SipServer();
  72 + ~SipServer();
  73 +
  74 + bool Init(ServerInfo* pInfo);
  75 +
  76 + int RequestInvite_UDP(const char* dst_channel, const char* rtpIp, int rtpPort);
  77 +
  78 + int RequestInvite_TCP_a(const char* dst_channel, int rtpPort);
  79 +
  80 + int ByeInvite(std::string channel_id, string ip, int rtpPort);
  81 +
  82 + void Close();
  83 +
  84 +public:
  85 + void event_loop();
  86 + void timing_getcatlog();
  87 +
  88 +private:
  89 + int init_sip_server();
  90 + int sip_event_handle(eXosip_event_t *evtp);
  91 +
  92 + void RequestCatalog(Client* client);
  93 + void cacheCatalog();
  94 +
  95 + void response_message_answer(eXosip_event_t *evtp,int code);
  96 + void response_register(eXosip_event_t *evtp);
  97 + void response_register_401unauthorized(eXosip_event_t *evt);
  98 + void response_message(eXosip_event_t *evtp);
  99 + void response_invite_ack(eXosip_event_t *evtp);
  100 + int request_bye(eXosip_event_t* evtp);// 通知相机停止推流
  101 + int parse_xml(const char* data, const char* s_mark, bool with_s_make, const char* e_mark, bool with_e_make, char* dest);
  102 + void dump_request(eXosip_event_t *evtp);
  103 + void dump_response(eXosip_event_t *evtp);
  104 +
  105 + int clearClientMap();
  106 + void deleteClientByDevice(string device);
  107 +
  108 + Client* get_parent_by_id(string id);
  109 + bool check_device_status(string id);
  110 +
  111 + void cache_invite_callinfo(eXosip_event_t *evtp);
  112 +
  113 +private:
  114 + bool mQuit{ false };
  115 + eXosip_t *mSipCtx;
  116 + ServerInfo mInfo;
  117 +
  118 + std::map<std::string, Client *> mClientMap;// <DeviceID,SipClient>
  119 + mutex m_client_map_mtx;
  120 + std::map<std::string, DeviceInfo> m_device_map;
  121 + mutex m_device_map_mtx;
  122 +
  123 + thread* m_event_loop_thread{nullptr};
  124 +
  125 + std::map< tuple<std::string, std::string, int>, CallInfo> m_invite_callinfo_map;
  126 +};
  127 +
  128 +
  129 +#endif //__SIPSERVER_H__
... ...
sip/Utils/HTTPDigest.cpp 0 → 100644
  1 +++ a/sip/Utils/HTTPDigest.cpp
  1 +#include "MD5.h"
  2 +#include <string.h>
  3 +#include "HTTPDigest.h"
  4 +
  5 +#ifdef _WIN32
  6 +//#define strcasecmp _stricmp
  7 +//#define strncasecmp _strnicmp
  8 +#endif
  9 +#ifdef _MSC_VER
  10 +#define strcasecmp _stricmp
  11 +#define strncasecmp _strnicmp
  12 +#endif
  13 +
  14 +void my_CvtHex(
  15 + IN HASH Bin,
  16 + OUT HASHHEX Hex
  17 + )
  18 +{
  19 + unsigned short i;
  20 + unsigned char j;
  21 + for (i = 0; i < HASHLEN; i++) {
  22 + j = (Bin[i] >> 4) & 0xf;
  23 + if (j <= 9)
  24 + Hex[i*2] = (j + '0');
  25 + else
  26 + Hex[i*2] = (j + 'a' - 10);
  27 + j = Bin[i] & 0xf;
  28 + if (j <= 9)
  29 + Hex[i*2+1] = (j + '0');
  30 + else
  31 + Hex[i*2+1] = (j + 'a' - 10);
  32 + };
  33 + Hex[HASHHEXLEN] = '\0';
  34 +}
  35 +
  36 +/* calculate H(A1) as per spec */
  37 +void
  38 +DigestCalcHA1 (IN const char *pszAlg,
  39 + IN const char *pszUserName,
  40 + IN const char *pszRealm,
  41 + IN const char *pszPassword,
  42 + IN const char *pszNonce,
  43 + IN const char *pszCNonce,
  44 + OUT HASHHEX SessionKey)
  45 +{
  46 + MD5_CTX Md5Ctx;
  47 + HASH HA1;
  48 +
  49 + MD5Init (&Md5Ctx);
  50 + MD5Update (&Md5Ctx, (unsigned char *) pszUserName, (unsigned int) strlen (pszUserName));
  51 + MD5Update (&Md5Ctx, (unsigned char *) ":", 1);
  52 + MD5Update (&Md5Ctx, (unsigned char *) pszRealm, (unsigned int) strlen (pszRealm));
  53 + MD5Update (&Md5Ctx, (unsigned char *) ":", 1);
  54 + MD5Update (&Md5Ctx, (unsigned char *) pszPassword, (unsigned int) strlen (pszPassword));
  55 + MD5Final ((unsigned char *) HA1, &Md5Ctx);
  56 + if ((pszAlg != NULL) && strcasecmp (pszAlg, "md5-sess") == 0) {
  57 + MD5Init (&Md5Ctx);
  58 + MD5Update (&Md5Ctx, (unsigned char *) HA1, HASHLEN);
  59 + MD5Update (&Md5Ctx, (unsigned char *) ":", 1);
  60 + MD5Update (&Md5Ctx, (unsigned char *) pszNonce, (unsigned int) strlen (pszNonce));
  61 + MD5Update (&Md5Ctx, (unsigned char *) ":", 1);
  62 + MD5Update (&Md5Ctx, (unsigned char *) pszCNonce, (unsigned int) strlen (pszCNonce));
  63 + MD5Final ((unsigned char *) HA1, &Md5Ctx);
  64 + }
  65 + my_CvtHex (HA1, SessionKey);
  66 +}
  67 +
  68 +/* calculate request-digest/response-digest as per HTTP Digest spec */
  69 +void
  70 +DigestCalcResponse (IN HASHHEX HA1, /* H(A1) */
  71 + IN const char *pszNonce, /* nonce from server */
  72 + IN const char *pszNonceCount, /* 8 hex digits */
  73 + IN const char *pszCNonce, /* client nonce */
  74 + IN const char *pszQop, /* qop-value: "", "auth", "auth-int" */
  75 + IN int Aka, /* Calculating AKAv1-MD5 response */
  76 + IN const char *pszMethod, /* method from the request */
  77 + IN const char *pszDigestUri, /* requested URL */
  78 + IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */
  79 + OUT HASHHEX Response
  80 + /* request-digest or response-digest */ )
  81 +{
  82 + MD5_CTX Md5Ctx;
  83 + HASH HA2;
  84 + HASH RespHash;
  85 + HASHHEX HA2Hex;
  86 +
  87 + /* calculate H(A2) */
  88 + MD5Init (&Md5Ctx);
  89 + MD5Update (&Md5Ctx, (unsigned char *) pszMethod, (unsigned int) strlen (pszMethod));
  90 + MD5Update (&Md5Ctx, (unsigned char *) ":", 1);
  91 + MD5Update (&Md5Ctx, (unsigned char *) pszDigestUri, (unsigned int) strlen (pszDigestUri));
  92 +
  93 + if (pszQop == NULL) {
  94 + goto auth_withoutqop;
  95 + }
  96 + else if (0 == strcasecmp (pszQop, "auth-int")) {
  97 + goto auth_withauth_int;
  98 + }
  99 + else if (0 == strcasecmp (pszQop, "auth")) {
  100 + goto auth_withauth;
  101 + }
  102 +
  103 +auth_withoutqop:
  104 + MD5Final ((unsigned char *) HA2, &Md5Ctx);
  105 + my_CvtHex (HA2, HA2Hex);
  106 +
  107 + /* calculate response */
  108 + MD5Init (&Md5Ctx);
  109 + MD5Update (&Md5Ctx, (unsigned char *) HA1, HASHHEXLEN);
  110 + MD5Update (&Md5Ctx, (unsigned char *) ":", 1);
  111 + MD5Update (&Md5Ctx, (unsigned char *) pszNonce, (unsigned int) strlen (pszNonce));
  112 + MD5Update (&Md5Ctx, (unsigned char *) ":", 1);
  113 +
  114 + goto end;
  115 +
  116 +auth_withauth_int:
  117 +
  118 + MD5Update (&Md5Ctx, (unsigned char *) ":", 1);
  119 + MD5Update (&Md5Ctx, (unsigned char *) HEntity, HASHHEXLEN);
  120 +
  121 +auth_withauth:
  122 + MD5Final ((unsigned char *) HA2, &Md5Ctx);
  123 + my_CvtHex (HA2, HA2Hex);
  124 +
  125 + /* calculate response */
  126 + MD5Init (&Md5Ctx);
  127 + MD5Update (&Md5Ctx, (unsigned char *) HA1, HASHHEXLEN);
  128 + MD5Update (&Md5Ctx, (unsigned char *) ":", 1);
  129 + MD5Update (&Md5Ctx, (unsigned char *) pszNonce, (unsigned int) strlen (pszNonce));
  130 + MD5Update (&Md5Ctx, (unsigned char *) ":", 1);
  131 + if (Aka == 0) {
  132 + MD5Update (&Md5Ctx, (unsigned char *) pszNonceCount, (unsigned int) strlen (pszNonceCount));
  133 + MD5Update (&Md5Ctx, (unsigned char *) ":", 1);
  134 + MD5Update (&Md5Ctx, (unsigned char *) pszCNonce, (unsigned int) strlen (pszCNonce));
  135 + MD5Update (&Md5Ctx, (unsigned char *) ":", 1);
  136 + MD5Update (&Md5Ctx, (unsigned char *) pszQop, (unsigned int) strlen (pszQop));
  137 + MD5Update (&Md5Ctx, (unsigned char *) ":", 1);
  138 + }
  139 +end:
  140 + MD5Update (&Md5Ctx, (unsigned char *) HA2Hex, HASHHEXLEN);
  141 + MD5Final ((unsigned char *) RespHash, &Md5Ctx);
  142 + my_CvtHex (RespHash, Response);
  143 +}
... ...
sip/Utils/HTTPDigest.h 0 → 100644
  1 +++ a/sip/Utils/HTTPDigest.h
  1 +#define HASHLEN 16
  2 +typedef char HASH[HASHLEN];
  3 +#define HASHHEXLEN 32
  4 +typedef char HASHHEX[HASHHEXLEN+1];
  5 +#define IN
  6 +#define OUT
  7 +/* calculate H(A1) as per HTTP Digest spec */
  8 +void DigestCalcHA1 (IN const char *pszAlg,
  9 + IN const char *pszUserName,
  10 + IN const char *pszRealm,
  11 + IN const char *pszPassword,
  12 + IN const char *pszNonce,
  13 + IN const char *pszCNonce,
  14 + OUT HASHHEX SessionKey);
  15 +/* calculate request-digest/response-digest as per HTTP Digest spec */
  16 +void DigestCalcResponse (IN HASHHEX HA1, /* H(A1) */
  17 + IN const char *pszNonce, /* nonce from server */
  18 + IN const char *pszNonceCount, /* 8 hex digits */
  19 + IN const char *pszCNonce, /* client nonce */
  20 + IN const char *pszQop, /* qop-value: "", "auth", "auth-int" */
  21 + IN int Aka, /* Calculating AKAv1-MD5 response */
  22 + IN const char *pszMethod, /* method from the request */
  23 + IN const char *pszDigestUri, /* requested URL */
  24 + IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */
  25 + OUT HASHHEX Response
  26 + /* request-digest or response-digest */ );
... ...
sip/Utils/MD5.cpp 0 → 100644
  1 +++ a/sip/Utils/MD5.cpp
  1 +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
  2 +*/
  3 +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
  4 +rights reserved.
  5 +License to copy and use this software is granted provided that it
  6 +is identified as the "RSA Data Security, Inc. MD5 Message-Digest
  7 +Algorithm" in all material mentioning or referencing this software
  8 +or this function.
  9 +License is also granted to make and use derivative works provided
  10 +that such works are identified as "derived from the RSA Data
  11 +Security, Inc. MD5 Message-Digest Algorithm" in all material
  12 +mentioning or referencing the derived work.
  13 +RSA Data Security, Inc. makes no representations concerning either
  14 +the merchantability of this software or the suitability of this
  15 +software for any particular purpose. It is provided "as is"
  16 +without express or implied warranty of any kind.
  17 +These notices must be retained in any copies of any part of this
  18 +documentation and/or software.
  19 +*/
  20 +#include "MD5.h"
  21 +/* Constants for MD5Transform routine.
  22 +*/
  23 +#define S11 7
  24 +#define S12 12
  25 +#define S13 17
  26 +#define S14 22
  27 +#define S21 5
  28 +#define S22 9
  29 +#define S23 14
  30 +#define S24 20
  31 +#define S31 4
  32 +#define S32 11
  33 +#define S33 16
  34 +#define S34 23
  35 +#define S41 6
  36 +#define S42 10
  37 +#define S43 15
  38 +#define S44 21
  39 +
  40 +#if 0
  41 +static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64]));
  42 +static void Encode PROTO_LIST
  43 + ((unsigned char *, UINT4 *, unsigned int));
  44 +static void Decode PROTO_LIST
  45 + ((UINT4 *, unsigned char *, unsigned int));
  46 +static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int));
  47 +static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int));
  48 +#endif
  49 +
  50 +static void MD5Transform(UINT4 [4], unsigned char [64]);
  51 +static void Encode(unsigned char *, UINT4 *, unsigned int);
  52 +static void Decode(UINT4 *, unsigned char *, unsigned int);
  53 +static void MD5_memcpy(POINTER, POINTER, unsigned int);
  54 +static void MD5_memset(POINTER, int, unsigned int);
  55 +
  56 +
  57 +static unsigned char PADDING[64] = {
  58 + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  59 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  60 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  61 +};
  62 +/* F, G, H and I are basic MD5 functions.
  63 +*/
  64 +#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
  65 +#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
  66 +#define H(x, y, z) ((x) ^ (y) ^ (z))
  67 +#define I(x, y, z) ((y) ^ ((x) | (~z)))
  68 +/* ROTATE_LEFT rotates x left n bits.
  69 +*/
  70 +#define ROTATE_LEFT(x , n) (((x) << (n)) | ((x) >> (32-(n))))
  71 +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
  72 +Rotation is separate from addition to prevent recomputation.
  73 +*/
  74 +#define FF(a, b, c, d, x, s, ac) { \
  75 + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
  76 + (a) = ROTATE_LEFT ((a), (s));\
  77 + (a) += (b); \
  78 + }
  79 +#define GG(a, b, c, d, x, s, ac) { \
  80 + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
  81 + (a) = ROTATE_LEFT ((a), (s)); \
  82 + (a) += (b); \
  83 + }
  84 +#define HH(a, b, c, d, x, s, ac) { \
  85 + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
  86 + (a) = ROTATE_LEFT ((a), (s)); \
  87 + (a) += (b); \
  88 + }
  89 +#define II(a, b, c, d, x, s, ac) { \
  90 + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
  91 + (a) = ROTATE_LEFT ((a), (s)); \
  92 + (a) += (b); \
  93 + }
  94 +/* MD5 initialization. Begins an MD5 operation, writing a new context.
  95 +*/
  96 +void MD5Init (MD5_CTX * context)
  97 +{
  98 + context->count[0] = context->count[1] = 0;
  99 + /* Load magic initialization constants.
  100 + */
  101 + context->state[0] = 0x67452301;
  102 + context->state[1] = 0xefcdab89;
  103 + context->state[2] = 0x98badcfe;
  104 + context->state[3] = 0x10325476;
  105 +}
  106 +/* MD5 block update operation. Continues an MD5 message-digest
  107 +operation, processing another message block, and updating the
  108 +context.
  109 +*/
  110 +void MD5Update (MD5_CTX * context,unsigned char *input,unsigned int inputLen)
  111 +{
  112 + unsigned int i, index, partLen;
  113 + /* Compute number of bytes mod 64 */
  114 + index = (unsigned int)((context->count[0] >> 3) & 0x3F);
  115 + /* Update number of bits */
  116 + if ((context->count[0] += ((UINT4)inputLen << 3))< ((UINT4)inputLen << 3))
  117 + context->count[1]++;
  118 + context->count[1] += ((UINT4)inputLen >> 29);
  119 + partLen = 64 - index;
  120 + /* Transform as many times as possible.
  121 + */
  122 + if (inputLen >= partLen) {
  123 + MD5_memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen);
  124 + MD5Transform (context->state, context->buffer);
  125 + for (i = partLen; i + 63 < inputLen; i += 64)
  126 + MD5Transform (context->state, &input[i]);
  127 + index = 0;
  128 + }
  129 + else
  130 + i = 0;
  131 + /* Buffer remaining input */
  132 + MD5_memcpy((POINTER)&context->buffer[index], (POINTER)&input[i],inputLen-i);
  133 +}
  134 +/* MD5 finalization. Ends an MD5 message-digest operation, writing the
  135 +the message digest and zeroizing the context.
  136 +*/
  137 +void MD5Final(unsigned char digest[16],MD5_CTX * context)
  138 +{
  139 + unsigned char bits[8];
  140 + unsigned int index, padLen;
  141 + /* Save number of bits */
  142 + Encode(bits, context->count, 8);
  143 + /* Pad out to 56 mod 64.
  144 + */
  145 + index = (unsigned int)((context->count[0] >> 3) & 0x3f);
  146 + padLen = (index < 56) ? (56 - index) : (120 - index);
  147 + MD5Update (context, PADDING, padLen);
  148 + /* Append length (before padding) */
  149 + MD5Update (context, bits, 8);
  150 + /* Store state in digest */
  151 + Encode (digest, context->state, 16);
  152 + /* Zeroize sensitive information.
  153 + */
  154 + MD5_memset ((POINTER)context, 0, sizeof (*context));
  155 +}
  156 +
  157 +
  158 +/* MD5 basic transformation. Transforms state based on block.
  159 +*/
  160 +static void MD5Transform(UINT4 state[4], unsigned char block[64])
  161 +{
  162 + UINT4 a = state[0], b = state[1], c = state[2], d = state[3],x[16];
  163 + Decode(x, block, 64);
  164 + /* Round 1 */
  165 + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
  166 + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
  167 + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
  168 + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
  169 + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
  170 + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
  171 + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
  172 + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
  173 + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
  174 + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
  175 + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
  176 + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
  177 + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
  178 + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
  179 + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
  180 + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
  181 + /* Round 2 */
  182 + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
  183 + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
  184 + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
  185 + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
  186 + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
  187 + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
  188 + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
  189 + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
  190 + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
  191 + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
  192 + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
  193 +
  194 + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
  195 + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
  196 + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
  197 + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
  198 + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
  199 + /* Round 3 */
  200 + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
  201 + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
  202 + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
  203 + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
  204 + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
  205 + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
  206 + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
  207 + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
  208 + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
  209 + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
  210 + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
  211 + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
  212 + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
  213 + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
  214 + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
  215 + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
  216 +
  217 + /* Round 4 */
  218 + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
  219 + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
  220 + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
  221 + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
  222 + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
  223 + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
  224 + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
  225 + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
  226 + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
  227 + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
  228 + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
  229 + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
  230 + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
  231 + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
  232 + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
  233 + II (b, c, d, a, x[ 9], S44,0xeb86d391); /* 64 */
  234 +
  235 + state[0] += a;
  236 + state[1] += b;
  237 + state[2] += c;
  238 + state[3] += d;
  239 + /* Zeroize sensitive information.
  240 +
  241 + */
  242 + MD5_memset((POINTER)x, 0, sizeof (x));
  243 +}
  244 +/* Encodes input (UINT4) into output (unsigned char). Assumes len is
  245 +a multiple of 4.
  246 +*/
  247 +static void Encode(unsigned char * output,UINT4 * input,unsigned int len)
  248 +{
  249 + unsigned int i, j;
  250 + for (i = 0, j = 0; j < len; i++, j += 4) {
  251 + output[j] = (unsigned char)(input[i] & 0xff);
  252 + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
  253 + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
  254 + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
  255 + }
  256 +}
  257 +
  258 +/* Decodes input (unsigned char) into output (UINT4). Assumes len is
  259 +a multiple of 4.
  260 +*/
  261 +static void Decode(UINT4 * output,unsigned char * input,unsigned int len)
  262 +{
  263 + unsigned int i, j;
  264 + for (i = 0, j = 0; j < len; i++, j += 4)
  265 + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
  266 + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
  267 +}
  268 +
  269 +/* Note: Replace "for loop" with standard memcpy if possible.
  270 +*/
  271 +static void MD5_memcpy(POINTER output,POINTER input,unsigned int len)
  272 +{
  273 + unsigned int i;
  274 + for (i = 0; i < len; i++)
  275 + output[i] = input[i];
  276 +}
  277 +/* Note: Replace "for loop" with standard memset if possible.
  278 +*/
  279 +static void MD5_memset( POINTER output, int value,unsigned int len)
  280 +{
  281 + unsigned int i;
  282 + for (i = 0; i < len; i++)
  283 + ((char *)output)[i] = (char)value;
  284 +}
... ...
sip/Utils/MD5.h 0 → 100644
  1 +++ a/sip/Utils/MD5.h
  1 +
  2 +/* GLOBAL.H - RSAREF types and constants
  3 +*/
  4 +/* PROTOTYPES should be set to one if and only if the compiler supports
  5 +function argument prototyping.
  6 +The following makes PROTOTYPES default to 0 if it has not already been defined with C compiler flags.
  7 +*/
  8 +
  9 +
  10 +#ifndef PROTOTYPES
  11 +#define PROTOTYPES 0
  12 +#endif
  13 +/* POINTER defines a generic pointer type */
  14 +typedef unsigned char *POINTER;
  15 +/* UINT2 defines a two byte word */
  16 +typedef unsigned short int UINT2;
  17 +/* UINT4 defines a four byte word */
  18 +typedef unsigned long int UINT4;
  19 +/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
  20 +If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
  21 +returns an empty list.
  22 +*/
  23 +#if PROTOTYPES
  24 +#define PROTO_LIST(list) list
  25 +#else
  26 +#define PROTO_LIST(list) ()
  27 +#endif
  28 +
  29 +
  30 +
  31 +/* MD5.H - header file for MD5C.C
  32 +*/
  33 +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
  34 +rights reserved.
  35 +License to copy and use this software is granted provided that it
  36 +is identified as the "RSA Data Security, Inc. MD5 Message-Digest
  37 +Algorithm" in all material mentioning or referencing this software
  38 +or this function.
  39 +License is also granted to make and use derivative works provided
  40 +that such works are identified as "derived from the RSA Data
  41 +Security, Inc. MD5 Message-Digest Algorithm" in all material
  42 +mentioning or referencing the derived work.
  43 +RSA Data Security, Inc. makes no representations concerning either
  44 +the merchantability of this software or the suitability of this
  45 +software for any particular purpose. It is provided "as is"
  46 +without express or implied warranty of any kind.
  47 +These notices must be retained in any copies of any part of this
  48 +documentation and/or software.
  49 +*/
  50 +/* MD5 context. */
  51 +
  52 +
  53 +typedef struct {
  54 + UINT4 state[4]; /* state (ABCD) */
  55 + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
  56 + unsigned char buffer[64]; /* input buffer */
  57 +} MD5_CTX;
  58 +
  59 +#if 0
  60 +void MD5Init PROTO_LIST ((MD5_CTX *));
  61 +void MD5Update PROTO_LIST
  62 + ((MD5_CTX *, unsigned char *, unsigned int));
  63 +void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *));
  64 +#endif
  65 +
  66 +void MD5Init(MD5_CTX *);
  67 +void MD5Update(MD5_CTX *, unsigned char *, unsigned int);
  68 +void MD5Final(unsigned char [16], MD5_CTX *);
... ...
sip/Utils/StringTools.hpp 0 → 100644
  1 +++ a/sip/Utils/StringTools.hpp
  1 +#ifndef __STRING_TOOLS_HPP__
  2 +#define __STRING_TOOLS_HPP__
  3 +
  4 +#include <string>
  5 +#include <algorithm>
  6 +#include <vector>
  7 +
  8 +using namespace std;
  9 +
  10 +namespace StringTools {
  11 +
  12 + static bool isntspace(const char &ch) {
  13 + return !isspace(ch);
  14 + }
  15 +
  16 + static std::string trim(const std::string &s) {
  17 + std::string::const_iterator iter1 = find_if(s.begin(), s.end(), isntspace);
  18 + std::string::const_iterator iter2 = find_if(s.rbegin(), s.rend(), isntspace).base();
  19 +
  20 + return iter1 < iter2 ? string(iter1, iter2) : std::string("");
  21 + }
  22 +
  23 + static vector<string> split(const string &origin,const string &sep){
  24 + size_t found = origin.find(sep);
  25 + size_t s = 0;
  26 + std::vector<string> vec;
  27 + while (found != string::npos)
  28 + {
  29 + string kv = origin.substr(s,found - s);
  30 + vec.push_back(trim(kv));
  31 + s = found + 1;
  32 + found = origin.find(sep,s);
  33 + }
  34 + vec.push_back(origin.substr(s,origin.length() - s));
  35 + return vec;
  36 + }
  37 +
  38 + static string to_lower(string origin) {
  39 + string str_lower = trim(origin);
  40 + transform(str_lower.begin(), str_lower.end(), str_lower.begin(),::tolower);
  41 + return str_lower;
  42 + }
  43 +}
  44 +
  45 +#endif
0 46 \ No newline at end of file
... ...
sip/Utils/Utools.hpp 0 → 100644
  1 +++ a/sip/Utils/Utools.hpp
  1 +#ifndef __UTOOLS_HPP__
  2 +#define __UTOOLS_HPP__
  3 +
  4 +#include <chrono>
  5 +
  6 +using namespace std;
  7 +
  8 +namespace Utools{
  9 +
  10 + static long get_cur_time_ms() {
  11 + chrono::time_point<chrono::system_clock, chrono::milliseconds> tpMicro
  12 + = chrono::time_point_cast<chrono::milliseconds>(chrono::system_clock::now());
  13 + return tpMicro.time_since_epoch().count();
  14 + }
  15 +
  16 +}
  17 +
  18 +#endif
0 19 \ No newline at end of file
... ...
sip/Utils/logger.hpp 0 → 100644
  1 +++ a/sip/Utils/logger.hpp
  1 +/*
  2 + * @Author: yangzilong
  3 + * @Date: 2021-12-21 11:07:11
  4 + * @Last Modified by: yangzilong
  5 + * @Email: yangzilong@objecteye.com
  6 + * @Description:
  7 + */
  8 +
  9 +#ifndef __LOGGER_HPP__
  10 +#define __LOGGER_HPP__
  11 +
  12 +
  13 +#include <spdlog/spdlog.h>
  14 +#include <spdlog/common.h>
  15 +#include <spdlog/details/file_helper.h>
  16 +#include <spdlog/details/null_mutex.h>
  17 +#include <spdlog/fmt/fmt.h>
  18 +#include <spdlog/sinks/base_sink.h>
  19 +#include <spdlog/details/os.h>
  20 +#include <spdlog/details/circular_q.h>
  21 +#include <spdlog/details/synchronous_factory.h>
  22 +
  23 +#include <set>
  24 +#include <chrono>
  25 +#include <cstdio>
  26 +#include <ctime>
  27 +#include <mutex>
  28 +#include <string>
  29 +#include <memory>
  30 +#include <vector>
  31 +
  32 +
  33 +
  34 +#define LOG_TRACE_WITH_LOGGER(logger, ...) {SPDLOG_LOGGER_TRACE(logger, __VA_ARGS__);}
  35 +#define LOG_DEBUG_WITH_LOGGER(logger, ...) {SPDLOG_LOGGER_DEBUG(logger, __VA_ARGS__);}
  36 +#define LOG_WARN_WITH_LOGGER(logger, ...) {SPDLOG_LOGGER_WARN(logger, __VA_ARGS__);}
  37 +#define LOG_ERROR_WITH_LOGGER(logger, ...) {SPDLOG_LOGGER_ERROR(logger, __VA_ARGS__);}
  38 +#define LOG_INFO_WITH_LOGGER(logger, ...) {SPDLOG_LOGGER_INFO(logger, __VA_ARGS__);}
  39 +#define LOG_CRITICAL_WITH_LOGGER(logger, ...) {SPDLOG_LOGGER_CRITICAL(logger, __VA_ARGS__);}
  40 +
  41 +
  42 +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
  43 +
  44 +// use fmt lib, e.g. LOG_WARN("warn log, {1}, {1}, {2}", 1, 2);
  45 +#define LOG_TRACE(msg, ...) spdlog::log({__FILENAME__, __LINE__, __FUNCTION__}, spdlog::level::trace, msg, ##__VA_ARGS__)
  46 +#define LOG_DEBUG(msg, ...) spdlog::log({__FILENAME__, __LINE__, __FUNCTION__}, spdlog::level::debug, msg, ##__VA_ARGS__)
  47 +#define LOG_INFO(msg,...) spdlog::log({__FILENAME__, __LINE__, __FUNCTION__}, spdlog::level::info, msg, ##__VA_ARGS__)
  48 +#define LOG_WARN(msg,...) spdlog::log({__FILENAME__, __LINE__, __FUNCTION__}, spdlog::level::warn, msg, ##__VA_ARGS__)
  49 +#define LOG_ERROR(msg,...) spdlog::log({__FILENAME__, __LINE__, __FUNCTION__}, spdlog::level::err, msg, ##__VA_ARGS__)
  50 +#define LOG_FATAL(msg,...) spdlog::log({__FILENAME__, __LINE__, __FUNCTION__}, spdlog::level::critical, msg, ##__VA_ARGS__)
  51 +
  52 +
  53 +
  54 +namespace spdlog
  55 +{
  56 + namespace sinks
  57 + {
  58 + template<typename Mutex>
  59 + class easy_file_sink final : public base_sink<Mutex>
  60 + {
  61 + public:
  62 + easy_file_sink(filename_t base_filename, size_t max_size, size_t max_keep_days = 0)
  63 + : base_filename_(std::move(base_filename))
  64 + , max_size_(max_size)
  65 + , max_keep_days_(max_keep_days)
  66 + {
  67 + auto now = log_clock::now();
  68 + auto filename = gen_filename_by_daliy(base_filename_, now_tm(now));
  69 +
  70 + file_helper_.open(filename, false);
  71 + current_size_ = file_helper_.size();
  72 + rotation_tp_ = next_rotation_tp_();
  73 +
  74 + if (max_keep_days_ > 0)
  75 + {
  76 + filespath_q_.push_back(std::move(std::set<filename_t>()));
  77 + filespath_q_[filespath_q_.size() - 1].insert(filename);
  78 + }
  79 + }
  80 +
  81 + filename_t filename()
  82 + {
  83 + std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
  84 + return file_helper_.filename();
  85 + }
  86 +
  87 + protected:
  88 + void sink_it_(const details::log_msg &msg) override
  89 + {
  90 + memory_buf_t formatted;
  91 + base_sink<Mutex>::formatter_->format(msg, formatted);
  92 + current_size_ += formatted.size();
  93 +
  94 + auto time = msg.time;
  95 + if (time >= rotation_tp_)
  96 + {
  97 + file_helper_.close();
  98 + auto filename = gen_filename_by_daliy(base_filename_, now_tm(time));
  99 + file_helper_.open(filename, false);
  100 + current_size_ = file_helper_.size();
  101 + rotation_tp_ = next_rotation_tp_();
  102 +
  103 + {
  104 + filespath_q_.push_back(std::move(std::set<filename_t>()));
  105 + filespath_q_[filespath_q_.size() - 1].emplace(filename);
  106 + }
  107 +
  108 + // Do the cleaning only at the end because it might throw on failure.
  109 + if (max_keep_days_ > 0 && filespath_q_.size() > max_keep_days_)
  110 + delete_old_();
  111 + }
  112 + else if (current_size_ >= max_size_)
  113 + {
  114 + file_helper_.close();
  115 + auto src_name = gen_filename_by_daliy(base_filename_, now_tm(time));
  116 + auto target_name = gen_filename_by_filesize(base_filename_, now_tm(time), filespath_q_[filespath_q_.size() - 1].size());
  117 +
  118 + // rename file if failed then us `target_name` as src_name.
  119 + if (!rename_file_(src_name, target_name))
  120 + {
  121 + details::os::sleep_for_millis(200);
  122 + if (!rename_file_(src_name, target_name))
  123 + {
  124 + fprintf(stderr, "%s:%d rename %s to %s failed\n", __FILENAME__, __LINE__, src_name.c_str(), target_name.c_str());
  125 + src_name = target_name;
  126 + }
  127 + }
  128 +
  129 + filespath_q_[filespath_q_.size() - 1].emplace(src_name);
  130 + if (src_name != target_name)
  131 + filespath_q_[filespath_q_.size() - 1].emplace(target_name);
  132 +
  133 + file_helper_.open(src_name, false);
  134 + current_size_ = file_helper_.size();
  135 + rotation_tp_ = next_rotation_tp_();
  136 + }
  137 +
  138 + file_helper_.write(formatted);
  139 +
  140 +
  141 + }
  142 +
  143 + void flush_() override
  144 + {
  145 + file_helper_.flush();
  146 + }
  147 +
  148 + private:
  149 +
  150 + tm now_tm(log_clock::time_point tp)
  151 + {
  152 + time_t tnow = log_clock::to_time_t(tp);
  153 + return spdlog::details::os::localtime(tnow);
  154 + }
  155 +
  156 + /**
  157 + * @brief Get next day tm.
  158 + *
  159 + * @return log_clock::time_point
  160 + */
  161 + log_clock::time_point next_rotation_tp_()
  162 + {
  163 + auto now = log_clock::now();
  164 + tm date = now_tm(now);
  165 + date.tm_hour = 0;
  166 + date.tm_min = 0;
  167 + date.tm_sec = 0;
  168 + auto rotation_time = log_clock::from_time_t(std::mktime(&date));
  169 + if (rotation_time > now)
  170 + return rotation_time;
  171 + return {rotation_time + std::chrono::hours(24)};
  172 + }
  173 +
  174 + // Delete the file N rotations ago.
  175 + // Throw spdlog_ex on failure to delete the old file.
  176 + void delete_old_()
  177 + {
  178 + for (auto iter = filespath_q_.begin(); iter != filespath_q_.end();)
  179 + {
  180 + if (filespath_q_.size() <= max_keep_days_)
  181 + break;
  182 +
  183 + for (auto it = iter->begin(); it != iter->end(); ++it)
  184 + {
  185 + bool ok = details::os::remove_if_exists(*it) == 0;
  186 + if (!ok)
  187 + throw_spdlog_ex("Failed removing daily file " + details::os::filename_to_str(*it), errno);
  188 + }
  189 + filespath_q_.erase(iter);
  190 + }
  191 + }
  192 +
  193 + /* */
  194 + static filename_t gen_filename_by_daliy(const filename_t &filename, const tm &now_tm)
  195 + {
  196 + filename_t basename, ext;
  197 + std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
  198 + return fmt::format(SPDLOG_FILENAME_T("{}_{:04d}_{:02d}_{:02d}{}"),
  199 + basename,
  200 + now_tm.tm_year + 1900,
  201 + now_tm.tm_mon + 1,
  202 + now_tm.tm_mday,
  203 + ext);
  204 + }
  205 +
  206 + //
  207 + static filename_t gen_filename_by_filesize(const filename_t &filename, const tm &now_tm, const int &idx)
  208 + {
  209 + filename_t basename, ext;
  210 + std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
  211 + return fmt::format(SPDLOG_FILENAME_T("{}_{:04d}_{:02d}_{:02d}_{:02d}{:02d}{:02d}.{:d}{}"),
  212 + basename,
  213 + now_tm.tm_year + 1900,
  214 + now_tm.tm_mon + 1,
  215 + now_tm.tm_mday,
  216 + now_tm.tm_hour,
  217 + now_tm.tm_min,
  218 + now_tm.tm_sec,
  219 + idx,
  220 + ext);
  221 + }
  222 +
  223 + static bool rename_file_(const filename_t &src_filename, const filename_t &target_filename)
  224 + {
  225 + (void)details::os::remove(target_filename);
  226 + return details::os::rename(src_filename, target_filename) == 0;
  227 + }
  228 +
  229 + filename_t base_filename_;
  230 + log_clock::time_point rotation_tp_;
  231 + details::file_helper file_helper_;
  232 + std::size_t max_size_;
  233 + std::size_t max_keep_days_;
  234 + std::size_t current_size_;
  235 + // std::vector<<std::set<filename_t>> filespath_q_;
  236 + std::vector<std::set<filename_t>> filespath_q_;
  237 + };
  238 +
  239 + using easy_file_sink_mt = easy_file_sink<std::mutex>;
  240 + using easy_file_sink_st = easy_file_sink<details::null_mutex>;
  241 +
  242 + } // namespace sinks
  243 +
  244 + template<typename Factory = spdlog::synchronous_factory>
  245 + inline std::shared_ptr<logger> easy_logger_mt(
  246 + const std::string &logger_name, const filename_t &filename, size_t max_size, size_t max_keep_days = -1)
  247 + {
  248 + return Factory::template create<sinks::easy_file_sink_mt>(logger_name, filename, max_size, max_keep_days);
  249 + }
  250 +
  251 + template<typename Factory = spdlog::synchronous_factory>
  252 + inline std::shared_ptr<logger> easy_logger_st(
  253 + const std::string &logger_name, const filename_t &filename, size_t max_size, size_t max_keep_days = -1)
  254 + {
  255 + return Factory::template create<sinks::easy_file_sink_st>(logger_name, filename, max_size, max_keep_days);
  256 + }
  257 +
  258 +} // namespace spdlog
  259 +
  260 +
  261 +enum class LogLevel
  262 +{
  263 + SPD_LOG_CLOSE = -1,
  264 + SPD_LOG_TRACE = 0,
  265 + SPD_LOG_DEBUG = 1,
  266 + SPD_LOG_INFO = 2,
  267 + SPD_LOG_WARN = 3,
  268 + SPD_LOG_ERROR = 4,
  269 + SPD_LOG_FATAL = 5,
  270 +};
  271 +
  272 +
  273 +class LoggerGenerator
  274 +{
  275 +public:
  276 + static LoggerGenerator* get_instance()
  277 + {
  278 + static LoggerGenerator logger;
  279 + return &logger;
  280 + }
  281 +
  282 + void destory(LoggerGenerator *ptr)
  283 + {
  284 + if (ptr != nullptr)
  285 + {
  286 + delete ptr;
  287 + ptr = nullptr;
  288 + }
  289 + }
  290 +
  291 + std::shared_ptr<spdlog::logger> gen_logger(const LogLevel &level, const std::string &logger_name,
  292 + const std::string &file_path, size_t max_file_size, size_t max_keep_days)
  293 + {
  294 + spdlog::level::level_enum spd_level;
  295 + if (LogLevel::SPD_LOG_TRACE == level)
  296 + spd_level = spdlog::level::trace;
  297 + else if (LogLevel::SPD_LOG_DEBUG == level)
  298 + spd_level = spdlog::level::debug;
  299 + else if (LogLevel::SPD_LOG_INFO == level)
  300 + spd_level = spdlog::level::info;
  301 + else if (LogLevel::SPD_LOG_WARN == level)
  302 + spd_level = spdlog::level::warn;
  303 + else if (LogLevel::SPD_LOG_ERROR == level)
  304 + spd_level = spdlog::level::err;
  305 + else if (LogLevel::SPD_LOG_FATAL == level)
  306 + spd_level = spdlog::level::critical;
  307 + else if (LogLevel::SPD_LOG_CLOSE == level)
  308 + spd_level = spdlog::level::off;
  309 +
  310 + auto sink_ptr = std::make_shared<spdlog::sinks::easy_file_sink_mt>(file_path, max_file_size, max_keep_days);
  311 + auto logger = std::make_shared<spdlog::logger>(logger_name, sink_ptr);
  312 + logger->set_level(spd_level);
  313 + logger->set_pattern("%s(%#): [%L %D %T.%e %P %t %!] %v");
  314 +
  315 + return logger;
  316 + }
  317 +
  318 + void set_default_logger(const LogLevel &level, const std::string &logger_name,
  319 + const std::string &file_name, size_t max_file_size, size_t max_keep_days)
  320 + {
  321 +
  322 + auto logger = gen_logger(level, logger_name, file_name, max_file_size, max_keep_days);
  323 + spdlog::set_default_logger(logger);
  324 + spdlog::set_level(logger->level());
  325 + spdlog::set_pattern("%s(%#): [%L %D %T.%e %P %t %!] %v");
  326 +
  327 + spdlog::flush_on(spdlog::level::trace);
  328 + spdlog::flush_every(std::chrono::seconds(1));
  329 + }
  330 +
  331 +};
  332 +
  333 +
  334 +static void set_default_logger(const LogLevel &level, const std::string &logger_name,
  335 + const std::string &file_path, size_t max_file_size, size_t max_keep_days)
  336 +{
  337 + static LoggerGenerator loggerGenerator;
  338 + loggerGenerator.set_default_logger(level, logger_name, file_path, max_file_size, max_keep_days);
  339 +}
  340 +
  341 +
  342 +static std::shared_ptr<spdlog::logger> get_simple_logger(const LogLevel &level, const std::string &logger_name,
  343 + const std::string &file_path, size_t max_file_size, size_t max_keep_days)
  344 +{
  345 + static LoggerGenerator loggerGenerator;
  346 + return loggerGenerator.gen_logger(level, logger_name, file_path, max_file_size, max_keep_days);
  347 +}
  348 +
  349 +
  350 +#endif // __LOGGER_HPP__
0 351 \ No newline at end of file
... ...
sip/WebSocketServer.cpp 0 → 100644
  1 +++ a/sip/WebSocketServer.cpp
  1 +#include <iostream>
  2 +
  3 +#include "WebSocketServer.h"
  4 +#include "./Utils/StringTools.hpp"
  5 +#include "ConfigParser.hpp"
  6 +#include "./Utils/logger.hpp"
  7 +
  8 +using websocketpp::lib::placeholders::_1;
  9 +using websocketpp::lib::placeholders::_2;
  10 +using websocketpp::lib::bind;
  11 +
  12 +
  13 +WebSocketServer::WebSocketServer(/* args */)
  14 +{
  15 +}
  16 +
  17 +WebSocketServer::~WebSocketServer()
  18 +{
  19 +}
  20 +
  21 +int WebSocketServer::parse_invite(vector<string>& vec_msg, std::string ip) {
  22 + string net_type = "";
  23 + if (StringTools::to_lower(vec_msg[1]) == "udp") {
  24 + net_type = "udp";
  25 + } else if (StringTools::to_lower(vec_msg[1]) == "tcp") {
  26 + net_type = "tcp";
  27 + }
  28 + std::cout << "net type: " << vec_msg[1] << std::endl;
  29 +
  30 + std::cout << "invite " << vec_msg[2] << ":" << vec_msg[3] << std::endl;
  31 + int ret = -100;
  32 + if ("udp" == net_type) {
  33 + ret = sip_server.RequestInvite_UDP(vec_msg[2].c_str(), ip.c_str(), atoi(vec_msg[3].c_str()));
  34 + // ret = sip_server.RequestInvite_UDP(vec_data[0].c_str(), "176.10.0.3", 30026);
  35 + } else if ("tcp" == net_type)
  36 + {
  37 + /* code */
  38 + }
  39 +
  40 + return ret;
  41 +}
  42 +
  43 +string WebSocketServer::get_ip_from_hdl(websocketpp::connection_hdl hdl) {
  44 + server::connection_ptr con = ws_server.get_con_from_hdl(hdl);
  45 + // 获取客户端IP地址
  46 + auto addr = con->get_socket().remote_endpoint().address().to_string();
  47 + std::string ip = addr.substr(addr.find_last_of(":")+1);
  48 + std::cout << "ip:" << ip << endl;
  49 + return ip;
  50 +}
  51 +
  52 +int WebSocketServer::msg_parser(websocketpp::connection_hdl hdl, string msg) {
  53 + vector<string> vec_msg = StringTools::split(msg, "|");
  54 + if (vec_msg.size() <= 0) {
  55 + return -1;
  56 + }
  57 +
  58 + string ip = get_ip_from_hdl(hdl);
  59 +
  60 + int ret = -100;
  61 + if ( StringTools::to_lower(vec_msg[0]) == "invite") {
  62 + ret = parse_invite(vec_msg, ip);
  63 + } else if ( StringTools::to_lower(vec_msg[0]) == "bye") {
  64 + if (StringTools::to_lower(vec_msg[1]) == "invite") {
  65 + std::cout << "bye invite: " << vec_msg[2] << std::endl;
  66 + ret = sip_server.ByeInvite(vec_msg[2], ip, atoi(vec_msg[3].c_str()));
  67 + }
  68 + } else {
  69 + std::cout << "on_message called with hdl: " << hdl.lock().get()
  70 + << " and message: " << msg
  71 + << std::endl;
  72 + }
  73 +
  74 + return ret;
  75 +}
  76 +
  77 +// Define a callback to handle incoming messages
  78 +void WebSocketServer::on_message(websocketpp::connection_hdl hdl, message_ptr msg) {
  79 +
  80 + if (msg->get_payload() == "stop-listening") {
  81 + ws_server.stop_listening();
  82 + return;
  83 + }
  84 +
  85 + try {
  86 + int ret = msg_parser(hdl,msg->get_payload());
  87 + // 返回执行结果
  88 + ws_server.send(hdl, to_string(ret), websocketpp::frame::opcode::text);
  89 + } catch (websocketpp::exception const & e) {
  90 + std::cout << "Echo failed because: " << "(" << e.what() << ")" << std::endl;
  91 + ws_server.send(hdl, to_string(-101), websocketpp::frame::opcode::text);
  92 + }
  93 +}
  94 +
  95 +void WebSocketServer::on_open(websocketpp::connection_hdl hdl)
  96 +{
  97 + // std::string msg = "hello";
  98 + // c.send(hdl, msg, websocketpp::frame::opcode::text);
  99 + // c.get_alog().write(websocketpp::log::alevel::app, "Tx: " + msg);
  100 + std::cout << "连接sip服务器成功!" << std::endl;
  101 +}
  102 +
  103 +void WebSocketServer::on_fail(websocketpp::connection_hdl h){
  104 + std::cout << "连接sip服务器失败!" << std::endl;
  105 +}
  106 +
  107 +void WebSocketServer::on_close(websocketpp::connection_hdl h){
  108 + std::cout << "on_close" << std::endl;
  109 +}
  110 +
  111 +
  112 +void WebSocketServer::start() {
  113 + ConfigParser* pConfig = ConfigParser::getInstance();
  114 + if (!pConfig->init())
  115 + {
  116 + LOG_ERROR("get config failed!");
  117 + return;
  118 + }
  119 +
  120 + ServerInfo info = pConfig->getServerCfg();
  121 +
  122 + sip_server.Init(&info);
  123 +
  124 + try {
  125 + // Set logging settings
  126 + ws_server.set_access_channels(websocketpp::log::alevel::all);
  127 + ws_server.clear_access_channels(websocketpp::log::alevel::frame_payload);
  128 +
  129 + // Initialize Asio
  130 + ws_server.init_asio();
  131 +
  132 + // Register our message handler
  133 + ws_server.set_message_handler(bind(&WebSocketServer::on_message,this,::_1,::_2));
  134 + ws_server.set_open_handler(bind(&WebSocketServer::on_open, this, _1));
  135 + ws_server.set_fail_handler(bind(&WebSocketServer::on_fail, this, _1));
  136 + ws_server.set_close_handler(bind(&WebSocketServer::on_close, this, _1));
  137 +
  138 + // Listen on port 9002
  139 + int port = info.getWsPort();
  140 + std::cout << "websocket server listen:" << port << std::endl;
  141 + ws_server.listen(port);
  142 +
  143 + // Start the server accept loop
  144 + ws_server.start_accept();
  145 +
  146 + // Start the ASIO io_service run loop
  147 + ws_server.run();
  148 + } catch (websocketpp::exception const & e) {
  149 + std::cout << e.what() << std::endl;
  150 + } catch (...) {
  151 + std::cout << "other exception" << std::endl;
  152 + }
  153 +}
... ...
sip/WebSocketServer.h 0 → 100644
  1 +++ a/sip/WebSocketServer.h
  1 +#include <websocketpp/config/asio_no_tls.hpp>
  2 +#include <websocketpp/server.hpp>
  3 +
  4 +#include "SipServer.h"
  5 +
  6 +typedef websocketpp::server<websocketpp::config::asio> server;
  7 +
  8 +// pull out the type of messages sent by our config
  9 +typedef server::message_ptr message_ptr;
  10 +
  11 +
  12 +class WebSocketServer
  13 +{
  14 +public:
  15 + WebSocketServer(/* args */);
  16 + ~WebSocketServer();
  17 +
  18 + // Define a callback to handle incoming messages
  19 + void on_message(websocketpp::connection_hdl hdl, message_ptr msg) ;
  20 +
  21 + void on_open(websocketpp::connection_hdl hdl);
  22 + void on_close(websocketpp::connection_hdl hdl);
  23 + void on_fail(websocketpp::connection_hdl hdl);
  24 +
  25 + void start();
  26 +
  27 +private:
  28 + int msg_parser(websocketpp::connection_hdl hdl, string msg);
  29 + int parse_invite(vector<string>& vec_msg, std::string ip);
  30 +
  31 + string get_ip_from_hdl(websocketpp::connection_hdl hdl);
  32 +
  33 +private:
  34 + server ws_server;
  35 + SipServer sip_server;
  36 +};
0 37 \ No newline at end of file
... ...
sip/main.cpp 0 → 100644
  1 +++ a/sip/main.cpp
  1 +
  2 +#include "WebSocketServer.h"
  3 +
  4 +
  5 +int main(int argc, char *argv[]) {
  6 +
  7 + // if (argc <= 1)
  8 + // {
  9 + // printf("请输入待获取的摄像头的视频通道编码ID。");
  10 + // return -1;
  11 + // }
  12 +
  13 + WebSocketServer server;
  14 + server.start();
  15 +
  16 + // const char* sipid = "34020000001310000001";
  17 + // const char* sipid = argv[1];
  18 + // char oper = 'g';
  19 + // while (oper != 'q') {
  20 + // oper = getchar();
  21 + // switch (oper)
  22 + // {
  23 + // case 'g':
  24 + // break;
  25 + // case 'i':
  26 + // {
  27 + // pServer->RequestInvite_UDP(sipid, rtp_port);
  28 + // }
  29 + // break;
  30 + // case 'b':
  31 + // break;
  32 + // default:
  33 + // break;
  34 + // }
  35 + // }
  36 +
  37 + return 0;
  38 +}
0 39 \ No newline at end of file
... ...
sip/sip_header.h 0 → 100644
  1 +++ a/sip/sip_header.h
  1 +#ifndef __SIP_HEADER_H__
  2 +#define __SIP_HEADER_H__
  3 +
  4 +#include <string>
  5 +
  6 +using namespace std;
  7 +
  8 +enum EEventType
  9 +{
  10 + EVENT_ON = 0, // ����
  11 + EVENT_OFF, // ����
  12 + EVENT_VLOST, // ��Ƶ��ʧ
  13 + EVENT_DEFECT, // ����
  14 + EVENT_ADD, // ����
  15 + EVENT_DEL, // ɾ��
  16 + EVENT_UPDATE, // ����
  17 +
  18 + EVENT_UNKNOW,
  19 +};
  20 +
  21 +struct DeviceInfo
  22 +{
  23 + EEventType event;
  24 + std::string id;
  25 + std::string name;
  26 + std::string manufacturer;
  27 + std::string model;
  28 + std::string owner;
  29 + std::string civil;
  30 + std::string block;
  31 + std::string address;
  32 + std::string safetyway;
  33 + std::string registerway;
  34 + std::string certnum;
  35 + std::string certifiable;
  36 + std::string errcode;
  37 + std::string secrecy;
  38 + std::string parental;
  39 + std::string parentid;
  40 + std::string endtime;
  41 + std::string ip;
  42 + std::string port;
  43 + std::string password;
  44 + std::string status;
  45 + std::string longitude;
  46 + std::string latitude;
  47 + std::string ptz;
  48 + std::string position;
  49 + std::string room;
  50 + std::string use;
  51 + std::string supplylight;
  52 + std::string direction;
  53 + std::string resolution;
  54 + std::string businessgroup;
  55 +};
  56 +
  57 +class ServerInfo {
  58 +public:
  59 + ServerInfo() {}
  60 +
  61 + ServerInfo(string ua,string nonce, string ip, int port,
  62 + string sipId, string sipRealm, string sipPass, int sipTimeout, int sipExpiry, int minRtpPort, int maxRtpPort, int ws_port):
  63 + mUa(ua),mNonce(nonce),mIp(ip),mPort(port),mSipId(sipId),
  64 + mSipRealm(sipRealm),mSipPass(sipPass),mSipTimeout(sipTimeout),
  65 + mSipExpiry(sipExpiry),mMinRtpPort(minRtpPort),mMaxRtpPort(maxRtpPort),mWsPort(ws_port){}
  66 +
  67 + ~ServerInfo() = default;
  68 +public:
  69 + string getUa() const{
  70 + return mUa;
  71 + }
  72 + void setUa(string ua) {
  73 + mUa = ua;
  74 + }
  75 + string getNonce() const{
  76 + return mNonce;
  77 + }
  78 + void setNonce(string nonce) {
  79 + mNonce = nonce;
  80 + }
  81 + string getIp() const{
  82 + return mIp;
  83 + }
  84 + void setIp(string s) {
  85 + mIp = s;
  86 + }
  87 + int getPort() const {
  88 + return mPort;
  89 + }
  90 + void setPort(int i) {
  91 + mPort = i;
  92 + }
  93 +
  94 + string getSipId() const{
  95 + return mSipId;
  96 + }
  97 + void setSipId(string s) {
  98 + mSipId = s;
  99 + }
  100 + string getSipRealm() const{
  101 + return mSipRealm;
  102 + }
  103 + void setSipRealm(string s) {
  104 + mSipRealm = s;
  105 + }
  106 + string getSipPass() const{
  107 + return mSipPass;
  108 + }
  109 + void setSipPass(string s) {
  110 + mSipPass = s;
  111 + }
  112 + int getTimeout() const {
  113 + return mSipTimeout;
  114 + }
  115 + void setTimeout(int i) {
  116 + mSipTimeout = i;
  117 + }
  118 + int getExpiry() const {
  119 + return mSipExpiry;
  120 + }
  121 + void setExpiry(int i) {
  122 + mSipExpiry = i;
  123 + }
  124 + int getMinRtpPort() const {
  125 + return mMinRtpPort;
  126 + }
  127 + void setMinRtpPort(int i) {
  128 + mMinRtpPort = i;
  129 + }
  130 + int getMaxRtpPort() const {
  131 + return mMaxRtpPort;
  132 + }
  133 + void setMaxRtpPort(int i) {
  134 + mMaxRtpPort = i;
  135 + }
  136 + int getWsPort() const {
  137 + return mWsPort;
  138 + }
  139 + void setWsPort(int i) {
  140 + mWsPort = i;
  141 + }
  142 +
  143 +private:
  144 + string mUa;
  145 + string mNonce;//SIP服务随机数值
  146 + string mIp;//SIP服务IP
  147 + int mPort{0};//SIP服务端口
  148 + string mSipId; //SIP服务器ID
  149 + string mSipRealm;//SIP服务器域
  150 + string mSipPass;//SIP password
  151 + int mSipTimeout; //SIP timeout
  152 + int mSipExpiry;// SIP到期
  153 + int mMinRtpPort;
  154 + int mMaxRtpPort;
  155 + int mWsPort;
  156 +};
  157 +
  158 +struct CallInfo {
  159 + int cid{-1};
  160 + int did{-1};
  161 +};
  162 +
  163 +#endif //__SIP_HEADER_H__
0 164 \ No newline at end of file
... ...
sip/tinyxml2/tinyxml2.cpp 0 → 100644
  1 +++ a/sip/tinyxml2/tinyxml2.cpp
  1 +/*
  2 +Original code by Lee Thomason (www.grinninglizard.com)
  3 +
  4 +This software is provided 'as-is', without any express or implied
  5 +warranty. In no event will the authors be held liable for any
  6 +damages arising from the use of this software.
  7 +
  8 +Permission is granted to anyone to use this software for any
  9 +purpose, including commercial applications, and to alter it and
  10 +redistribute it freely, subject to the following restrictions:
  11 +
  12 +1. The origin of this software must not be misrepresented; you must
  13 +not claim that you wrote the original software. If you use this
  14 +software in a product, an acknowledgment in the product documentation
  15 +would be appreciated but is not required.
  16 +
  17 +2. Altered source versions must be plainly marked as such, and
  18 +must not be misrepresented as being the original software.
  19 +
  20 +3. This notice may not be removed or altered from any source
  21 +distribution.
  22 +*/
  23 +
  24 +#include "tinyxml2.h"
  25 +
  26 +#include <new> // yes, this one new style header, is in the Android SDK.
  27 +#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__)
  28 +# include <stddef.h>
  29 +# include <stdarg.h>
  30 +#else
  31 +# include <cstddef>
  32 +# include <cstdarg>
  33 +#endif
  34 +
  35 +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
  36 + // Microsoft Visual Studio, version 2005 and higher. Not WinCE.
  37 + /*int _snprintf_s(
  38 + char *buffer,
  39 + size_t sizeOfBuffer,
  40 + size_t count,
  41 + const char *format [,
  42 + argument] ...
  43 + );*/
  44 + static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... )
  45 + {
  46 + va_list va;
  47 + va_start( va, format );
  48 + int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
  49 + va_end( va );
  50 + return result;
  51 + }
  52 +
  53 + static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va )
  54 + {
  55 + int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
  56 + return result;
  57 + }
  58 +
  59 + #define TIXML_VSCPRINTF _vscprintf
  60 + #define TIXML_SSCANF sscanf_s
  61 +#elif defined _MSC_VER
  62 + // Microsoft Visual Studio 2003 and earlier or WinCE
  63 + #define TIXML_SNPRINTF _snprintf
  64 + #define TIXML_VSNPRINTF _vsnprintf
  65 + #define TIXML_SSCANF sscanf
  66 + #if (_MSC_VER < 1400 ) && (!defined WINCE)
  67 + // Microsoft Visual Studio 2003 and not WinCE.
  68 + #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have.
  69 + #else
  70 + // Microsoft Visual Studio 2003 and earlier or WinCE.
  71 + static inline int TIXML_VSCPRINTF( const char* format, va_list va )
  72 + {
  73 + int len = 512;
  74 + for (;;) {
  75 + len = len*2;
  76 + char* str = new char[len]();
  77 + const int required = _vsnprintf(str, len, format, va);
  78 + delete[] str;
  79 + if ( required != -1 ) {
  80 + TIXMLASSERT( required >= 0 );
  81 + len = required;
  82 + break;
  83 + }
  84 + }
  85 + TIXMLASSERT( len >= 0 );
  86 + return len;
  87 + }
  88 + #endif
  89 +#else
  90 + // GCC version 3 and higher
  91 + //#warning( "Using sn* functions." )
  92 + #define TIXML_SNPRINTF snprintf
  93 + #define TIXML_VSNPRINTF vsnprintf
  94 + static inline int TIXML_VSCPRINTF( const char* format, va_list va )
  95 + {
  96 + int len = vsnprintf( 0, 0, format, va );
  97 + TIXMLASSERT( len >= 0 );
  98 + return len;
  99 + }
  100 + #define TIXML_SSCANF sscanf
  101 +#endif
  102 +
  103 +
  104 +static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
  105 +static const char LF = LINE_FEED;
  106 +static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
  107 +static const char CR = CARRIAGE_RETURN;
  108 +static const char SINGLE_QUOTE = '\'';
  109 +static const char DOUBLE_QUOTE = '\"';
  110 +
  111 +// Bunch of unicode info at:
  112 +// http://www.unicode.org/faq/utf_bom.html
  113 +// ef bb bf (Microsoft "lead bytes") - designates UTF-8
  114 +
  115 +static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
  116 +static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
  117 +static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
  118 +
  119 +namespace tinyxml2
  120 +{
  121 +
  122 +struct Entity {
  123 + const char* pattern;
  124 + int length;
  125 + char value;
  126 +};
  127 +
  128 +static const int NUM_ENTITIES = 5;
  129 +static const Entity entities[NUM_ENTITIES] = {
  130 + { "quot", 4, DOUBLE_QUOTE },
  131 + { "amp", 3, '&' },
  132 + { "apos", 4, SINGLE_QUOTE },
  133 + { "lt", 2, '<' },
  134 + { "gt", 2, '>' }
  135 +};
  136 +
  137 +
  138 +StrPair::~StrPair()
  139 +{
  140 + Reset();
  141 +}
  142 +
  143 +
  144 +void StrPair::TransferTo( StrPair* other )
  145 +{
  146 + if ( this == other ) {
  147 + return;
  148 + }
  149 + // This in effect implements the assignment operator by "moving"
  150 + // ownership (as in auto_ptr).
  151 +
  152 + TIXMLASSERT( other != 0 );
  153 + TIXMLASSERT( other->_flags == 0 );
  154 + TIXMLASSERT( other->_start == 0 );
  155 + TIXMLASSERT( other->_end == 0 );
  156 +
  157 + other->Reset();
  158 +
  159 + other->_flags = _flags;
  160 + other->_start = _start;
  161 + other->_end = _end;
  162 +
  163 + _flags = 0;
  164 + _start = 0;
  165 + _end = 0;
  166 +}
  167 +
  168 +
  169 +void StrPair::Reset()
  170 +{
  171 + if ( _flags & NEEDS_DELETE ) {
  172 + delete [] _start;
  173 + }
  174 + _flags = 0;
  175 + _start = 0;
  176 + _end = 0;
  177 +}
  178 +
  179 +
  180 +void StrPair::SetStr( const char* str, int flags )
  181 +{
  182 + TIXMLASSERT( str );
  183 + Reset();
  184 + size_t len = strlen( str );
  185 + TIXMLASSERT( _start == 0 );
  186 + _start = new char[ len+1 ];
  187 + memcpy( _start, str, len+1 );
  188 + _end = _start + len;
  189 + _flags = flags | NEEDS_DELETE;
  190 +}
  191 +
  192 +
  193 +char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLineNumPtr )
  194 +{
  195 + TIXMLASSERT( p );
  196 + TIXMLASSERT( endTag && *endTag );
  197 + TIXMLASSERT(curLineNumPtr);
  198 +
  199 + char* start = p;
  200 + char endChar = *endTag;
  201 + size_t length = strlen( endTag );
  202 +
  203 + // Inner loop of text parsing.
  204 + while ( *p ) {
  205 + if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
  206 + Set( start, p, strFlags );
  207 + return p + length;
  208 + } else if (*p == '\n') {
  209 + ++(*curLineNumPtr);
  210 + }
  211 + ++p;
  212 + TIXMLASSERT( p );
  213 + }
  214 + return 0;
  215 +}
  216 +
  217 +
  218 +char* StrPair::ParseName( char* p )
  219 +{
  220 + if ( !p || !(*p) ) {
  221 + return 0;
  222 + }
  223 + if ( !XMLUtil::IsNameStartChar( *p ) ) {
  224 + return 0;
  225 + }
  226 +
  227 + char* const start = p;
  228 + ++p;
  229 + while ( *p && XMLUtil::IsNameChar( *p ) ) {
  230 + ++p;
  231 + }
  232 +
  233 + Set( start, p, 0 );
  234 + return p;
  235 +}
  236 +
  237 +
  238 +void StrPair::CollapseWhitespace()
  239 +{
  240 + // Adjusting _start would cause undefined behavior on delete[]
  241 + TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 );
  242 + // Trim leading space.
  243 + _start = XMLUtil::SkipWhiteSpace( _start, 0 );
  244 +
  245 + if ( *_start ) {
  246 + const char* p = _start; // the read pointer
  247 + char* q = _start; // the write pointer
  248 +
  249 + while( *p ) {
  250 + if ( XMLUtil::IsWhiteSpace( *p )) {
  251 + p = XMLUtil::SkipWhiteSpace( p, 0 );
  252 + if ( *p == 0 ) {
  253 + break; // don't write to q; this trims the trailing space.
  254 + }
  255 + *q = ' ';
  256 + ++q;
  257 + }
  258 + *q = *p;
  259 + ++q;
  260 + ++p;
  261 + }
  262 + *q = 0;
  263 + }
  264 +}
  265 +
  266 +
  267 +const char* StrPair::GetStr()
  268 +{
  269 + TIXMLASSERT( _start );
  270 + TIXMLASSERT( _end );
  271 + if ( _flags & NEEDS_FLUSH ) {
  272 + *_end = 0;
  273 + _flags ^= NEEDS_FLUSH;
  274 +
  275 + if ( _flags ) {
  276 + const char* p = _start; // the read pointer
  277 + char* q = _start; // the write pointer
  278 +
  279 + while( p < _end ) {
  280 + if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
  281 + // CR-LF pair becomes LF
  282 + // CR alone becomes LF
  283 + // LF-CR becomes LF
  284 + if ( *(p+1) == LF ) {
  285 + p += 2;
  286 + }
  287 + else {
  288 + ++p;
  289 + }
  290 + *q = LF;
  291 + ++q;
  292 + }
  293 + else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
  294 + if ( *(p+1) == CR ) {
  295 + p += 2;
  296 + }
  297 + else {
  298 + ++p;
  299 + }
  300 + *q = LF;
  301 + ++q;
  302 + }
  303 + else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
  304 + // Entities handled by tinyXML2:
  305 + // - special entities in the entity table [in/out]
  306 + // - numeric character reference [in]
  307 + // &#20013; or &#x4e2d;
  308 +
  309 + if ( *(p+1) == '#' ) {
  310 + const int buflen = 10;
  311 + char buf[buflen] = { 0 };
  312 + int len = 0;
  313 + char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
  314 + if ( adjusted == 0 ) {
  315 + *q = *p;
  316 + ++p;
  317 + ++q;
  318 + }
  319 + else {
  320 + TIXMLASSERT( 0 <= len && len <= buflen );
  321 + TIXMLASSERT( q + len <= adjusted );
  322 + p = adjusted;
  323 + memcpy( q, buf, len );
  324 + q += len;
  325 + }
  326 + }
  327 + else {
  328 + bool entityFound = false;
  329 + for( int i = 0; i < NUM_ENTITIES; ++i ) {
  330 + const Entity& entity = entities[i];
  331 + if ( strncmp( p + 1, entity.pattern, entity.length ) == 0
  332 + && *( p + entity.length + 1 ) == ';' ) {
  333 + // Found an entity - convert.
  334 + *q = entity.value;
  335 + ++q;
  336 + p += entity.length + 2;
  337 + entityFound = true;
  338 + break;
  339 + }
  340 + }
  341 + if ( !entityFound ) {
  342 + // fixme: treat as error?
  343 + ++p;
  344 + ++q;
  345 + }
  346 + }
  347 + }
  348 + else {
  349 + *q = *p;
  350 + ++p;
  351 + ++q;
  352 + }
  353 + }
  354 + *q = 0;
  355 + }
  356 + // The loop below has plenty going on, and this
  357 + // is a less useful mode. Break it out.
  358 + if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) {
  359 + CollapseWhitespace();
  360 + }
  361 + _flags = (_flags & NEEDS_DELETE);
  362 + }
  363 + TIXMLASSERT( _start );
  364 + return _start;
  365 +}
  366 +
  367 +
  368 +
  369 +
  370 +// --------- XMLUtil ----------- //
  371 +
  372 +const char* XMLUtil::writeBoolTrue = "true";
  373 +const char* XMLUtil::writeBoolFalse = "false";
  374 +
  375 +void XMLUtil::SetBoolSerialization(const char* writeTrue, const char* writeFalse)
  376 +{
  377 + static const char* defTrue = "true";
  378 + static const char* defFalse = "false";
  379 +
  380 + writeBoolTrue = (writeTrue) ? writeTrue : defTrue;
  381 + writeBoolFalse = (writeFalse) ? writeFalse : defFalse;
  382 +}
  383 +
  384 +
  385 +const char* XMLUtil::ReadBOM( const char* p, bool* bom )
  386 +{
  387 + TIXMLASSERT( p );
  388 + TIXMLASSERT( bom );
  389 + *bom = false;
  390 + const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
  391 + // Check for BOM:
  392 + if ( *(pu+0) == TIXML_UTF_LEAD_0
  393 + && *(pu+1) == TIXML_UTF_LEAD_1
  394 + && *(pu+2) == TIXML_UTF_LEAD_2 ) {
  395 + *bom = true;
  396 + p += 3;
  397 + }
  398 + TIXMLASSERT( p );
  399 + return p;
  400 +}
  401 +
  402 +
  403 +void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
  404 +{
  405 + const unsigned long BYTE_MASK = 0xBF;
  406 + const unsigned long BYTE_MARK = 0x80;
  407 + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
  408 +
  409 + if (input < 0x80) {
  410 + *length = 1;
  411 + }
  412 + else if ( input < 0x800 ) {
  413 + *length = 2;
  414 + }
  415 + else if ( input < 0x10000 ) {
  416 + *length = 3;
  417 + }
  418 + else if ( input < 0x200000 ) {
  419 + *length = 4;
  420 + }
  421 + else {
  422 + *length = 0; // This code won't convert this correctly anyway.
  423 + return;
  424 + }
  425 +
  426 + output += *length;
  427 +
  428 + // Scary scary fall throughs are annotated with carefully designed comments
  429 + // to suppress compiler warnings such as -Wimplicit-fallthrough in gcc
  430 + switch (*length) {
  431 + case 4:
  432 + --output;
  433 + *output = (char)((input | BYTE_MARK) & BYTE_MASK);
  434 + input >>= 6;
  435 + //fall through
  436 + case 3:
  437 + --output;
  438 + *output = (char)((input | BYTE_MARK) & BYTE_MASK);
  439 + input >>= 6;
  440 + //fall through
  441 + case 2:
  442 + --output;
  443 + *output = (char)((input | BYTE_MARK) & BYTE_MASK);
  444 + input >>= 6;
  445 + //fall through
  446 + case 1:
  447 + --output;
  448 + *output = (char)(input | FIRST_BYTE_MARK[*length]);
  449 + break;
  450 + default:
  451 + TIXMLASSERT( false );
  452 + }
  453 +}
  454 +
  455 +
  456 +const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
  457 +{
  458 + // Presume an entity, and pull it out.
  459 + *length = 0;
  460 +
  461 + if ( *(p+1) == '#' && *(p+2) ) {
  462 + unsigned long ucs = 0;
  463 + TIXMLASSERT( sizeof( ucs ) >= 4 );
  464 + ptrdiff_t delta = 0;
  465 + unsigned mult = 1;
  466 + static const char SEMICOLON = ';';
  467 +
  468 + if ( *(p+2) == 'x' ) {
  469 + // Hexadecimal.
  470 + const char* q = p+3;
  471 + if ( !(*q) ) {
  472 + return 0;
  473 + }
  474 +
  475 + q = strchr( q, SEMICOLON );
  476 +
  477 + if ( !q ) {
  478 + return 0;
  479 + }
  480 + TIXMLASSERT( *q == SEMICOLON );
  481 +
  482 + delta = q-p;
  483 + --q;
  484 +
  485 + while ( *q != 'x' ) {
  486 + unsigned int digit = 0;
  487 +
  488 + if ( *q >= '0' && *q <= '9' ) {
  489 + digit = *q - '0';
  490 + }
  491 + else if ( *q >= 'a' && *q <= 'f' ) {
  492 + digit = *q - 'a' + 10;
  493 + }
  494 + else if ( *q >= 'A' && *q <= 'F' ) {
  495 + digit = *q - 'A' + 10;
  496 + }
  497 + else {
  498 + return 0;
  499 + }
  500 + TIXMLASSERT( digit < 16 );
  501 + TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
  502 + const unsigned int digitScaled = mult * digit;
  503 + TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
  504 + ucs += digitScaled;
  505 + TIXMLASSERT( mult <= UINT_MAX / 16 );
  506 + mult *= 16;
  507 + --q;
  508 + }
  509 + }
  510 + else {
  511 + // Decimal.
  512 + const char* q = p+2;
  513 + if ( !(*q) ) {
  514 + return 0;
  515 + }
  516 +
  517 + q = strchr( q, SEMICOLON );
  518 +
  519 + if ( !q ) {
  520 + return 0;
  521 + }
  522 + TIXMLASSERT( *q == SEMICOLON );
  523 +
  524 + delta = q-p;
  525 + --q;
  526 +
  527 + while ( *q != '#' ) {
  528 + if ( *q >= '0' && *q <= '9' ) {
  529 + const unsigned int digit = *q - '0';
  530 + TIXMLASSERT( digit < 10 );
  531 + TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
  532 + const unsigned int digitScaled = mult * digit;
  533 + TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
  534 + ucs += digitScaled;
  535 + }
  536 + else {
  537 + return 0;
  538 + }
  539 + TIXMLASSERT( mult <= UINT_MAX / 10 );
  540 + mult *= 10;
  541 + --q;
  542 + }
  543 + }
  544 + // convert the UCS to UTF-8
  545 + ConvertUTF32ToUTF8( ucs, value, length );
  546 + return p + delta + 1;
  547 + }
  548 + return p+1;
  549 +}
  550 +
  551 +
  552 +void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
  553 +{
  554 + TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
  555 +}
  556 +
  557 +
  558 +void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
  559 +{
  560 + TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
  561 +}
  562 +
  563 +
  564 +void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
  565 +{
  566 + TIXML_SNPRINTF( buffer, bufferSize, "%s", v ? writeBoolTrue : writeBoolFalse);
  567 +}
  568 +
  569 +/*
  570 + ToStr() of a number is a very tricky topic.
  571 + https://github.com/leethomason/tinyxml2/issues/106
  572 +*/
  573 +void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
  574 +{
  575 + TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v );
  576 +}
  577 +
  578 +
  579 +void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
  580 +{
  581 + TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v );
  582 +}
  583 +
  584 +
  585 +void XMLUtil::ToStr(int64_t v, char* buffer, int bufferSize)
  586 +{
  587 + // horrible syntax trick to make the compiler happy about %lld
  588 + TIXML_SNPRINTF(buffer, bufferSize, "%lld", (long long)v);
  589 +}
  590 +
  591 +
  592 +bool XMLUtil::ToInt( const char* str, int* value )
  593 +{
  594 + if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
  595 + return true;
  596 + }
  597 + return false;
  598 +}
  599 +
  600 +bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
  601 +{
  602 + if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
  603 + return true;
  604 + }
  605 + return false;
  606 +}
  607 +
  608 +bool XMLUtil::ToBool( const char* str, bool* value )
  609 +{
  610 + int ival = 0;
  611 + if ( ToInt( str, &ival )) {
  612 + *value = (ival==0) ? false : true;
  613 + return true;
  614 + }
  615 + if ( StringEqual( str, "true" ) ) {
  616 + *value = true;
  617 + return true;
  618 + }
  619 + else if ( StringEqual( str, "false" ) ) {
  620 + *value = false;
  621 + return true;
  622 + }
  623 + return false;
  624 +}
  625 +
  626 +
  627 +bool XMLUtil::ToFloat( const char* str, float* value )
  628 +{
  629 + if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
  630 + return true;
  631 + }
  632 + return false;
  633 +}
  634 +
  635 +
  636 +bool XMLUtil::ToDouble( const char* str, double* value )
  637 +{
  638 + if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
  639 + return true;
  640 + }
  641 + return false;
  642 +}
  643 +
  644 +
  645 +bool XMLUtil::ToInt64(const char* str, int64_t* value)
  646 +{
  647 + long long v = 0; // horrible syntax trick to make the compiler happy about %lld
  648 + if (TIXML_SSCANF(str, "%lld", &v) == 1) {
  649 + *value = (int64_t)v;
  650 + return true;
  651 + }
  652 + return false;
  653 +}
  654 +
  655 +
  656 +char* XMLDocument::Identify( char* p, XMLNode** node )
  657 +{
  658 + TIXMLASSERT( node );
  659 + TIXMLASSERT( p );
  660 + char* const start = p;
  661 + int const startLine = _parseCurLineNum;
  662 + p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
  663 + if( !*p ) {
  664 + *node = 0;
  665 + TIXMLASSERT( p );
  666 + return p;
  667 + }
  668 +
  669 + // These strings define the matching patterns:
  670 + static const char* xmlHeader = { "<?" };
  671 + static const char* commentHeader = { "<!--" };
  672 + static const char* cdataHeader = { "<![CDATA[" };
  673 + static const char* dtdHeader = { "<!" };
  674 + static const char* elementHeader = { "<" }; // and a header for everything else; check last.
  675 +
  676 + static const int xmlHeaderLen = 2;
  677 + static const int commentHeaderLen = 4;
  678 + static const int cdataHeaderLen = 9;
  679 + static const int dtdHeaderLen = 2;
  680 + static const int elementHeaderLen = 1;
  681 +
  682 + TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
  683 + TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
  684 + XMLNode* returnNode = 0;
  685 + if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
  686 + returnNode = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
  687 + returnNode->_parseLineNum = _parseCurLineNum;
  688 + p += xmlHeaderLen;
  689 + }
  690 + else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
  691 + returnNode = CreateUnlinkedNode<XMLComment>( _commentPool );
  692 + returnNode->_parseLineNum = _parseCurLineNum;
  693 + p += commentHeaderLen;
  694 + }
  695 + else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
  696 + XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
  697 + returnNode = text;
  698 + returnNode->_parseLineNum = _parseCurLineNum;
  699 + p += cdataHeaderLen;
  700 + text->SetCData( true );
  701 + }
  702 + else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
  703 + returnNode = CreateUnlinkedNode<XMLUnknown>( _commentPool );
  704 + returnNode->_parseLineNum = _parseCurLineNum;
  705 + p += dtdHeaderLen;
  706 + }
  707 + else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
  708 + returnNode = CreateUnlinkedNode<XMLElement>( _elementPool );
  709 + returnNode->_parseLineNum = _parseCurLineNum;
  710 + p += elementHeaderLen;
  711 + }
  712 + else {
  713 + returnNode = CreateUnlinkedNode<XMLText>( _textPool );
  714 + returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character
  715 + p = start; // Back it up, all the text counts.
  716 + _parseCurLineNum = startLine;
  717 + }
  718 +
  719 + TIXMLASSERT( returnNode );
  720 + TIXMLASSERT( p );
  721 + *node = returnNode;
  722 + return p;
  723 +}
  724 +
  725 +
  726 +bool XMLDocument::Accept( XMLVisitor* visitor ) const
  727 +{
  728 + TIXMLASSERT( visitor );
  729 + if ( visitor->VisitEnter( *this ) ) {
  730 + for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
  731 + if ( !node->Accept( visitor ) ) {
  732 + break;
  733 + }
  734 + }
  735 + }
  736 + return visitor->VisitExit( *this );
  737 +}
  738 +
  739 +
  740 +// --------- XMLNode ----------- //
  741 +
  742 +XMLNode::XMLNode( XMLDocument* doc ) :
  743 + _document( doc ),
  744 + _parent( 0 ),
  745 + _value(),
  746 + _parseLineNum( 0 ),
  747 + _firstChild( 0 ), _lastChild( 0 ),
  748 + _prev( 0 ), _next( 0 ),
  749 + _userData( 0 ),
  750 + _memPool( 0 )
  751 +{
  752 +}
  753 +
  754 +
  755 +XMLNode::~XMLNode()
  756 +{
  757 + DeleteChildren();
  758 + if ( _parent ) {
  759 + _parent->Unlink( this );
  760 + }
  761 +}
  762 +
  763 +const char* XMLNode::Value() const
  764 +{
  765 + // Edge case: XMLDocuments don't have a Value. Return null.
  766 + if ( this->ToDocument() )
  767 + return 0;
  768 + return _value.GetStr();
  769 +}
  770 +
  771 +void XMLNode::SetValue( const char* str, bool staticMem )
  772 +{
  773 + if ( staticMem ) {
  774 + _value.SetInternedStr( str );
  775 + }
  776 + else {
  777 + _value.SetStr( str );
  778 + }
  779 +}
  780 +
  781 +XMLNode* XMLNode::DeepClone(XMLDocument* target) const
  782 +{
  783 + XMLNode* clone = this->ShallowClone(target);
  784 + if (!clone) return 0;
  785 +
  786 + for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) {
  787 + XMLNode* childClone = child->DeepClone(target);
  788 + TIXMLASSERT(childClone);
  789 + clone->InsertEndChild(childClone);
  790 + }
  791 + return clone;
  792 +}
  793 +
  794 +void XMLNode::DeleteChildren()
  795 +{
  796 + while( _firstChild ) {
  797 + TIXMLASSERT( _lastChild );
  798 + DeleteChild( _firstChild );
  799 + }
  800 + _firstChild = _lastChild = 0;
  801 +}
  802 +
  803 +
  804 +void XMLNode::Unlink( XMLNode* child )
  805 +{
  806 + TIXMLASSERT( child );
  807 + TIXMLASSERT( child->_document == _document );
  808 + TIXMLASSERT( child->_parent == this );
  809 + if ( child == _firstChild ) {
  810 + _firstChild = _firstChild->_next;
  811 + }
  812 + if ( child == _lastChild ) {
  813 + _lastChild = _lastChild->_prev;
  814 + }
  815 +
  816 + if ( child->_prev ) {
  817 + child->_prev->_next = child->_next;
  818 + }
  819 + if ( child->_next ) {
  820 + child->_next->_prev = child->_prev;
  821 + }
  822 + child->_next = 0;
  823 + child->_prev = 0;
  824 + child->_parent = 0;
  825 +}
  826 +
  827 +
  828 +void XMLNode::DeleteChild( XMLNode* node )
  829 +{
  830 + TIXMLASSERT( node );
  831 + TIXMLASSERT( node->_document == _document );
  832 + TIXMLASSERT( node->_parent == this );
  833 + Unlink( node );
  834 + TIXMLASSERT(node->_prev == 0);
  835 + TIXMLASSERT(node->_next == 0);
  836 + TIXMLASSERT(node->_parent == 0);
  837 + DeleteNode( node );
  838 +}
  839 +
  840 +
  841 +XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
  842 +{
  843 + TIXMLASSERT( addThis );
  844 + if ( addThis->_document != _document ) {
  845 + TIXMLASSERT( false );
  846 + return 0;
  847 + }
  848 + InsertChildPreamble( addThis );
  849 +
  850 + if ( _lastChild ) {
  851 + TIXMLASSERT( _firstChild );
  852 + TIXMLASSERT( _lastChild->_next == 0 );
  853 + _lastChild->_next = addThis;
  854 + addThis->_prev = _lastChild;
  855 + _lastChild = addThis;
  856 +
  857 + addThis->_next = 0;
  858 + }
  859 + else {
  860 + TIXMLASSERT( _firstChild == 0 );
  861 + _firstChild = _lastChild = addThis;
  862 +
  863 + addThis->_prev = 0;
  864 + addThis->_next = 0;
  865 + }
  866 + addThis->_parent = this;
  867 + return addThis;
  868 +}
  869 +
  870 +
  871 +XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
  872 +{
  873 + TIXMLASSERT( addThis );
  874 + if ( addThis->_document != _document ) {
  875 + TIXMLASSERT( false );
  876 + return 0;
  877 + }
  878 + InsertChildPreamble( addThis );
  879 +
  880 + if ( _firstChild ) {
  881 + TIXMLASSERT( _lastChild );
  882 + TIXMLASSERT( _firstChild->_prev == 0 );
  883 +
  884 + _firstChild->_prev = addThis;
  885 + addThis->_next = _firstChild;
  886 + _firstChild = addThis;
  887 +
  888 + addThis->_prev = 0;
  889 + }
  890 + else {
  891 + TIXMLASSERT( _lastChild == 0 );
  892 + _firstChild = _lastChild = addThis;
  893 +
  894 + addThis->_prev = 0;
  895 + addThis->_next = 0;
  896 + }
  897 + addThis->_parent = this;
  898 + return addThis;
  899 +}
  900 +
  901 +
  902 +XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
  903 +{
  904 + TIXMLASSERT( addThis );
  905 + if ( addThis->_document != _document ) {
  906 + TIXMLASSERT( false );
  907 + return 0;
  908 + }
  909 +
  910 + TIXMLASSERT( afterThis );
  911 +
  912 + if ( afterThis->_parent != this ) {
  913 + TIXMLASSERT( false );
  914 + return 0;
  915 + }
  916 + if ( afterThis == addThis ) {
  917 + // Current state: BeforeThis -> AddThis -> OneAfterAddThis
  918 + // Now AddThis must disappear from it's location and then
  919 + // reappear between BeforeThis and OneAfterAddThis.
  920 + // So just leave it where it is.
  921 + return addThis;
  922 + }
  923 +
  924 + if ( afterThis->_next == 0 ) {
  925 + // The last node or the only node.
  926 + return InsertEndChild( addThis );
  927 + }
  928 + InsertChildPreamble( addThis );
  929 + addThis->_prev = afterThis;
  930 + addThis->_next = afterThis->_next;
  931 + afterThis->_next->_prev = addThis;
  932 + afterThis->_next = addThis;
  933 + addThis->_parent = this;
  934 + return addThis;
  935 +}
  936 +
  937 +
  938 +
  939 +
  940 +const XMLElement* XMLNode::FirstChildElement( const char* name ) const
  941 +{
  942 + for( const XMLNode* node = _firstChild; node; node = node->_next ) {
  943 + const XMLElement* element = node->ToElementWithName( name );
  944 + if ( element ) {
  945 + return element;
  946 + }
  947 + }
  948 + return 0;
  949 +}
  950 +
  951 +
  952 +const XMLElement* XMLNode::LastChildElement( const char* name ) const
  953 +{
  954 + for( const XMLNode* node = _lastChild; node; node = node->_prev ) {
  955 + const XMLElement* element = node->ToElementWithName( name );
  956 + if ( element ) {
  957 + return element;
  958 + }
  959 + }
  960 + return 0;
  961 +}
  962 +
  963 +
  964 +const XMLElement* XMLNode::NextSiblingElement( const char* name ) const
  965 +{
  966 + for( const XMLNode* node = _next; node; node = node->_next ) {
  967 + const XMLElement* element = node->ToElementWithName( name );
  968 + if ( element ) {
  969 + return element;
  970 + }
  971 + }
  972 + return 0;
  973 +}
  974 +
  975 +
  976 +const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const
  977 +{
  978 + for( const XMLNode* node = _prev; node; node = node->_prev ) {
  979 + const XMLElement* element = node->ToElementWithName( name );
  980 + if ( element ) {
  981 + return element;
  982 + }
  983 + }
  984 + return 0;
  985 +}
  986 +
  987 +
  988 +char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
  989 +{
  990 + // This is a recursive method, but thinking about it "at the current level"
  991 + // it is a pretty simple flat list:
  992 + // <foo/>
  993 + // <!-- comment -->
  994 + //
  995 + // With a special case:
  996 + // <foo>
  997 + // </foo>
  998 + // <!-- comment -->
  999 + //
  1000 + // Where the closing element (/foo) *must* be the next thing after the opening
  1001 + // element, and the names must match. BUT the tricky bit is that the closing
  1002 + // element will be read by the child.
  1003 + //
  1004 + // 'endTag' is the end tag for this node, it is returned by a call to a child.
  1005 + // 'parentEnd' is the end tag for the parent, which is filled in and returned.
  1006 +
  1007 + XMLDocument::DepthTracker tracker(_document);
  1008 + if (_document->Error())
  1009 + return 0;
  1010 +
  1011 + while( p && *p ) {
  1012 + XMLNode* node = 0;
  1013 +
  1014 + p = _document->Identify( p, &node );
  1015 + TIXMLASSERT( p );
  1016 + if ( node == 0 ) {
  1017 + break;
  1018 + }
  1019 +
  1020 + int initialLineNum = node->_parseLineNum;
  1021 +
  1022 + StrPair endTag;
  1023 + p = node->ParseDeep( p, &endTag, curLineNumPtr );
  1024 + if ( !p ) {
  1025 + DeleteNode( node );
  1026 + if ( !_document->Error() ) {
  1027 + _document->SetError( XML_ERROR_PARSING, initialLineNum, 0);
  1028 + }
  1029 + break;
  1030 + }
  1031 +
  1032 + XMLDeclaration* decl = node->ToDeclaration();
  1033 + if ( decl ) {
  1034 + // Declarations are only allowed at document level
  1035 + bool wellLocated = ( ToDocument() != 0 );
  1036 + if ( wellLocated ) {
  1037 + // Multiple declarations are allowed but all declarations
  1038 + // must occur before anything else
  1039 + for ( const XMLNode* existingNode = _document->FirstChild(); existingNode; existingNode = existingNode->NextSibling() ) {
  1040 + if ( !existingNode->ToDeclaration() ) {
  1041 + wellLocated = false;
  1042 + break;
  1043 + }
  1044 + }
  1045 + }
  1046 + if ( !wellLocated ) {
  1047 + _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, "XMLDeclaration value=%s", decl->Value());
  1048 + DeleteNode( node );
  1049 + break;
  1050 + }
  1051 + }
  1052 +
  1053 + XMLElement* ele = node->ToElement();
  1054 + if ( ele ) {
  1055 + // We read the end tag. Return it to the parent.
  1056 + if ( ele->ClosingType() == XMLElement::CLOSING ) {
  1057 + if ( parentEndTag ) {
  1058 + ele->_value.TransferTo( parentEndTag );
  1059 + }
  1060 + node->_memPool->SetTracked(); // created and then immediately deleted.
  1061 + DeleteNode( node );
  1062 + return p;
  1063 + }
  1064 +
  1065 + // Handle an end tag returned to this level.
  1066 + // And handle a bunch of annoying errors.
  1067 + bool mismatch = false;
  1068 + if ( endTag.Empty() ) {
  1069 + if ( ele->ClosingType() == XMLElement::OPEN ) {
  1070 + mismatch = true;
  1071 + }
  1072 + }
  1073 + else {
  1074 + if ( ele->ClosingType() != XMLElement::OPEN ) {
  1075 + mismatch = true;
  1076 + }
  1077 + else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) {
  1078 + mismatch = true;
  1079 + }
  1080 + }
  1081 + if ( mismatch ) {
  1082 + _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, "XMLElement name=%s", ele->Name());
  1083 + DeleteNode( node );
  1084 + break;
  1085 + }
  1086 + }
  1087 + InsertEndChild( node );
  1088 + }
  1089 + return 0;
  1090 +}
  1091 +
  1092 +/*static*/ void XMLNode::DeleteNode( XMLNode* node )
  1093 +{
  1094 + if ( node == 0 ) {
  1095 + return;
  1096 + }
  1097 + TIXMLASSERT(node->_document);
  1098 + if (!node->ToDocument()) {
  1099 + node->_document->MarkInUse(node);
  1100 + }
  1101 +
  1102 + MemPool* pool = node->_memPool;
  1103 + node->~XMLNode();
  1104 + pool->Free( node );
  1105 +}
  1106 +
  1107 +void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const
  1108 +{
  1109 + TIXMLASSERT( insertThis );
  1110 + TIXMLASSERT( insertThis->_document == _document );
  1111 +
  1112 + if (insertThis->_parent) {
  1113 + insertThis->_parent->Unlink( insertThis );
  1114 + }
  1115 + else {
  1116 + insertThis->_document->MarkInUse(insertThis);
  1117 + insertThis->_memPool->SetTracked();
  1118 + }
  1119 +}
  1120 +
  1121 +const XMLElement* XMLNode::ToElementWithName( const char* name ) const
  1122 +{
  1123 + const XMLElement* element = this->ToElement();
  1124 + if ( element == 0 ) {
  1125 + return 0;
  1126 + }
  1127 + if ( name == 0 ) {
  1128 + return element;
  1129 + }
  1130 + if ( XMLUtil::StringEqual( element->Name(), name ) ) {
  1131 + return element;
  1132 + }
  1133 + return 0;
  1134 +}
  1135 +
  1136 +// --------- XMLText ---------- //
  1137 +char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
  1138 +{
  1139 + if ( this->CData() ) {
  1140 + p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
  1141 + if ( !p ) {
  1142 + _document->SetError( XML_ERROR_PARSING_CDATA, _parseLineNum, 0 );
  1143 + }
  1144 + return p;
  1145 + }
  1146 + else {
  1147 + int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
  1148 + if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
  1149 + flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;
  1150 + }
  1151 +
  1152 + p = _value.ParseText( p, "<", flags, curLineNumPtr );
  1153 + if ( p && *p ) {
  1154 + return p-1;
  1155 + }
  1156 + if ( !p ) {
  1157 + _document->SetError( XML_ERROR_PARSING_TEXT, _parseLineNum, 0 );
  1158 + }
  1159 + }
  1160 + return 0;
  1161 +}
  1162 +
  1163 +
  1164 +XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
  1165 +{
  1166 + if ( !doc ) {
  1167 + doc = _document;
  1168 + }
  1169 + XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
  1170 + text->SetCData( this->CData() );
  1171 + return text;
  1172 +}
  1173 +
  1174 +
  1175 +bool XMLText::ShallowEqual( const XMLNode* compare ) const
  1176 +{
  1177 + TIXMLASSERT( compare );
  1178 + const XMLText* text = compare->ToText();
  1179 + return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );
  1180 +}
  1181 +
  1182 +
  1183 +bool XMLText::Accept( XMLVisitor* visitor ) const
  1184 +{
  1185 + TIXMLASSERT( visitor );
  1186 + return visitor->Visit( *this );
  1187 +}
  1188 +
  1189 +
  1190 +// --------- XMLComment ---------- //
  1191 +
  1192 +XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
  1193 +{
  1194 +}
  1195 +
  1196 +
  1197 +XMLComment::~XMLComment()
  1198 +{
  1199 +}
  1200 +
  1201 +
  1202 +char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
  1203 +{
  1204 + // Comment parses as text.
  1205 + p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr );
  1206 + if ( p == 0 ) {
  1207 + _document->SetError( XML_ERROR_PARSING_COMMENT, _parseLineNum, 0 );
  1208 + }
  1209 + return p;
  1210 +}
  1211 +
  1212 +
  1213 +XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
  1214 +{
  1215 + if ( !doc ) {
  1216 + doc = _document;
  1217 + }
  1218 + XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
  1219 + return comment;
  1220 +}
  1221 +
  1222 +
  1223 +bool XMLComment::ShallowEqual( const XMLNode* compare ) const
  1224 +{
  1225 + TIXMLASSERT( compare );
  1226 + const XMLComment* comment = compare->ToComment();
  1227 + return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));
  1228 +}
  1229 +
  1230 +
  1231 +bool XMLComment::Accept( XMLVisitor* visitor ) const
  1232 +{
  1233 + TIXMLASSERT( visitor );
  1234 + return visitor->Visit( *this );
  1235 +}
  1236 +
  1237 +
  1238 +// --------- XMLDeclaration ---------- //
  1239 +
  1240 +XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
  1241 +{
  1242 +}
  1243 +
  1244 +
  1245 +XMLDeclaration::~XMLDeclaration()
  1246 +{
  1247 + //printf( "~XMLDeclaration\n" );
  1248 +}
  1249 +
  1250 +
  1251 +char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
  1252 +{
  1253 + // Declaration parses as text.
  1254 + p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
  1255 + if ( p == 0 ) {
  1256 + _document->SetError( XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0 );
  1257 + }
  1258 + return p;
  1259 +}
  1260 +
  1261 +
  1262 +XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
  1263 +{
  1264 + if ( !doc ) {
  1265 + doc = _document;
  1266 + }
  1267 + XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
  1268 + return dec;
  1269 +}
  1270 +
  1271 +
  1272 +bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
  1273 +{
  1274 + TIXMLASSERT( compare );
  1275 + const XMLDeclaration* declaration = compare->ToDeclaration();
  1276 + return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));
  1277 +}
  1278 +
  1279 +
  1280 +
  1281 +bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
  1282 +{
  1283 + TIXMLASSERT( visitor );
  1284 + return visitor->Visit( *this );
  1285 +}
  1286 +
  1287 +// --------- XMLUnknown ---------- //
  1288 +
  1289 +XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
  1290 +{
  1291 +}
  1292 +
  1293 +
  1294 +XMLUnknown::~XMLUnknown()
  1295 +{
  1296 +}
  1297 +
  1298 +
  1299 +char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
  1300 +{
  1301 + // Unknown parses as text.
  1302 + p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
  1303 + if ( !p ) {
  1304 + _document->SetError( XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0 );
  1305 + }
  1306 + return p;
  1307 +}
  1308 +
  1309 +
  1310 +XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
  1311 +{
  1312 + if ( !doc ) {
  1313 + doc = _document;
  1314 + }
  1315 + XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
  1316 + return text;
  1317 +}
  1318 +
  1319 +
  1320 +bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
  1321 +{
  1322 + TIXMLASSERT( compare );
  1323 + const XMLUnknown* unknown = compare->ToUnknown();
  1324 + return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));
  1325 +}
  1326 +
  1327 +
  1328 +bool XMLUnknown::Accept( XMLVisitor* visitor ) const
  1329 +{
  1330 + TIXMLASSERT( visitor );
  1331 + return visitor->Visit( *this );
  1332 +}
  1333 +
  1334 +// --------- XMLAttribute ---------- //
  1335 +
  1336 +const char* XMLAttribute::Name() const
  1337 +{
  1338 + return _name.GetStr();
  1339 +}
  1340 +
  1341 +const char* XMLAttribute::Value() const
  1342 +{
  1343 + return _value.GetStr();
  1344 +}
  1345 +
  1346 +char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr )
  1347 +{
  1348 + // Parse using the name rules: bug fix, was using ParseText before
  1349 + p = _name.ParseName( p );
  1350 + if ( !p || !*p ) {
  1351 + return 0;
  1352 + }
  1353 +
  1354 + // Skip white space before =
  1355 + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
  1356 + if ( *p != '=' ) {
  1357 + return 0;
  1358 + }
  1359 +
  1360 + ++p; // move up to opening quote
  1361 + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
  1362 + if ( *p != '\"' && *p != '\'' ) {
  1363 + return 0;
  1364 + }
  1365 +
  1366 + char endTag[2] = { *p, 0 };
  1367 + ++p; // move past opening quote
  1368 +
  1369 + p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr );
  1370 + return p;
  1371 +}
  1372 +
  1373 +
  1374 +void XMLAttribute::SetName( const char* n )
  1375 +{
  1376 + _name.SetStr( n );
  1377 +}
  1378 +
  1379 +
  1380 +XMLError XMLAttribute::QueryIntValue( int* value ) const
  1381 +{
  1382 + if ( XMLUtil::ToInt( Value(), value )) {
  1383 + return XML_SUCCESS;
  1384 + }
  1385 + return XML_WRONG_ATTRIBUTE_TYPE;
  1386 +}
  1387 +
  1388 +
  1389 +XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
  1390 +{
  1391 + if ( XMLUtil::ToUnsigned( Value(), value )) {
  1392 + return XML_SUCCESS;
  1393 + }
  1394 + return XML_WRONG_ATTRIBUTE_TYPE;
  1395 +}
  1396 +
  1397 +
  1398 +XMLError XMLAttribute::QueryInt64Value(int64_t* value) const
  1399 +{
  1400 + if (XMLUtil::ToInt64(Value(), value)) {
  1401 + return XML_SUCCESS;
  1402 + }
  1403 + return XML_WRONG_ATTRIBUTE_TYPE;
  1404 +}
  1405 +
  1406 +
  1407 +XMLError XMLAttribute::QueryBoolValue( bool* value ) const
  1408 +{
  1409 + if ( XMLUtil::ToBool( Value(), value )) {
  1410 + return XML_SUCCESS;
  1411 + }
  1412 + return XML_WRONG_ATTRIBUTE_TYPE;
  1413 +}
  1414 +
  1415 +
  1416 +XMLError XMLAttribute::QueryFloatValue( float* value ) const
  1417 +{
  1418 + if ( XMLUtil::ToFloat( Value(), value )) {
  1419 + return XML_SUCCESS;
  1420 + }
  1421 + return XML_WRONG_ATTRIBUTE_TYPE;
  1422 +}
  1423 +
  1424 +
  1425 +XMLError XMLAttribute::QueryDoubleValue( double* value ) const
  1426 +{
  1427 + if ( XMLUtil::ToDouble( Value(), value )) {
  1428 + return XML_SUCCESS;
  1429 + }
  1430 + return XML_WRONG_ATTRIBUTE_TYPE;
  1431 +}
  1432 +
  1433 +
  1434 +void XMLAttribute::SetAttribute( const char* v )
  1435 +{
  1436 + _value.SetStr( v );
  1437 +}
  1438 +
  1439 +
  1440 +void XMLAttribute::SetAttribute( int v )
  1441 +{
  1442 + char buf[BUF_SIZE];
  1443 + XMLUtil::ToStr( v, buf, BUF_SIZE );
  1444 + _value.SetStr( buf );
  1445 +}
  1446 +
  1447 +
  1448 +void XMLAttribute::SetAttribute( unsigned v )
  1449 +{
  1450 + char buf[BUF_SIZE];
  1451 + XMLUtil::ToStr( v, buf, BUF_SIZE );
  1452 + _value.SetStr( buf );
  1453 +}
  1454 +
  1455 +
  1456 +void XMLAttribute::SetAttribute(int64_t v)
  1457 +{
  1458 + char buf[BUF_SIZE];
  1459 + XMLUtil::ToStr(v, buf, BUF_SIZE);
  1460 + _value.SetStr(buf);
  1461 +}
  1462 +
  1463 +
  1464 +
  1465 +void XMLAttribute::SetAttribute( bool v )
  1466 +{
  1467 + char buf[BUF_SIZE];
  1468 + XMLUtil::ToStr( v, buf, BUF_SIZE );
  1469 + _value.SetStr( buf );
  1470 +}
  1471 +
  1472 +void XMLAttribute::SetAttribute( double v )
  1473 +{
  1474 + char buf[BUF_SIZE];
  1475 + XMLUtil::ToStr( v, buf, BUF_SIZE );
  1476 + _value.SetStr( buf );
  1477 +}
  1478 +
  1479 +void XMLAttribute::SetAttribute( float v )
  1480 +{
  1481 + char buf[BUF_SIZE];
  1482 + XMLUtil::ToStr( v, buf, BUF_SIZE );
  1483 + _value.SetStr( buf );
  1484 +}
  1485 +
  1486 +
  1487 +// --------- XMLElement ---------- //
  1488 +XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
  1489 + _closingType( OPEN ),
  1490 + _rootAttribute( 0 )
  1491 +{
  1492 +}
  1493 +
  1494 +
  1495 +XMLElement::~XMLElement()
  1496 +{
  1497 + while( _rootAttribute ) {
  1498 + XMLAttribute* next = _rootAttribute->_next;
  1499 + DeleteAttribute( _rootAttribute );
  1500 + _rootAttribute = next;
  1501 + }
  1502 +}
  1503 +
  1504 +
  1505 +const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
  1506 +{
  1507 + for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
  1508 + if ( XMLUtil::StringEqual( a->Name(), name ) ) {
  1509 + return a;
  1510 + }
  1511 + }
  1512 + return 0;
  1513 +}
  1514 +
  1515 +
  1516 +const char* XMLElement::Attribute( const char* name, const char* value ) const
  1517 +{
  1518 + const XMLAttribute* a = FindAttribute( name );
  1519 + if ( !a ) {
  1520 + return 0;
  1521 + }
  1522 + if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
  1523 + return a->Value();
  1524 + }
  1525 + return 0;
  1526 +}
  1527 +
  1528 +int XMLElement::IntAttribute(const char* name, int defaultValue) const
  1529 +{
  1530 + int i = defaultValue;
  1531 + QueryIntAttribute(name, &i);
  1532 + return i;
  1533 +}
  1534 +
  1535 +unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const
  1536 +{
  1537 + unsigned i = defaultValue;
  1538 + QueryUnsignedAttribute(name, &i);
  1539 + return i;
  1540 +}
  1541 +
  1542 +int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const
  1543 +{
  1544 + int64_t i = defaultValue;
  1545 + QueryInt64Attribute(name, &i);
  1546 + return i;
  1547 +}
  1548 +
  1549 +bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const
  1550 +{
  1551 + bool b = defaultValue;
  1552 + QueryBoolAttribute(name, &b);
  1553 + return b;
  1554 +}
  1555 +
  1556 +double XMLElement::DoubleAttribute(const char* name, double defaultValue) const
  1557 +{
  1558 + double d = defaultValue;
  1559 + QueryDoubleAttribute(name, &d);
  1560 + return d;
  1561 +}
  1562 +
  1563 +float XMLElement::FloatAttribute(const char* name, float defaultValue) const
  1564 +{
  1565 + float f = defaultValue;
  1566 + QueryFloatAttribute(name, &f);
  1567 + return f;
  1568 +}
  1569 +
  1570 +const char* XMLElement::GetText() const
  1571 +{
  1572 + if ( FirstChild() && FirstChild()->ToText() ) {
  1573 + return FirstChild()->Value();
  1574 + }
  1575 + return 0;
  1576 +}
  1577 +
  1578 +
  1579 +void XMLElement::SetText( const char* inText )
  1580 +{
  1581 + if ( FirstChild() && FirstChild()->ToText() )
  1582 + FirstChild()->SetValue( inText );
  1583 + else {
  1584 + XMLText* theText = GetDocument()->NewText( inText );
  1585 + InsertFirstChild( theText );
  1586 + }
  1587 +}
  1588 +
  1589 +
  1590 +void XMLElement::SetText( int v )
  1591 +{
  1592 + char buf[BUF_SIZE];
  1593 + XMLUtil::ToStr( v, buf, BUF_SIZE );
  1594 + SetText( buf );
  1595 +}
  1596 +
  1597 +
  1598 +void XMLElement::SetText( unsigned v )
  1599 +{
  1600 + char buf[BUF_SIZE];
  1601 + XMLUtil::ToStr( v, buf, BUF_SIZE );
  1602 + SetText( buf );
  1603 +}
  1604 +
  1605 +
  1606 +void XMLElement::SetText(int64_t v)
  1607 +{
  1608 + char buf[BUF_SIZE];
  1609 + XMLUtil::ToStr(v, buf, BUF_SIZE);
  1610 + SetText(buf);
  1611 +}
  1612 +
  1613 +
  1614 +void XMLElement::SetText( bool v )
  1615 +{
  1616 + char buf[BUF_SIZE];
  1617 + XMLUtil::ToStr( v, buf, BUF_SIZE );
  1618 + SetText( buf );
  1619 +}
  1620 +
  1621 +
  1622 +void XMLElement::SetText( float v )
  1623 +{
  1624 + char buf[BUF_SIZE];
  1625 + XMLUtil::ToStr( v, buf, BUF_SIZE );
  1626 + SetText( buf );
  1627 +}
  1628 +
  1629 +
  1630 +void XMLElement::SetText( double v )
  1631 +{
  1632 + char buf[BUF_SIZE];
  1633 + XMLUtil::ToStr( v, buf, BUF_SIZE );
  1634 + SetText( buf );
  1635 +}
  1636 +
  1637 +
  1638 +XMLError XMLElement::QueryIntText( int* ival ) const
  1639 +{
  1640 + if ( FirstChild() && FirstChild()->ToText() ) {
  1641 + const char* t = FirstChild()->Value();
  1642 + if ( XMLUtil::ToInt( t, ival ) ) {
  1643 + return XML_SUCCESS;
  1644 + }
  1645 + return XML_CAN_NOT_CONVERT_TEXT;
  1646 + }
  1647 + return XML_NO_TEXT_NODE;
  1648 +}
  1649 +
  1650 +
  1651 +XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
  1652 +{
  1653 + if ( FirstChild() && FirstChild()->ToText() ) {
  1654 + const char* t = FirstChild()->Value();
  1655 + if ( XMLUtil::ToUnsigned( t, uval ) ) {
  1656 + return XML_SUCCESS;
  1657 + }
  1658 + return XML_CAN_NOT_CONVERT_TEXT;
  1659 + }
  1660 + return XML_NO_TEXT_NODE;
  1661 +}
  1662 +
  1663 +
  1664 +XMLError XMLElement::QueryInt64Text(int64_t* ival) const
  1665 +{
  1666 + if (FirstChild() && FirstChild()->ToText()) {
  1667 + const char* t = FirstChild()->Value();
  1668 + if (XMLUtil::ToInt64(t, ival)) {
  1669 + return XML_SUCCESS;
  1670 + }
  1671 + return XML_CAN_NOT_CONVERT_TEXT;
  1672 + }
  1673 + return XML_NO_TEXT_NODE;
  1674 +}
  1675 +
  1676 +
  1677 +XMLError XMLElement::QueryBoolText( bool* bval ) const
  1678 +{
  1679 + if ( FirstChild() && FirstChild()->ToText() ) {
  1680 + const char* t = FirstChild()->Value();
  1681 + if ( XMLUtil::ToBool( t, bval ) ) {
  1682 + return XML_SUCCESS;
  1683 + }
  1684 + return XML_CAN_NOT_CONVERT_TEXT;
  1685 + }
  1686 + return XML_NO_TEXT_NODE;
  1687 +}
  1688 +
  1689 +
  1690 +XMLError XMLElement::QueryDoubleText( double* dval ) const
  1691 +{
  1692 + if ( FirstChild() && FirstChild()->ToText() ) {
  1693 + const char* t = FirstChild()->Value();
  1694 + if ( XMLUtil::ToDouble( t, dval ) ) {
  1695 + return XML_SUCCESS;
  1696 + }
  1697 + return XML_CAN_NOT_CONVERT_TEXT;
  1698 + }
  1699 + return XML_NO_TEXT_NODE;
  1700 +}
  1701 +
  1702 +
  1703 +XMLError XMLElement::QueryFloatText( float* fval ) const
  1704 +{
  1705 + if ( FirstChild() && FirstChild()->ToText() ) {
  1706 + const char* t = FirstChild()->Value();
  1707 + if ( XMLUtil::ToFloat( t, fval ) ) {
  1708 + return XML_SUCCESS;
  1709 + }
  1710 + return XML_CAN_NOT_CONVERT_TEXT;
  1711 + }
  1712 + return XML_NO_TEXT_NODE;
  1713 +}
  1714 +
  1715 +int XMLElement::IntText(int defaultValue) const
  1716 +{
  1717 + int i = defaultValue;
  1718 + QueryIntText(&i);
  1719 + return i;
  1720 +}
  1721 +
  1722 +unsigned XMLElement::UnsignedText(unsigned defaultValue) const
  1723 +{
  1724 + unsigned i = defaultValue;
  1725 + QueryUnsignedText(&i);
  1726 + return i;
  1727 +}
  1728 +
  1729 +int64_t XMLElement::Int64Text(int64_t defaultValue) const
  1730 +{
  1731 + int64_t i = defaultValue;
  1732 + QueryInt64Text(&i);
  1733 + return i;
  1734 +}
  1735 +
  1736 +bool XMLElement::BoolText(bool defaultValue) const
  1737 +{
  1738 + bool b = defaultValue;
  1739 + QueryBoolText(&b);
  1740 + return b;
  1741 +}
  1742 +
  1743 +double XMLElement::DoubleText(double defaultValue) const
  1744 +{
  1745 + double d = defaultValue;
  1746 + QueryDoubleText(&d);
  1747 + return d;
  1748 +}
  1749 +
  1750 +float XMLElement::FloatText(float defaultValue) const
  1751 +{
  1752 + float f = defaultValue;
  1753 + QueryFloatText(&f);
  1754 + return f;
  1755 +}
  1756 +
  1757 +
  1758 +XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
  1759 +{
  1760 + XMLAttribute* last = 0;
  1761 + XMLAttribute* attrib = 0;
  1762 + for( attrib = _rootAttribute;
  1763 + attrib;
  1764 + last = attrib, attrib = attrib->_next ) {
  1765 + if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
  1766 + break;
  1767 + }
  1768 + }
  1769 + if ( !attrib ) {
  1770 + attrib = CreateAttribute();
  1771 + TIXMLASSERT( attrib );
  1772 + if ( last ) {
  1773 + TIXMLASSERT( last->_next == 0 );
  1774 + last->_next = attrib;
  1775 + }
  1776 + else {
  1777 + TIXMLASSERT( _rootAttribute == 0 );
  1778 + _rootAttribute = attrib;
  1779 + }
  1780 + attrib->SetName( name );
  1781 + }
  1782 + return attrib;
  1783 +}
  1784 +
  1785 +
  1786 +void XMLElement::DeleteAttribute( const char* name )
  1787 +{
  1788 + XMLAttribute* prev = 0;
  1789 + for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
  1790 + if ( XMLUtil::StringEqual( name, a->Name() ) ) {
  1791 + if ( prev ) {
  1792 + prev->_next = a->_next;
  1793 + }
  1794 + else {
  1795 + _rootAttribute = a->_next;
  1796 + }
  1797 + DeleteAttribute( a );
  1798 + break;
  1799 + }
  1800 + prev = a;
  1801 + }
  1802 +}
  1803 +
  1804 +
  1805 +char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr )
  1806 +{
  1807 + XMLAttribute* prevAttribute = 0;
  1808 +
  1809 + // Read the attributes.
  1810 + while( p ) {
  1811 + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
  1812 + if ( !(*p) ) {
  1813 + _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, "XMLElement name=%s", Name() );
  1814 + return 0;
  1815 + }
  1816 +
  1817 + // attribute.
  1818 + if (XMLUtil::IsNameStartChar( *p ) ) {
  1819 + XMLAttribute* attrib = CreateAttribute();
  1820 + TIXMLASSERT( attrib );
  1821 + attrib->_parseLineNum = _document->_parseCurLineNum;
  1822 +
  1823 + int attrLineNum = attrib->_parseLineNum;
  1824 +
  1825 + p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr );
  1826 + if ( !p || Attribute( attrib->Name() ) ) {
  1827 + DeleteAttribute( attrib );
  1828 + _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, "XMLElement name=%s", Name() );
  1829 + return 0;
  1830 + }
  1831 + // There is a minor bug here: if the attribute in the source xml
  1832 + // document is duplicated, it will not be detected and the
  1833 + // attribute will be doubly added. However, tracking the 'prevAttribute'
  1834 + // avoids re-scanning the attribute list. Preferring performance for
  1835 + // now, may reconsider in the future.
  1836 + if ( prevAttribute ) {
  1837 + TIXMLASSERT( prevAttribute->_next == 0 );
  1838 + prevAttribute->_next = attrib;
  1839 + }
  1840 + else {
  1841 + TIXMLASSERT( _rootAttribute == 0 );
  1842 + _rootAttribute = attrib;
  1843 + }
  1844 + prevAttribute = attrib;
  1845 + }
  1846 + // end of the tag
  1847 + else if ( *p == '>' ) {
  1848 + ++p;
  1849 + break;
  1850 + }
  1851 + // end of the tag
  1852 + else if ( *p == '/' && *(p+1) == '>' ) {
  1853 + _closingType = CLOSED;
  1854 + return p+2; // done; sealed element.
  1855 + }
  1856 + else {
  1857 + _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 );
  1858 + return 0;
  1859 + }
  1860 + }
  1861 + return p;
  1862 +}
  1863 +
  1864 +void XMLElement::DeleteAttribute( XMLAttribute* attribute )
  1865 +{
  1866 + if ( attribute == 0 ) {
  1867 + return;
  1868 + }
  1869 + MemPool* pool = attribute->_memPool;
  1870 + attribute->~XMLAttribute();
  1871 + pool->Free( attribute );
  1872 +}
  1873 +
  1874 +XMLAttribute* XMLElement::CreateAttribute()
  1875 +{
  1876 + TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
  1877 + XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
  1878 + TIXMLASSERT( attrib );
  1879 + attrib->_memPool = &_document->_attributePool;
  1880 + attrib->_memPool->SetTracked();
  1881 + return attrib;
  1882 +}
  1883 +
  1884 +//
  1885 +// <ele></ele>
  1886 +// <ele>foo<b>bar</b></ele>
  1887 +//
  1888 +char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
  1889 +{
  1890 + // Read the element name.
  1891 + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
  1892 +
  1893 + // The closing element is the </element> form. It is
  1894 + // parsed just like a regular element then deleted from
  1895 + // the DOM.
  1896 + if ( *p == '/' ) {
  1897 + _closingType = CLOSING;
  1898 + ++p;
  1899 + }
  1900 +
  1901 + p = _value.ParseName( p );
  1902 + if ( _value.Empty() ) {
  1903 + return 0;
  1904 + }
  1905 +
  1906 + p = ParseAttributes( p, curLineNumPtr );
  1907 + if ( !p || !*p || _closingType != OPEN ) {
  1908 + return p;
  1909 + }
  1910 +
  1911 + p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr );
  1912 + return p;
  1913 +}
  1914 +
  1915 +
  1916 +
  1917 +XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
  1918 +{
  1919 + if ( !doc ) {
  1920 + doc = _document;
  1921 + }
  1922 + XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
  1923 + for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
  1924 + element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
  1925 + }
  1926 + return element;
  1927 +}
  1928 +
  1929 +
  1930 +bool XMLElement::ShallowEqual( const XMLNode* compare ) const
  1931 +{
  1932 + TIXMLASSERT( compare );
  1933 + const XMLElement* other = compare->ToElement();
  1934 + if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {
  1935 +
  1936 + const XMLAttribute* a=FirstAttribute();
  1937 + const XMLAttribute* b=other->FirstAttribute();
  1938 +
  1939 + while ( a && b ) {
  1940 + if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
  1941 + return false;
  1942 + }
  1943 + a = a->Next();
  1944 + b = b->Next();
  1945 + }
  1946 + if ( a || b ) {
  1947 + // different count
  1948 + return false;
  1949 + }
  1950 + return true;
  1951 + }
  1952 + return false;
  1953 +}
  1954 +
  1955 +
  1956 +bool XMLElement::Accept( XMLVisitor* visitor ) const
  1957 +{
  1958 + TIXMLASSERT( visitor );
  1959 + if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
  1960 + for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
  1961 + if ( !node->Accept( visitor ) ) {
  1962 + break;
  1963 + }
  1964 + }
  1965 + }
  1966 + return visitor->VisitExit( *this );
  1967 +}
  1968 +
  1969 +
  1970 +// --------- XMLDocument ----------- //
  1971 +
  1972 +// Warning: List must match 'enum XMLError'
  1973 +const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
  1974 + "XML_SUCCESS",
  1975 + "XML_NO_ATTRIBUTE",
  1976 + "XML_WRONG_ATTRIBUTE_TYPE",
  1977 + "XML_ERROR_FILE_NOT_FOUND",
  1978 + "XML_ERROR_FILE_COULD_NOT_BE_OPENED",
  1979 + "XML_ERROR_FILE_READ_ERROR",
  1980 + "UNUSED_XML_ERROR_ELEMENT_MISMATCH",
  1981 + "XML_ERROR_PARSING_ELEMENT",
  1982 + "XML_ERROR_PARSING_ATTRIBUTE",
  1983 + "UNUSED_XML_ERROR_IDENTIFYING_TAG",
  1984 + "XML_ERROR_PARSING_TEXT",
  1985 + "XML_ERROR_PARSING_CDATA",
  1986 + "XML_ERROR_PARSING_COMMENT",
  1987 + "XML_ERROR_PARSING_DECLARATION",
  1988 + "XML_ERROR_PARSING_UNKNOWN",
  1989 + "XML_ERROR_EMPTY_DOCUMENT",
  1990 + "XML_ERROR_MISMATCHED_ELEMENT",
  1991 + "XML_ERROR_PARSING",
  1992 + "XML_CAN_NOT_CONVERT_TEXT",
  1993 + "XML_NO_TEXT_NODE",
  1994 + "XML_ELEMENT_DEPTH_EXCEEDED"
  1995 +};
  1996 +
  1997 +
  1998 +XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) :
  1999 + XMLNode( 0 ),
  2000 + _writeBOM( false ),
  2001 + _processEntities( processEntities ),
  2002 + _errorID(XML_SUCCESS),
  2003 + _whitespaceMode( whitespaceMode ),
  2004 + _errorStr(),
  2005 + _errorLineNum( 0 ),
  2006 + _charBuffer( 0 ),
  2007 + _parseCurLineNum( 0 ),
  2008 + _parsingDepth(0),
  2009 + _unlinked(),
  2010 + _elementPool(),
  2011 + _attributePool(),
  2012 + _textPool(),
  2013 + _commentPool()
  2014 +{
  2015 + // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
  2016 + _document = this;
  2017 +}
  2018 +
  2019 +
  2020 +XMLDocument::~XMLDocument()
  2021 +{
  2022 + Clear();
  2023 +}
  2024 +
  2025 +
  2026 +void XMLDocument::MarkInUse(XMLNode* node)
  2027 +{
  2028 + TIXMLASSERT(node);
  2029 + TIXMLASSERT(node->_parent == 0);
  2030 +
  2031 + for (int i = 0; i < _unlinked.Size(); ++i) {
  2032 + if (node == _unlinked[i]) {
  2033 + _unlinked.SwapRemove(i);
  2034 + break;
  2035 + }
  2036 + }
  2037 +}
  2038 +
  2039 +void XMLDocument::Clear()
  2040 +{
  2041 + DeleteChildren();
  2042 + while( _unlinked.Size()) {
  2043 + DeleteNode(_unlinked[0]); // Will remove from _unlinked as part of delete.
  2044 + }
  2045 +
  2046 +#ifdef TINYXML2_DEBUG
  2047 + const bool hadError = Error();
  2048 +#endif
  2049 + ClearError();
  2050 +
  2051 + delete [] _charBuffer;
  2052 + _charBuffer = 0;
  2053 + _parsingDepth = 0;
  2054 +
  2055 +#if 0
  2056 + _textPool.Trace( "text" );
  2057 + _elementPool.Trace( "element" );
  2058 + _commentPool.Trace( "comment" );
  2059 + _attributePool.Trace( "attribute" );
  2060 +#endif
  2061 +
  2062 +#ifdef TINYXML2_DEBUG
  2063 + if ( !hadError ) {
  2064 + TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() );
  2065 + TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
  2066 + TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() );
  2067 + TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() );
  2068 + }
  2069 +#endif
  2070 +}
  2071 +
  2072 +
  2073 +void XMLDocument::DeepCopy(XMLDocument* target) const
  2074 +{
  2075 + TIXMLASSERT(target);
  2076 + if (target == this) {
  2077 + return; // technically success - a no-op.
  2078 + }
  2079 +
  2080 + target->Clear();
  2081 + for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) {
  2082 + target->InsertEndChild(node->DeepClone(target));
  2083 + }
  2084 +}
  2085 +
  2086 +XMLElement* XMLDocument::NewElement( const char* name )
  2087 +{
  2088 + XMLElement* ele = CreateUnlinkedNode<XMLElement>( _elementPool );
  2089 + ele->SetName( name );
  2090 + return ele;
  2091 +}
  2092 +
  2093 +
  2094 +XMLComment* XMLDocument::NewComment( const char* str )
  2095 +{
  2096 + XMLComment* comment = CreateUnlinkedNode<XMLComment>( _commentPool );
  2097 + comment->SetValue( str );
  2098 + return comment;
  2099 +}
  2100 +
  2101 +
  2102 +XMLText* XMLDocument::NewText( const char* str )
  2103 +{
  2104 + XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
  2105 + text->SetValue( str );
  2106 + return text;
  2107 +}
  2108 +
  2109 +
  2110 +XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
  2111 +{
  2112 + XMLDeclaration* dec = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
  2113 + dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
  2114 + return dec;
  2115 +}
  2116 +
  2117 +
  2118 +XMLUnknown* XMLDocument::NewUnknown( const char* str )
  2119 +{
  2120 + XMLUnknown* unk = CreateUnlinkedNode<XMLUnknown>( _commentPool );
  2121 + unk->SetValue( str );
  2122 + return unk;
  2123 +}
  2124 +
  2125 +static FILE* callfopen( const char* filepath, const char* mode )
  2126 +{
  2127 + TIXMLASSERT( filepath );
  2128 + TIXMLASSERT( mode );
  2129 +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
  2130 + FILE* fp = 0;
  2131 + errno_t err = fopen_s( &fp, filepath, mode );
  2132 + if ( err ) {
  2133 + return 0;
  2134 + }
  2135 +#else
  2136 + FILE* fp = fopen( filepath, mode );
  2137 +#endif
  2138 + return fp;
  2139 +}
  2140 +
  2141 +void XMLDocument::DeleteNode( XMLNode* node ) {
  2142 + TIXMLASSERT( node );
  2143 + TIXMLASSERT(node->_document == this );
  2144 + if (node->_parent) {
  2145 + node->_parent->DeleteChild( node );
  2146 + }
  2147 + else {
  2148 + // Isn't in the tree.
  2149 + // Use the parent delete.
  2150 + // Also, we need to mark it tracked: we 'know'
  2151 + // it was never used.
  2152 + node->_memPool->SetTracked();
  2153 + // Call the static XMLNode version:
  2154 + XMLNode::DeleteNode(node);
  2155 + }
  2156 +}
  2157 +
  2158 +
  2159 +XMLError XMLDocument::LoadFile( const char* filename )
  2160 +{
  2161 + if ( !filename ) {
  2162 + TIXMLASSERT( false );
  2163 + SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>" );
  2164 + return _errorID;
  2165 + }
  2166 +
  2167 + Clear();
  2168 + FILE* fp = callfopen( filename, "rb" );
  2169 + if ( !fp ) {
  2170 + SetError( XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename );
  2171 + return _errorID;
  2172 + }
  2173 + LoadFile( fp );
  2174 + fclose( fp );
  2175 + return _errorID;
  2176 +}
  2177 +
  2178 +// This is likely overengineered template art to have a check that unsigned long value incremented
  2179 +// by one still fits into size_t. If size_t type is larger than unsigned long type
  2180 +// (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit
  2181 +// -Wtype-limits warning. This piece makes the compiler select code with a check when a check
  2182 +// is useful and code with no check when a check is redundant depending on how size_t and unsigned long
  2183 +// types sizes relate to each other.
  2184 +template
  2185 +<bool = (sizeof(unsigned long) >= sizeof(size_t))>
  2186 +struct LongFitsIntoSizeTMinusOne {
  2187 + static bool Fits( unsigned long value )
  2188 + {
  2189 + return value < (size_t)-1;
  2190 + }
  2191 +};
  2192 +
  2193 +template <>
  2194 +struct LongFitsIntoSizeTMinusOne<false> {
  2195 + static bool Fits( unsigned long )
  2196 + {
  2197 + return true;
  2198 + }
  2199 +};
  2200 +
  2201 +XMLError XMLDocument::LoadFile( FILE* fp )
  2202 +{
  2203 + Clear();
  2204 +
  2205 + fseek( fp, 0, SEEK_SET );
  2206 + if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
  2207 + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
  2208 + return _errorID;
  2209 + }
  2210 +
  2211 + fseek( fp, 0, SEEK_END );
  2212 + const long filelength = ftell( fp );
  2213 + fseek( fp, 0, SEEK_SET );
  2214 + if ( filelength == -1L ) {
  2215 + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
  2216 + return _errorID;
  2217 + }
  2218 + TIXMLASSERT( filelength >= 0 );
  2219 +
  2220 + if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) {
  2221 + // Cannot handle files which won't fit in buffer together with null terminator
  2222 + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
  2223 + return _errorID;
  2224 + }
  2225 +
  2226 + if ( filelength == 0 ) {
  2227 + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
  2228 + return _errorID;
  2229 + }
  2230 +
  2231 + const size_t size = filelength;
  2232 + TIXMLASSERT( _charBuffer == 0 );
  2233 + _charBuffer = new char[size+1];
  2234 + size_t read = fread( _charBuffer, 1, size, fp );
  2235 + if ( read != size ) {
  2236 + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
  2237 + return _errorID;
  2238 + }
  2239 +
  2240 + _charBuffer[size] = 0;
  2241 +
  2242 + Parse();
  2243 + return _errorID;
  2244 +}
  2245 +
  2246 +
  2247 +XMLError XMLDocument::SaveFile( const char* filename, bool compact )
  2248 +{
  2249 + if ( !filename ) {
  2250 + TIXMLASSERT( false );
  2251 + SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>" );
  2252 + return _errorID;
  2253 + }
  2254 +
  2255 + FILE* fp = callfopen( filename, "w" );
  2256 + if ( !fp ) {
  2257 + SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename );
  2258 + return _errorID;
  2259 + }
  2260 + SaveFile(fp, compact);
  2261 + fclose( fp );
  2262 + return _errorID;
  2263 +}
  2264 +
  2265 +
  2266 +XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
  2267 +{
  2268 + // Clear any error from the last save, otherwise it will get reported
  2269 + // for *this* call.
  2270 + ClearError();
  2271 + XMLPrinter stream( fp, compact );
  2272 + Print( &stream );
  2273 + return _errorID;
  2274 +}
  2275 +
  2276 +
  2277 +XMLError XMLDocument::Parse( const char* p, size_t len )
  2278 +{
  2279 + Clear();
  2280 +
  2281 + if ( len == 0 || !p || !*p ) {
  2282 + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
  2283 + return _errorID;
  2284 + }
  2285 + if ( len == (size_t)(-1) ) {
  2286 + len = strlen( p );
  2287 + }
  2288 + TIXMLASSERT( _charBuffer == 0 );
  2289 + _charBuffer = new char[ len+1 ];
  2290 + memcpy( _charBuffer, p, len );
  2291 + _charBuffer[len] = 0;
  2292 +
  2293 + Parse();
  2294 + if ( Error() ) {
  2295 + // clean up now essentially dangling memory.
  2296 + // and the parse fail can put objects in the
  2297 + // pools that are dead and inaccessible.
  2298 + DeleteChildren();
  2299 + _elementPool.Clear();
  2300 + _attributePool.Clear();
  2301 + _textPool.Clear();
  2302 + _commentPool.Clear();
  2303 + }
  2304 + return _errorID;
  2305 +}
  2306 +
  2307 +
  2308 +void XMLDocument::Print( XMLPrinter* streamer ) const
  2309 +{
  2310 + if ( streamer ) {
  2311 + Accept( streamer );
  2312 + }
  2313 + else {
  2314 + XMLPrinter stdoutStreamer( stdout );
  2315 + Accept( &stdoutStreamer );
  2316 + }
  2317 +}
  2318 +
  2319 +
  2320 +void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... )
  2321 +{
  2322 + TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );
  2323 + _errorID = error;
  2324 + _errorLineNum = lineNum;
  2325 + _errorStr.Reset();
  2326 +
  2327 + size_t BUFFER_SIZE = 1000;
  2328 + char* buffer = new char[BUFFER_SIZE];
  2329 +
  2330 + TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum);
  2331 +
  2332 + if (format) {
  2333 + size_t len = strlen(buffer);
  2334 + TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, ": ");
  2335 + len = strlen(buffer);
  2336 +
  2337 + va_list va;
  2338 + va_start(va, format);
  2339 + TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va);
  2340 + va_end(va);
  2341 + }
  2342 + _errorStr.SetStr(buffer);
  2343 + delete[] buffer;
  2344 +}
  2345 +
  2346 +
  2347 +/*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID)
  2348 +{
  2349 + TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT );
  2350 + const char* errorName = _errorNames[errorID];
  2351 + TIXMLASSERT( errorName && errorName[0] );
  2352 + return errorName;
  2353 +}
  2354 +
  2355 +const char* XMLDocument::ErrorStr() const
  2356 +{
  2357 + return _errorStr.Empty() ? "" : _errorStr.GetStr();
  2358 +}
  2359 +
  2360 +
  2361 +void XMLDocument::PrintError() const
  2362 +{
  2363 + printf("%s\n", ErrorStr());
  2364 +}
  2365 +
  2366 +const char* XMLDocument::ErrorName() const
  2367 +{
  2368 + return ErrorIDToName(_errorID);
  2369 +}
  2370 +
  2371 +void XMLDocument::Parse()
  2372 +{
  2373 + TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
  2374 + TIXMLASSERT( _charBuffer );
  2375 + _parseCurLineNum = 1;
  2376 + _parseLineNum = 1;
  2377 + char* p = _charBuffer;
  2378 + p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
  2379 + p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
  2380 + if ( !*p ) {
  2381 + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
  2382 + return;
  2383 + }
  2384 + ParseDeep(p, 0, &_parseCurLineNum );
  2385 +}
  2386 +
  2387 +void XMLDocument::PushDepth()
  2388 +{
  2389 + _parsingDepth++;
  2390 + if (_parsingDepth == TINYXML2_MAX_ELEMENT_DEPTH) {
  2391 + SetError(XML_ELEMENT_DEPTH_EXCEEDED, _parseCurLineNum, "Element nesting is too deep." );
  2392 + }
  2393 +}
  2394 +
  2395 +void XMLDocument::PopDepth()
  2396 +{
  2397 + TIXMLASSERT(_parsingDepth > 0);
  2398 + --_parsingDepth;
  2399 +}
  2400 +
  2401 +XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
  2402 + _elementJustOpened( false ),
  2403 + _stack(),
  2404 + _firstElement( true ),
  2405 + _fp( file ),
  2406 + _depth( depth ),
  2407 + _textDepth( -1 ),
  2408 + _processEntities( true ),
  2409 + _compactMode( compact ),
  2410 + _buffer()
  2411 +{
  2412 + for( int i=0; i<ENTITY_RANGE; ++i ) {
  2413 + _entityFlag[i] = false;
  2414 + _restrictedEntityFlag[i] = false;
  2415 + }
  2416 + for( int i=0; i<NUM_ENTITIES; ++i ) {
  2417 + const char entityValue = entities[i].value;
  2418 + const unsigned char flagIndex = (unsigned char)entityValue;
  2419 + TIXMLASSERT( flagIndex < ENTITY_RANGE );
  2420 + _entityFlag[flagIndex] = true;
  2421 + }
  2422 + _restrictedEntityFlag[(unsigned char)'&'] = true;
  2423 + _restrictedEntityFlag[(unsigned char)'<'] = true;
  2424 + _restrictedEntityFlag[(unsigned char)'>'] = true; // not required, but consistency is nice
  2425 + _buffer.Push( 0 );
  2426 +}
  2427 +
  2428 +
  2429 +void XMLPrinter::Print( const char* format, ... )
  2430 +{
  2431 + va_list va;
  2432 + va_start( va, format );
  2433 +
  2434 + if ( _fp ) {
  2435 + vfprintf( _fp, format, va );
  2436 + }
  2437 + else {
  2438 + const int len = TIXML_VSCPRINTF( format, va );
  2439 + // Close out and re-start the va-args
  2440 + va_end( va );
  2441 + TIXMLASSERT( len >= 0 );
  2442 + va_start( va, format );
  2443 + TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
  2444 + char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator.
  2445 + TIXML_VSNPRINTF( p, len+1, format, va );
  2446 + }
  2447 + va_end( va );
  2448 +}
  2449 +
  2450 +
  2451 +void XMLPrinter::Write( const char* data, size_t size )
  2452 +{
  2453 + if ( _fp ) {
  2454 + fwrite ( data , sizeof(char), size, _fp);
  2455 + }
  2456 + else {
  2457 + char* p = _buffer.PushArr( static_cast<int>(size) ) - 1; // back up over the null terminator.
  2458 + memcpy( p, data, size );
  2459 + p[size] = 0;
  2460 + }
  2461 +}
  2462 +
  2463 +
  2464 +void XMLPrinter::Putc( char ch )
  2465 +{
  2466 + if ( _fp ) {
  2467 + fputc ( ch, _fp);
  2468 + }
  2469 + else {
  2470 + char* p = _buffer.PushArr( sizeof(char) ) - 1; // back up over the null terminator.
  2471 + p[0] = ch;
  2472 + p[1] = 0;
  2473 + }
  2474 +}
  2475 +
  2476 +
  2477 +void XMLPrinter::PrintSpace( int depth )
  2478 +{
  2479 + for( int i=0; i<depth; ++i ) {
  2480 + Write( " " );
  2481 + }
  2482 +}
  2483 +
  2484 +
  2485 +void XMLPrinter::PrintString( const char* p, bool restricted )
  2486 +{
  2487 + // Look for runs of bytes between entities to print.
  2488 + const char* q = p;
  2489 +
  2490 + if ( _processEntities ) {
  2491 + const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
  2492 + while ( *q ) {
  2493 + TIXMLASSERT( p <= q );
  2494 + // Remember, char is sometimes signed. (How many times has that bitten me?)
  2495 + if ( *q > 0 && *q < ENTITY_RANGE ) {
  2496 + // Check for entities. If one is found, flush
  2497 + // the stream up until the entity, write the
  2498 + // entity, and keep looking.
  2499 + if ( flag[(unsigned char)(*q)] ) {
  2500 + while ( p < q ) {
  2501 + const size_t delta = q - p;
  2502 + const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
  2503 + Write( p, toPrint );
  2504 + p += toPrint;
  2505 + }
  2506 + bool entityPatternPrinted = false;
  2507 + for( int i=0; i<NUM_ENTITIES; ++i ) {
  2508 + if ( entities[i].value == *q ) {
  2509 + Putc( '&' );
  2510 + Write( entities[i].pattern, entities[i].length );
  2511 + Putc( ';' );
  2512 + entityPatternPrinted = true;
  2513 + break;
  2514 + }
  2515 + }
  2516 + if ( !entityPatternPrinted ) {
  2517 + // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
  2518 + TIXMLASSERT( false );
  2519 + }
  2520 + ++p;
  2521 + }
  2522 + }
  2523 + ++q;
  2524 + TIXMLASSERT( p <= q );
  2525 + }
  2526 + }
  2527 + // Flush the remaining string. This will be the entire
  2528 + // string if an entity wasn't found.
  2529 + TIXMLASSERT( p <= q );
  2530 + if ( !_processEntities || ( p < q ) ) {
  2531 + const size_t delta = q - p;
  2532 + const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
  2533 + Write( p, toPrint );
  2534 + }
  2535 +}
  2536 +
  2537 +
  2538 +void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
  2539 +{
  2540 + if ( writeBOM ) {
  2541 + static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
  2542 + Write( reinterpret_cast< const char* >( bom ) );
  2543 + }
  2544 + if ( writeDec ) {
  2545 + PushDeclaration( "xml version=\"1.0\"" );
  2546 + }
  2547 +}
  2548 +
  2549 +
  2550 +void XMLPrinter::OpenElement( const char* name, bool compactMode )
  2551 +{
  2552 + SealElementIfJustOpened();
  2553 + _stack.Push( name );
  2554 +
  2555 + if ( _textDepth < 0 && !_firstElement && !compactMode ) {
  2556 + Putc( '\n' );
  2557 + }
  2558 + if ( !compactMode ) {
  2559 + PrintSpace( _depth );
  2560 + }
  2561 +
  2562 + Write ( "<" );
  2563 + Write ( name );
  2564 +
  2565 + _elementJustOpened = true;
  2566 + _firstElement = false;
  2567 + ++_depth;
  2568 +}
  2569 +
  2570 +
  2571 +void XMLPrinter::PushAttribute( const char* name, const char* value )
  2572 +{
  2573 + TIXMLASSERT( _elementJustOpened );
  2574 + Putc ( ' ' );
  2575 + Write( name );
  2576 + Write( "=\"" );
  2577 + PrintString( value, false );
  2578 + Putc ( '\"' );
  2579 +}
  2580 +
  2581 +
  2582 +void XMLPrinter::PushAttribute( const char* name, int v )
  2583 +{
  2584 + char buf[BUF_SIZE];
  2585 + XMLUtil::ToStr( v, buf, BUF_SIZE );
  2586 + PushAttribute( name, buf );
  2587 +}
  2588 +
  2589 +
  2590 +void XMLPrinter::PushAttribute( const char* name, unsigned v )
  2591 +{
  2592 + char buf[BUF_SIZE];
  2593 + XMLUtil::ToStr( v, buf, BUF_SIZE );
  2594 + PushAttribute( name, buf );
  2595 +}
  2596 +
  2597 +
  2598 +void XMLPrinter::PushAttribute(const char* name, int64_t v)
  2599 +{
  2600 + char buf[BUF_SIZE];
  2601 + XMLUtil::ToStr(v, buf, BUF_SIZE);
  2602 + PushAttribute(name, buf);
  2603 +}
  2604 +
  2605 +
  2606 +void XMLPrinter::PushAttribute( const char* name, bool v )
  2607 +{
  2608 + char buf[BUF_SIZE];
  2609 + XMLUtil::ToStr( v, buf, BUF_SIZE );
  2610 + PushAttribute( name, buf );
  2611 +}
  2612 +
  2613 +
  2614 +void XMLPrinter::PushAttribute( const char* name, double v )
  2615 +{
  2616 + char buf[BUF_SIZE];
  2617 + XMLUtil::ToStr( v, buf, BUF_SIZE );
  2618 + PushAttribute( name, buf );
  2619 +}
  2620 +
  2621 +
  2622 +void XMLPrinter::CloseElement( bool compactMode )
  2623 +{
  2624 + --_depth;
  2625 + const char* name = _stack.Pop();
  2626 +
  2627 + if ( _elementJustOpened ) {
  2628 + Write( "/>" );
  2629 + }
  2630 + else {
  2631 + if ( _textDepth < 0 && !compactMode) {
  2632 + Putc( '\n' );
  2633 + PrintSpace( _depth );
  2634 + }
  2635 + Write ( "</" );
  2636 + Write ( name );
  2637 + Write ( ">" );
  2638 + }
  2639 +
  2640 + if ( _textDepth == _depth ) {
  2641 + _textDepth = -1;
  2642 + }
  2643 + if ( _depth == 0 && !compactMode) {
  2644 + Putc( '\n' );
  2645 + }
  2646 + _elementJustOpened = false;
  2647 +}
  2648 +
  2649 +
  2650 +void XMLPrinter::SealElementIfJustOpened()
  2651 +{
  2652 + if ( !_elementJustOpened ) {
  2653 + return;
  2654 + }
  2655 + _elementJustOpened = false;
  2656 + Putc( '>' );
  2657 +}
  2658 +
  2659 +
  2660 +void XMLPrinter::PushText( const char* text, bool cdata )
  2661 +{
  2662 + _textDepth = _depth-1;
  2663 +
  2664 + SealElementIfJustOpened();
  2665 + if ( cdata ) {
  2666 + Write( "<![CDATA[" );
  2667 + Write( text );
  2668 + Write( "]]>" );
  2669 + }
  2670 + else {
  2671 + PrintString( text, true );
  2672 + }
  2673 +}
  2674 +
  2675 +void XMLPrinter::PushText( int64_t value )
  2676 +{
  2677 + char buf[BUF_SIZE];
  2678 + XMLUtil::ToStr( value, buf, BUF_SIZE );
  2679 + PushText( buf, false );
  2680 +}
  2681 +
  2682 +void XMLPrinter::PushText( int value )
  2683 +{
  2684 + char buf[BUF_SIZE];
  2685 + XMLUtil::ToStr( value, buf, BUF_SIZE );
  2686 + PushText( buf, false );
  2687 +}
  2688 +
  2689 +
  2690 +void XMLPrinter::PushText( unsigned value )
  2691 +{
  2692 + char buf[BUF_SIZE];
  2693 + XMLUtil::ToStr( value, buf, BUF_SIZE );
  2694 + PushText( buf, false );
  2695 +}
  2696 +
  2697 +
  2698 +void XMLPrinter::PushText( bool value )
  2699 +{
  2700 + char buf[BUF_SIZE];
  2701 + XMLUtil::ToStr( value, buf, BUF_SIZE );
  2702 + PushText( buf, false );
  2703 +}
  2704 +
  2705 +
  2706 +void XMLPrinter::PushText( float value )
  2707 +{
  2708 + char buf[BUF_SIZE];
  2709 + XMLUtil::ToStr( value, buf, BUF_SIZE );
  2710 + PushText( buf, false );
  2711 +}
  2712 +
  2713 +
  2714 +void XMLPrinter::PushText( double value )
  2715 +{
  2716 + char buf[BUF_SIZE];
  2717 + XMLUtil::ToStr( value, buf, BUF_SIZE );
  2718 + PushText( buf, false );
  2719 +}
  2720 +
  2721 +
  2722 +void XMLPrinter::PushComment( const char* comment )
  2723 +{
  2724 + SealElementIfJustOpened();
  2725 + if ( _textDepth < 0 && !_firstElement && !_compactMode) {
  2726 + Putc( '\n' );
  2727 + PrintSpace( _depth );
  2728 + }
  2729 + _firstElement = false;
  2730 +
  2731 + Write( "<!--" );
  2732 + Write( comment );
  2733 + Write( "-->" );
  2734 +}
  2735 +
  2736 +
  2737 +void XMLPrinter::PushDeclaration( const char* value )
  2738 +{
  2739 + SealElementIfJustOpened();
  2740 + if ( _textDepth < 0 && !_firstElement && !_compactMode) {
  2741 + Putc( '\n' );
  2742 + PrintSpace( _depth );
  2743 + }
  2744 + _firstElement = false;
  2745 +
  2746 + Write( "<?" );
  2747 + Write( value );
  2748 + Write( "?>" );
  2749 +}
  2750 +
  2751 +
  2752 +void XMLPrinter::PushUnknown( const char* value )
  2753 +{
  2754 + SealElementIfJustOpened();
  2755 + if ( _textDepth < 0 && !_firstElement && !_compactMode) {
  2756 + Putc( '\n' );
  2757 + PrintSpace( _depth );
  2758 + }
  2759 + _firstElement = false;
  2760 +
  2761 + Write( "<!" );
  2762 + Write( value );
  2763 + Putc( '>' );
  2764 +}
  2765 +
  2766 +
  2767 +bool XMLPrinter::VisitEnter( const XMLDocument& doc )
  2768 +{
  2769 + _processEntities = doc.ProcessEntities();
  2770 + if ( doc.HasBOM() ) {
  2771 + PushHeader( true, false );
  2772 + }
  2773 + return true;
  2774 +}
  2775 +
  2776 +
  2777 +bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
  2778 +{
  2779 + const XMLElement* parentElem = 0;
  2780 + if ( element.Parent() ) {
  2781 + parentElem = element.Parent()->ToElement();
  2782 + }
  2783 + const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
  2784 + OpenElement( element.Name(), compactMode );
  2785 + while ( attribute ) {
  2786 + PushAttribute( attribute->Name(), attribute->Value() );
  2787 + attribute = attribute->Next();
  2788 + }
  2789 + return true;
  2790 +}
  2791 +
  2792 +
  2793 +bool XMLPrinter::VisitExit( const XMLElement& element )
  2794 +{
  2795 + CloseElement( CompactMode(element) );
  2796 + return true;
  2797 +}
  2798 +
  2799 +
  2800 +bool XMLPrinter::Visit( const XMLText& text )
  2801 +{
  2802 + PushText( text.Value(), text.CData() );
  2803 + return true;
  2804 +}
  2805 +
  2806 +
  2807 +bool XMLPrinter::Visit( const XMLComment& comment )
  2808 +{
  2809 + PushComment( comment.Value() );
  2810 + return true;
  2811 +}
  2812 +
  2813 +bool XMLPrinter::Visit( const XMLDeclaration& declaration )
  2814 +{
  2815 + PushDeclaration( declaration.Value() );
  2816 + return true;
  2817 +}
  2818 +
  2819 +
  2820 +bool XMLPrinter::Visit( const XMLUnknown& unknown )
  2821 +{
  2822 + PushUnknown( unknown.Value() );
  2823 + return true;
  2824 +}
  2825 +
  2826 +} // namespace tinyxml2
... ...
sip/tinyxml2/tinyxml2.h 0 → 100644
  1 +++ a/sip/tinyxml2/tinyxml2.h
  1 +/*
  2 +Original code by Lee Thomason (www.grinninglizard.com)
  3 +
  4 +This software is provided 'as-is', without any express or implied
  5 +warranty. In no event will the authors be held liable for any
  6 +damages arising from the use of this software.
  7 +
  8 +Permission is granted to anyone to use this software for any
  9 +purpose, including commercial applications, and to alter it and
  10 +redistribute it freely, subject to the following restrictions:
  11 +
  12 +1. The origin of this software must not be misrepresented; you must
  13 +not claim that you wrote the original software. If you use this
  14 +software in a product, an acknowledgment in the product documentation
  15 +would be appreciated but is not required.
  16 +
  17 +2. Altered source versions must be plainly marked as such, and
  18 +must not be misrepresented as being the original software.
  19 +
  20 +3. This notice may not be removed or altered from any source
  21 +distribution.
  22 +*/
  23 +
  24 +#ifndef TINYXML2_INCLUDED
  25 +#define TINYXML2_INCLUDED
  26 +
  27 +#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__)
  28 +# include <ctype.h>
  29 +# include <limits.h>
  30 +# include <stdio.h>
  31 +# include <stdlib.h>
  32 +# include <string.h>
  33 +# if defined(__PS3__)
  34 +# include <stddef.h>
  35 +# endif
  36 +#else
  37 +# include <cctype>
  38 +# include <climits>
  39 +# include <cstdio>
  40 +# include <cstdlib>
  41 +# include <cstring>
  42 +#endif
  43 +#include <stdint.h>
  44 +
  45 +/*
  46 + TODO: intern strings instead of allocation.
  47 +*/
  48 +/*
  49 + gcc:
  50 + g++ -Wall -DTINYXML2_DEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe
  51 +
  52 + Formatting, Artistic Style:
  53 + AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h
  54 +*/
  55 +
  56 +#if defined( _DEBUG ) || defined (__DEBUG__)
  57 +# ifndef TINYXML2_DEBUG
  58 +# define TINYXML2_DEBUG
  59 +# endif
  60 +#endif
  61 +
  62 +#ifdef _MSC_VER
  63 +# pragma warning(push)
  64 +# pragma warning(disable: 4251)
  65 +#endif
  66 +
  67 +#ifdef _WIN32
  68 +# ifdef TINYXML2_EXPORT
  69 +# define TINYXML2_LIB __declspec(dllexport)
  70 +# elif defined(TINYXML2_IMPORT)
  71 +# define TINYXML2_LIB __declspec(dllimport)
  72 +# else
  73 +# define TINYXML2_LIB
  74 +# endif
  75 +#elif __GNUC__ >= 4
  76 +# define TINYXML2_LIB __attribute__((visibility("default")))
  77 +#else
  78 +# define TINYXML2_LIB
  79 +#endif
  80 +
  81 +
  82 +#if defined(TINYXML2_DEBUG)
  83 +# if defined(_MSC_VER)
  84 +# // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like
  85 +# define TIXMLASSERT( x ) if ( !((void)0,(x))) { __debugbreak(); }
  86 +# elif defined (ANDROID_NDK)
  87 +# include <android/log.h>
  88 +# define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); }
  89 +# else
  90 +# include <assert.h>
  91 +# define TIXMLASSERT assert
  92 +# endif
  93 +#else
  94 +# define TIXMLASSERT( x ) {}
  95 +#endif
  96 +
  97 +
  98 +/* Versioning, past 1.0.14:
  99 + http://semver.org/
  100 +*/
  101 +static const int TIXML2_MAJOR_VERSION = 6;
  102 +static const int TIXML2_MINOR_VERSION = 2;
  103 +static const int TIXML2_PATCH_VERSION = 0;
  104 +
  105 +#define TINYXML2_MAJOR_VERSION 6
  106 +#define TINYXML2_MINOR_VERSION 2
  107 +#define TINYXML2_PATCH_VERSION 0
  108 +
  109 +// A fixed element depth limit is problematic. There needs to be a
  110 +// limit to avoid a stack overflow. However, that limit varies per
  111 +// system, and the capacity of the stack. On the other hand, it's a trivial
  112 +// attack that can result from ill, malicious, or even correctly formed XML,
  113 +// so there needs to be a limit in place.
  114 +static const int TINYXML2_MAX_ELEMENT_DEPTH = 100;
  115 +
  116 +namespace tinyxml2
  117 +{
  118 +class XMLDocument;
  119 +class XMLElement;
  120 +class XMLAttribute;
  121 +class XMLComment;
  122 +class XMLText;
  123 +class XMLDeclaration;
  124 +class XMLUnknown;
  125 +class XMLPrinter;
  126 +
  127 +/*
  128 + A class that wraps strings. Normally stores the start and end
  129 + pointers into the XML file itself, and will apply normalization
  130 + and entity translation if actually read. Can also store (and memory
  131 + manage) a traditional char[]
  132 +*/
  133 +class StrPair
  134 +{
  135 +public:
  136 + enum {
  137 + NEEDS_ENTITY_PROCESSING = 0x01,
  138 + NEEDS_NEWLINE_NORMALIZATION = 0x02,
  139 + NEEDS_WHITESPACE_COLLAPSING = 0x04,
  140 +
  141 + TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
  142 + TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION,
  143 + ATTRIBUTE_NAME = 0,
  144 + ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
  145 + ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION,
  146 + COMMENT = NEEDS_NEWLINE_NORMALIZATION
  147 + };
  148 +
  149 + StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {}
  150 + ~StrPair();
  151 +
  152 + void Set( char* start, char* end, int flags ) {
  153 + TIXMLASSERT( start );
  154 + TIXMLASSERT( end );
  155 + Reset();
  156 + _start = start;
  157 + _end = end;
  158 + _flags = flags | NEEDS_FLUSH;
  159 + }
  160 +
  161 + const char* GetStr();
  162 +
  163 + bool Empty() const {
  164 + return _start == _end;
  165 + }
  166 +
  167 + void SetInternedStr( const char* str ) {
  168 + Reset();
  169 + _start = const_cast<char*>(str);
  170 + }
  171 +
  172 + void SetStr( const char* str, int flags=0 );
  173 +
  174 + char* ParseText( char* in, const char* endTag, int strFlags, int* curLineNumPtr );
  175 + char* ParseName( char* in );
  176 +
  177 + void TransferTo( StrPair* other );
  178 + void Reset();
  179 +
  180 +private:
  181 + void CollapseWhitespace();
  182 +
  183 + enum {
  184 + NEEDS_FLUSH = 0x100,
  185 + NEEDS_DELETE = 0x200
  186 + };
  187 +
  188 + int _flags;
  189 + char* _start;
  190 + char* _end;
  191 +
  192 + StrPair( const StrPair& other ); // not supported
  193 + void operator=( StrPair& other ); // not supported, use TransferTo()
  194 +};
  195 +
  196 +
  197 +/*
  198 + A dynamic array of Plain Old Data. Doesn't support constructors, etc.
  199 + Has a small initial memory pool, so that low or no usage will not
  200 + cause a call to new/delete
  201 +*/
  202 +template <class T, int INITIAL_SIZE>
  203 +class DynArray
  204 +{
  205 +public:
  206 + DynArray() :
  207 + _mem( _pool ),
  208 + _allocated( INITIAL_SIZE ),
  209 + _size( 0 )
  210 + {
  211 + }
  212 +
  213 + ~DynArray() {
  214 + if ( _mem != _pool ) {
  215 + delete [] _mem;
  216 + }
  217 + }
  218 +
  219 + void Clear() {
  220 + _size = 0;
  221 + }
  222 +
  223 + void Push( T t ) {
  224 + TIXMLASSERT( _size < INT_MAX );
  225 + EnsureCapacity( _size+1 );
  226 + _mem[_size] = t;
  227 + ++_size;
  228 + }
  229 +
  230 + T* PushArr( int count ) {
  231 + TIXMLASSERT( count >= 0 );
  232 + TIXMLASSERT( _size <= INT_MAX - count );
  233 + EnsureCapacity( _size+count );
  234 + T* ret = &_mem[_size];
  235 + _size += count;
  236 + return ret;
  237 + }
  238 +
  239 + T Pop() {
  240 + TIXMLASSERT( _size > 0 );
  241 + --_size;
  242 + return _mem[_size];
  243 + }
  244 +
  245 + void PopArr( int count ) {
  246 + TIXMLASSERT( _size >= count );
  247 + _size -= count;
  248 + }
  249 +
  250 + bool Empty() const {
  251 + return _size == 0;
  252 + }
  253 +
  254 + T& operator[](int i) {
  255 + TIXMLASSERT( i>= 0 && i < _size );
  256 + return _mem[i];
  257 + }
  258 +
  259 + const T& operator[](int i) const {
  260 + TIXMLASSERT( i>= 0 && i < _size );
  261 + return _mem[i];
  262 + }
  263 +
  264 + const T& PeekTop() const {
  265 + TIXMLASSERT( _size > 0 );
  266 + return _mem[ _size - 1];
  267 + }
  268 +
  269 + int Size() const {
  270 + TIXMLASSERT( _size >= 0 );
  271 + return _size;
  272 + }
  273 +
  274 + int Capacity() const {
  275 + TIXMLASSERT( _allocated >= INITIAL_SIZE );
  276 + return _allocated;
  277 + }
  278 +
  279 + void SwapRemove(int i) {
  280 + TIXMLASSERT(i >= 0 && i < _size);
  281 + TIXMLASSERT(_size > 0);
  282 + _mem[i] = _mem[_size - 1];
  283 + --_size;
  284 + }
  285 +
  286 + const T* Mem() const {
  287 + TIXMLASSERT( _mem );
  288 + return _mem;
  289 + }
  290 +
  291 + T* Mem() {
  292 + TIXMLASSERT( _mem );
  293 + return _mem;
  294 + }
  295 +
  296 +private:
  297 + DynArray( const DynArray& ); // not supported
  298 + void operator=( const DynArray& ); // not supported
  299 +
  300 + void EnsureCapacity( int cap ) {
  301 + TIXMLASSERT( cap > 0 );
  302 + if ( cap > _allocated ) {
  303 + TIXMLASSERT( cap <= INT_MAX / 2 );
  304 + int newAllocated = cap * 2;
  305 + T* newMem = new T[newAllocated];
  306 + TIXMLASSERT( newAllocated >= _size );
  307 + memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs
  308 + if ( _mem != _pool ) {
  309 + delete [] _mem;
  310 + }
  311 + _mem = newMem;
  312 + _allocated = newAllocated;
  313 + }
  314 + }
  315 +
  316 + T* _mem;
  317 + T _pool[INITIAL_SIZE];
  318 + int _allocated; // objects allocated
  319 + int _size; // number objects in use
  320 +};
  321 +
  322 +
  323 +/*
  324 + Parent virtual class of a pool for fast allocation
  325 + and deallocation of objects.
  326 +*/
  327 +class MemPool
  328 +{
  329 +public:
  330 + MemPool() {}
  331 + virtual ~MemPool() {}
  332 +
  333 + virtual int ItemSize() const = 0;
  334 + virtual void* Alloc() = 0;
  335 + virtual void Free( void* ) = 0;
  336 + virtual void SetTracked() = 0;
  337 + virtual void Clear() = 0;
  338 +};
  339 +
  340 +
  341 +/*
  342 + Template child class to create pools of the correct type.
  343 +*/
  344 +template< int ITEM_SIZE >
  345 +class MemPoolT : public MemPool
  346 +{
  347 +public:
  348 + MemPoolT() : _blockPtrs(), _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {}
  349 + ~MemPoolT() {
  350 + Clear();
  351 + }
  352 +
  353 + void Clear() {
  354 + // Delete the blocks.
  355 + while( !_blockPtrs.Empty()) {
  356 + Block* lastBlock = _blockPtrs.Pop();
  357 + delete lastBlock;
  358 + }
  359 + _root = 0;
  360 + _currentAllocs = 0;
  361 + _nAllocs = 0;
  362 + _maxAllocs = 0;
  363 + _nUntracked = 0;
  364 + }
  365 +
  366 + virtual int ItemSize() const {
  367 + return ITEM_SIZE;
  368 + }
  369 + int CurrentAllocs() const {
  370 + return _currentAllocs;
  371 + }
  372 +
  373 + virtual void* Alloc() {
  374 + if ( !_root ) {
  375 + // Need a new block.
  376 + Block* block = new Block();
  377 + _blockPtrs.Push( block );
  378 +
  379 + Item* blockItems = block->items;
  380 + for( int i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) {
  381 + blockItems[i].next = &(blockItems[i + 1]);
  382 + }
  383 + blockItems[ITEMS_PER_BLOCK - 1].next = 0;
  384 + _root = blockItems;
  385 + }
  386 + Item* const result = _root;
  387 + TIXMLASSERT( result != 0 );
  388 + _root = _root->next;
  389 +
  390 + ++_currentAllocs;
  391 + if ( _currentAllocs > _maxAllocs ) {
  392 + _maxAllocs = _currentAllocs;
  393 + }
  394 + ++_nAllocs;
  395 + ++_nUntracked;
  396 + return result;
  397 + }
  398 +
  399 + virtual void Free( void* mem ) {
  400 + if ( !mem ) {
  401 + return;
  402 + }
  403 + --_currentAllocs;
  404 + Item* item = static_cast<Item*>( mem );
  405 +#ifdef TINYXML2_DEBUG
  406 + memset( item, 0xfe, sizeof( *item ) );
  407 +#endif
  408 + item->next = _root;
  409 + _root = item;
  410 + }
  411 + void Trace( const char* name ) {
  412 + printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n",
  413 + name, _maxAllocs, _maxAllocs * ITEM_SIZE / 1024, _currentAllocs,
  414 + ITEM_SIZE, _nAllocs, _blockPtrs.Size() );
  415 + }
  416 +
  417 + void SetTracked() {
  418 + --_nUntracked;
  419 + }
  420 +
  421 + int Untracked() const {
  422 + return _nUntracked;
  423 + }
  424 +
  425 + // This number is perf sensitive. 4k seems like a good tradeoff on my machine.
  426 + // The test file is large, 170k.
  427 + // Release: VS2010 gcc(no opt)
  428 + // 1k: 4000
  429 + // 2k: 4000
  430 + // 4k: 3900 21000
  431 + // 16k: 5200
  432 + // 32k: 4300
  433 + // 64k: 4000 21000
  434 + // Declared public because some compilers do not accept to use ITEMS_PER_BLOCK
  435 + // in private part if ITEMS_PER_BLOCK is private
  436 + enum { ITEMS_PER_BLOCK = (4 * 1024) / ITEM_SIZE };
  437 +
  438 +private:
  439 + MemPoolT( const MemPoolT& ); // not supported
  440 + void operator=( const MemPoolT& ); // not supported
  441 +
  442 + union Item {
  443 + Item* next;
  444 + char itemData[ITEM_SIZE];
  445 + };
  446 + struct Block {
  447 + Item items[ITEMS_PER_BLOCK];
  448 + };
  449 + DynArray< Block*, 10 > _blockPtrs;
  450 + Item* _root;
  451 +
  452 + int _currentAllocs;
  453 + int _nAllocs;
  454 + int _maxAllocs;
  455 + int _nUntracked;
  456 +};
  457 +
  458 +
  459 +
  460 +/**
  461 + Implements the interface to the "Visitor pattern" (see the Accept() method.)
  462 + If you call the Accept() method, it requires being passed a XMLVisitor
  463 + class to handle callbacks. For nodes that contain other nodes (Document, Element)
  464 + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs
  465 + are simply called with Visit().
  466 +
  467 + If you return 'true' from a Visit method, recursive parsing will continue. If you return
  468 + false, <b>no children of this node or its siblings</b> will be visited.
  469 +
  470 + All flavors of Visit methods have a default implementation that returns 'true' (continue
  471 + visiting). You need to only override methods that are interesting to you.
  472 +
  473 + Generally Accept() is called on the XMLDocument, although all nodes support visiting.
  474 +
  475 + You should never change the document from a callback.
  476 +
  477 + @sa XMLNode::Accept()
  478 +*/
  479 +class TINYXML2_LIB XMLVisitor
  480 +{
  481 +public:
  482 + virtual ~XMLVisitor() {}
  483 +
  484 + /// Visit a document.
  485 + virtual bool VisitEnter( const XMLDocument& /*doc*/ ) {
  486 + return true;
  487 + }
  488 + /// Visit a document.
  489 + virtual bool VisitExit( const XMLDocument& /*doc*/ ) {
  490 + return true;
  491 + }
  492 +
  493 + /// Visit an element.
  494 + virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ ) {
  495 + return true;
  496 + }
  497 + /// Visit an element.
  498 + virtual bool VisitExit( const XMLElement& /*element*/ ) {
  499 + return true;
  500 + }
  501 +
  502 + /// Visit a declaration.
  503 + virtual bool Visit( const XMLDeclaration& /*declaration*/ ) {
  504 + return true;
  505 + }
  506 + /// Visit a text node.
  507 + virtual bool Visit( const XMLText& /*text*/ ) {
  508 + return true;
  509 + }
  510 + /// Visit a comment node.
  511 + virtual bool Visit( const XMLComment& /*comment*/ ) {
  512 + return true;
  513 + }
  514 + /// Visit an unknown node.
  515 + virtual bool Visit( const XMLUnknown& /*unknown*/ ) {
  516 + return true;
  517 + }
  518 +};
  519 +
  520 +// WARNING: must match XMLDocument::_errorNames[]
  521 +enum XMLError {
  522 + XML_SUCCESS = 0,
  523 + XML_NO_ATTRIBUTE,
  524 + XML_WRONG_ATTRIBUTE_TYPE,
  525 + XML_ERROR_FILE_NOT_FOUND,
  526 + XML_ERROR_FILE_COULD_NOT_BE_OPENED,
  527 + XML_ERROR_FILE_READ_ERROR,
  528 + UNUSED_XML_ERROR_ELEMENT_MISMATCH, // remove at next major version
  529 + XML_ERROR_PARSING_ELEMENT,
  530 + XML_ERROR_PARSING_ATTRIBUTE,
  531 + UNUSED_XML_ERROR_IDENTIFYING_TAG, // remove at next major version
  532 + XML_ERROR_PARSING_TEXT,
  533 + XML_ERROR_PARSING_CDATA,
  534 + XML_ERROR_PARSING_COMMENT,
  535 + XML_ERROR_PARSING_DECLARATION,
  536 + XML_ERROR_PARSING_UNKNOWN,
  537 + XML_ERROR_EMPTY_DOCUMENT,
  538 + XML_ERROR_MISMATCHED_ELEMENT,
  539 + XML_ERROR_PARSING,
  540 + XML_CAN_NOT_CONVERT_TEXT,
  541 + XML_NO_TEXT_NODE,
  542 + XML_ELEMENT_DEPTH_EXCEEDED,
  543 +
  544 + XML_ERROR_COUNT
  545 +};
  546 +
  547 +
  548 +/*
  549 + Utility functionality.
  550 +*/
  551 +class TINYXML2_LIB XMLUtil
  552 +{
  553 +public:
  554 + static const char* SkipWhiteSpace( const char* p, int* curLineNumPtr ) {
  555 + TIXMLASSERT( p );
  556 +
  557 + while( IsWhiteSpace(*p) ) {
  558 + if (curLineNumPtr && *p == '\n') {
  559 + ++(*curLineNumPtr);
  560 + }
  561 + ++p;
  562 + }
  563 + TIXMLASSERT( p );
  564 + return p;
  565 + }
  566 + static char* SkipWhiteSpace( char* p, int* curLineNumPtr ) {
  567 + return const_cast<char*>( SkipWhiteSpace( const_cast<const char*>(p), curLineNumPtr ) );
  568 + }
  569 +
  570 + // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't
  571 + // correct, but simple, and usually works.
  572 + static bool IsWhiteSpace( char p ) {
  573 + return !IsUTF8Continuation(p) && isspace( static_cast<unsigned char>(p) );
  574 + }
  575 +
  576 + inline static bool IsNameStartChar( unsigned char ch ) {
  577 + if ( ch >= 128 ) {
  578 + // This is a heuristic guess in attempt to not implement Unicode-aware isalpha()
  579 + return true;
  580 + }
  581 + if ( isalpha( ch ) ) {
  582 + return true;
  583 + }
  584 + return ch == ':' || ch == '_';
  585 + }
  586 +
  587 + inline static bool IsNameChar( unsigned char ch ) {
  588 + return IsNameStartChar( ch )
  589 + || isdigit( ch )
  590 + || ch == '.'
  591 + || ch == '-';
  592 + }
  593 +
  594 + inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) {
  595 + if ( p == q ) {
  596 + return true;
  597 + }
  598 + TIXMLASSERT( p );
  599 + TIXMLASSERT( q );
  600 + TIXMLASSERT( nChar >= 0 );
  601 + return strncmp( p, q, nChar ) == 0;
  602 + }
  603 +
  604 + inline static bool IsUTF8Continuation( char p ) {
  605 + return ( p & 0x80 ) != 0;
  606 + }
  607 +
  608 + static const char* ReadBOM( const char* p, bool* hasBOM );
  609 + // p is the starting location,
  610 + // the UTF-8 value of the entity will be placed in value, and length filled in.
  611 + static const char* GetCharacterRef( const char* p, char* value, int* length );
  612 + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length );
  613 +
  614 + // converts primitive types to strings
  615 + static void ToStr( int v, char* buffer, int bufferSize );
  616 + static void ToStr( unsigned v, char* buffer, int bufferSize );
  617 + static void ToStr( bool v, char* buffer, int bufferSize );
  618 + static void ToStr( float v, char* buffer, int bufferSize );
  619 + static void ToStr( double v, char* buffer, int bufferSize );
  620 + static void ToStr(int64_t v, char* buffer, int bufferSize);
  621 +
  622 + // converts strings to primitive types
  623 + static bool ToInt( const char* str, int* value );
  624 + static bool ToUnsigned( const char* str, unsigned* value );
  625 + static bool ToBool( const char* str, bool* value );
  626 + static bool ToFloat( const char* str, float* value );
  627 + static bool ToDouble( const char* str, double* value );
  628 + static bool ToInt64(const char* str, int64_t* value);
  629 +
  630 + // Changes what is serialized for a boolean value.
  631 + // Default to "true" and "false". Shouldn't be changed
  632 + // unless you have a special testing or compatibility need.
  633 + // Be careful: static, global, & not thread safe.
  634 + // Be sure to set static const memory as parameters.
  635 + static void SetBoolSerialization(const char* writeTrue, const char* writeFalse);
  636 +
  637 +private:
  638 + static const char* writeBoolTrue;
  639 + static const char* writeBoolFalse;
  640 +};
  641 +
  642 +
  643 +/** XMLNode is a base class for every object that is in the
  644 + XML Document Object Model (DOM), except XMLAttributes.
  645 + Nodes have siblings, a parent, and children which can
  646 + be navigated. A node is always in a XMLDocument.
  647 + The type of a XMLNode can be queried, and it can
  648 + be cast to its more defined type.
  649 +
  650 + A XMLDocument allocates memory for all its Nodes.
  651 + When the XMLDocument gets deleted, all its Nodes
  652 + will also be deleted.
  653 +
  654 + @verbatim
  655 + A Document can contain: Element (container or leaf)
  656 + Comment (leaf)
  657 + Unknown (leaf)
  658 + Declaration( leaf )
  659 +
  660 + An Element can contain: Element (container or leaf)
  661 + Text (leaf)
  662 + Attributes (not on tree)
  663 + Comment (leaf)
  664 + Unknown (leaf)
  665 +
  666 + @endverbatim
  667 +*/
  668 +class TINYXML2_LIB XMLNode
  669 +{
  670 + friend class XMLDocument;
  671 + friend class XMLElement;
  672 +public:
  673 +
  674 + /// Get the XMLDocument that owns this XMLNode.
  675 + const XMLDocument* GetDocument() const {
  676 + TIXMLASSERT( _document );
  677 + return _document;
  678 + }
  679 + /// Get the XMLDocument that owns this XMLNode.
  680 + XMLDocument* GetDocument() {
  681 + TIXMLASSERT( _document );
  682 + return _document;
  683 + }
  684 +
  685 + /// Safely cast to an Element, or null.
  686 + virtual XMLElement* ToElement() {
  687 + return 0;
  688 + }
  689 + /// Safely cast to Text, or null.
  690 + virtual XMLText* ToText() {
  691 + return 0;
  692 + }
  693 + /// Safely cast to a Comment, or null.
  694 + virtual XMLComment* ToComment() {
  695 + return 0;
  696 + }
  697 + /// Safely cast to a Document, or null.
  698 + virtual XMLDocument* ToDocument() {
  699 + return 0;
  700 + }
  701 + /// Safely cast to a Declaration, or null.
  702 + virtual XMLDeclaration* ToDeclaration() {
  703 + return 0;
  704 + }
  705 + /// Safely cast to an Unknown, or null.
  706 + virtual XMLUnknown* ToUnknown() {
  707 + return 0;
  708 + }
  709 +
  710 + virtual const XMLElement* ToElement() const {
  711 + return 0;
  712 + }
  713 + virtual const XMLText* ToText() const {
  714 + return 0;
  715 + }
  716 + virtual const XMLComment* ToComment() const {
  717 + return 0;
  718 + }
  719 + virtual const XMLDocument* ToDocument() const {
  720 + return 0;
  721 + }
  722 + virtual const XMLDeclaration* ToDeclaration() const {
  723 + return 0;
  724 + }
  725 + virtual const XMLUnknown* ToUnknown() const {
  726 + return 0;
  727 + }
  728 +
  729 + /** The meaning of 'value' changes for the specific type.
  730 + @verbatim
  731 + Document: empty (NULL is returned, not an empty string)
  732 + Element: name of the element
  733 + Comment: the comment text
  734 + Unknown: the tag contents
  735 + Text: the text string
  736 + @endverbatim
  737 + */
  738 + const char* Value() const;
  739 +
  740 + /** Set the Value of an XML node.
  741 + @sa Value()
  742 + */
  743 + void SetValue( const char* val, bool staticMem=false );
  744 +
  745 + /// Gets the line number the node is in, if the document was parsed from a file.
  746 + int GetLineNum() const { return _parseLineNum; }
  747 +
  748 + /// Get the parent of this node on the DOM.
  749 + const XMLNode* Parent() const {
  750 + return _parent;
  751 + }
  752 +
  753 + XMLNode* Parent() {
  754 + return _parent;
  755 + }
  756 +
  757 + /// Returns true if this node has no children.
  758 + bool NoChildren() const {
  759 + return !_firstChild;
  760 + }
  761 +
  762 + /// Get the first child node, or null if none exists.
  763 + const XMLNode* FirstChild() const {
  764 + return _firstChild;
  765 + }
  766 +
  767 + XMLNode* FirstChild() {
  768 + return _firstChild;
  769 + }
  770 +
  771 + /** Get the first child element, or optionally the first child
  772 + element with the specified name.
  773 + */
  774 + const XMLElement* FirstChildElement( const char* name = 0 ) const;
  775 +
  776 + XMLElement* FirstChildElement( const char* name = 0 ) {
  777 + return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->FirstChildElement( name ));
  778 + }
  779 +
  780 + /// Get the last child node, or null if none exists.
  781 + const XMLNode* LastChild() const {
  782 + return _lastChild;
  783 + }
  784 +
  785 + XMLNode* LastChild() {
  786 + return _lastChild;
  787 + }
  788 +
  789 + /** Get the last child element or optionally the last child
  790 + element with the specified name.
  791 + */
  792 + const XMLElement* LastChildElement( const char* name = 0 ) const;
  793 +
  794 + XMLElement* LastChildElement( const char* name = 0 ) {
  795 + return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->LastChildElement(name) );
  796 + }
  797 +
  798 + /// Get the previous (left) sibling node of this node.
  799 + const XMLNode* PreviousSibling() const {
  800 + return _prev;
  801 + }
  802 +
  803 + XMLNode* PreviousSibling() {
  804 + return _prev;
  805 + }
  806 +
  807 + /// Get the previous (left) sibling element of this node, with an optionally supplied name.
  808 + const XMLElement* PreviousSiblingElement( const char* name = 0 ) const ;
  809 +
  810 + XMLElement* PreviousSiblingElement( const char* name = 0 ) {
  811 + return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->PreviousSiblingElement( name ) );
  812 + }
  813 +
  814 + /// Get the next (right) sibling node of this node.
  815 + const XMLNode* NextSibling() const {
  816 + return _next;
  817 + }
  818 +
  819 + XMLNode* NextSibling() {
  820 + return _next;
  821 + }
  822 +
  823 + /// Get the next (right) sibling element of this node, with an optionally supplied name.
  824 + const XMLElement* NextSiblingElement( const char* name = 0 ) const;
  825 +
  826 + XMLElement* NextSiblingElement( const char* name = 0 ) {
  827 + return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->NextSiblingElement( name ) );
  828 + }
  829 +
  830 + /**
  831 + Add a child node as the last (right) child.
  832 + If the child node is already part of the document,
  833 + it is moved from its old location to the new location.
  834 + Returns the addThis argument or 0 if the node does not
  835 + belong to the same document.
  836 + */
  837 + XMLNode* InsertEndChild( XMLNode* addThis );
  838 +
  839 + XMLNode* LinkEndChild( XMLNode* addThis ) {
  840 + return InsertEndChild( addThis );
  841 + }
  842 + /**
  843 + Add a child node as the first (left) child.
  844 + If the child node is already part of the document,
  845 + it is moved from its old location to the new location.
  846 + Returns the addThis argument or 0 if the node does not
  847 + belong to the same document.
  848 + */
  849 + XMLNode* InsertFirstChild( XMLNode* addThis );
  850 + /**
  851 + Add a node after the specified child node.
  852 + If the child node is already part of the document,
  853 + it is moved from its old location to the new location.
  854 + Returns the addThis argument or 0 if the afterThis node
  855 + is not a child of this node, or if the node does not
  856 + belong to the same document.
  857 + */
  858 + XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis );
  859 +
  860 + /**
  861 + Delete all the children of this node.
  862 + */
  863 + void DeleteChildren();
  864 +
  865 + /**
  866 + Delete a child of this node.
  867 + */
  868 + void DeleteChild( XMLNode* node );
  869 +
  870 + /**
  871 + Make a copy of this node, but not its children.
  872 + You may pass in a Document pointer that will be
  873 + the owner of the new Node. If the 'document' is
  874 + null, then the node returned will be allocated
  875 + from the current Document. (this->GetDocument())
  876 +
  877 + Note: if called on a XMLDocument, this will return null.
  878 + */
  879 + virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0;
  880 +
  881 + /**
  882 + Make a copy of this node and all its children.
  883 +
  884 + If the 'target' is null, then the nodes will
  885 + be allocated in the current document. If 'target'
  886 + is specified, the memory will be allocated is the
  887 + specified XMLDocument.
  888 +
  889 + NOTE: This is probably not the correct tool to
  890 + copy a document, since XMLDocuments can have multiple
  891 + top level XMLNodes. You probably want to use
  892 + XMLDocument::DeepCopy()
  893 + */
  894 + XMLNode* DeepClone( XMLDocument* target ) const;
  895 +
  896 + /**
  897 + Test if 2 nodes are the same, but don't test children.
  898 + The 2 nodes do not need to be in the same Document.
  899 +
  900 + Note: if called on a XMLDocument, this will return false.
  901 + */
  902 + virtual bool ShallowEqual( const XMLNode* compare ) const = 0;
  903 +
  904 + /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the
  905 + XML tree will be conditionally visited and the host will be called back
  906 + via the XMLVisitor interface.
  907 +
  908 + This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse
  909 + the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this
  910 + interface versus any other.)
  911 +
  912 + The interface has been based on ideas from:
  913 +
  914 + - http://www.saxproject.org/
  915 + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern
  916 +
  917 + Which are both good references for "visiting".
  918 +
  919 + An example of using Accept():
  920 + @verbatim
  921 + XMLPrinter printer;
  922 + tinyxmlDoc.Accept( &printer );
  923 + const char* xmlcstr = printer.CStr();
  924 + @endverbatim
  925 + */
  926 + virtual bool Accept( XMLVisitor* visitor ) const = 0;
  927 +
  928 + /**
  929 + Set user data into the XMLNode. TinyXML-2 in
  930 + no way processes or interprets user data.
  931 + It is initially 0.
  932 + */
  933 + void SetUserData(void* userData) { _userData = userData; }
  934 +
  935 + /**
  936 + Get user data set into the XMLNode. TinyXML-2 in
  937 + no way processes or interprets user data.
  938 + It is initially 0.
  939 + */
  940 + void* GetUserData() const { return _userData; }
  941 +
  942 +protected:
  943 + XMLNode( XMLDocument* );
  944 + virtual ~XMLNode();
  945 +
  946 + virtual char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr);
  947 +
  948 + XMLDocument* _document;
  949 + XMLNode* _parent;
  950 + mutable StrPair _value;
  951 + int _parseLineNum;
  952 +
  953 + XMLNode* _firstChild;
  954 + XMLNode* _lastChild;
  955 +
  956 + XMLNode* _prev;
  957 + XMLNode* _next;
  958 +
  959 + void* _userData;
  960 +
  961 +private:
  962 + MemPool* _memPool;
  963 + void Unlink( XMLNode* child );
  964 + static void DeleteNode( XMLNode* node );
  965 + void InsertChildPreamble( XMLNode* insertThis ) const;
  966 + const XMLElement* ToElementWithName( const char* name ) const;
  967 +
  968 + XMLNode( const XMLNode& ); // not supported
  969 + XMLNode& operator=( const XMLNode& ); // not supported
  970 +};
  971 +
  972 +
  973 +/** XML text.
  974 +
  975 + Note that a text node can have child element nodes, for example:
  976 + @verbatim
  977 + <root>This is <b>bold</b></root>
  978 + @endverbatim
  979 +
  980 + A text node can have 2 ways to output the next. "normal" output
  981 + and CDATA. It will default to the mode it was parsed from the XML file and
  982 + you generally want to leave it alone, but you can change the output mode with
  983 + SetCData() and query it with CData().
  984 +*/
  985 +class TINYXML2_LIB XMLText : public XMLNode
  986 +{
  987 + friend class XMLDocument;
  988 +public:
  989 + virtual bool Accept( XMLVisitor* visitor ) const;
  990 +
  991 + virtual XMLText* ToText() {
  992 + return this;
  993 + }
  994 + virtual const XMLText* ToText() const {
  995 + return this;
  996 + }
  997 +
  998 + /// Declare whether this should be CDATA or standard text.
  999 + void SetCData( bool isCData ) {
  1000 + _isCData = isCData;
  1001 + }
  1002 + /// Returns true if this is a CDATA text element.
  1003 + bool CData() const {
  1004 + return _isCData;
  1005 + }
  1006 +
  1007 + virtual XMLNode* ShallowClone( XMLDocument* document ) const;
  1008 + virtual bool ShallowEqual( const XMLNode* compare ) const;
  1009 +
  1010 +protected:
  1011 + XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {}
  1012 + virtual ~XMLText() {}
  1013 +
  1014 + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );
  1015 +
  1016 +private:
  1017 + bool _isCData;
  1018 +
  1019 + XMLText( const XMLText& ); // not supported
  1020 + XMLText& operator=( const XMLText& ); // not supported
  1021 +};
  1022 +
  1023 +
  1024 +/** An XML Comment. */
  1025 +class TINYXML2_LIB XMLComment : public XMLNode
  1026 +{
  1027 + friend class XMLDocument;
  1028 +public:
  1029 + virtual XMLComment* ToComment() {
  1030 + return this;
  1031 + }
  1032 + virtual const XMLComment* ToComment() const {
  1033 + return this;
  1034 + }
  1035 +
  1036 + virtual bool Accept( XMLVisitor* visitor ) const;
  1037 +
  1038 + virtual XMLNode* ShallowClone( XMLDocument* document ) const;
  1039 + virtual bool ShallowEqual( const XMLNode* compare ) const;
  1040 +
  1041 +protected:
  1042 + XMLComment( XMLDocument* doc );
  1043 + virtual ~XMLComment();
  1044 +
  1045 + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr);
  1046 +
  1047 +private:
  1048 + XMLComment( const XMLComment& ); // not supported
  1049 + XMLComment& operator=( const XMLComment& ); // not supported
  1050 +};
  1051 +
  1052 +
  1053 +/** In correct XML the declaration is the first entry in the file.
  1054 + @verbatim
  1055 + <?xml version="1.0" standalone="yes"?>
  1056 + @endverbatim
  1057 +
  1058 + TinyXML-2 will happily read or write files without a declaration,
  1059 + however.
  1060 +
  1061 + The text of the declaration isn't interpreted. It is parsed
  1062 + and written as a string.
  1063 +*/
  1064 +class TINYXML2_LIB XMLDeclaration : public XMLNode
  1065 +{
  1066 + friend class XMLDocument;
  1067 +public:
  1068 + virtual XMLDeclaration* ToDeclaration() {
  1069 + return this;
  1070 + }
  1071 + virtual const XMLDeclaration* ToDeclaration() const {
  1072 + return this;
  1073 + }
  1074 +
  1075 + virtual bool Accept( XMLVisitor* visitor ) const;
  1076 +
  1077 + virtual XMLNode* ShallowClone( XMLDocument* document ) const;
  1078 + virtual bool ShallowEqual( const XMLNode* compare ) const;
  1079 +
  1080 +protected:
  1081 + XMLDeclaration( XMLDocument* doc );
  1082 + virtual ~XMLDeclaration();
  1083 +
  1084 + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );
  1085 +
  1086 +private:
  1087 + XMLDeclaration( const XMLDeclaration& ); // not supported
  1088 + XMLDeclaration& operator=( const XMLDeclaration& ); // not supported
  1089 +};
  1090 +
  1091 +
  1092 +/** Any tag that TinyXML-2 doesn't recognize is saved as an
  1093 + unknown. It is a tag of text, but should not be modified.
  1094 + It will be written back to the XML, unchanged, when the file
  1095 + is saved.
  1096 +
  1097 + DTD tags get thrown into XMLUnknowns.
  1098 +*/
  1099 +class TINYXML2_LIB XMLUnknown : public XMLNode
  1100 +{
  1101 + friend class XMLDocument;
  1102 +public:
  1103 + virtual XMLUnknown* ToUnknown() {
  1104 + return this;
  1105 + }
  1106 + virtual const XMLUnknown* ToUnknown() const {
  1107 + return this;
  1108 + }
  1109 +
  1110 + virtual bool Accept( XMLVisitor* visitor ) const;
  1111 +
  1112 + virtual XMLNode* ShallowClone( XMLDocument* document ) const;
  1113 + virtual bool ShallowEqual( const XMLNode* compare ) const;
  1114 +
  1115 +protected:
  1116 + XMLUnknown( XMLDocument* doc );
  1117 + virtual ~XMLUnknown();
  1118 +
  1119 + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );
  1120 +
  1121 +private:
  1122 + XMLUnknown( const XMLUnknown& ); // not supported
  1123 + XMLUnknown& operator=( const XMLUnknown& ); // not supported
  1124 +};
  1125 +
  1126 +
  1127 +
  1128 +/** An attribute is a name-value pair. Elements have an arbitrary
  1129 + number of attributes, each with a unique name.
  1130 +
  1131 + @note The attributes are not XMLNodes. You may only query the
  1132 + Next() attribute in a list.
  1133 +*/
  1134 +class TINYXML2_LIB XMLAttribute
  1135 +{
  1136 + friend class XMLElement;
  1137 +public:
  1138 + /// The name of the attribute.
  1139 + const char* Name() const;
  1140 +
  1141 + /// The value of the attribute.
  1142 + const char* Value() const;
  1143 +
  1144 + /// Gets the line number the attribute is in, if the document was parsed from a file.
  1145 + int GetLineNum() const { return _parseLineNum; }
  1146 +
  1147 + /// The next attribute in the list.
  1148 + const XMLAttribute* Next() const {
  1149 + return _next;
  1150 + }
  1151 +
  1152 + /** IntValue interprets the attribute as an integer, and returns the value.
  1153 + If the value isn't an integer, 0 will be returned. There is no error checking;
  1154 + use QueryIntValue() if you need error checking.
  1155 + */
  1156 + int IntValue() const {
  1157 + int i = 0;
  1158 + QueryIntValue(&i);
  1159 + return i;
  1160 + }
  1161 +
  1162 + int64_t Int64Value() const {
  1163 + int64_t i = 0;
  1164 + QueryInt64Value(&i);
  1165 + return i;
  1166 + }
  1167 +
  1168 + /// Query as an unsigned integer. See IntValue()
  1169 + unsigned UnsignedValue() const {
  1170 + unsigned i=0;
  1171 + QueryUnsignedValue( &i );
  1172 + return i;
  1173 + }
  1174 + /// Query as a boolean. See IntValue()
  1175 + bool BoolValue() const {
  1176 + bool b=false;
  1177 + QueryBoolValue( &b );
  1178 + return b;
  1179 + }
  1180 + /// Query as a double. See IntValue()
  1181 + double DoubleValue() const {
  1182 + double d=0;
  1183 + QueryDoubleValue( &d );
  1184 + return d;
  1185 + }
  1186 + /// Query as a float. See IntValue()
  1187 + float FloatValue() const {
  1188 + float f=0;
  1189 + QueryFloatValue( &f );
  1190 + return f;
  1191 + }
  1192 +
  1193 + /** QueryIntValue interprets the attribute as an integer, and returns the value
  1194 + in the provided parameter. The function will return XML_SUCCESS on success,
  1195 + and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful.
  1196 + */
  1197 + XMLError QueryIntValue( int* value ) const;
  1198 + /// See QueryIntValue
  1199 + XMLError QueryUnsignedValue( unsigned int* value ) const;
  1200 + /// See QueryIntValue
  1201 + XMLError QueryInt64Value(int64_t* value) const;
  1202 + /// See QueryIntValue
  1203 + XMLError QueryBoolValue( bool* value ) const;
  1204 + /// See QueryIntValue
  1205 + XMLError QueryDoubleValue( double* value ) const;
  1206 + /// See QueryIntValue
  1207 + XMLError QueryFloatValue( float* value ) const;
  1208 +
  1209 + /// Set the attribute to a string value.
  1210 + void SetAttribute( const char* value );
  1211 + /// Set the attribute to value.
  1212 + void SetAttribute( int value );
  1213 + /// Set the attribute to value.
  1214 + void SetAttribute( unsigned value );
  1215 + /// Set the attribute to value.
  1216 + void SetAttribute(int64_t value);
  1217 + /// Set the attribute to value.
  1218 + void SetAttribute( bool value );
  1219 + /// Set the attribute to value.
  1220 + void SetAttribute( double value );
  1221 + /// Set the attribute to value.
  1222 + void SetAttribute( float value );
  1223 +
  1224 +private:
  1225 + enum { BUF_SIZE = 200 };
  1226 +
  1227 + XMLAttribute() : _name(), _value(),_parseLineNum( 0 ), _next( 0 ), _memPool( 0 ) {}
  1228 + virtual ~XMLAttribute() {}
  1229 +
  1230 + XMLAttribute( const XMLAttribute& ); // not supported
  1231 + void operator=( const XMLAttribute& ); // not supported
  1232 + void SetName( const char* name );
  1233 +
  1234 + char* ParseDeep( char* p, bool processEntities, int* curLineNumPtr );
  1235 +
  1236 + mutable StrPair _name;
  1237 + mutable StrPair _value;
  1238 + int _parseLineNum;
  1239 + XMLAttribute* _next;
  1240 + MemPool* _memPool;
  1241 +};
  1242 +
  1243 +
  1244 +/** The element is a container class. It has a value, the element name,
  1245 + and can contain other elements, text, comments, and unknowns.
  1246 + Elements also contain an arbitrary number of attributes.
  1247 +*/
  1248 +class TINYXML2_LIB XMLElement : public XMLNode
  1249 +{
  1250 + friend class XMLDocument;
  1251 +public:
  1252 + /// Get the name of an element (which is the Value() of the node.)
  1253 + const char* Name() const {
  1254 + return Value();
  1255 + }
  1256 + /// Set the name of the element.
  1257 + void SetName( const char* str, bool staticMem=false ) {
  1258 + SetValue( str, staticMem );
  1259 + }
  1260 +
  1261 + virtual XMLElement* ToElement() {
  1262 + return this;
  1263 + }
  1264 + virtual const XMLElement* ToElement() const {
  1265 + return this;
  1266 + }
  1267 + virtual bool Accept( XMLVisitor* visitor ) const;
  1268 +
  1269 + /** Given an attribute name, Attribute() returns the value
  1270 + for the attribute of that name, or null if none
  1271 + exists. For example:
  1272 +
  1273 + @verbatim
  1274 + const char* value = ele->Attribute( "foo" );
  1275 + @endverbatim
  1276 +
  1277 + The 'value' parameter is normally null. However, if specified,
  1278 + the attribute will only be returned if the 'name' and 'value'
  1279 + match. This allow you to write code:
  1280 +
  1281 + @verbatim
  1282 + if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar();
  1283 + @endverbatim
  1284 +
  1285 + rather than:
  1286 + @verbatim
  1287 + if ( ele->Attribute( "foo" ) ) {
  1288 + if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar();
  1289 + }
  1290 + @endverbatim
  1291 + */
  1292 + const char* Attribute( const char* name, const char* value=0 ) const;
  1293 +
  1294 + /** Given an attribute name, IntAttribute() returns the value
  1295 + of the attribute interpreted as an integer. The default
  1296 + value will be returned if the attribute isn't present,
  1297 + or if there is an error. (For a method with error
  1298 + checking, see QueryIntAttribute()).
  1299 + */
  1300 + int IntAttribute(const char* name, int defaultValue = 0) const;
  1301 + /// See IntAttribute()
  1302 + unsigned UnsignedAttribute(const char* name, unsigned defaultValue = 0) const;
  1303 + /// See IntAttribute()
  1304 + int64_t Int64Attribute(const char* name, int64_t defaultValue = 0) const;
  1305 + /// See IntAttribute()
  1306 + bool BoolAttribute(const char* name, bool defaultValue = false) const;
  1307 + /// See IntAttribute()
  1308 + double DoubleAttribute(const char* name, double defaultValue = 0) const;
  1309 + /// See IntAttribute()
  1310 + float FloatAttribute(const char* name, float defaultValue = 0) const;
  1311 +
  1312 + /** Given an attribute name, QueryIntAttribute() returns
  1313 + XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion
  1314 + can't be performed, or XML_NO_ATTRIBUTE if the attribute
  1315 + doesn't exist. If successful, the result of the conversion
  1316 + will be written to 'value'. If not successful, nothing will
  1317 + be written to 'value'. This allows you to provide default
  1318 + value:
  1319 +
  1320 + @verbatim
  1321 + int value = 10;
  1322 + QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10
  1323 + @endverbatim
  1324 + */
  1325 + XMLError QueryIntAttribute( const char* name, int* value ) const {
  1326 + const XMLAttribute* a = FindAttribute( name );
  1327 + if ( !a ) {
  1328 + return XML_NO_ATTRIBUTE;
  1329 + }
  1330 + return a->QueryIntValue( value );
  1331 + }
  1332 +
  1333 + /// See QueryIntAttribute()
  1334 + XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const {
  1335 + const XMLAttribute* a = FindAttribute( name );
  1336 + if ( !a ) {
  1337 + return XML_NO_ATTRIBUTE;
  1338 + }
  1339 + return a->QueryUnsignedValue( value );
  1340 + }
  1341 +
  1342 + /// See QueryIntAttribute()
  1343 + XMLError QueryInt64Attribute(const char* name, int64_t* value) const {
  1344 + const XMLAttribute* a = FindAttribute(name);
  1345 + if (!a) {
  1346 + return XML_NO_ATTRIBUTE;
  1347 + }
  1348 + return a->QueryInt64Value(value);
  1349 + }
  1350 +
  1351 + /// See QueryIntAttribute()
  1352 + XMLError QueryBoolAttribute( const char* name, bool* value ) const {
  1353 + const XMLAttribute* a = FindAttribute( name );
  1354 + if ( !a ) {
  1355 + return XML_NO_ATTRIBUTE;
  1356 + }
  1357 + return a->QueryBoolValue( value );
  1358 + }
  1359 + /// See QueryIntAttribute()
  1360 + XMLError QueryDoubleAttribute( const char* name, double* value ) const {
  1361 + const XMLAttribute* a = FindAttribute( name );
  1362 + if ( !a ) {
  1363 + return XML_NO_ATTRIBUTE;
  1364 + }
  1365 + return a->QueryDoubleValue( value );
  1366 + }
  1367 + /// See QueryIntAttribute()
  1368 + XMLError QueryFloatAttribute( const char* name, float* value ) const {
  1369 + const XMLAttribute* a = FindAttribute( name );
  1370 + if ( !a ) {
  1371 + return XML_NO_ATTRIBUTE;
  1372 + }
  1373 + return a->QueryFloatValue( value );
  1374 + }
  1375 +
  1376 + /// See QueryIntAttribute()
  1377 + XMLError QueryStringAttribute(const char* name, const char** value) const {
  1378 + const XMLAttribute* a = FindAttribute(name);
  1379 + if (!a) {
  1380 + return XML_NO_ATTRIBUTE;
  1381 + }
  1382 + *value = a->Value();
  1383 + return XML_SUCCESS;
  1384 + }
  1385 +
  1386 +
  1387 +
  1388 + /** Given an attribute name, QueryAttribute() returns
  1389 + XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion
  1390 + can't be performed, or XML_NO_ATTRIBUTE if the attribute
  1391 + doesn't exist. It is overloaded for the primitive types,
  1392 + and is a generally more convenient replacement of
  1393 + QueryIntAttribute() and related functions.
  1394 +
  1395 + If successful, the result of the conversion
  1396 + will be written to 'value'. If not successful, nothing will
  1397 + be written to 'value'. This allows you to provide default
  1398 + value:
  1399 +
  1400 + @verbatim
  1401 + int value = 10;
  1402 + QueryAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10
  1403 + @endverbatim
  1404 + */
  1405 + int QueryAttribute( const char* name, int* value ) const {
  1406 + return QueryIntAttribute( name, value );
  1407 + }
  1408 +
  1409 + int QueryAttribute( const char* name, unsigned int* value ) const {
  1410 + return QueryUnsignedAttribute( name, value );
  1411 + }
  1412 +
  1413 + int QueryAttribute(const char* name, int64_t* value) const {
  1414 + return QueryInt64Attribute(name, value);
  1415 + }
  1416 +
  1417 + int QueryAttribute( const char* name, bool* value ) const {
  1418 + return QueryBoolAttribute( name, value );
  1419 + }
  1420 +
  1421 + int QueryAttribute( const char* name, double* value ) const {
  1422 + return QueryDoubleAttribute( name, value );
  1423 + }
  1424 +
  1425 + int QueryAttribute( const char* name, float* value ) const {
  1426 + return QueryFloatAttribute( name, value );
  1427 + }
  1428 +
  1429 + /// Sets the named attribute to value.
  1430 + void SetAttribute( const char* name, const char* value ) {
  1431 + XMLAttribute* a = FindOrCreateAttribute( name );
  1432 + a->SetAttribute( value );
  1433 + }
  1434 + /// Sets the named attribute to value.
  1435 + void SetAttribute( const char* name, int value ) {
  1436 + XMLAttribute* a = FindOrCreateAttribute( name );
  1437 + a->SetAttribute( value );
  1438 + }
  1439 + /// Sets the named attribute to value.
  1440 + void SetAttribute( const char* name, unsigned value ) {
  1441 + XMLAttribute* a = FindOrCreateAttribute( name );
  1442 + a->SetAttribute( value );
  1443 + }
  1444 +
  1445 + /// Sets the named attribute to value.
  1446 + void SetAttribute(const char* name, int64_t value) {
  1447 + XMLAttribute* a = FindOrCreateAttribute(name);
  1448 + a->SetAttribute(value);
  1449 + }
  1450 +
  1451 + /// Sets the named attribute to value.
  1452 + void SetAttribute( const char* name, bool value ) {
  1453 + XMLAttribute* a = FindOrCreateAttribute( name );
  1454 + a->SetAttribute( value );
  1455 + }
  1456 + /// Sets the named attribute to value.
  1457 + void SetAttribute( const char* name, double value ) {
  1458 + XMLAttribute* a = FindOrCreateAttribute( name );
  1459 + a->SetAttribute( value );
  1460 + }
  1461 + /// Sets the named attribute to value.
  1462 + void SetAttribute( const char* name, float value ) {
  1463 + XMLAttribute* a = FindOrCreateAttribute( name );
  1464 + a->SetAttribute( value );
  1465 + }
  1466 +
  1467 + /**
  1468 + Delete an attribute.
  1469 + */
  1470 + void DeleteAttribute( const char* name );
  1471 +
  1472 + /// Return the first attribute in the list.
  1473 + const XMLAttribute* FirstAttribute() const {
  1474 + return _rootAttribute;
  1475 + }
  1476 + /// Query a specific attribute in the list.
  1477 + const XMLAttribute* FindAttribute( const char* name ) const;
  1478 +
  1479 + /** Convenience function for easy access to the text inside an element. Although easy
  1480 + and concise, GetText() is limited compared to getting the XMLText child
  1481 + and accessing it directly.
  1482 +
  1483 + If the first child of 'this' is a XMLText, the GetText()
  1484 + returns the character string of the Text node, else null is returned.
  1485 +
  1486 + This is a convenient method for getting the text of simple contained text:
  1487 + @verbatim
  1488 + <foo>This is text</foo>
  1489 + const char* str = fooElement->GetText();
  1490 + @endverbatim
  1491 +
  1492 + 'str' will be a pointer to "This is text".
  1493 +
  1494 + Note that this function can be misleading. If the element foo was created from
  1495 + this XML:
  1496 + @verbatim
  1497 + <foo><b>This is text</b></foo>
  1498 + @endverbatim
  1499 +
  1500 + then the value of str would be null. The first child node isn't a text node, it is
  1501 + another element. From this XML:
  1502 + @verbatim
  1503 + <foo>This is <b>text</b></foo>
  1504 + @endverbatim
  1505 + GetText() will return "This is ".
  1506 + */
  1507 + const char* GetText() const;
  1508 +
  1509 + /** Convenience function for easy access to the text inside an element. Although easy
  1510 + and concise, SetText() is limited compared to creating an XMLText child
  1511 + and mutating it directly.
  1512 +
  1513 + If the first child of 'this' is a XMLText, SetText() sets its value to
  1514 + the given string, otherwise it will create a first child that is an XMLText.
  1515 +
  1516 + This is a convenient method for setting the text of simple contained text:
  1517 + @verbatim
  1518 + <foo>This is text</foo>
  1519 + fooElement->SetText( "Hullaballoo!" );
  1520 + <foo>Hullaballoo!</foo>
  1521 + @endverbatim
  1522 +
  1523 + Note that this function can be misleading. If the element foo was created from
  1524 + this XML:
  1525 + @verbatim
  1526 + <foo><b>This is text</b></foo>
  1527 + @endverbatim
  1528 +
  1529 + then it will not change "This is text", but rather prefix it with a text element:
  1530 + @verbatim
  1531 + <foo>Hullaballoo!<b>This is text</b></foo>
  1532 + @endverbatim
  1533 +
  1534 + For this XML:
  1535 + @verbatim
  1536 + <foo />
  1537 + @endverbatim
  1538 + SetText() will generate
  1539 + @verbatim
  1540 + <foo>Hullaballoo!</foo>
  1541 + @endverbatim
  1542 + */
  1543 + void SetText( const char* inText );
  1544 + /// Convenience method for setting text inside an element. See SetText() for important limitations.
  1545 + void SetText( int value );
  1546 + /// Convenience method for setting text inside an element. See SetText() for important limitations.
  1547 + void SetText( unsigned value );
  1548 + /// Convenience method for setting text inside an element. See SetText() for important limitations.
  1549 + void SetText(int64_t value);
  1550 + /// Convenience method for setting text inside an element. See SetText() for important limitations.
  1551 + void SetText( bool value );
  1552 + /// Convenience method for setting text inside an element. See SetText() for important limitations.
  1553 + void SetText( double value );
  1554 + /// Convenience method for setting text inside an element. See SetText() for important limitations.
  1555 + void SetText( float value );
  1556 +
  1557 + /**
  1558 + Convenience method to query the value of a child text node. This is probably best
  1559 + shown by example. Given you have a document is this form:
  1560 + @verbatim
  1561 + <point>
  1562 + <x>1</x>
  1563 + <y>1.4</y>
  1564 + </point>
  1565 + @endverbatim
  1566 +
  1567 + The QueryIntText() and similar functions provide a safe and easier way to get to the
  1568 + "value" of x and y.
  1569 +
  1570 + @verbatim
  1571 + int x = 0;
  1572 + float y = 0; // types of x and y are contrived for example
  1573 + const XMLElement* xElement = pointElement->FirstChildElement( "x" );
  1574 + const XMLElement* yElement = pointElement->FirstChildElement( "y" );
  1575 + xElement->QueryIntText( &x );
  1576 + yElement->QueryFloatText( &y );
  1577 + @endverbatim
  1578 +
  1579 + @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted
  1580 + to the requested type, and XML_NO_TEXT_NODE if there is no child text to query.
  1581 +
  1582 + */
  1583 + XMLError QueryIntText( int* ival ) const;
  1584 + /// See QueryIntText()
  1585 + XMLError QueryUnsignedText( unsigned* uval ) const;
  1586 + /// See QueryIntText()
  1587 + XMLError QueryInt64Text(int64_t* uval) const;
  1588 + /// See QueryIntText()
  1589 + XMLError QueryBoolText( bool* bval ) const;
  1590 + /// See QueryIntText()
  1591 + XMLError QueryDoubleText( double* dval ) const;
  1592 + /// See QueryIntText()
  1593 + XMLError QueryFloatText( float* fval ) const;
  1594 +
  1595 + int IntText(int defaultValue = 0) const;
  1596 +
  1597 + /// See QueryIntText()
  1598 + unsigned UnsignedText(unsigned defaultValue = 0) const;
  1599 + /// See QueryIntText()
  1600 + int64_t Int64Text(int64_t defaultValue = 0) const;
  1601 + /// See QueryIntText()
  1602 + bool BoolText(bool defaultValue = false) const;
  1603 + /// See QueryIntText()
  1604 + double DoubleText(double defaultValue = 0) const;
  1605 + /// See QueryIntText()
  1606 + float FloatText(float defaultValue = 0) const;
  1607 +
  1608 + // internal:
  1609 + enum ElementClosingType {
  1610 + OPEN, // <foo>
  1611 + CLOSED, // <foo/>
  1612 + CLOSING // </foo>
  1613 + };
  1614 + ElementClosingType ClosingType() const {
  1615 + return _closingType;
  1616 + }
  1617 + virtual XMLNode* ShallowClone( XMLDocument* document ) const;
  1618 + virtual bool ShallowEqual( const XMLNode* compare ) const;
  1619 +
  1620 +protected:
  1621 + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );
  1622 +
  1623 +private:
  1624 + XMLElement( XMLDocument* doc );
  1625 + virtual ~XMLElement();
  1626 + XMLElement( const XMLElement& ); // not supported
  1627 + void operator=( const XMLElement& ); // not supported
  1628 +
  1629 + XMLAttribute* FindAttribute( const char* name ) {
  1630 + return const_cast<XMLAttribute*>(const_cast<const XMLElement*>(this)->FindAttribute( name ));
  1631 + }
  1632 + XMLAttribute* FindOrCreateAttribute( const char* name );
  1633 + //void LinkAttribute( XMLAttribute* attrib );
  1634 + char* ParseAttributes( char* p, int* curLineNumPtr );
  1635 + static void DeleteAttribute( XMLAttribute* attribute );
  1636 + XMLAttribute* CreateAttribute();
  1637 +
  1638 + enum { BUF_SIZE = 200 };
  1639 + ElementClosingType _closingType;
  1640 + // The attribute list is ordered; there is no 'lastAttribute'
  1641 + // because the list needs to be scanned for dupes before adding
  1642 + // a new attribute.
  1643 + XMLAttribute* _rootAttribute;
  1644 +};
  1645 +
  1646 +
  1647 +enum Whitespace {
  1648 + PRESERVE_WHITESPACE,
  1649 + COLLAPSE_WHITESPACE
  1650 +};
  1651 +
  1652 +
  1653 +/** A Document binds together all the functionality.
  1654 + It can be saved, loaded, and printed to the screen.
  1655 + All Nodes are connected and allocated to a Document.
  1656 + If the Document is deleted, all its Nodes are also deleted.
  1657 +*/
  1658 +class TINYXML2_LIB XMLDocument : public XMLNode
  1659 +{
  1660 + friend class XMLElement;
  1661 + // Gives access to SetError and Push/PopDepth, but over-access for everything else.
  1662 + // Wishing C++ had "internal" scope.
  1663 + friend class XMLNode;
  1664 + friend class XMLText;
  1665 + friend class XMLComment;
  1666 + friend class XMLDeclaration;
  1667 + friend class XMLUnknown;
  1668 +public:
  1669 + /// constructor
  1670 + XMLDocument( bool processEntities = true, Whitespace whitespaceMode = PRESERVE_WHITESPACE );
  1671 + ~XMLDocument();
  1672 +
  1673 + virtual XMLDocument* ToDocument() {
  1674 + TIXMLASSERT( this == _document );
  1675 + return this;
  1676 + }
  1677 + virtual const XMLDocument* ToDocument() const {
  1678 + TIXMLASSERT( this == _document );
  1679 + return this;
  1680 + }
  1681 +
  1682 + /**
  1683 + Parse an XML file from a character string.
  1684 + Returns XML_SUCCESS (0) on success, or
  1685 + an errorID.
  1686 +
  1687 + You may optionally pass in the 'nBytes', which is
  1688 + the number of bytes which will be parsed. If not
  1689 + specified, TinyXML-2 will assume 'xml' points to a
  1690 + null terminated string.
  1691 + */
  1692 + XMLError Parse( const char* xml, size_t nBytes=(size_t)(-1) );
  1693 +
  1694 + /**
  1695 + Load an XML file from disk.
  1696 + Returns XML_SUCCESS (0) on success, or
  1697 + an errorID.
  1698 + */
  1699 + XMLError LoadFile( const char* filename );
  1700 +
  1701 + /**
  1702 + Load an XML file from disk. You are responsible
  1703 + for providing and closing the FILE*.
  1704 +
  1705 + NOTE: The file should be opened as binary ("rb")
  1706 + not text in order for TinyXML-2 to correctly
  1707 + do newline normalization.
  1708 +
  1709 + Returns XML_SUCCESS (0) on success, or
  1710 + an errorID.
  1711 + */
  1712 + XMLError LoadFile( FILE* );
  1713 +
  1714 + /**
  1715 + Save the XML file to disk.
  1716 + Returns XML_SUCCESS (0) on success, or
  1717 + an errorID.
  1718 + */
  1719 + XMLError SaveFile( const char* filename, bool compact = false );
  1720 +
  1721 + /**
  1722 + Save the XML file to disk. You are responsible
  1723 + for providing and closing the FILE*.
  1724 +
  1725 + Returns XML_SUCCESS (0) on success, or
  1726 + an errorID.
  1727 + */
  1728 + XMLError SaveFile( FILE* fp, bool compact = false );
  1729 +
  1730 + bool ProcessEntities() const {
  1731 + return _processEntities;
  1732 + }
  1733 + Whitespace WhitespaceMode() const {
  1734 + return _whitespaceMode;
  1735 + }
  1736 +
  1737 + /**
  1738 + Returns true if this document has a leading Byte Order Mark of UTF8.
  1739 + */
  1740 + bool HasBOM() const {
  1741 + return _writeBOM;
  1742 + }
  1743 + /** Sets whether to write the BOM when writing the file.
  1744 + */
  1745 + void SetBOM( bool useBOM ) {
  1746 + _writeBOM = useBOM;
  1747 + }
  1748 +
  1749 + /** Return the root element of DOM. Equivalent to FirstChildElement().
  1750 + To get the first node, use FirstChild().
  1751 + */
  1752 + XMLElement* RootElement() {
  1753 + return FirstChildElement();
  1754 + }
  1755 + const XMLElement* RootElement() const {
  1756 + return FirstChildElement();
  1757 + }
  1758 +
  1759 + /** Print the Document. If the Printer is not provided, it will
  1760 + print to stdout. If you provide Printer, this can print to a file:
  1761 + @verbatim
  1762 + XMLPrinter printer( fp );
  1763 + doc.Print( &printer );
  1764 + @endverbatim
  1765 +
  1766 + Or you can use a printer to print to memory:
  1767 + @verbatim
  1768 + XMLPrinter printer;
  1769 + doc.Print( &printer );
  1770 + // printer.CStr() has a const char* to the XML
  1771 + @endverbatim
  1772 + */
  1773 + void Print( XMLPrinter* streamer=0 ) const;
  1774 + virtual bool Accept( XMLVisitor* visitor ) const;
  1775 +
  1776 + /**
  1777 + Create a new Element associated with
  1778 + this Document. The memory for the Element
  1779 + is managed by the Document.
  1780 + */
  1781 + XMLElement* NewElement( const char* name );
  1782 + /**
  1783 + Create a new Comment associated with
  1784 + this Document. The memory for the Comment
  1785 + is managed by the Document.
  1786 + */
  1787 + XMLComment* NewComment( const char* comment );
  1788 + /**
  1789 + Create a new Text associated with
  1790 + this Document. The memory for the Text
  1791 + is managed by the Document.
  1792 + */
  1793 + XMLText* NewText( const char* text );
  1794 + /**
  1795 + Create a new Declaration associated with
  1796 + this Document. The memory for the object
  1797 + is managed by the Document.
  1798 +
  1799 + If the 'text' param is null, the standard
  1800 + declaration is used.:
  1801 + @verbatim
  1802 + <?xml version="1.0" encoding="UTF-8"?>
  1803 + @endverbatim
  1804 + */
  1805 + XMLDeclaration* NewDeclaration( const char* text=0 );
  1806 + /**
  1807 + Create a new Unknown associated with
  1808 + this Document. The memory for the object
  1809 + is managed by the Document.
  1810 + */
  1811 + XMLUnknown* NewUnknown( const char* text );
  1812 +
  1813 + /**
  1814 + Delete a node associated with this document.
  1815 + It will be unlinked from the DOM.
  1816 + */
  1817 + void DeleteNode( XMLNode* node );
  1818 +
  1819 + void ClearError() {
  1820 + SetError(XML_SUCCESS, 0, 0);
  1821 + }
  1822 +
  1823 + /// Return true if there was an error parsing the document.
  1824 + bool Error() const {
  1825 + return _errorID != XML_SUCCESS;
  1826 + }
  1827 + /// Return the errorID.
  1828 + XMLError ErrorID() const {
  1829 + return _errorID;
  1830 + }
  1831 + const char* ErrorName() const;
  1832 + static const char* ErrorIDToName(XMLError errorID);
  1833 +
  1834 + /** Returns a "long form" error description. A hopefully helpful
  1835 + diagnostic with location, line number, and/or additional info.
  1836 + */
  1837 + const char* ErrorStr() const;
  1838 +
  1839 + /// A (trivial) utility function that prints the ErrorStr() to stdout.
  1840 + void PrintError() const;
  1841 +
  1842 + /// Return the line where the error occured, or zero if unknown.
  1843 + int ErrorLineNum() const
  1844 + {
  1845 + return _errorLineNum;
  1846 + }
  1847 +
  1848 + /// Clear the document, resetting it to the initial state.
  1849 + void Clear();
  1850 +
  1851 + /**
  1852 + Copies this document to a target document.
  1853 + The target will be completely cleared before the copy.
  1854 + If you want to copy a sub-tree, see XMLNode::DeepClone().
  1855 +
  1856 + NOTE: that the 'target' must be non-null.
  1857 + */
  1858 + void DeepCopy(XMLDocument* target) const;
  1859 +
  1860 + // internal
  1861 + char* Identify( char* p, XMLNode** node );
  1862 +
  1863 + // internal
  1864 + void MarkInUse(XMLNode*);
  1865 +
  1866 + virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const {
  1867 + return 0;
  1868 + }
  1869 + virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const {
  1870 + return false;
  1871 + }
  1872 +
  1873 +private:
  1874 + XMLDocument( const XMLDocument& ); // not supported
  1875 + void operator=( const XMLDocument& ); // not supported
  1876 +
  1877 + bool _writeBOM;
  1878 + bool _processEntities;
  1879 + XMLError _errorID;
  1880 + Whitespace _whitespaceMode;
  1881 + mutable StrPair _errorStr;
  1882 + int _errorLineNum;
  1883 + char* _charBuffer;
  1884 + int _parseCurLineNum;
  1885 + int _parsingDepth;
  1886 + // Memory tracking does add some overhead.
  1887 + // However, the code assumes that you don't
  1888 + // have a bunch of unlinked nodes around.
  1889 + // Therefore it takes less memory to track
  1890 + // in the document vs. a linked list in the XMLNode,
  1891 + // and the performance is the same.
  1892 + DynArray<XMLNode*, 10> _unlinked;
  1893 +
  1894 + MemPoolT< sizeof(XMLElement) > _elementPool;
  1895 + MemPoolT< sizeof(XMLAttribute) > _attributePool;
  1896 + MemPoolT< sizeof(XMLText) > _textPool;
  1897 + MemPoolT< sizeof(XMLComment) > _commentPool;
  1898 +
  1899 + static const char* _errorNames[XML_ERROR_COUNT];
  1900 +
  1901 + void Parse();
  1902 +
  1903 + void SetError( XMLError error, int lineNum, const char* format, ... );
  1904 +
  1905 + // Something of an obvious security hole, once it was discovered.
  1906 + // Either an ill-formed XML or an excessively deep one can overflow
  1907 + // the stack. Track stack depth, and error out if needed.
  1908 + class DepthTracker {
  1909 + public:
  1910 + DepthTracker(XMLDocument * document) {
  1911 + this->_document = document;
  1912 + document->PushDepth();
  1913 + }
  1914 + ~DepthTracker() {
  1915 + _document->PopDepth();
  1916 + }
  1917 + private:
  1918 + XMLDocument * _document;
  1919 + };
  1920 + void PushDepth();
  1921 + void PopDepth();
  1922 +
  1923 + template<class NodeType, int PoolElementSize>
  1924 + NodeType* CreateUnlinkedNode( MemPoolT<PoolElementSize>& pool );
  1925 +};
  1926 +
  1927 +template<class NodeType, int PoolElementSize>
  1928 +inline NodeType* XMLDocument::CreateUnlinkedNode( MemPoolT<PoolElementSize>& pool )
  1929 +{
  1930 + TIXMLASSERT( sizeof( NodeType ) == PoolElementSize );
  1931 + TIXMLASSERT( sizeof( NodeType ) == pool.ItemSize() );
  1932 + NodeType* returnNode = new (pool.Alloc()) NodeType( this );
  1933 + TIXMLASSERT( returnNode );
  1934 + returnNode->_memPool = &pool;
  1935 +
  1936 + _unlinked.Push(returnNode);
  1937 + return returnNode;
  1938 +}
  1939 +
  1940 +/**
  1941 + A XMLHandle is a class that wraps a node pointer with null checks; this is
  1942 + an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2
  1943 + DOM structure. It is a separate utility class.
  1944 +
  1945 + Take an example:
  1946 + @verbatim
  1947 + <Document>
  1948 + <Element attributeA = "valueA">
  1949 + <Child attributeB = "value1" />
  1950 + <Child attributeB = "value2" />
  1951 + </Element>
  1952 + </Document>
  1953 + @endverbatim
  1954 +
  1955 + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very
  1956 + easy to write a *lot* of code that looks like:
  1957 +
  1958 + @verbatim
  1959 + XMLElement* root = document.FirstChildElement( "Document" );
  1960 + if ( root )
  1961 + {
  1962 + XMLElement* element = root->FirstChildElement( "Element" );
  1963 + if ( element )
  1964 + {
  1965 + XMLElement* child = element->FirstChildElement( "Child" );
  1966 + if ( child )
  1967 + {
  1968 + XMLElement* child2 = child->NextSiblingElement( "Child" );
  1969 + if ( child2 )
  1970 + {
  1971 + // Finally do something useful.
  1972 + @endverbatim
  1973 +
  1974 + And that doesn't even cover "else" cases. XMLHandle addresses the verbosity
  1975 + of such code. A XMLHandle checks for null pointers so it is perfectly safe
  1976 + and correct to use:
  1977 +
  1978 + @verbatim
  1979 + XMLHandle docHandle( &document );
  1980 + XMLElement* child2 = docHandle.FirstChildElement( "Document" ).FirstChildElement( "Element" ).FirstChildElement().NextSiblingElement();
  1981 + if ( child2 )
  1982 + {
  1983 + // do something useful
  1984 + @endverbatim
  1985 +
  1986 + Which is MUCH more concise and useful.
  1987 +
  1988 + It is also safe to copy handles - internally they are nothing more than node pointers.
  1989 + @verbatim
  1990 + XMLHandle handleCopy = handle;
  1991 + @endverbatim
  1992 +
  1993 + See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects.
  1994 +*/
  1995 +class TINYXML2_LIB XMLHandle
  1996 +{
  1997 +public:
  1998 + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer.
  1999 + XMLHandle( XMLNode* node ) : _node( node ) {
  2000 + }
  2001 + /// Create a handle from a node.
  2002 + XMLHandle( XMLNode& node ) : _node( &node ) {
  2003 + }
  2004 + /// Copy constructor
  2005 + XMLHandle( const XMLHandle& ref ) : _node( ref._node ) {
  2006 + }
  2007 + /// Assignment
  2008 + XMLHandle& operator=( const XMLHandle& ref ) {
  2009 + _node = ref._node;
  2010 + return *this;
  2011 + }
  2012 +
  2013 + /// Get the first child of this handle.
  2014 + XMLHandle FirstChild() {
  2015 + return XMLHandle( _node ? _node->FirstChild() : 0 );
  2016 + }
  2017 + /// Get the first child element of this handle.
  2018 + XMLHandle FirstChildElement( const char* name = 0 ) {
  2019 + return XMLHandle( _node ? _node->FirstChildElement( name ) : 0 );
  2020 + }
  2021 + /// Get the last child of this handle.
  2022 + XMLHandle LastChild() {
  2023 + return XMLHandle( _node ? _node->LastChild() : 0 );
  2024 + }
  2025 + /// Get the last child element of this handle.
  2026 + XMLHandle LastChildElement( const char* name = 0 ) {
  2027 + return XMLHandle( _node ? _node->LastChildElement( name ) : 0 );
  2028 + }
  2029 + /// Get the previous sibling of this handle.
  2030 + XMLHandle PreviousSibling() {
  2031 + return XMLHandle( _node ? _node->PreviousSibling() : 0 );
  2032 + }
  2033 + /// Get the previous sibling element of this handle.
  2034 + XMLHandle PreviousSiblingElement( const char* name = 0 ) {
  2035 + return XMLHandle( _node ? _node->PreviousSiblingElement( name ) : 0 );
  2036 + }
  2037 + /// Get the next sibling of this handle.
  2038 + XMLHandle NextSibling() {
  2039 + return XMLHandle( _node ? _node->NextSibling() : 0 );
  2040 + }
  2041 + /// Get the next sibling element of this handle.
  2042 + XMLHandle NextSiblingElement( const char* name = 0 ) {
  2043 + return XMLHandle( _node ? _node->NextSiblingElement( name ) : 0 );
  2044 + }
  2045 +
  2046 + /// Safe cast to XMLNode. This can return null.
  2047 + XMLNode* ToNode() {
  2048 + return _node;
  2049 + }
  2050 + /// Safe cast to XMLElement. This can return null.
  2051 + XMLElement* ToElement() {
  2052 + return ( _node ? _node->ToElement() : 0 );
  2053 + }
  2054 + /// Safe cast to XMLText. This can return null.
  2055 + XMLText* ToText() {
  2056 + return ( _node ? _node->ToText() : 0 );
  2057 + }
  2058 + /// Safe cast to XMLUnknown. This can return null.
  2059 + XMLUnknown* ToUnknown() {
  2060 + return ( _node ? _node->ToUnknown() : 0 );
  2061 + }
  2062 + /// Safe cast to XMLDeclaration. This can return null.
  2063 + XMLDeclaration* ToDeclaration() {
  2064 + return ( _node ? _node->ToDeclaration() : 0 );
  2065 + }
  2066 +
  2067 +private:
  2068 + XMLNode* _node;
  2069 +};
  2070 +
  2071 +
  2072 +/**
  2073 + A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the
  2074 + same in all regards, except for the 'const' qualifiers. See XMLHandle for API.
  2075 +*/
  2076 +class TINYXML2_LIB XMLConstHandle
  2077 +{
  2078 +public:
  2079 + XMLConstHandle( const XMLNode* node ) : _node( node ) {
  2080 + }
  2081 + XMLConstHandle( const XMLNode& node ) : _node( &node ) {
  2082 + }
  2083 + XMLConstHandle( const XMLConstHandle& ref ) : _node( ref._node ) {
  2084 + }
  2085 +
  2086 + XMLConstHandle& operator=( const XMLConstHandle& ref ) {
  2087 + _node = ref._node;
  2088 + return *this;
  2089 + }
  2090 +
  2091 + const XMLConstHandle FirstChild() const {
  2092 + return XMLConstHandle( _node ? _node->FirstChild() : 0 );
  2093 + }
  2094 + const XMLConstHandle FirstChildElement( const char* name = 0 ) const {
  2095 + return XMLConstHandle( _node ? _node->FirstChildElement( name ) : 0 );
  2096 + }
  2097 + const XMLConstHandle LastChild() const {
  2098 + return XMLConstHandle( _node ? _node->LastChild() : 0 );
  2099 + }
  2100 + const XMLConstHandle LastChildElement( const char* name = 0 ) const {
  2101 + return XMLConstHandle( _node ? _node->LastChildElement( name ) : 0 );
  2102 + }
  2103 + const XMLConstHandle PreviousSibling() const {
  2104 + return XMLConstHandle( _node ? _node->PreviousSibling() : 0 );
  2105 + }
  2106 + const XMLConstHandle PreviousSiblingElement( const char* name = 0 ) const {
  2107 + return XMLConstHandle( _node ? _node->PreviousSiblingElement( name ) : 0 );
  2108 + }
  2109 + const XMLConstHandle NextSibling() const {
  2110 + return XMLConstHandle( _node ? _node->NextSibling() : 0 );
  2111 + }
  2112 + const XMLConstHandle NextSiblingElement( const char* name = 0 ) const {
  2113 + return XMLConstHandle( _node ? _node->NextSiblingElement( name ) : 0 );
  2114 + }
  2115 +
  2116 +
  2117 + const XMLNode* ToNode() const {
  2118 + return _node;
  2119 + }
  2120 + const XMLElement* ToElement() const {
  2121 + return ( _node ? _node->ToElement() : 0 );
  2122 + }
  2123 + const XMLText* ToText() const {
  2124 + return ( _node ? _node->ToText() : 0 );
  2125 + }
  2126 + const XMLUnknown* ToUnknown() const {
  2127 + return ( _node ? _node->ToUnknown() : 0 );
  2128 + }
  2129 + const XMLDeclaration* ToDeclaration() const {
  2130 + return ( _node ? _node->ToDeclaration() : 0 );
  2131 + }
  2132 +
  2133 +private:
  2134 + const XMLNode* _node;
  2135 +};
  2136 +
  2137 +
  2138 +/**
  2139 + Printing functionality. The XMLPrinter gives you more
  2140 + options than the XMLDocument::Print() method.
  2141 +
  2142 + It can:
  2143 + -# Print to memory.
  2144 + -# Print to a file you provide.
  2145 + -# Print XML without a XMLDocument.
  2146 +
  2147 + Print to Memory
  2148 +
  2149 + @verbatim
  2150 + XMLPrinter printer;
  2151 + doc.Print( &printer );
  2152 + SomeFunction( printer.CStr() );
  2153 + @endverbatim
  2154 +
  2155 + Print to a File
  2156 +
  2157 + You provide the file pointer.
  2158 + @verbatim
  2159 + XMLPrinter printer( fp );
  2160 + doc.Print( &printer );
  2161 + @endverbatim
  2162 +
  2163 + Print without a XMLDocument
  2164 +
  2165 + When loading, an XML parser is very useful. However, sometimes
  2166 + when saving, it just gets in the way. The code is often set up
  2167 + for streaming, and constructing the DOM is just overhead.
  2168 +
  2169 + The Printer supports the streaming case. The following code
  2170 + prints out a trivially simple XML file without ever creating
  2171 + an XML document.
  2172 +
  2173 + @verbatim
  2174 + XMLPrinter printer( fp );
  2175 + printer.OpenElement( "foo" );
  2176 + printer.PushAttribute( "foo", "bar" );
  2177 + printer.CloseElement();
  2178 + @endverbatim
  2179 +*/
  2180 +class TINYXML2_LIB XMLPrinter : public XMLVisitor
  2181 +{
  2182 +public:
  2183 + /** Construct the printer. If the FILE* is specified,
  2184 + this will print to the FILE. Else it will print
  2185 + to memory, and the result is available in CStr().
  2186 + If 'compact' is set to true, then output is created
  2187 + with only required whitespace and newlines.
  2188 + */
  2189 + XMLPrinter( FILE* file=0, bool compact = false, int depth = 0 );
  2190 + virtual ~XMLPrinter() {}
  2191 +
  2192 + /** If streaming, write the BOM and declaration. */
  2193 + void PushHeader( bool writeBOM, bool writeDeclaration );
  2194 + /** If streaming, start writing an element.
  2195 + The element must be closed with CloseElement()
  2196 + */
  2197 + void OpenElement( const char* name, bool compactMode=false );
  2198 + /// If streaming, add an attribute to an open element.
  2199 + void PushAttribute( const char* name, const char* value );
  2200 + void PushAttribute( const char* name, int value );
  2201 + void PushAttribute( const char* name, unsigned value );
  2202 + void PushAttribute(const char* name, int64_t value);
  2203 + void PushAttribute( const char* name, bool value );
  2204 + void PushAttribute( const char* name, double value );
  2205 + /// If streaming, close the Element.
  2206 + virtual void CloseElement( bool compactMode=false );
  2207 +
  2208 + /// Add a text node.
  2209 + void PushText( const char* text, bool cdata=false );
  2210 + /// Add a text node from an integer.
  2211 + void PushText( int value );
  2212 + /// Add a text node from an unsigned.
  2213 + void PushText( unsigned value );
  2214 + /// Add a text node from an unsigned.
  2215 + void PushText(int64_t value);
  2216 + /// Add a text node from a bool.
  2217 + void PushText( bool value );
  2218 + /// Add a text node from a float.
  2219 + void PushText( float value );
  2220 + /// Add a text node from a double.
  2221 + void PushText( double value );
  2222 +
  2223 + /// Add a comment
  2224 + void PushComment( const char* comment );
  2225 +
  2226 + void PushDeclaration( const char* value );
  2227 + void PushUnknown( const char* value );
  2228 +
  2229 + virtual bool VisitEnter( const XMLDocument& /*doc*/ );
  2230 + virtual bool VisitExit( const XMLDocument& /*doc*/ ) {
  2231 + return true;
  2232 + }
  2233 +
  2234 + virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute );
  2235 + virtual bool VisitExit( const XMLElement& element );
  2236 +
  2237 + virtual bool Visit( const XMLText& text );
  2238 + virtual bool Visit( const XMLComment& comment );
  2239 + virtual bool Visit( const XMLDeclaration& declaration );
  2240 + virtual bool Visit( const XMLUnknown& unknown );
  2241 +
  2242 + /**
  2243 + If in print to memory mode, return a pointer to
  2244 + the XML file in memory.
  2245 + */
  2246 + const char* CStr() const {
  2247 + return _buffer.Mem();
  2248 + }
  2249 + /**
  2250 + If in print to memory mode, return the size
  2251 + of the XML file in memory. (Note the size returned
  2252 + includes the terminating null.)
  2253 + */
  2254 + int CStrSize() const {
  2255 + return _buffer.Size();
  2256 + }
  2257 + /**
  2258 + If in print to memory mode, reset the buffer to the
  2259 + beginning.
  2260 + */
  2261 + void ClearBuffer() {
  2262 + _buffer.Clear();
  2263 + _buffer.Push(0);
  2264 + _firstElement = true;
  2265 + }
  2266 +
  2267 +protected:
  2268 + virtual bool CompactMode( const XMLElement& ) { return _compactMode; }
  2269 +
  2270 + /** Prints out the space before an element. You may override to change
  2271 + the space and tabs used. A PrintSpace() override should call Print().
  2272 + */
  2273 + virtual void PrintSpace( int depth );
  2274 + void Print( const char* format, ... );
  2275 + void Write( const char* data, size_t size );
  2276 + inline void Write( const char* data ) { Write( data, strlen( data ) ); }
  2277 + void Putc( char ch );
  2278 +
  2279 + void SealElementIfJustOpened();
  2280 + bool _elementJustOpened;
  2281 + DynArray< const char*, 10 > _stack;
  2282 +
  2283 +private:
  2284 + void PrintString( const char*, bool restrictedEntitySet ); // prints out, after detecting entities.
  2285 +
  2286 + bool _firstElement;
  2287 + FILE* _fp;
  2288 + int _depth;
  2289 + int _textDepth;
  2290 + bool _processEntities;
  2291 + bool _compactMode;
  2292 +
  2293 + enum {
  2294 + ENTITY_RANGE = 64,
  2295 + BUF_SIZE = 200
  2296 + };
  2297 + bool _entityFlag[ENTITY_RANGE];
  2298 + bool _restrictedEntityFlag[ENTITY_RANGE];
  2299 +
  2300 + DynArray< char, 20 > _buffer;
  2301 +
  2302 + // Prohibit cloning, intentionally not implemented
  2303 + XMLPrinter( const XMLPrinter& );
  2304 + XMLPrinter& operator=( const XMLPrinter& );
  2305 +};
  2306 +
  2307 +
  2308 +} // tinyxml2
  2309 +
  2310 +#if defined(_MSC_VER)
  2311 +# pragma warning(pop)
  2312 +#endif
  2313 +
  2314 +#endif // TINYXML2_INCLUDED
... ...