This file is indexed.

/usr/share/awl/inc/PgQuery.php is in libawl-php 0.53-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
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
<?php
/**
* PostgreSQL query class and associated functions
*
* This subpackage provides some functions that are useful around database
* activity and a PgQuery class to simplify handling of database queries.
*
* The class is intended to be a very lightweight wrapper with no pretentions
* towards database independence, but it does include some features that have
* proved useful in developing and debugging web-based applications:
*  - All queries are timed, and an expected time can be provided.
*  - Parameters replaced into the SQL will be escaped correctly in order to
*    minimise the chances of SQL injection errors.
*  - Queries which fail, or which exceed their expected execution time, will
*    be logged for potential further analysis.
*  - Debug logging of queries may be enabled globally, or restricted to
*    particular sets of queries.
*  - Simple syntax for iterating through a result set.
*
* The database should be connected in a variable $dbconn before
* PgQuery.php is included.  If not already connected, PgQuery will attempt to
* connect to the database, successively applying connection parameters from
* the array in $c->pg_connect.
*
* We will die if the database is not currently connected and we fail to find
* a working connection.
*
* @package   awl
* @subpackage   PgQuery
* @author    Andrew McMillan <andrew@mcmillan.net.nz>
* @copyright Catalyst IT Ltd, Morphoss Ltd <http://www.morphoss.com/>
* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
*/


if ( ! function_exists('pg_Connect') ) {
  echo <<<EOERRMSG
<html>
<head>
<title>PostgreSQL Support Not Present</title>
</head>
<body>
<h1>PostgreSQL Support Not Present</h1>
<h3>PHP is not configured to support the PostgreSQL database</h3>
<p>You need to ensure that the PostgreSQL support module is installed, and then to configure
it in your php.ini file by adding a line like this:</p>
<pre>
extension=pgsql.so
</pre>
  </body>
  </html>
EOERRMSG;
  exit;
}

require_once("AWLUtilities.php");

/**
* Connect to the database defined in the $c->dbconn[] array
*/
function connect_configured_database() {
  global $c, $dbconn;

  if ( isset($dbconn) ) return;
  /**
  * Attempt to connect to the configured connect strings
  */
  $dbconn = false;
  dbg_error_log('pgquery', 'Attempting to connect to database');
  if ( isset($c->pg_connect) && is_array($c->pg_connect) ) {
    foreach( $c->pg_connect AS $k => $v ) {
      if ( !$dbconn ) {
        if ( $dbconn = ((isset($c->use_persistent) && $c->use_persistent) ? pg_pConnect($v) : pg_Connect($v) ) ) break;
      }
    }
  }
  if ( ! $dbconn ) {
    echo <<<EOERRMSG
  <html><head><title>Database Connection Failure</title></head><body>
  <h1>Database Error</h1>
  <h3>Could not connect to PostgreSQL database</h3>
  </body>
  </html>
EOERRMSG;
    if ( isset($c->pg_connect) && is_array($c->pg_connect) ) {
      dbg_error_log("ERROR", "Failed to connect to database" );
    }
    exit;
  }

  if ( isset($c->db_schema) && $c->db_schema != '' ) {
    $result = pg_exec( $dbconn, "SET Search_path TO ".$c->db_schema.",public;" );
    $row = pg_fetch_array($result, 0);
  }

  $result = pg_exec( $dbconn, "SELECT version()" );
  $row = pg_fetch_array($result, 0);
  $c->found_dbversion = preg_replace( '/^PostgreSQL (\d+\.\d+)\..*$/i', '$1', $row[0]);
}


/**
* @global resource $dbconn
* @name $dbconn
* The database connection.
*/
$dbconn = null;

if ( !function_exists('duration') ) {
  /**
  * A duration (in decimal seconds) between two times which are the result of calls to microtime()
  *
  * This simple function is used by the PgQuery class because the
  * microtime function doesn't return a decimal time, so a simple
  * subtraction is not sufficient.
  *
  * @param microtime $t1 start time
  * @param microtime $t2 end time
  * @return double difference
  */
  function duration( $t1, $t2 ) {
    list ( $ms1, $s1 ) = explode ( " ", $t1 );   // Format times - by spliting seconds and microseconds
    list ( $ms2, $s2 ) = explode ( " ", $t2 );
    $s1 = $s2 - $s1;
    $s1 = $s1 + ( $ms2 -$ms1 );
    return $s1;                                  // Return duration of time
  }
}


