Commit c887a0f0dcc104c68e4004c400c7c95c07742be8
0 parents
提交初成版代码
Showing
21 changed files
with
8991 additions
and
0 deletions
.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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 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, ®); | ||
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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | + // 中 or 中 | ||
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 |