]> git.ipfire.org Git - thirdparty/squid.git/blame - src/esi/CustomParser.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / esi / CustomParser.cc
CommitLineData
43ae1d95 1/*
ef57eb7b 2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
43ae1d95 3 *
bbc27441
AJ
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
43ae1d95 7 */
8
bbc27441
AJ
9/* DEBUG: section 86 ESI processing */
10
582c2af2 11#include "squid.h"
582c2af2 12#include "Debug.h"
602d9612 13#include "esi/CustomParser.h"
c8ea3cc0 14#include "fatal.h"
99742866
FC
15#include "libTrie/Trie.h"
16#include "libTrie/TrieCharTransform.h"
43ae1d95 17
c8ea3cc0
FC
18#include <vector>
19
43ae1d95 20Trie *ESICustomParser::SearchTrie=NULL;
43ae1d95 21
019db3c6 22EsiParserDefinition(ESICustomParser);
c2d4889f 23
43ae1d95 24Trie *
25ESICustomParser::GetTrie()
26{
27 if (SearchTrie)
28 return SearchTrie;
29
924f73bc 30 SearchTrie = new Trie(new TrieCaseless);
43ae1d95 31
137a13ea 32 static const ESITAG_t ESITAG_value = ESITAG;
33
43ae1d95 34 assert (SearchTrie->add
137a13ea 35 ("<esi:",5,(void *)&ESITAG_value));
36
37 static const ESITAG_t ESIENDTAG_value = ESIENDTAG;
43ae1d95 38
39 assert (SearchTrie->add
137a13ea 40 ("</esi:",6,(void *)&ESIENDTAG_value));
41
42 static const ESITAG_t ESICOMMENT_value = ESICOMMENT;
43ae1d95 43
44 assert (SearchTrie->add
137a13ea 45 ("<!--",4,(void *)&ESICOMMENT_value));
43ae1d95 46
47 return SearchTrie;
48}
49
a5488e5d 50ESICustomParser::ESICustomParser(ESIParserClient *aClient) :
f53969cc
SM
51 theClient(aClient),
52 lastTag(ESITAG)
43ae1d95 53{}
54
55ESICustomParser::~ESICustomParser()
56{
57 theClient = NULL;
58}
59
60char const *
924f73bc 61ESICustomParser::findTag(char const *buffer, size_t bufferLength)
43ae1d95 62{
63 size_t myOffset (0);
137a13ea 64 ESITAG_t *resulttype = NULL;
43ae1d95 65
924f73bc 66 while (myOffset < bufferLength &&
137a13ea 67 (resulttype = static_cast<ESITAG_t *>(GetTrie()->findPrefix (buffer + myOffset, bufferLength - myOffset)))
68 == NULL)
43ae1d95 69 ++myOffset;
70
924f73bc 71 if (myOffset == bufferLength)
43ae1d95 72 return NULL;
73
bf8fe701 74 debugs(86, 9, "ESICustomParser::findTag: found " << *resulttype);
43ae1d95 75
137a13ea 76 lastTag = *resulttype;
43ae1d95 77
924f73bc 78 return buffer + myOffset;
43ae1d95 79}
80
81bool
82ESICustomParser::parse(char const *dataToParse, size_t const lengthOfData, bool const endOfStream)
83{
bf8fe701 84 debugs(86, 9, "ESICustomParser::parse: Appending data to internal buffer");
43ae1d95 85 content.append (dataToParse, lengthOfData);
86
87 if (!endOfStream) {
88 return true;
89 }
90
91 size_t openESITags (0);
a7a42b14
FC
92 //erring on the safe side. Probably rawBuf would be ok too
93 char const *currentPos = content.termedBuf();
43ae1d95 94 size_t remainingCount = content.size();
5aeabf95 95 char const *tag = NULL;
43ae1d95 96
97 while ((tag = findTag(currentPos, remainingCount))) {
98 if (tag - currentPos)
99 theClient->parserDefault (currentPos,tag - currentPos);
100
101 switch (lastTag) {
102
103 case ESITAG: {
26ac0430
AJ
104 ++openESITags;
105 char *tagEnd = strchr(const_cast<char *>(tag), '>');
43ae1d95 106
26ac0430
AJ
107 if (!tagEnd) {
108 error = "Could not find end ('>') of tag";
109 return false;
110 }
43ae1d95 111
26ac0430
AJ
112 if (tagEnd - tag > (ssize_t)remainingCount) {
113 error = "Tag ends beyond the parse buffer.";
114 return false;
115 }
43ae1d95 116
26ac0430
AJ
117 if (*(tagEnd - 1) == '/')
118 --openESITags;
43ae1d95 119
26ac0430 120 char * endofName = strpbrk(const_cast<char *>(tag), w_space);
43ae1d95 121
26ac0430
AJ
122 if (endofName > tagEnd)
123 endofName = const_cast<char *>(tagEnd);
43ae1d95 124
26ac0430 125 *endofName = '\0';
43ae1d95 126
26ac0430 127 *tagEnd = '\0';
43ae1d95 128
c8ea3cc0 129 std::vector<char *>attributes;
43ae1d95 130
26ac0430 131 char *attribute = const_cast<char *>(endofName + 1);
43ae1d95 132
26ac0430
AJ
133 while (attribute > tag && attribute < tagEnd) {
134 /* leading spaces */
43ae1d95 135
26ac0430
AJ
136 while (attribute < tagEnd && (xisspace(*attribute) || (*attribute == '/')))
137 ++attribute;
43ae1d95 138
26ac0430
AJ
139 if (! (attribute < tagEnd))
140 break;
43ae1d95 141
26ac0430
AJ
142 /* attribute name */
143 attributes.push_back(attribute);
43ae1d95 144
26ac0430 145 char *nextSpace = strpbrk(attribute, w_space);
43ae1d95 146
26ac0430 147 char *equals = strchr(attribute, '=');
43ae1d95 148
26ac0430
AJ
149 if (!equals) {
150 error = "Missing attribute value.";
151 return false;
152 }
43ae1d95 153
26ac0430
AJ
154 if (nextSpace && nextSpace < equals)
155 *nextSpace = '\0';
156 else
157 *equals = '\0';
43ae1d95 158
26ac0430 159 ++equals;
43ae1d95 160
26ac0430
AJ
161 while (equals < tagEnd && xisspace(*equals))
162 ++equals;
43ae1d95 163
26ac0430 164 char sep = *equals;
43ae1d95 165
26ac0430
AJ
166 if (sep != '\'' && sep != '"') {
167 error = "Unknown identifier (";
168 error.append (sep);
169 error.append (")");
170 return false;
43ae1d95 171 }
172
26ac0430 173 char *value = equals + 1;
a5488e5d
AJ
174 char *end = strchr(value, sep);
175
176 if (!end) {
177 error = "Missing attribute ending separator (";
178 error.append(sep);
179 error.append(")");
180 return false;
181 }
26ac0430
AJ
182 attributes.push_back(value);
183 *end = '\0';
184 attribute = end + 1;
185 }
43ae1d95 186
4814631e
FC
187 // TODO: after c++11, replace &attributes.front() with attributes.data()
188 theClient->start (tag + 1, const_cast<const char **>(&attributes.front()), attributes.size() >> 1);
26ac0430 189 /* TODO: attributes */
43ae1d95 190
26ac0430
AJ
191 if (*(tagEnd - 1) == '/')
192 theClient->end (tag + 1);
43ae1d95 193
26ac0430 194 remainingCount -= tagEnd - currentPos + 1;
43ae1d95 195
26ac0430
AJ
196 currentPos = tagEnd + 1;
197 }
198
199 break;
43ae1d95 200
201 case ESIENDTAG: {
26ac0430
AJ
202 if (!openESITags)
203 return false;
43ae1d95 204
26ac0430 205 char const *tagEnd = strchr(tag, '>');
43ae1d95 206
26ac0430
AJ
207 if (!tagEnd)
208 return false;
43ae1d95 209
26ac0430
AJ
210 if (tagEnd - tag > (ssize_t)remainingCount)
211 return false;
43ae1d95 212
26ac0430 213 char * endofName = strpbrk(const_cast<char *>(tag), w_space);
43ae1d95 214
26ac0430
AJ
215 if (endofName > tagEnd)
216 endofName = const_cast<char *>(tagEnd);
43ae1d95 217
26ac0430 218 *endofName = '\0';
43ae1d95 219
26ac0430 220 theClient->end (tag + 2);
43ae1d95 221
26ac0430 222 --openESITags;
43ae1d95 223
26ac0430 224 remainingCount -= tagEnd - currentPos + 1;
43ae1d95 225
26ac0430
AJ
226 currentPos = tagEnd + 1;
227 }
43ae1d95 228
26ac0430 229 break;
43ae1d95 230
231 case ESICOMMENT: {
26ac0430
AJ
232 /* Further optimisation potential:
233 * 1) recognize end comments for esi and don't callback on
234 * comments.
235 * 2) provide the comment length to the caller.
236 */
237 /* Comments must not be nested, without CDATA
238 * and we don't support CDATA
239 */
240 char *commentEnd = strstr (const_cast<char *>(tag), "-->");
241
242 if (!commentEnd) {
243 error = "missing end of comment";
244 return false;
245 }
43ae1d95 246
26ac0430
AJ
247 if (commentEnd - tag > (ssize_t)remainingCount) {
248 error = "comment ends beyond parse buffer";
249 return false;
43ae1d95 250 }
251
26ac0430
AJ
252 *commentEnd = '\0';
253 theClient->parserComment (tag + 4);
254 remainingCount -= commentEnd - currentPos + 3;
255 currentPos = commentEnd + 3;
256 }
257
258 break;
259 break;
43ae1d95 260
261 default:
262 fatal ("unknown ESI tag type found");
263 };
264
265 /*
266 * Find next esi tag (open or closing) or comment
267 * send tag, or full comment text
268 * rinse
269 */
270 }
271
272 if (remainingCount)
273 theClient->parserDefault (currentPos,remainingCount);
274
bf8fe701 275 debugs(86, 5, "ESICustomParser::parse: Finished parsing, will return " << !openESITags);
43ae1d95 276
277 if (openESITags)
278 error = "ESI Tags still open";
279
280 return !openESITags;
281}
282
137a13ea 283long int
43ae1d95 284ESICustomParser::lineNumber() const
285{
286 /* We don't track lines in the body */
287 return 0;
288}
289
290char const *
291ESICustomParser::errorString() const
292{
293 if (error.size())
a7a42b14 294 return error.termedBuf();
43ae1d95 295 else
296 return "Parsing error strings not implemented";
297}
f53969cc 298