/**
* Quote the given string (depending on its type) so that it can be used
* safely in a PostgreSQL query without fear of SQL injection errors.
*
* Although this function effectively achieves a similar goal to the pg_escape_string()
* function, it is needed for older versions of PHP (< 4.2.0) and older versions
* of PostgreSQL (< 7.2.0), however.  PgQuery does not attempt to use the newer
* pg_escape_string() function at this stage.
*
* This function is outside the PgQuery class because it is sometimes
* desirable to quote values for SQL command strings in circumstances
* where there is no benefit to using the class.
*
* @param mixed $str Data to be converted to a string suitable for including as a value in SQL.
* @return string NULL, TRUE, FALSE, a plain number, or the original string quoted and with ' and \ characters escaped
*/
function qpg($str = null) {
  global $c;

  switch (strtolower(gettype($str))) {
    case 'null':
      $rv = 'NULL';
      break;
    case 'integer':
    case 'double' :
      return $str;
    case 'boolean':
      $rv = $str ? 'TRUE' : 'FALSE';
      break;
    case 'string':
    default:
      $str = str_replace("'", "''", $str);
      //PostgreSQL treats a backslash as an escape character.
      $str = str_replace('\\', '\\\\', $str);
      $rv = "'$str'";
      if ( !isset($c->found_dbversion) || $c->found_dbversion > 8.0 ) $rv = 'E'.$rv;
  }
  return $rv;
}

/**
* Clean a string of many suspicious characters
*
* While this is a fairly aggressive approach, it applies in many circumstances
* where various strings should not contain things that might screw up (e.g.)
* filesystem semantics.  Although not strictly a PgQuery function it's here
* for the time being until I invent a new "generally useful functions" include.
*
* @param string $unclean The dirty filthy string needing washing.
* @return string The pristine uncontaminated string we can safely use for Just About Anything(tm).
*/
function clean_string( $unclean, $type = 'full' ) {
  if ( ! isset($unclean) ) return null;
  if ( is_array($unclean) ) {
    $result = array();
    foreach( $unclean AS $k => $v ) {
      $result[$k] = clean_string( $v, $type );
    }
    return $result;
  }
  if ( $type != 'basic' ) $cleaned = strtolower($unclean); else $cleaned = &$unclean;
  $cleaned = preg_replace( "/['\"!\\\\()\[\]|*\/{}&%@~;:?<>]/", '', $cleaned ); //"// Stupid Bluefish Syntax highlighting...
  dbg_error_log( "PgQuery", "clean_string: Cleaned string from <<%s>> to <<%s>>", $unclean, $cleaned );
  return $cleaned;
}

/**
* Replaces PostgreSQL query with escaped parameters in preparation
* for execution.
*
* The function takes a variable number of arguments, the first is the
* SQL string, with replaceable '?' characters (a la DBI).  The subsequent
* parameters being the values to replace into the SQL string.
*
* The values passed to the routine are analyzed for type, and quoted if
* they appear to need quoting.  This can go wrong for (e.g.) NULL or
* other special SQL values which are not straightforwardly identifiable
* as needing quoting (or not).  In such cases the parameter can be forced
* to be inserted unquoted by passing it as "array( 'plain' => $param )".
*
* This function is outside the PgQuery class because it is sometimes
* desirable to build SQL command strings in circumstances where there
* is no benefit to using the class.
*
* @param  string The query string with replacable '?' characters.
* @param mixed The values to replace into the SQL string.
* @return The built query string
*/
function awl_replace_sql_args() {
  $argc = func_num_args(); //number of arguments passed to the function
  $qry = func_get_arg(0); //first argument
  $args = func_get_args(); //all argument in an array

  if ( is_array($qry) ) {
    $qry = $args[0][0];
    $args = $args[0];
    $argc = count($args);
  }

// building query string by replacing ? with
// escaped parameters
  $parts = explode( '?', $qry );
  $querystring = $parts[0];
  $z = min( count($parts), $argc );

  for( $i = 1; $i < $z; $i++ ) {
    $arg = $args[$i];
    if ( !isset($arg) ) {
      $querystring .= 'NULL';
    }
    elseif ( is_array($arg) && $arg['plain'] != '' ) {
      // We abuse this, but people should access it through the PgQuery::Plain($v) function
      $querystring .= $arg['plain'];
    }
    else {
  $querystring .= qpg($arg);  //parameter
    }
    $querystring .= $parts[$i]; //extras eg. ","
  }
  if ( isset($parts[$z]) ) $querystring .= $parts[$z]; //puts last part on the end

  return $querystring;
}


