This file is indexed.

/usr/share/awl/inc/XMLElement.php is in libawl-php 0.55-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
<?php
/**
* A class to assist with construction of XML documents
*
* @package   awl
* @subpackage   XMLElement
* @author    Andrew McMillan <andrew@mcmillan.net.nz>
* @copyright Catalyst .Net Ltd, Morphoss Ltd <http://www.morphoss.com/>
* @license   http://www.gnu.org/licenses/lgpl-3.0.txt  GNU LGPL version 3 or later
*/

require_once('AWLUtilities.php');

/**
* A class for XML elements which may have attributes, or contain
* other XML sub-elements
*
* @package   awl
*/
class XMLElement {
  protected $tagname;
  protected $xmlns;
  protected $attributes;
  protected $content;
  protected $_parent;

  /**
  * Constructor - nothing fancy as yet.
  *
  * @param string $tagname The tag name of the new element
  * @param mixed $content Either a string of content, or an array of sub-elements
  * @param array $attributes An array of attribute name/value pairs
  * @param string $xmlns An XML namespace specifier
  */
  function __construct( $tagname, $content=false, $attributes=false, $xmlns=null ) {
    $this->tagname=$tagname;
    if ( gettype($content) == "object" ) {
      // Subtree to be parented here
      $this->content = array(&$content);
    }
    else {
      // Array or text
      $this->content = $content;
    }
    $this->attributes = $attributes;
    if ( isset($xmlns) ) {
      $this->xmlns = $xmlns;
    }
    else {
      if ( preg_match( '{^(.*):([^:]*)$}', $tagname, $matches) ) {
        $prefix = $matches[1];
        $tag = $matches[2];
        if ( isset($this->attributes['xmlns:'.$prefix]) ) {
          $this->xmlns = $this->attributes['xmlns:'.$prefix];
        }
      }
      else if ( isset($this->attributes['xmlns']) ) {
        $this->xmlns = $this->attributes['xmlns'];
      }
    }
  }


  /**
  * Count the number of elements
  * @return int The number of elements
  */
  function CountElements( ) {
    if ( $this->content === false ) return 0;
    if ( is_array($this->content) ) return count($this->content);
    if ( $this->content == '' ) return 0;
    return 1;
  }

  /**
  * Set an element attribute to a value
  *
  * @param string The attribute name
  * @param string The attribute value
  */
  function SetAttribute($k,$v) {
    if ( gettype($this->attributes) != "array" ) $this->attributes = array();
    $this->attributes[$k] = $v;
    if ( strtolower($k) == 'xmlns' ) {
      $this->xmlns = $v;
    }
  }

  /**
  * Set the whole content to a value
  *
  * @param mixed The element content, which may be text, or an array of sub-elements
  */
  function SetContent($v) {
    $this->content = $v;
  }

  /**
  * Accessor for the tag name
  *
  * @return string The tag name of the element
  */
  function GetTag() {
    return $this->tagname;
  }

  /**
  * Accessor for the full-namespaced tag name
  *
  * @return string The tag name of the element, prefixed by the namespace
  */
  function GetNSTag() {
    return (empty($this->xmlns) ? '' : $this->xmlns . ':') . $this->tagname;
  }

  /**
  * Accessor for a single attribute
  * @param string $attr The name of the attribute.
  * @return string The value of that attribute of the element
  */
  function GetAttribute( $attr ) {
    if ( $attr == 'xmlns' ) return $this->xmlns;
    if ( isset($this->attributes[$attr]) ) return $this->attributes[$attr];
    return null;
  }

  /**
  * Accessor for the attributes
  *
  * @return array The attributes of this element
  */
  function GetAttributes() {
    return $this->attributes;
  }

  /**
  * Accessor for the content
  *
  * @return array The content of this element
  */
  function GetContent() {
    return $this->content;
  }

  /**
  * Return an array of elements matching the specified tag, or all elements if no tag is supplied.
  * Unlike GetContent() this will always return an array.
  *
  * @return array The XMLElements within the tree which match this tag
  */
  function GetElements( $tag=null, $recursive=false ) {
    $elements = array();
    if ( gettype($this->content) == "array" ) {
      foreach( $this->content AS $k => $v ) {
        if ( empty($tag) || $v->GetNSTag() == $tag ) {
          $elements[] = $v;
        }
        if ( $recursive ) {
          $elements = $elements + $v->GetElements($tag,true);
        }
      }
    }
    else if ( empty($tag) || (isset($v->content->tagname) && $v->content->GetNSTag() == $tag) ) {
      $elements[] = $this->content;
    }
    return $elements;
  }


