1 /* Copyright (C) 2000-2015 Lavtech.com corp. All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2 of the License, or
6    (at your option) any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16 */
17 
18 #include "udm_config.h"
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/types.h>
24 
25 #include "udm_common.h"
26 #include "udm_utils.h"
27 #include "udm_uniconv.h"
28 #include "udm_vars.h"
29 #include "udm_textlist.h"
30 #include "udm_parsexml.h"
31 #include "udm_hrefs.h"
32 #include "udm_sgml.h"
33 #include "udm_http.h"
34 #include "udm_lex.h"
35 
36 typedef struct
37 {
38   UDM_AGENT *Indexer;
39   UDM_DOCUMENT *Doc;
40   UDM_HREF Href;
41   int body_sec;
42   UDM_CONST_STR XMLDefaultSection;
43   char *section_name;  /* TODO34: change to UDM_CONST_STR */
44   char *secpath;       /* TODO34: change to UDM_CONST_STR */
45   udm_bool_t is_sitemap;
46 } XML_PARSER_DATA;
47 
48 
49 /************ MY_XML **************/
50 
51 /* TODO34: change XML path delimiter from '.' to '/' */
52 #define UDM_XML_PATH_DELIMITER  '/'
53 #define UDM_XML_ATTR_DELIMITER  '@'
54 
55 static inline void
UdmConstTokenSkipLeadingSpaces(UDM_CONST_TOKEN * a)56 UdmConstTokenSkipLeadingSpaces(UDM_CONST_TOKEN *a)
57 {
58   for ( ; a->str < a->end ; a->str++)
59   {
60     if (!UdmLexScannerIsSpace(a->str[0]))
61       break;
62   }
63 }
64 
65 
66 static inline void
UdmConstTokenSkipTrailingSpaces(UDM_CONST_TOKEN * a)67 UdmConstTokenSkipTrailingSpaces(UDM_CONST_TOKEN *a)
68 {
69   for ( ; a->str < a->end ; a->end--)
70   {
71     if (!UdmLexScannerIsSpace(a->end[-1]))
72       break;
73   }
74 }
75 
76 
77 static inline void
UdmXMLParserSkipSpaces(UDM_XML_PARSER * p)78 UdmXMLParserSkipSpaces(UDM_XML_PARSER *p)
79 {
80   UdmLexScannerSkipSpaces(&p->scanner);
81 }
82 static inline const char *
UdmXMLParserCur(UDM_XML_PARSER * p)83 UdmXMLParserCur(UDM_XML_PARSER *p)
84 {
85   return UdmLexScannerCur(&p->scanner);
86 }
87 static inline const char *
UdmXMLParserStr(UDM_XML_PARSER * p)88 UdmXMLParserStr(UDM_XML_PARSER *p)
89 {
90   return UdmLexScannerStr(&p->scanner);
91 }
92 static inline const char *
UdmXMLParserEnd(UDM_XML_PARSER * p)93 UdmXMLParserEnd(UDM_XML_PARSER *p)
94 {
95   return UdmLexScannerEnd(&p->scanner);
96 }
97 static inline udm_bool_t
UdmXMLParserEOF(UDM_XML_PARSER * p)98 UdmXMLParserEOF(UDM_XML_PARSER *p)
99 {
100   return (udm_bool_t) UdmLexScannerEOF(&p->scanner);
101 }
102 static inline void
UdmXMLParserShift(UDM_XML_PARSER * p)103 UdmXMLParserShift(UDM_XML_PARSER *p)
104 {
105   UdmLexScannerShift(&p->scanner);
106 }
107 static inline void
UdmXMLParserShiftN(UDM_XML_PARSER * p,size_t n)108 UdmXMLParserShiftN(UDM_XML_PARSER *p, size_t n)
109 {
110   UdmLexScannerShiftN(&p->scanner, n);
111 }
112 static inline void
UdmXMLParserUnexpectedError(UDM_XML_PARSER * p,const char * prefix,const char * wanted)113 UdmXMLParserUnexpectedError(UDM_XML_PARSER *p,
114                             const char *prefix, const char *wanted)
115 {
116   udm_snprintf(p->errstr, sizeof(p->errstr), "%s: %s unexpected (%s wanted)",
117                prefix, UdmLex2str(UdmXMLParserLastTokenType(p)), wanted);
118 }
119 static inline void
UdmXMLParserUnexpectedErrorNear(UDM_XML_PARSER * p,const char * prefix,const char * wanted)120 UdmXMLParserUnexpectedErrorNear(UDM_XML_PARSER *p,
121                                 const char *prefix, const char *wanted)
122 {
123   size_t length= UdmXMLParserEnd(p) - UdmXMLParserCur(p);
124   if (length > 32)
125     length= 32;
126   udm_snprintf(p->errstr, sizeof(p->errstr),
127                "%s: %s unexpected (%s wanted) near '%.*s'",
128                prefix, UdmLex2str(UdmXMLParserLastTokenType(p)), wanted,
129                (int) length, UdmXMLParserCur(p));
130 }
131 
UdmXMLNormText(UDM_CONST_TOKEN * a)132 static void UdmXMLNormText(UDM_CONST_TOKEN *a)
133 {
134   UdmConstTokenSkipLeadingSpaces(a);
135   UdmConstTokenSkipTrailingSpaces(a);
136 }
137 
138 
139 typedef enum
140 {
141   UDM_XML_LEX_COMMENT,
142   UDM_XML_LEX_CDATA,
143   UDM_XML_LEX_PI,
144   UDM_XML_LEX_XMLDECL,
145   UDM_XML_LEX_OTHER,
146   UDM_XML_LEX_EOF
147 } udm_xml_lex_t;
148 
149 
150 static udm_xml_lex_t
UdmXMLParserCheckSpecialSection(UDM_XML_PARSER * p,UDM_CONST_TOKEN * a)151 UdmXMLParserCheckSpecialSection(UDM_XML_PARSER *p, UDM_CONST_TOKEN *a)
152 {
153   UdmXMLParserSkipSpaces(p);
154   if (UdmXMLParserEOF(p))
155   {
156     a->str= UdmXMLParserEnd(p);
157     a->end= UdmXMLParserEnd(p);
158     return UDM_XML_LEX_EOF;
159   }
160 
161   a->str= UdmXMLParserCur(p);
162   a->end= UdmXMLParserCur(p);
163   if (UdmXMLParserCur(p)[0] != '<')
164     return UDM_XML_LEX_OTHER;
165 
166   if (!memcmp(UdmXMLParserCur(p), "<!--", 4))
167   {
168     for ( ; !UdmXMLParserEOF(p) ; UdmXMLParserShift(p))
169     {
170       if (!memcmp(UdmXMLParserCur(p), "-->", 3))
171         break;
172     }
173     if (!memcmp(UdmXMLParserCur(p), "-->", 3))
174       UdmXMLParserShiftN(p, 3);
175     a->end= UdmXMLParserCur(p);
176     return UDM_XML_LEX_COMMENT;
177   }
178   else if (!memcmp(UdmXMLParserCur(p), "<![CDATA[",9))
179   {
180     UdmXMLParserShiftN(p, 9);
181     for ( ; !UdmXMLParserEOF(p) ; UdmXMLParserShift(p))
182     {
183       if (UdmXMLParserCur(p)[0] == ']' &&
184           UdmXMLParserCur(p)[1] == ']' &&
185           UdmXMLParserCur(p)[2] == '>')
186       {
187         UdmXMLParserShiftN(p, 3);
188         a->end= UdmXMLParserCur(p);
189         break;
190       }
191     }
192     return UDM_XML_LEX_CDATA;
193   }
194   else if (UdmXMLParserCur(p)[1] == '?')
195   {
196     if (!strncasecmp(UdmXMLParserCur(p) + 2, UDM_CSTR_WITH_LEN("xml")))
197       return UDM_XML_LEX_XMLDECL; /* Declaration */
198     for ( ; !UdmXMLParserEOF(p); UdmXMLParserShift(p))
199     {
200       if (UdmXMLParserCur(p)[0] == '?' &&
201           UdmXMLParserCur(p)[1] == '>')
202       {
203         UdmXMLParserShiftN(p, 2);
204         a->end= UdmXMLParserCur(p);
205         return UDM_XML_LEX_PI;
206       }
207     }
208   }
209   return UDM_XML_LEX_OTHER;
210 }
211 
212 
213 static void
UdmXMLScanLexToken(UDM_XML_PARSER * p,UDM_LEX_TOKEN * a)214 UdmXMLScanLexToken(UDM_XML_PARSER *p, UDM_LEX_TOKEN *a)
215 {
216   UdmXMLParserSkipSpaces(p);
217   a->token.str= UdmXMLParserCur(p);
218   if (!UdmLexScannerScanXMLIdentifier(&p->scanner, a))
219   {
220   }
221   else if (!UdmLexScannerScanPunctuation(&p->scanner, a))
222   {
223   }
224   else if (!UdmLexScannerScanString(&p->scanner, a) ||
225            !UdmLexScannerScanChar(&p->scanner, a))
226   {
227     if (!(p->flags & UDM_XML_SKIP_TEXT_NORMALIZATION))
228       UdmXMLNormText(&a->token);
229   }
230   else
231   {
232     a->type= UDM_LEX_UNKNOWN;
233   }
234 
235 #if 0
236   fprintf(stderr, "LEX=%s='%.*s'\n",
237           UdmLex2str(a->type), (int) (a->token.end-a->token.str), a->token.str);
238 #endif
239 }
240 
241 
242 static void
UdmXMLParserScan(UDM_XML_PARSER * p)243 UdmXMLParserScan(UDM_XML_PARSER *p)
244 {
245   UdmXMLScanLexToken(p, UdmXMLParserLastToken(p));
246 }
247 
248 
249 static udm_rc_t
UdmXMLValue(UDM_XML_PARSER * st,const char * str,size_t len)250 UdmXMLValue(UDM_XML_PARSER *st, const char *str, size_t len)
251 {
252 #ifdef DEBUG_XML
253   fprintf(stderr, "UdmXMLValue: %.*s\n", (int) len, str);
254 #endif
255   return st->handler.value_action ?
256          (st->handler.value_action)(st, str, len) : UDM_OK;
257 }
258 
259 
260 static udm_rc_t
UdmXMLEnterTagOrAttr(UDM_XML_PARSER * st,const char * str,size_t len,int delimiter)261 UdmXMLEnterTagOrAttr(UDM_XML_PARSER *st,
262                      const char *str, size_t len, int delimiter)
263 {
264 #ifdef DEBUG_XML
265   fprintf(stderr, "UdmXMLEnter: %.*s\n", (int) len, str);
266 #endif
267   if ((st->attrend - st->attr + len + 1) > sizeof(st->attr))
268   {
269     sprintf(st->errstr, "To deep XML");
270     return UDM_ERROR;
271   }
272   st->attrend[0]= delimiter;
273   st->attrend++;
274   memcpy(st->attrend, str, len);
275   st->attrend += len;
276   st->attrend[0]= '\0';
277   return st->handler.enter_action ?
278          (st->handler.enter_action)(st, st->attr, st->attrend - st->attr) :
279          UDM_OK;
280 }
281 
282 
283 static udm_rc_t
UdmXMLEnterTag(UDM_XML_PARSER * p,const char * str,size_t length)284 UdmXMLEnterTag(UDM_XML_PARSER *p, const char *str, size_t length)
285 {
286   return UdmXMLEnterTagOrAttr(p, str, length, UDM_XML_PATH_DELIMITER);
287 }
288 
289 
290 static udm_rc_t
UdmXMLEnterAttr(UDM_XML_PARSER * p,const char * str,size_t length)291 UdmXMLEnterAttr(UDM_XML_PARSER *p, const char *str, size_t length)
292 {
293   return UdmXMLEnterTagOrAttr(p, str, length, UDM_XML_ATTR_DELIMITER);
294 }
295 
296 
297 static udm_rc_t
UdmXMLLeave(UDM_XML_PARSER * p,const char * str,size_t slen)298 UdmXMLLeave(UDM_XML_PARSER *p, const char *str, size_t slen)
299 {
300   char *e;
301   size_t glen, delimiter= 0;
302   udm_rc_t rc;
303 
304 #ifdef DEBUG_XML
305   fprintf(stderr, "UdmXMLLeave: %.*s\n", (int) slen, str);
306 #endif
307   /* Find previous UDM_XML_PATH_DELIMITER or beginning */
308   for(e= p->attrend; e > p->attr ; e--)
309   {
310     if (e[0] == UDM_XML_PATH_DELIMITER || e[0] == UDM_XML_ATTR_DELIMITER)
311     {
312       delimiter= 1;
313       break;
314     }
315   }
316   glen= p->attrend - e - delimiter;
317 
318   if (str && (slen != glen))
319   {
320     udm_snprintf(p->errstr, sizeof(p->errstr),
321                  "'</%.*s>' unexpected ('</%.*s>' wanted)",
322                  (int) slen, str, (int) glen, e + 1);
323     return UDM_ERROR;;
324   }
325 
326   rc= p->handler.leave_action ?
327       (p->handler.leave_action)(p, p->attr, p->attrend - p->attr) : UDM_OK;
328 
329   *e= '\0';
330   p->attrend= e;
331   return(rc);
332 }
333 
334 
335 
336 static inline udm_rc_t
UdmXMLValueUsingLastToken(UDM_XML_PARSER * st)337 UdmXMLValueUsingLastToken(UDM_XML_PARSER *st)
338 {
339   return UdmXMLValue(st, UdmConstTokenStr(UdmXMLParserLastStrToken(st)),
340                          UdmConstTokenLength(UdmXMLParserLastStrToken(st)));
341 }
342 
343 
344 static inline udm_rc_t
UdmXMLEnterTagUsingLastToken(UDM_XML_PARSER * st)345 UdmXMLEnterTagUsingLastToken(UDM_XML_PARSER *st)
346 {
347   return UdmXMLEnterTag(st, UdmConstTokenStr(UdmXMLParserLastStrToken(st)),
348                             UdmConstTokenLength(UdmXMLParserLastStrToken(st)));
349 }
350 
351 
352 static inline udm_rc_t
UdmXMLEnterAttrUsingLastToken(UDM_XML_PARSER * st)353 UdmXMLEnterAttrUsingLastToken(UDM_XML_PARSER *st)
354 {
355   return UdmXMLEnterAttr(st, UdmConstTokenStr(UdmXMLParserLastStrToken(st)),
356                              UdmConstTokenLength(UdmXMLParserLastStrToken(st)));
357 }
358 
359 
360 static inline udm_rc_t
UdmXMLLeaveUsingLastToken(UDM_XML_PARSER * st)361 UdmXMLLeaveUsingLastToken(UDM_XML_PARSER *st)
362 {
363   return UdmXMLLeave(st, UdmConstTokenStr(UdmXMLParserLastStrToken(st)),
364                          UdmConstTokenLength(UdmXMLParserLastStrToken(st)));
365 }
366 
367 
368 
369 static void
UdmXMLParserScanText(UDM_XML_PARSER * p)370 UdmXMLParserScanText(UDM_XML_PARSER *p)
371 {
372   UDM_CONST_TOKEN a;
373   a.str= UdmXMLParserCur(p);
374   for ( ; !UdmXMLParserEOF(p) ; UdmXMLParserShift(p))
375   {
376     if (UdmXMLParserCur(p)[0] == '<')
377       break;
378   }
379   a.end= UdmXMLParserCur(p);
380 
381   if (!(p->flags & UDM_XML_SKIP_TEXT_NORMALIZATION))
382     UdmXMLNormText(&a);
383   if (!UdmConstTokenIsEmpty(&a))
384     UdmXMLValue(p, UdmConstTokenStr(&a), UdmConstTokenLength(&a));
385 }
386 
387 
388 /*
389   Scan attributes. Assumes that the next token is already scanned in "lex".
390 */
391 static udm_rc_t
UdmXMLParserScanAttributes(UDM_XML_PARSER * p)392 UdmXMLParserScanAttributes(UDM_XML_PARSER *p)
393 {
394   for ( ; UdmXMLParserLastTokenType(p) == UDM_LEX_IDENT ||
395           UdmXMLParserLastTokenType(p) == UDM_LEX_STRING ||
396           UdmXMLParserLastTokenType(p) == UDM_LEX_CHAR_LITERAL;
397           UdmXMLParserScan(p))
398   {
399     UDM_CONST_TOKEN t= UdmXMLParserLastToken(p)->token;
400     UdmXMLParserScan(p);
401     if (UdmXMLParserLastTokenType(p) == UDM_LEX_EQ)
402     {
403       /* Scan attr=value */
404       UdmXMLParserScan(p);
405       if (UdmXMLParserLastTokenType(p) == UDM_LEX_IDENT ||
406           UdmXMLParserLastTokenType(p) == UDM_LEX_STRING ||
407           UdmXMLParserLastTokenType(p) == UDM_LEX_CHAR_LITERAL)
408       {
409         if ((UDM_OK != UdmXMLEnterAttr(p, UdmConstTokenStr(&t), UdmConstTokenLength(&t))) ||
410             (UDM_OK != UdmXMLValueUsingLastToken(p)) ||
411             (UDM_OK != UdmXMLLeave(p, UdmConstTokenStr(&t), UdmConstTokenLength(&t))))
412           return UDM_ERROR;
413       }
414       else
415       {
416         UdmXMLParserUnexpectedError(p, "ScanAttributes", "ident or string");
417         return UDM_ERROR;
418       }
419     }
420     else if (UdmXMLParserLastTokenType(p) == UDM_LEX_IDENT)
421     {
422       if (UDM_OK != UdmXMLEnterAttrUsingLastToken(p) ||
423           UDM_OK != UdmXMLLeaveUsingLastToken(p))
424         return UDM_ERROR;
425     }
426     else if (UdmXMLParserLastTokenType(p) == UDM_LEX_STRING)
427     {
428       /* Do nothing */
429     }
430     else
431       break;
432   }
433   return UDM_OK;
434 }
435 
436 
437 /*
438   Skip the intSubset list of a DOCTYPE declaration.
439   Assumes that the next token is already scanned to "lex".
440 
441   Example:
442 
443 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" [
444   <!ENTITY install SYSTEM "install.xml">
445   <!ENTITY general SYSTEM "general.xml">
446   ]>
447 <book>...</book>
448 
449   This functions scans everything between [ and ],
450   including the brackets.
451 */
452 static udm_rc_t
UdmXMLParserScanIntSubset(UDM_XML_PARSER * p)453 UdmXMLParserScanIntSubset(UDM_XML_PARSER *p)
454 {
455   if (UdmXMLParserLastTokenType(p) != UDM_LEX_LB)
456     return UDM_OK;
457 
458   for (UdmXMLParserScan(p); ; UdmXMLParserScan(p))
459   {
460     switch (UdmXMLParserLastToken(p)->type)
461     {
462     case UDM_LEX_UNKNOWN:
463     case UDM_LEX_EOF:
464       UdmXMLParserUnexpectedErrorNear(p, "ScanIntSubset", "']'");
465       return UDM_ERROR;
466 
467     case UDM_LEX_RB:
468       UdmXMLParserScan(p);
469       return UDM_OK;
470 
471     case UDM_LEX_EXCLAM:
472     case UDM_LEX_HASH:
473     case UDM_LEX_DOLLAR:
474     case UDM_LEX_PERCENT:
475     case UDM_LEX_AMPERSAND:
476     case UDM_LEX_LP:
477     case UDM_LEX_RP:
478     case UDM_LEX_ASTERISK:
479     case UDM_LEX_PLUS:
480     case UDM_LEX_COMMA:
481     case UDM_LEX_MINUS:
482     case UDM_LEX_DOT:
483     case UDM_LEX_SLASH:
484     case UDM_LEX_COLON:
485     case UDM_LEX_SEMICOLON:
486     case UDM_LEX_LT:
487     case UDM_LEX_LE:
488     case UDM_LEX_EQ:
489     case UDM_LEX_GT:
490     case UDM_LEX_GE:
491     case UDM_LEX_EQ_EQ:
492     case UDM_LEX_NOT_EQ:
493     case UDM_LEX_QUESTION:
494     case UDM_LEX_AT:
495     case UDM_LEX_LB:
496     case UDM_LEX_CARET:
497     case UDM_LEX_LCB:
498     case UDM_LEX_VBAR:
499     case UDM_LEX_BOOL_OR:
500     case UDM_LEX_BOOL_AND:
501     case UDM_LEX_RCB:
502     case UDM_LEX_TILDE:
503     case UDM_LEX_TEXT:
504 
505     case UDM_LEX_ESCAPED_STRING:
506     case UDM_LEX_STRING:
507     case UDM_LEX_CHAR_LITERAL:
508     case UDM_LEX_IDENT:
509     case UDM_LEX_UINT:
510     case UDM_LEX_IF:
511     case UDM_LEX_ELSE:
512     case UDM_LEX_WHILE:
513     case UDM_LEX_DO:
514     case UDM_LEX_FOR:
515     case UDM_LEX_BREAK:
516     case UDM_LEX_CONTINUE:
517     case UDM_LEX_SIZEOF:
518     case UDM_LEX_COUT:
519     case UDM_LEX_AUTO:
520     case UDM_LEX_CASE:
521     case UDM_LEX_CONST:
522     case UDM_LEX_DEFAULT:
523     case UDM_LEX_ENUM:
524     case UDM_LEX_EXTERN:
525     case UDM_LEX_GOTO:
526     case UDM_LEX_REGISTER:
527     case UDM_LEX_RETURN:
528     case UDM_LEX_STATIC:
529     case UDM_LEX_STRUCT:
530     case UDM_LEX_SWITCH:
531     case UDM_LEX_TYPEDEF:
532     case UDM_LEX_UNION:
533     case UDM_LEX_VOLATILE:
534 
535     case UDM_LEX_CHAR:
536     case UDM_LEX_DOUBLE:
537     case UDM_LEX_FLOAT:
538     case UDM_LEX_INT:
539     case UDM_LEX_LONG:
540     case UDM_LEX_SHORT:
541     case UDM_LEX_SIGNED:
542     case UDM_LEX_UNSIGNED:
543       continue;
544 
545     case UDM_LEX_OPERATOR:
546       continue;
547 
548     case UDM_LEX_COMMENT:
549     case UDM_LEX_INC:
550     case UDM_LEX_DEC:
551     case UDM_LEX_MUL_EQ:
552     case UDM_LEX_DIV_EQ:
553     case UDM_LEX_REM_EQ:
554     case UDM_LEX_INC_EQ:
555     case UDM_LEX_DEC_EQ:
556     case UDM_LEX_AND_EQ:
557     case UDM_LEX_OR_EQ:
558     case UDM_LEX_XOR_EQ:
559     case UDM_LEX_LSHIFT_EQ:
560     case UDM_LEX_LSHIFT:
561     case UDM_LEX_RSHIFT:
562     case UDM_LEX_RSHIFT_EQ:
563       UDM_ASSERT(0);
564       continue;
565     }
566   }
567   return UDM_OK;
568 }
569 
570 
571 /*
572   Scan the ETag body, everything between '<' and '>'.
573   ETag ::= '</' Name S? '>'
574 */
575 static udm_rc_t
UdmXMLParserScanETagBody(UDM_XML_PARSER * p)576 UdmXMLParserScanETagBody(UDM_XML_PARSER *p)
577 {
578   if (UdmXMLParserLastTokenType(p) !=  UDM_LEX_SLASH)
579     return UDM_ERROR;
580   UdmXMLParserScan(p);
581   if (UdmXMLParserLastTokenType(p) != UDM_LEX_IDENT)
582   {
583     UdmXMLParserUnexpectedError(p, "ETagBody", "ident");
584     return UDM_ERROR;
585   }
586   if (UDM_OK != UdmXMLLeaveUsingLastToken(p))
587     return UDM_ERROR;
588   UdmXMLParserScan(p);
589   return UDM_OK;
590 }
591 
592 
593 /*
594   Name (S Attribute)* S?
595 */
596 static udm_rc_t
UdmXMLParserScanTagNameAndAttributes(UDM_XML_PARSER * p)597 UdmXMLParserScanTagNameAndAttributes(UDM_XML_PARSER *p)
598 {
599   if (UdmXMLParserLastTokenType(p) != UDM_LEX_IDENT)
600     return UDM_ERROR;
601   if (UDM_OK != UdmXMLEnterTagUsingLastToken(p))
602       return UDM_ERROR;
603   UdmXMLParserScan(p);
604   if (UDM_OK != UdmXMLParserScanAttributes(p))
605     return UDM_ERROR;
606   return UDM_OK;
607 }
608 
609 
610 /*
611   Scan the body of a processing instruction (PI) or XMLDecl, i.e.
612   everything between '<' and '>'.
613 
614   XMLDecl  ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
615   PI       ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
616   PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))
617 */
618 static udm_rc_t
UdmXMLParserScanXMLDecl(UDM_XML_PARSER * p)619 UdmXMLParserScanXMLDecl(UDM_XML_PARSER *p)
620 {
621   if (UdmXMLParserLastTokenType(p) != UDM_LEX_QUESTION)
622     return UDM_ERROR;
623 
624   UdmXMLParserScan(p);
625   p->xmldecl= UDM_TRUE;
626 
627   if (UDM_OK != UdmXMLParserScanTagNameAndAttributes(p))
628     return UDM_ERROR;
629 
630   if (UdmXMLParserLastTokenType(p) != UDM_LEX_QUESTION)
631   {
632     UdmXMLParserUnexpectedError(p, "ScanPIBody", "'?'");
633     return UDM_ERROR;
634   }
635   if (UDM_OK != UdmXMLLeave(p, NULL, 0))
636     return UDM_ERROR;
637   p->xmldecl= UDM_FALSE;
638   UdmXMLParserScan(p);
639   return UDM_OK;
640 }
641 
642 
643 /*
644   Scan a declaration body
645 */
646 static udm_rc_t
UdmXMLParserScanDeclarationBody(UDM_XML_PARSER * p)647 UdmXMLParserScanDeclarationBody(UDM_XML_PARSER *p)
648 {
649   if (UdmXMLParserLastTokenType(p) != UDM_LEX_EXCLAM)
650     return UDM_ERROR;
651   UdmXMLParserScan(p);
652   if (UDM_OK != UdmXMLParserScanTagNameAndAttributes(p))
653     return UDM_ERROR;
654   if (UDM_OK != UdmXMLParserScanIntSubset(p))
655     return UDM_ERROR;
656   if (UDM_OK != UdmXMLLeave(p, NULL, 0))
657     return UDM_ERROR;
658   return UDM_OK;
659 }
660 
661 
662 /*
663   Scan the body of STag or EmptyElemTag
664   i.e. everything between '<' and '>'
665 
666   STag         ::=  '<' Name (S Attribute)* S? '>'
667   EmptyElemTag ::=  '<' Name (S Attribute)* S? '/>'
668 */
669 static udm_rc_t
UdmXMLParserScanSTagBody(UDM_XML_PARSER * p)670 UdmXMLParserScanSTagBody(UDM_XML_PARSER *p)
671 {
672   if (UDM_OK != UdmXMLParserScanTagNameAndAttributes(p))
673     return UDM_ERROR;
674 
675   if (UdmXMLParserLastTokenType(p) == UDM_LEX_SLASH)
676   {
677     if (UDM_OK != UdmXMLLeave(p, NULL, 0))
678       return UDM_ERROR;
679     UdmXMLParserScan(p);
680   }
681   return UDM_OK;
682 }
683 
684 
685 static udm_rc_t
UdmXMLParserScanTag(UDM_XML_PARSER * p)686 UdmXMLParserScanTag(UDM_XML_PARSER *p)
687 {
688   if (UdmXMLParserLastTokenType(p) != UDM_LEX_LT)
689   {
690     UdmXMLParserUnexpectedError(p, "ScanTag", "'<'");
691     return UDM_ERROR;
692   }
693   UdmXMLParserScan(p);
694 
695   if (UDM_OK != UdmXMLParserScanETagBody(p) &&
696       UDM_OK != UdmXMLParserScanSTagBody(p) &&
697       UDM_OK != UdmXMLParserScanXMLDecl(p) &&
698       UDM_OK != UdmXMLParserScanDeclarationBody(p))
699   {
700     if (!p->errstr[0])
701       UdmXMLParserUnexpectedError(p, "ScanTag", "<Name> or '/' or '!' or '?'");
702     return UDM_ERROR;
703   }
704 
705   if (UdmXMLParserLastTokenType(p) != UDM_LEX_GT)
706   {
707     UdmXMLParserUnexpectedError(p, "ScanTag", "'>'");
708     return UDM_ERROR;
709   }
710   return UDM_OK;
711 }
712 
713 
714 udm_rc_t
UdmXMLParserExec(UDM_XML_PARSER * p,const char * str,size_t len)715 UdmXMLParserExec(UDM_XML_PARSER *p, const char *str, size_t len)
716 {
717   p->attrend= p->attr;
718   UdmLexScannerInit(&p->scanner, str, len);
719 
720   while (!UdmXMLParserEOF(p))
721   {
722     if (UdmXMLParserCur(p)[0] == '<')
723     {
724       switch (UdmXMLParserCheckSpecialSection(p, UdmXMLParserLastStrToken(p)))
725       {
726       case UDM_XML_LEX_EOF:
727         UdmXMLParserLastToken(p)->type= UDM_LEX_EOF;
728         UdmXMLParserUnexpectedError(p, "XMLParserExec", "tag body");
729         return UDM_ERROR;
730 
731       case UDM_XML_LEX_COMMENT:
732       case UDM_XML_LEX_PI:
733         continue;
734 
735       case UDM_XML_LEX_CDATA:
736         {
737           UDM_CONST_TOKEN tmp= UdmXMLParserLastStrToken(p)[0];
738           tmp.str+= 9;
739           tmp.end-= 3;
740           UdmXMLValue(p, tmp.str, tmp.end - tmp.str);
741         }
742         continue;
743       case UDM_XML_LEX_XMLDECL:
744       case UDM_XML_LEX_OTHER:
745         break;
746       }
747 
748       UdmXMLParserScan(p);
749       if (UDM_OK != UdmXMLParserScanTag(p))
750         return UDM_ERROR;
751     }
752     else
753     {
754       UdmXMLParserScanText(p);
755     }
756   }
757   return UDM_OK;
758 }
759 
760 
UdmXMLParserCreate(UDM_XML_PARSER * p)761 void UdmXMLParserCreate (UDM_XML_PARSER *p)
762 {
763   bzero((void*)p, sizeof(p[0]));
764 }
765 
766 
UdmXMLParserFree(UDM_XML_PARSER * p)767 void UdmXMLParserFree (UDM_XML_PARSER *p)
768 {
769 }
770 
771 
UdmXMLSetValueHandler(UDM_XML_PARSER * p,udm_rc_t (* action)(UDM_XML_PARSER * p,const char * s,size_t l))772 void UdmXMLSetValueHandler (UDM_XML_PARSER *p, udm_rc_t (*action)(UDM_XML_PARSER *p, const char *s, size_t l))
773 {
774   p->handler.value_action= action;
775 }
776 
777 
UdmXMLSetEnterHandler(UDM_XML_PARSER * p,udm_rc_t (* action)(UDM_XML_PARSER * p,const char * s,size_t l))778 void UdmXMLSetEnterHandler (UDM_XML_PARSER *p, udm_rc_t (*action)(UDM_XML_PARSER *p, const char *s, size_t l))
779 {
780   p->handler.enter_action= action;
781 }
782 
783 
UdmXMLSetLeaveHandler(UDM_XML_PARSER * p,udm_rc_t (* action)(UDM_XML_PARSER * p,const char * s,size_t l))784 void UdmXMLSetLeaveHandler (UDM_XML_PARSER *p, udm_rc_t (*action)(UDM_XML_PARSER *p, const char *s, size_t l))
785 {
786   p->handler.leave_action= action;
787 }
788 
789 
UdmXMLSetHandler(UDM_XML_PARSER * p,const UDM_XML_HANDLER * handler)790 void UdmXMLSetHandler(UDM_XML_PARSER *p, const UDM_XML_HANDLER *handler)
791 {
792   p->handler= *handler;
793 }
794 
795 
UdmXMLSetUserData(UDM_XML_PARSER * p,void * user_data)796 void UdmXMLSetUserData (UDM_XML_PARSER *p, void *user_data)
797 {
798   p->user_data= user_data;
799 }
800 
801 
UdmXMLErrorString(UDM_XML_PARSER * p)802 const char *UdmXMLErrorString (UDM_XML_PARSER *p)
803 {
804   return(p->errstr);
805 }
806 
807 
UdmXMLErrorPos(UDM_XML_PARSER * p)808 size_t UdmXMLErrorPos (UDM_XML_PARSER *p)
809 {
810   const char *s, *beg= UdmXMLParserStr(p);
811   for (s= beg; s < UdmXMLParserCur(p); s++)
812   {
813     if (s[0] == '\n')
814       beg= s;
815   }
816   return UdmXMLParserCur(p) - beg;
817 }
818 
819 
UdmXMLErrorLineno(UDM_XML_PARSER * p)820 size_t UdmXMLErrorLineno (UDM_XML_PARSER *p)
821 {
822   size_t res= 0;
823   const char *s;
824   for (s= UdmXMLParserStr(p); s < UdmXMLParserCur(p); s++)
825   {
826     if (s[0]=='\n')
827       res++;
828   }
829   return(res);
830 }
831 
832 /************ /MY_XML **************/
833 
834 
835 static udm_rc_t
startElement(UDM_XML_PARSER * parser,const char * name,size_t l)836 startElement(UDM_XML_PARSER *parser, const char *name, size_t l)
837 {
838   XML_PARSER_DATA *D= (XML_PARSER_DATA*) parser->user_data;
839   UDM_VARLIST *Vars= &D->Indexer->Conf->XMLEnterHooks;
840   const char *val;
841 
842   UdmFree(D->section_name);
843   D->section_name= udm_strndup(name, l);
844   UdmFree(D->secpath);
845   D->secpath= udm_strndup(name, l);
846   if (Vars->nvars && (val= UdmVarListFindStr(Vars, D->secpath, NULL)))
847   {
848     if (!strcasecmp(val, "HrefVarInit"))
849     {
850       UdmVarListFree(&D->Href.HrefVars);
851     }
852     else if (!strcasecmp(val, "HrefInit"))
853     {
854       UdmHrefFree(&D->Href);
855       UdmHrefInit(&D->Href);
856     }
857   }
858   return UDM_OK;
859 }
860 
861 
862 static udm_rc_t
endElement(UDM_XML_PARSER * parser,const char * name,size_t l)863 endElement(UDM_XML_PARSER *parser, const char *name, size_t l)
864 {
865   XML_PARSER_DATA *D= (XML_PARSER_DATA*) parser->user_data;
866   size_t i= l;
867 
868   if (D->Indexer->Conf->XMLLeaveHooks.nvars)
869   {
870     char *prevname= udm_strndup(name, l);
871     const char *val;
872     if (D->Href.url &&
873         (val= UdmVarListFindStr(&D->Indexer->Conf->XMLLeaveHooks,
874                                 prevname, NULL)))
875     {
876       UDM_DOCUMENT *Doc= D->Doc;
877       D->Href.Param.referrer= UdmVarListFindInt(&Doc->Sections, "Referrer-ID", 0);
878       D->Href.Param.hops= 1 + UdmVarListFindInt(&Doc->Sections, "Hops", 0);
879       D->Href.Param.link_source= UDM_LINK_SOURCE_XML;
880       UdmHrefListAdd(&Doc->Hrefs, &D->Href);
881     }
882     UdmFree(prevname);
883   }
884 
885   while (--i && name[i] != UDM_XML_PATH_DELIMITER);
886 
887   UdmFree(D->section_name);
888   D->section_name= udm_strndup(name, i);
889   UdmFree(D->secpath);
890   D->secpath= udm_strndup(name, i);
891   return UDM_OK;
892 }
893 
894 
895 static udm_rc_t
Text(UDM_XML_PARSER * parser,const char * s,size_t len)896 Text(UDM_XML_PARSER *parser, const char *s, size_t len)
897 {
898   XML_PARSER_DATA *D= (XML_PARSER_DATA*) parser->user_data;
899   UDM_DOCUMENT *Doc= D->Doc;
900   UDM_CONST_TEXTITEM  ConstItem;
901   UDM_TEXT_PARAM TextParam;
902   UDM_VARLIST  *Vars= &D->Indexer->Conf->XMLDataHooks;
903   const UDM_VAR *Sec;
904   const char *val;
905   size_t slen= 0;
906 
907   if (D->section_name == NULL)
908     return UDM_OK;
909 
910   /* fprintf(stderr, "%s=%.*s\n", D->secpath, (int) len, s); */
911   /* Process XMLDataHook commands */
912   if ((val= UdmVarListFindStr(Vars, D->section_name, NULL)))
913   {
914     if (!strcasecmp(val, "HrefSet"))
915     {
916       UdmFree(D->Href.url);
917       D->Href.url= udm_strndup(s, (size_t)len);
918       UdmSGMLUnescape(D->Href.url);
919     }
920     else if (!strcasecmp(val, "HrefVarAdd"))
921     {
922       UdmVarListReplaceStrn(&D->Href.HrefVars, D->section_name, s, len);
923     }
924     else if (!strcasecmp(val, "HrefVarAppend"))
925     {
926       UDM_VAR *var;
927       if ((var= UdmVarListFindVar(&D->Href.HrefVars, D->section_name)))
928       {
929         UdmVarAppendStrn(var, " ", 1);
930         UdmVarAppendStrn(var, s, len);
931       }
932       else
933       {
934         UdmVarListReplaceStrn(&D->Href.HrefVars, D->section_name, s, len);
935       }
936     }
937   }
938 
939 
940   if (D->secpath &&
941       (!strcasecmp(D->secpath, "/urlset@xmlns") ||
942        !strcasecmp(D->secpath, "/sitemapindex@xmlns")))
943   {
944     D->is_sitemap= UDM_TRUE;
945     D->Doc->Spider.robots.index= UDM_FALSE;
946     UdmTextListFree(&Doc->TextList);
947   }
948 
949   UdmTextParamInit(&TextParam, UDM_TEXTLIST_FLAG_HTML, 0);
950   UdmConstTextItemInit(&ConstItem);
951   UdmConstStrSet(&ConstItem.text, s, len);
952   if ((Sec= UdmVarListFind(&Doc->Sections, D->section_name)))
953   {
954     TextParam.secno= UdmVarSecno(Sec);
955     ConstItem.section_name.str= D->section_name;
956     ConstItem.section_name.length= D->section_name ? strlen(D->section_name) : 0;
957   }
958   else if (D->XMLDefaultSection.str && !D->is_sitemap)
959   {
960     TextParam.secno= D->body_sec;
961     ConstItem.section_name= D->XMLDefaultSection;
962   }
963   else
964   {
965     TextParam.secno= 0;
966     ConstItem.section_name.str= D->section_name;
967     ConstItem.section_name.length= D->section_name ? strlen(D->section_name) : 0;
968   }
969   UdmTextListAddConst(&Doc->TextList, &ConstItem, &TextParam);
970 
971   if (D->secpath &&
972       (slen= strlen(D->secpath)) >= 5 &&
973       (!strncasecmp(&D->secpath[slen - 5], "/href", 5) ||
974        !strcasecmp(D->secpath, "/sitemapindex/sitemap/loc") ||
975        !strcasecmp(D->secpath, "/urlset/url/loc")))
976   {
977     UdmHrefFree(&D->Href);
978     UdmHrefInit(&D->Href);
979     D->Href.url= udm_strndup(s, (size_t)len);
980     UdmSGMLUnescape(D->Href.url);
981     D->Href.Param.referrer= UdmVarListFindInt(&Doc->Sections, "Referrer-ID", 0);
982     D->Href.Param.hops= 1 + UdmVarListFindInt(&Doc->Sections, "Hops", 0);
983     D->Href.Param.link_source= UDM_LINK_SOURCE_XML;
984     UdmHrefListAdd(&Doc->Hrefs, &D->Href);
985   }
986 
987 
988   if (!strncasecmp(D->secpath, UDM_CSTR_WITH_LEN("/rss@encoding")) ||
989       (parser->xmldecl &&
990        !strncasecmp(D->secpath, UDM_CSTR_WITH_LEN("/xml@encoding"))))
991   {
992     char buf[64];
993     if (len > 0 && len < sizeof(buf))
994     {
995       const char *csname;
996       memcpy(buf, s, len);
997       buf[len]= '\0';
998       csname= UdmCharsetCanonicalName(buf);
999       if (csname)
1000         UdmVarListReplaceStr(&Doc->Sections, "Meta-Charset", csname);
1001     }
1002   }
1003   return UDM_OK;
1004 }
1005 
1006 
1007 #if 0
1008 static void Decl(void *userData, const XML_Char *version, const XML_Char *encoding, int standalone)
1009 {
1010   XML_PARSER_DATA *D= userData;
1011   UDM_DOCUMENT *Doc= D->Doc;
1012 
1013   if (encoding != NULL)
1014     UdmVarListReplaceStr(&Doc->Sections,
1015                          "Meta-Charset", UdmCharsetCanonicalName(encoding));
1016 
1017 }
1018 
1019 static int EncHandler(void *encodingHandlerData, const XML_Char *name, XML_Encoding *info)
1020 {
1021   UDM_AGENT *indexer= encodingHandlerData;
1022   UDM_CHARSET  *cs;
1023   size_t i;
1024 
1025   if (!(cs= UdmGetCharSet(name)))
1026   {
1027     return 0;
1028   }
1029 
1030   /* FIXME: rewrite this for multibytes encodings */
1031   if (cs->tab_to_uni == NULL) return 0;
1032 
1033   info->convert= NULL;
1034   info->release= NULL;
1035   info->data= NULL;
1036   for(i= 0; i < 256; i++)
1037     info->map[i]= cs->tab_to_uni[i];
1038 
1039   return 1;
1040 }
1041 #endif
1042 
1043 
1044 udm_rc_t
UdmXMLParse(UDM_AGENT * Indexer,UDM_DOCUMENT * Doc)1045 UdmXMLParse(UDM_AGENT *Indexer, UDM_DOCUMENT *Doc)
1046 {
1047   udm_rc_t rc= UDM_OK;
1048   XML_PARSER_DATA Data;
1049   UDM_XML_PARSER parser;
1050   const char *XMLDefaultSection= UdmVarListFindStr(&Indexer->Conf->Vars, "XMLDefaultSection", NULL);
1051   const UDM_VAR *BSec= XMLDefaultSection ? UdmVarListFind(&Doc->Sections,XMLDefaultSection) : NULL;
1052   int body_sec= BSec ? UdmVarSecno(BSec) : 0;
1053   UDM_CONST_STR content;
1054 
1055   if (UdmHTTPBufContentToConstStr(&Doc->Buf, &content))
1056     return UDM_ERROR;
1057 
1058   UdmXMLParserCreate(&parser);
1059   bzero(&Data, sizeof(Data));
1060   Data.Indexer= Indexer;
1061   Data.Doc= Doc;
1062   Data.body_sec= body_sec;
1063   Data.XMLDefaultSection.str= XMLDefaultSection;
1064   Data.XMLDefaultSection.length= XMLDefaultSection ? strlen(XMLDefaultSection) : 0;
1065 
1066   UdmXMLSetUserData(&parser, &Data);
1067   UdmXMLSetEnterHandler(&parser, startElement);
1068   UdmXMLSetLeaveHandler(&parser, endElement);
1069   UdmXMLSetValueHandler(&parser, Text);
1070 
1071   if (UDM_OK != (rc= UdmXMLParserExec(&parser, content.str, content.length)))
1072   {
1073     char err[256];
1074     udm_snprintf(err, sizeof(err),
1075                  "XML parsing error: %s at line %d pos %d",
1076                   UdmXMLErrorString(&parser),
1077                   (int) UdmXMLErrorLineno(&parser),
1078                   (int) UdmXMLErrorPos(&parser));
1079     UdmVarListReplaceStr(&Doc->Sections, "X-Reason", err);
1080   }
1081 
1082   UdmXMLParserFree(&parser);
1083   UdmFree(Data.section_name);
1084   UdmFree(Data.secpath);
1085   UdmHrefFree(&Data.Href);
1086   return rc;
1087 }
1088