/**
* The PgQuery Class.
*
* This class builds and executes PostgreSQL Queries and traverses the
* set of results returned from the query.
*
* <b>Example usage</b>
* <code>
* $sql = "SELECT * FROM mytable WHERE mytype = ?";
* $qry = new PgQuery( $sql, $myunsanitisedtype );
* if ( $qry->Exec("typeselect", __line__, __file__ )
*      && $qry->rows() > 0 )
* {
*   while( $row = $qry->Fetch() ) {
*     do_something_with($row);
*   }
* }
* </code>
*
* @package   awl
*/
class PgQuery
{
  /**#@+
  * @access private
  */
  /**
  * holds the connection to the database
  * should be internal
  * @var resource
  */
  var $connection;

  /**
  * stores a query string
  * should be read-only
  * @var string
  */
  var $querystring;

  /**
  * stores a resource result
  * should be internal
  * @var resource
  */
  var $result;

  /**
  * number of current row
  * should be internal, or at least read-only
  * @var int
  */
  var $rownum = -1;

  /**
  * Where we called this query from so we can find it in our code!
  * Debugging may also be selectively enabled for a $location.
  * @var string
  */
  var $location;

  /**
  * The row most recently fetched by a call to Fetch() or FetchBackwards
  * which will either be an array or an object (depending on the Fetch call).
  * @var mixed
  */
  var $object;

  /**#@-*/

  /**#@+
  * @access public
  */
  /**
  * number of rows from pg_numrows - for fetching result
  * should be read-only
  * @var int
  */
  var $rows;

  /**
  * The PostgreSQL error message, if the query fails.
  * Should be read-only, although any successful Exec should clear it
  * @var string
  */
  var $errorstring;

  /**
  * Stores the query execution time - used to deal with long queries.
  * should be read-only
  * @var string
  */
  var $execution_time;

  /**
  * How long the query should take before a warning is issued.
  *
  * This is writable, but a method to set it might be a better interface.
  * The default is 0.3 seconds.
  * @var double
  */
  var $query_time_warning = 0.3;
  /**#@-*/


 /**
  * Constructor
  * @param  string The query string with replacable '?' characters.
  * @param mixed The values to replace into the SQL string.
  * @return The PgQuery object
  */
  function __construct() {
    global $dbconn;
    $this->result = 0;
    $this->rows = 0;
    $this->execution_time = 0;
    $this->rownum = -1;
    $this->connection = $dbconn;

    $argc = func_num_args();

    if ( 1 < $argc ) {
      $this->querystring = awl_replace_sql_args( func_get_args() );
    }
    else {
      // If we are only called with a single argument, we do
      // nothing special with any question marks.
      $this->querystring = func_get_arg(0);
    }

    return $this;
  }


 /**
  * Use a different database connection for this query
  * @param  resource $new_connection The database connection to use.
  */
  function SetConnection( $new_connection ) {
    $this->connection = $new_connection;
  }



  /**
  * Log error, optionally with file and line location of the caller.
  *
  * This function should not really be used outside of PgQuery.  For a more
  * useful generic logging interface consider calling dbg_error_log(...);
  *
  * @param string $locn    A string identifying the calling location.
  * @param string $tag     A tag string, e.g. identifying the type of event.
  * @param string $string  The information to be logged.
  * @param int    $line    The line number where the logged event occurred.
  * @param string $file    The file name where the logged event occurred.
  */
  function _log_error( $locn, $tag, $string, $line = 0, $file = "") {
    // replace more than one space with one space
    $string = preg_replace('/\s+/', ' ', $string);

    if ( ($tag == 'QF' || $tag == 'SQ') && ( $line != 0 && $file != "" ) ) {
      dbg_error_log( "LOG-$locn", " Query: %s: Error in '%s' on line %d", $tag, $file, $line );
    }

    while( strlen( $string ) > 0 )  {
      dbg_error_log( "LOG-$locn", " Query: %s: %s", $tag, substr( $string, 0, 240) );
      $string = substr( "$string", 240 );
    }
  }