  /**
  * Return an array of elements matching the specified path
  *
  * @return array The XMLElements within the tree which match this tag
  */
  function GetPath( $path ) {
    $elements = array();
    // printf( "Querying within '%s' for path '%s'\n", $this->tagname, $path );
    if ( !preg_match( '#(/)?([^/]+)(/?.*)$#', $path, $matches ) ) return $elements;
    // printf( "Matches: %s -- %s -- %s\n", $matches[1], $matches[2], $matches[3] );
    if ( $matches[2] == '*' || $matches[2] == $this->GetNSTag()) {
      if ( $matches[3] == '' ) {
        /**
        * That is the full path
        */
        $elements[] = $this;
      }
      else if ( gettype($this->content) == "array" ) {
        /**
        * There is more to the path, so we recurse into that sub-part
        */
        foreach( $this->content AS $k => $v ) {
          $elements = array_merge( $elements, $v->GetPath($matches[3]) );
        }
      }
    }

    if ( $matches[1] != '/' && gettype($this->content) == "array" ) {
      /**
      * If our input $path was not rooted, we recurse further
      */
      foreach( $this->content AS $k => $v ) {
        $elements = array_merge( $elements, $v->GetPath($path) );
      }
    }
    // printf( "Found %d within '%s' for path '%s'\n", count($elements), $this->tagname, $path );
    return $elements;
  }


  /**
  * Add a sub-element
  *
  * @param object An XMLElement to be appended to the array of sub-elements
  */
  function AddSubTag(&$v) {
    if ( gettype($this->content) != "array" ) $this->content = array();
    $this->content[] =& $v;
    return count($this->content);
  }

  /**
  * Add a new sub-element
  *
  * @param string The tag name of the new element
  * @param mixed Either a string of content, or an array of sub-elements
  * @param array An array of attribute name/value pairs
  *
  * @return objectref A reference to the new XMLElement
  */
  function &NewElement( $tagname, $content=false, $attributes=false, $xmlns=null ) {
    if ( gettype($this->content) != "array" ) $this->content = array();
    $element = new XMLElement($tagname,$content,$attributes,$xmlns);
    $this->content[] =& $element;
    return $element;
  }


  /**
  * Render just the internal content
  *
  * @return string The content of this element, as a string without this element wrapping it.
  */
  function RenderContent($indent=0, $nslist=null, $force_xmlns=false ) {
    $r = "";
    if ( is_array($this->content) ) {
      /**
      * Render the sub-elements with a deeper indent level
      */
      $r .= "\n";
      foreach( $this->content AS $k => $v ) {
        if ( is_object($v) ) {
          $r .= $v->Render($indent+1, "", $nslist, $force_xmlns);
        }
      }
      $r .= substr("                        ",0,$indent);
    }
    else {
      /**
      * Render the content, with special characters escaped
      *
      */
      if(strpos($this->content, '<![CDATA[')===0 && strrpos($this->content, ']]>')===strlen($this->content)-3)
        $r .= '<![CDATA[' . str_replace(']]>', ']]]]><![CDATA[>', substr($this->content, 9, -3)) . ']]>';
      else if ( defined('ENT_XML1') && defined('ENT_DISALLOWED') )
        // Newer PHP versions allow specifying ENT_XML1, but default to ENT_HTML401.  Go figure.  #PHPWTF
        $r .= htmlspecialchars($this->content, ENT_NOQUOTES |  ENT_XML1 | ENT_DISALLOWED );
      // Need to work out exactly how to do this in PHP.
      // else if ( preg_match('{^[\t\n\r\x0020-\xD7FF\xE000-\xFFFD\x10000-\x10FFFF]+$}u', utf8ToUnicode($this->content)) )
      //   $r .= '<![CDATA[' . $this->content . ']]>';
      else
        // Older PHP versions default to ENT_XML1.
        $r .= htmlspecialchars($this->content, ENT_NOQUOTES );
    }
    return $r;
  }


