]> git.ipfire.org Git - thirdparty/squid.git/blame - src/esi/CustomParser.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / esi / CustomParser.cc
CommitLineData
43ae1d95 1
2/*
43ae1d95 3 * DEBUG: section 86 ESI processing
4 * AUTHOR: Robert Collins
5 *
6 * SQUID Web Proxy Cache http://www.squid-cache.org/
7 * ----------------------------------------------------------
8 *
9 * Squid is the result of efforts by numerous individuals from
10 * the Internet community; see the CONTRIBUTORS file for full
11 * details. Many organizations have provided support for Squid's
12 * development; see the SPONSORS file for full details. Squid is
13 * Copyrighted (C) 2001 by the Regents of the University of
14 * California; see the COPYRIGHT file for full details. Squid
15 * incorporates software developed and/or copyrighted by other
16 * sources; see the CREDITS file for full details.
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
26ac0430 22 *
43ae1d95 23 * This program is distributed in the hope that it will be useful,
24 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
26ac0430 27 *
43ae1d95 28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
31 *
32 */
33
582c2af2 34#include "squid.h"
08f774de 35#include "base/Vector.h"
582c2af2 36#include "Debug.h"
602d9612 37#include "esi/CustomParser.h"
99742866
FC
38#include "libTrie/Trie.h"
39#include "libTrie/TrieCharTransform.h"
43ae1d95 40
41Trie *ESICustomParser::SearchTrie=NULL;
43ae1d95 42
019db3c6 43EsiParserDefinition(ESICustomParser);
c2d4889f 44
43ae1d95 45Trie *
46ESICustomParser::GetTrie()
47{
48 if (SearchTrie)
49 return SearchTrie;
50
924f73bc 51 SearchTrie = new Trie(new TrieCaseless);
43ae1d95 52
137a13ea 53 static const ESITAG_t ESITAG_value = ESITAG;
54
43ae1d95 55 assert (SearchTrie->add
137a13ea 56 ("<esi:",5,(void *)&ESITAG_value));
57
58 static const ESITAG_t ESIENDTAG_value = ESIENDTAG;
43ae1d95 59
60 assert (SearchTrie->add
137a13ea 61 ("</esi:",6,(void *)&ESIENDTAG_value));
62
63 static const ESITAG_t ESICOMMENT_value = ESICOMMENT;
43ae1d95 64
65 assert (SearchTrie->add
137a13ea 66 ("<!--",4,(void *)&ESICOMMENT_value));
43ae1d95 67
68 return SearchTrie;
69}
70
a5488e5d
AJ
71ESICustomParser::ESICustomParser(ESIParserClient *aClient) :
72 theClient(aClient),
73 lastTag(ESITAG)
43ae1d95 74{}
75
76ESICustomParser::~ESICustomParser()
77{
78 theClient = NULL;
79}
80
81char const *
924f73bc 82ESICustomParser::findTag(char const *buffer, size_t bufferLength)
43ae1d95 83{
84 size_t myOffset (0);
137a13ea 85 ESITAG_t *resulttype = NULL;
43ae1d95 86
924f73bc 87 while (myOffset < bufferLength &&
137a13ea 88 (resulttype = static_cast<ESITAG_t *>(GetTrie()->findPrefix (buffer + myOffset, bufferLength - myOffset)))
89 == NULL)
43ae1d95 90 ++myOffset;
91
924f73bc 92 if (myOffset == bufferLength)
43ae1d95 93 return NULL;
94
bf8fe701 95 debugs(86, 9, "ESICustomParser::findTag: found " << *resulttype);
43ae1d95 96
137a13ea 97 lastTag = *resulttype;
43ae1d95 98
924f73bc 99 return buffer + myOffset;
43ae1d95 100}
101
102bool
103ESICustomParser::parse(char const *dataToParse, size_t const lengthOfData, bool const endOfStream)
104{
bf8fe701 105 debugs(86, 9, "ESICustomParser::parse: Appending data to internal buffer");
43ae1d95 106 content.append (dataToParse, lengthOfData);
107
108 if (!endOfStream) {
109 return true;
110 }
111
112 size_t openESITags (0);
a7a42b14
FC
113 //erring on the safe side. Probably rawBuf would be ok too
114 char const *currentPos = content.termedBuf();
43ae1d95 115 size_t remainingCount = content.size();
5aeabf95 116 char const *tag = NULL;
43ae1d95 117
118 while ((tag = findTag(currentPos, remainingCount))) {
119 if (tag - currentPos)
120 theClient->parserDefault (currentPos,tag - currentPos);
121
122 switch (lastTag) {
123
124 case ESITAG: {
26ac0430
AJ
125 ++openESITags;
126 char *tagEnd = strchr(const_cast<char *>(tag), '>');
43ae1d95 127
26ac0430
AJ
128 if (!tagEnd) {
129 error = "Could not find end ('>') of tag";
130 return false;
131 }
43ae1d95 132
26ac0430
AJ
133 if (tagEnd - tag > (ssize_t)remainingCount) {
134 error = "Tag ends beyond the parse buffer.";
135 return false;
136 }
43ae1d95 137
26ac0430
AJ
138 if (*(tagEnd - 1) == '/')
139 --openESITags;
43ae1d95 140
26ac0430 141 char * endofName = strpbrk(const_cast<char *>(tag), w_space);
43ae1d95 142
26ac0430
AJ
143 if (endofName > tagEnd)
144 endofName = const_cast<char *>(tagEnd);
43ae1d95 145
26ac0430 146 *endofName = '\0';
43ae1d95 147
26ac0430 148 *tagEnd = '\0';
43ae1d95 149
26ac0430 150 Vector<char *>attributes;
43ae1d95 151
26ac0430 152 char *attribute = const_cast<char *>(endofName + 1);
43ae1d95 153
26ac0430
AJ
154 while (attribute > tag && attribute < tagEnd) {
155 /* leading spaces */
43ae1d95 156
26ac0430
AJ
157 while (attribute < tagEnd && (xisspace(*attribute) || (*attribute == '/')))
158 ++attribute;
43ae1d95 159
26ac0430
AJ
160 if (! (attribute < tagEnd))
161 break;
43ae1d95 162
26ac0430
AJ
163 /* attribute name */
164 attributes.push_back(attribute);
43ae1d95 165
26ac0430 166 char *nextSpace = strpbrk(attribute, w_space);
43ae1d95 167
26ac0430 168 char *equals = strchr(attribute, '=');
43ae1d95 169
26ac0430
AJ
170 if (!equals) {
171 error = "Missing attribute value.";
172 return false;
173 }
43ae1d95 174
26ac0430
AJ
175 if (nextSpace && nextSpace < equals)
176 *nextSpace = '\0';
177 else
178 *equals = '\0';
43ae1d95 179
26ac0430 180 ++equals;
43ae1d95 181
26ac0430
AJ
182 while (equals < tagEnd && xisspace(*equals))
183 ++equals;
43ae1d95 184
26ac0430 185 char sep = *equals;
43ae1d95 186
26ac0430
AJ
187 if (sep != '\'' && sep != '"') {
188 error = "Unknown identifier (";
189 error.append (sep);
190 error.append (")");
191 return false;
43ae1d95 192 }
193
26ac0430 194 char *value = equals + 1;
a5488e5d
AJ
195 char *end = strchr(value, sep);
196
197 if (!end) {
198 error = "Missing attribute ending separator (";
199 error.append(sep);
200 error.append(")");
201 return false;
202 }
26ac0430
AJ
203 attributes.push_back(value);
204 *end = '\0';
205 attribute = end + 1;
206 }
43ae1d95 207
26ac0430
AJ
208 theClient->start (tag + 1, (const char **)attributes.items, attributes.size() >> 1);
209 /* TODO: attributes */
43ae1d95 210
26ac0430
AJ
211 if (*(tagEnd - 1) == '/')
212 theClient->end (tag + 1);
43ae1d95 213
26ac0430 214 remainingCount -= tagEnd - currentPos + 1;
43ae1d95 215
26ac0430
AJ
216 currentPos = tagEnd + 1;
217 }
218
219 break;
43ae1d95 220
221 case ESIENDTAG: {
26ac0430
AJ
222 if (!openESITags)
223 return false;
43ae1d95 224
26ac0430 225 char const *tagEnd = strchr(tag, '>');
43ae1d95 226
26ac0430
AJ
227 if (!tagEnd)
228 return false;
43ae1d95 229
26ac0430
AJ
230 if (tagEnd - tag > (ssize_t)remainingCount)
231 return false;
43ae1d95 232
26ac0430 233 char * endofName = strpbrk(const_cast<char *>(tag), w_space);
43ae1d95 234
26ac0430
AJ
235 if (endofName > tagEnd)
236 endofName = const_cast<char *>(tagEnd);
43ae1d95 237
26ac0430 238 *endofName = '\0';
43ae1d95 239
26ac0430 240 theClient->end (tag + 2);
43ae1d95 241
26ac0430 242 --openESITags;
43ae1d95 243
26ac0430 244 remainingCount -= tagEnd - currentPos + 1;
43ae1d95 245
26ac0430
AJ
246 currentPos = tagEnd + 1;
247 }
43ae1d95 248
26ac0430 249 break;
43ae1d95 250
251 case ESICOMMENT: {
26ac0430
AJ
252 /* Further optimisation potential:
253 * 1) recognize end comments for esi and don't callback on
254 * comments.
255 * 2) provide the comment length to the caller.
256 */
257 /* Comments must not be nested, without CDATA
258 * and we don't support CDATA
259 */
260 char *commentEnd = strstr (const_cast<char *>(tag), "-->");
261
262 if (!commentEnd) {
263 error = "missing end of comment";
264 return false;
265 }
43ae1d95 266
26ac0430
AJ
267 if (commentEnd - tag > (ssize_t)remainingCount) {
268 error = "comment ends beyond parse buffer";
269 return false;
43ae1d95 270 }
271
26ac0430
AJ
272 *commentEnd = '\0';
273 theClient->parserComment (tag + 4);
274 remainingCount -= commentEnd - currentPos + 3;
275 currentPos = commentEnd + 3;
276 }
277
278 break;
279 break;
43ae1d95 280
281 default:
282 fatal ("unknown ESI tag type found");
283 };
284
285 /*
286 * Find next esi tag (open or closing) or comment
287 * send tag, or full comment text
288 * rinse
289 */
290 }
291
292 if (remainingCount)
293 theClient->parserDefault (currentPos,remainingCount);
294
bf8fe701 295 debugs(86, 5, "ESICustomParser::parse: Finished parsing, will return " << !openESITags);
43ae1d95 296
297 if (openESITags)
298 error = "ESI Tags still open";
299
300 return !openESITags;
301}
302
137a13ea 303long int
43ae1d95 304ESICustomParser::lineNumber() const
305{
306 /* We don't track lines in the body */
307 return 0;
308}
309
310char const *
311ESICustomParser::errorString() const
312{
313 if (error.size())
a7a42b14 314 return error.termedBuf();
43ae1d95 315 else
316 return "Parsing error strings not implemented";
317}