  /**
  * Provide a rows() method for forward compatibility with AwlQuery.
  */
  function rows() {
    return $this->rows;
  }

  /**
  * Quote the given string so it can be safely used within string delimiters
  * in a query.
  *
  * @see qpg()
  * which is where this is really done.
  *
  * @param mixed $str Data to be converted to a string suitable for including as a value in SQL.
  * @return string NULL, TRUE, FALSE, a plain number, or the original string quoted and with ' and \ characters escaped
  */
  function quote($str = null) {
    return qpg($str);
  }

  /**
  * Convert a string which has already been quoted and escaped for PostgreSQL
  * into a magic array so that it will be inserted unmodified into the SQL
  * string.  Use with care!
  *
  * @param string $field The value which has alread been quoted and escaped.
  * @return array An array with the value associated with a key of 'plain'
  */
  function Plain( $field ) {
    // Abuse the array type to extend our ability to avoid \\ and ' replacement
    $rv = array( 'plain' => $field );
    return $rv;
  }

  /**
  * Execute the query, logging any debugging.
  *
  * <b>Example</b>
  * So that you can nicely enable/disable the queries for a particular class, you
  * could use some of PHPs magic constants in your call.
  * <code>
  * $qry->Exec(__CLASS__, __LINE__, __FILE__);
  * </code>
  *
  *
  * @param string $location The name of the location for enabling debugging or just
  *                         to help our children find the source of a problem.
  * @param int $line The line number where Exec was called
  * @param string $file The file where Exec was called
  * @return resource The actual result of the query (FWIW)
  */
  function Exec( $location = '', $line = 0, $file = '' ) {
    global $debuggroups, $c, $dbconn;
    if ( !isset($this->connection) ) {
      if ( !isset($dbconn) ) {
        connect_configured_database();
      }
      $this->connection = $dbconn;
    }
    $this->location = trim($location);
    if ( $this->location == "" ) $this->location = substr($_SERVER['PHP_SELF'],1);

    if ( isset($debuggroups['querystring']) || isset($c->dbg['querystring']) || isset($c->dbg['ALL']) ) {
      $this->_log_error( $this->location, 'DBGQ', $this->querystring, $line, $file );
    }

    $t1 = microtime(); // get start time
    $this->result = @pg_exec( $this->connection, $this->querystring ); // execute the query
    $this->rows = ($this->result ? pg_numrows($this->result) : -1); // number of rows returned
    $t2 = microtime(); // get end time
    $i_took = duration( $t1, $t2 );   // calculate difference
    $c->total_query_time += $i_took;
    $this->execution_time = sprintf( "%2.06lf", $i_took);

    if ( !$this->result ) {
     // query simply failed
      $this->errorstring = @pg_errormessage(); // returns database error message
      $this->_log_error( $this->location, 'QF', $this->querystring, $line, $file );
      $this->_log_error( $this->location, 'QF', $this->errorstring, $line, $file );
    }
    elseif ( $this->execution_time > $this->query_time_warning ) {
     // if execution time is too long
      $this->_log_error( $this->location, 'SQ', "Took: $this->execution_time for $this->querystring", $line, $file ); // SQ == Slow Query :-)
    }
    elseif ( isset($debuggroups[$this->location]) || isset($c->dbg[strtolower($this->location)]) || isset($c->dbg['ALL']) ) {
     // query successful, but we're debugging and want to know how long it took anyway
      $this->_log_error( $this->location, 'DBGQ', "Took: $this->execution_time for $this->querystring to find $this->rows rows.", $line, $file );
    }

    return $this->result;
  }