  /**
  * Render the document tree into (nicely formatted) XML
  *
  * @param int The indenting level for the pretty formatting of the element
  */
  function Render($indent=0, $xmldef="", $nslist=null, $force_xmlns=false) {
    $r = ( $xmldef == "" ? "" : $xmldef."\n");

    $attr = "";
    $tagname = $this->tagname;
    $xmlns_done = false;
    if ( gettype($this->attributes) == "array" ) {
      /**
      * Render the element attribute values
      */
      foreach( $this->attributes AS $k => $v ) {
        if ( preg_match('#^xmlns(:?(.+))?$#', $k, $matches ) ) {
//          if ( $force_xmlns ) printf( "1: %s: %s\n", $this->tagname, $this->xmlns );
          if ( !isset($nslist) ) $nslist = array();
          $prefix = (isset($matches[2]) ? $matches[2] : '');
          if ( isset($nslist[$v]) && $nslist[$v] == $prefix ) continue; // No need to include in list as it's in a wrapping element
          $nslist[$v] = $prefix;
          if ( !isset($this->xmlns) ) $this->xmlns = $v;
          $xmlns_done = true;
        }
        $attr .= sprintf( ' %s="%s"', $k, htmlspecialchars($v) );
      }
    }
    if ( isset($this->xmlns) && isset($nslist[$this->xmlns]) && $nslist[$this->xmlns] != '' ) {
//      if ( $force_xmlns ) printf( "2: %s: %s\n", $this->tagname, $this->xmlns );
      $tagname = $nslist[$this->xmlns] . ':' . $tagname;
      if ( $force_xmlns ) $attr .= sprintf( ' xmlns="%s"', $this->xmlns);
    }
    else if ( isset($this->xmlns) && !isset($nslist[$this->xmlns]) && gettype($this->attributes) == 'array' && !isset($this->attributes[$this->xmlns]) ) {
//      if ( $force_xmlns ) printf( "3: %s: %s\n", $this->tagname, $this->xmlns );
      $attr .= sprintf( ' xmlns="%s"', $this->xmlns);
    }
    else if ( $force_xmlns && isset($this->xmlns) && ! $xmlns_done ) {
//      printf( "4: %s: %s\n", $this->tagname, $this->xmlns );
      $attr .= sprintf( ' xmlns="%s"', $this->xmlns);
    }
    
    $r .= substr("                        ",0,$indent) . '<' . $tagname . $attr;

    if ( (is_array($this->content) && count($this->content) > 0) || (!is_array($this->content) && strlen($this->content) > 0) ) {
      $r .= ">";
      $r .= $this->RenderContent($indent,$nslist,$force_xmlns);
      $r .= '</' . $tagname.">\n";
    }
    else {
      $r .= "/>\n";
    }
    return $r;
  }


  function __tostring() {
    return $this->Render();
  }
}


/**
* Rebuild an XML tree in our own style from the parsed XML tags using
* a tail-recursive approach.
*
* @param array $xmltags An array of XML tags we get from using the PHP XML parser
* @param intref &$start_from A pointer to our current integer offset into $xmltags
* @return mixed Either a single XMLElement, or an array of XMLElement objects.
*/
function BuildXMLTree( $xmltags, &$start_from ) {
  $content = array();

  if ( !isset($start_from) ) $start_from = 0;

  for( $i=0; $i < 50000 && isset($xmltags[$start_from]); $i++) {
    $tagdata = $xmltags[$start_from++];
    if ( !isset($tagdata) || !isset($tagdata['tag']) || !isset($tagdata['type']) ) break;
    if ( $tagdata['type'] == "close" ) break;
    $xmlns = null;
    $tag = $tagdata['tag'];
    if ( preg_match( '{^(.*):([^:]*)$}', $tag, $matches) ) {
      $xmlns = $matches[1];
      $tag = $matches[2];
    }
    $attributes = ( isset($tagdata['attributes']) ? $tagdata['attributes'] : false );
    if ( $tagdata['type'] == "open" ) {
      $subtree = BuildXMLTree( $xmltags, $start_from );
      $content[] = new XMLElement($tag, $subtree, $attributes, $xmlns );
    }
    else if ( $tagdata['type'] == "complete" ) {
      $value = ( isset($tagdata['value']) ? $tagdata['value'] : false );
      $content[] = new XMLElement($tag, $value, $attributes, $xmlns );
    }
  }

  /**
  * If there is only one element, return it directly, otherwise return the
  * array of them
  */
  if ( count($content) == 1 ) {
    return $content[0];
  }
  return $content;
}