1/*
2 *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/libjingle/xmllite/xmlparser.h"
12
13#include <string>
14#include <vector>
15
16#include "webrtc/libjingle/xmllite/xmlconstants.h"
17#include "webrtc/libjingle/xmllite/xmlelement.h"
18#include "webrtc/libjingle/xmllite/xmlnsstack.h"
19#include "webrtc/libjingle/xmllite/xmlnsstack.h"
20#include "webrtc/base/common.h"
21
22namespace buzz {
23
24
25static void
26StartElementCallback(void * userData, const char *name, const char **atts) {
27  (static_cast<XmlParser *>(userData))->ExpatStartElement(name, atts);
28}
29
30static void
31EndElementCallback(void * userData, const char *name) {
32  (static_cast<XmlParser *>(userData))->ExpatEndElement(name);
33}
34
35static void
36CharacterDataCallback(void * userData, const char *text, int len) {
37  (static_cast<XmlParser *>(userData))->ExpatCharacterData(text, len);
38}
39
40static void
41XmlDeclCallback(void * userData, const char * ver, const char * enc, int st) {
42  (static_cast<XmlParser *>(userData))->ExpatXmlDecl(ver, enc, st);
43}
44
45XmlParser::XmlParser(XmlParseHandler *pxph) :
46    pxph_(pxph), sentError_(false) {
47  expat_ = XML_ParserCreate(NULL);
48  XML_SetUserData(expat_, this);
49  XML_SetElementHandler(expat_, StartElementCallback, EndElementCallback);
50  XML_SetCharacterDataHandler(expat_, CharacterDataCallback);
51  XML_SetXmlDeclHandler(expat_, XmlDeclCallback);
52}
53
54void
55XmlParser::Reset() {
56  if (!XML_ParserReset(expat_, NULL)) {
57    XML_ParserFree(expat_);
58    expat_ = XML_ParserCreate(NULL);
59  }
60  XML_SetUserData(expat_, this);
61  XML_SetElementHandler(expat_, StartElementCallback, EndElementCallback);
62  XML_SetCharacterDataHandler(expat_, CharacterDataCallback);
63  XML_SetXmlDeclHandler(expat_, XmlDeclCallback);
64  context_.Reset();
65  sentError_ = false;
66}
67
68static bool
69XmlParser_StartsWithXmlns(const char *name) {
70  return name[0] == 'x' &&
71         name[1] == 'm' &&
72         name[2] == 'l' &&
73         name[3] == 'n' &&
74         name[4] == 's';
75}
76
77void
78XmlParser::ExpatStartElement(const char *name, const char **atts) {
79  if (context_.RaisedError() != XML_ERROR_NONE)
80    return;
81  const char **att;
82  context_.StartElement();
83  for (att = atts; *att; att += 2) {
84    if (XmlParser_StartsWithXmlns(*att)) {
85      if ((*att)[5] == '\0') {
86        context_.StartNamespace("", *(att + 1));
87      }
88      else if ((*att)[5] == ':') {
89        if (**(att + 1) == '\0') {
90          // In XML 1.0 empty namespace illegal with prefix (not in 1.1)
91          context_.RaiseError(XML_ERROR_SYNTAX);
92          return;
93        }
94        context_.StartNamespace((*att) + 6, *(att + 1));
95      }
96    }
97  }
98  context_.SetPosition(XML_GetCurrentLineNumber(expat_),
99                       XML_GetCurrentColumnNumber(expat_),
100                       XML_GetCurrentByteIndex(expat_));
101  pxph_->StartElement(&context_, name, atts);
102}
103
104void
105XmlParser::ExpatEndElement(const char *name) {
106  if (context_.RaisedError() != XML_ERROR_NONE)
107    return;
108  context_.EndElement();
109  context_.SetPosition(XML_GetCurrentLineNumber(expat_),
110                       XML_GetCurrentColumnNumber(expat_),
111                       XML_GetCurrentByteIndex(expat_));
112  pxph_->EndElement(&context_, name);
113}
114
115void
116XmlParser::ExpatCharacterData(const char *text, int len) {
117  if (context_.RaisedError() != XML_ERROR_NONE)
118    return;
119  context_.SetPosition(XML_GetCurrentLineNumber(expat_),
120                       XML_GetCurrentColumnNumber(expat_),
121                       XML_GetCurrentByteIndex(expat_));
122  pxph_->CharacterData(&context_, text, len);
123}
124
125void
126XmlParser::ExpatXmlDecl(const char * ver, const char * enc, int standalone) {
127  if (context_.RaisedError() != XML_ERROR_NONE)
128    return;
129
130  if (ver && std::string("1.0") != ver) {
131    context_.RaiseError(XML_ERROR_SYNTAX);
132    return;
133  }
134
135  if (standalone == 0) {
136    context_.RaiseError(XML_ERROR_SYNTAX);
137    return;
138  }
139
140  if (enc && !((enc[0] == 'U' || enc[0] == 'u') &&
141               (enc[1] == 'T' || enc[1] == 't') &&
142               (enc[2] == 'F' || enc[2] == 'f') &&
143                enc[3] == '-' && enc[4] =='8')) {
144    context_.RaiseError(XML_ERROR_INCORRECT_ENCODING);
145    return;
146  }
147
148}
149
150bool
151XmlParser::Parse(const char *data, size_t len, bool isFinal) {
152  if (sentError_)
153    return false;
154
155  if (XML_Parse(expat_, data, static_cast<int>(len), isFinal) !=
156      XML_STATUS_OK) {
157    context_.SetPosition(XML_GetCurrentLineNumber(expat_),
158                         XML_GetCurrentColumnNumber(expat_),
159                         XML_GetCurrentByteIndex(expat_));
160    context_.RaiseError(XML_GetErrorCode(expat_));
161  }
162
163  if (context_.RaisedError() != XML_ERROR_NONE) {
164    sentError_ = true;
165    pxph_->Error(&context_, context_.RaisedError());
166    return false;
167  }
168
169  return true;
170}
171
172XmlParser::~XmlParser() {
173  XML_ParserFree(expat_);
174}
175
176void
177XmlParser::ParseXml(XmlParseHandler *pxph, std::string text) {
178  XmlParser parser(pxph);
179  parser.Parse(text.c_str(), text.length(), true);
180}
181
182XmlParser::ParseContext::ParseContext() :
183    xmlnsstack_(),
184    raised_(XML_ERROR_NONE),
185    line_number_(0),
186    column_number_(0),
187    byte_index_(0) {
188}
189
190void
191XmlParser::ParseContext::StartNamespace(const char *prefix, const char *ns) {
192  xmlnsstack_.AddXmlns(*prefix ? prefix : STR_EMPTY, ns);
193}
194
195void
196XmlParser::ParseContext::StartElement() {
197  xmlnsstack_.PushFrame();
198}
199
200void
201XmlParser::ParseContext::EndElement() {
202  xmlnsstack_.PopFrame();
203}
204
205QName
206XmlParser::ParseContext::ResolveQName(const char* qname, bool isAttr) {
207  const char *c;
208  for (c = qname; *c; ++c) {
209    if (*c == ':') {
210      const std::pair<std::string, bool> result =
211          xmlnsstack_.NsForPrefix(std::string(qname, c - qname));
212      if (!result.second)
213        return QName();
214      return QName(result.first, c + 1);
215    }
216  }
217  if (isAttr)
218    return QName(STR_EMPTY, qname);
219
220  std::pair<std::string, bool> result = xmlnsstack_.NsForPrefix(STR_EMPTY);
221  if (!result.second)
222    return QName();
223
224  return QName(result.first, qname);
225}
226
227void
228XmlParser::ParseContext::Reset() {
229  xmlnsstack_.Reset();
230  raised_ = XML_ERROR_NONE;
231}
232
233void
234XmlParser::ParseContext::SetPosition(int line, int column,
235                                          long byte_index) {
236  line_number_ = line;
237  column_number_ = column;
238  byte_index_ = byte_index;
239}
240
241void
242XmlParser::ParseContext::GetPosition(unsigned long * line,
243                                     unsigned long * column,
244                                     unsigned long * byte_index) {
245  if (line != NULL) {
246    *line = static_cast<unsigned long>(line_number_);
247  }
248
249  if (column != NULL) {
250    *column = static_cast<unsigned long>(column_number_);
251  }
252
253  if (byte_index != NULL) {
254    *byte_index = static_cast<unsigned long>(byte_index_);
255  }
256}
257
258XmlParser::ParseContext::~ParseContext() {
259}
260
261}  // namespace buzz
262