  /**
  * Fetch the next row from the query results
  * @param boolean $as_array True if thing to be returned is array
  * @return mixed query row
  */
  function Fetch($as_array = false) {
    global $c, $debuggroups;

    if ( ( isset($debuggroups["$this->location"]) && $debuggroups["$this->location"] > 2 )
       || (isset($c) && is_object($c) && ( isset($c->dbg[strtolower($this->location)]) && isset($c->dbg[strtolower($this->location)]) )
                                        || isset($c->dbg['ALL']) ) ) {
        $this->_log_error( $this->location, "Fetch", "$this->result Rows: $this->rows, Rownum: $this->rownum");
    }
    if ( ! $this->result ) return false; // no results
    if ( ($this->rownum + 1) >= $this->rows ) return false; // reached the end of results

    $this->rownum++;
    if ( isset($debuggroups["$this->location"]) && $debuggroups["$this->location"] > 1 ) {
      $this->_log_error( $this->location, "Fetch", "Fetching row $this->rownum" );
    }
    if ( $as_array )
    {
      $this->object = pg_fetch_array($this->result, $this->rownum);
    }
    else
    {
      $this->object = pg_fetch_object($this->result, $this->rownum);
    }

    return $this->object;
  }

  /**
  * Set row counter back one
  *
  * In the case that you may like to fetch the same row twice, for example
  * if your SQL returns some columns that are the same for each row, and you
  * want to display them cleanly before displaying the other data repeatedly
  * for each row.
  *
  * <b>Example</b>
  * <code>
  * $master_row = $qry->Fetch();
  * $qry->UnFetch();
  * do_something_first($master_row);
  * while( $row = $qry->Fetch() ) {
  *   do_something_repeatedly($row);
  * }
  * </code>
  */
  function UnFetch() {
    global $debuggroups;
    $this->rownum--;
    if ( $this->rownum < -1 ) $this->rownum = -1;
  }

  /**
  * Fetch backwards from the result resource
  * @param boolean $as_array True if thing to be returned is array (default: <b>False</b>
  * @return mixed query row
  */
  function FetchBackwards($as_array = false) {
    global $debuggroups;

    if ( isset($debuggroups["$this->location"]) && $debuggroups["$this->location"] > 2 ) {
      $this->_log_error( $this->location, "FetchBackwards", "$this->result Rows: $this->rows, Rownum: $this->rownum");
    }
    if ( ! $this->result ) return false;
    if ( ($this->rownum - 1) == -1 ) return false;
    if ( $this->rownum == -1 ) $this->rownum = $this->rows;

    $this->rownum--;

    if ( isset($debuggroups["$this->location"]) && $debuggroups["$this->location"] > 1 ) {
      $this->_log_error( $this->location, "Fetch", "Fetching row $this->rownum" );
    }
    if ( $as_array )
    {
      $this->object = pg_fetch_array($this->result, $this->rownum);
    }
    else
    {
      $this->object = pg_fetch_object($this->result, $this->rownum);
    }

    return $this->object;
  }

  /**
  * Build an option list from the query.
  * @param string $current Default selection of drop down box (optional)
  * @param string $location for debugging purposes
  * @param array $parameters an array further parameters, including 'maxwidth' => 20 to set a maximum width
  * @return string Select box HTML
  */
  function BuildOptionList( $current = '', $location = 'options', $parameters = false ) {
    global $debuggroups;
    $result = '';
    $translate = false;

    if ( isset($maxwidth) ) unset($maxwidth);
    if ( is_array($parameters) ) {
      if ( isset($parameters['maxwidth']) ) $maxwidth = max(4,intval($parameters['maxwidth']));
      if ( isset($parameters['translate']) ) $translate = true;
    }

    // The query may already have been executed
    if ( $this->rows > 0 || $this->Exec($location) ) {
      $this->rownum = -1;
      while( $row = $this->Fetch(true) )
      {
        if (is_array($current)) {
          $selected = ( ( in_array($row[0],$current,true) || in_array($row[1],$current,true)) ? ' selected="selected"' : '' );
        }
        else {
          $selected = ( ( "$row[0]" == "$current" || "$row[1]" == "$current" ) ? ' selected="selected"' : '' );
        }
        $display_value = $row[1];
        if ( isset($translate) ) $display_value = translate( $display_value );
        if ( isset($maxwidth) ) $display_value = substr( $display_value, 0, $maxwidth);
        $nextrow = "<option value=\"".htmlspecialchars($row[0])."\"$selected>".htmlspecialchars($display_value)."</option>";
        $result .= $nextrow;
      }
    }
    return $result;
   }

}