source view: current.php


<?php



  $post_url_url     = '';
  $directory_prefix = './';
  if ( preg_match( '/(^https?:\/\/[^\/]+)?(\/(?:current\/))?([\w\-]+)\//', $_SERVER['REQUEST_URI'], $matches ) )
  {
    $directory_prefix = '../' . ( strlen( $matches[2] ) > 1 ? '../' : '' );
    $post_url_url     = $matches[3];
  }



  function kstripslashes ( $s )
  {
    if ( get_magic_quotes_gpc() )
    {
      return stripslashes( $s );
    }

    return $s;
  }



  function utf8ord ( $c )
  {
    $s = strlen( $c );
    $o = ord( $c[0] ) & ( 0xff >> $s );

    for ( $i = 1; $i < $s; ++$i )
    {
      $o = $o << 6 | ( ord( $c[$i] ) & 127 );
    }

    return $o;
  }



  // character codes from http://www.localizingjapan.com/blog/2012/01/20/regular-expressions-for-japanese-text/
  function getKaCount ( $c )
  {
    $k = 0;
    $s = 0;
    for ( $i = 0; $i < strlen( $c ); )
    {
      $o = ord( $c[$i] );
      $L = $o < 192 ? 1 : ( $o < 224 ? 2 : ( $o < 240 ? 3 : ( $o < 248 ? 4 : ( $o < 252 ? 5 : ( $o < 254 ? 6 : 1 ) ) ) ) );
      $o = utf8ord( substr( $c, $i, $L ) );

      $k += ( ( $o >= 0x3041 && $o <= 0x3096 )
           || ( $o >= 0x30a0 && $o <= 0x30ff )
           || ( $o >= 0x3400 && $o <= 0x4db5 )
           || ( $o >= 0x4e00 && $o <= 0x9fcb )
           || ( $o >= 0xf900 && $o <= 0xfa6a )
           || ( $o >= 0x2e80 && $o <= 0x2fd5 )
           || ( $o >= 0x3000 && $o <= 0x303f ) ) ? 1 : 0;
      $i += $L;
      $s += ( ( $o >= 9 && $o <= 13 ) || $o == 32 || $o == 160 ) ? 0 : 1;
    }

    return array( $k, $s );
  }



  function looksLikeSpam ( $c )
  {
    $disallowed_nocase = array(
      'canada goose', 'ebook', '\\.ru', 'url=', '^\\s*$', '\\buggs?\\b',
      'cheap jersey', 'nike jersey', 'laboutin', 'louis vuitton', 'pandora jewelry', 'pas cher',
      'provigil', 'xanax', 'tramadol', 'klonopin', 'ambien', 'valium', 'flomax', 'oxycontin', 'cialis',
      'kyle woodward ::', '\\bair\\s*max\\b', '\\blongchamps?\\b'
    );
    $disallowed_case   = array(
      '^\\s*[A-Z][a-z]{3}[A-Z]{4}'
    );
    $disallowed_nocase = join( '|', $disallowed_nocase );
    $disallowed_case   = join( '|', $disallowed_case );
    $ka_thresh         = 0.7;
    $ka_count          = getKaCount( $c );
    $strlen            = strlen( $c ) / 2;
    
    return preg_match( "/$disallowed_nocase/i", $c )
        || preg_match( "/$disallowed_case/", $c )
        || ( $strlen % 2 == 0 && substr( $c, 0, $strlen ) == substr( $c, $strlen ) )
        || ( $ka_count[0] / $ka_count[1] > $ka_thresh );
  }
  
  

  //
  // Work through redirects if we must
  //
  if ( !isset( $_as_include ) )
  {
    require $directory_prefix . 'headers.php';
  }

  require sprintf( '%sdb.connect.php',    $directory_prefix );
  require sprintf( '%sclasses/Page.php',  $directory_prefix );
  require_once sprintf( '%sclasses/Admin.php', $directory_prefix );



  $post_id          = -1;
  $replacement_name = '';
  $override         = ' AND isVisible = 1';
  if ( isset( $_GET['p'] ) )
  {
    $post_id = intval( $_GET['p'] );
    
    if ( Admin::isLoggedIn() )
    {
      $override = '';
    }

    $post_exists_query = $mysql->prepare( 'SELECT blogPostTitle FROM tblBlogPosts WHERE blogPostID = ? ' . $override );
    $post_exists_query->bind_param( 'i', $post_id );
    $post_exists_query->execute();
    $post_exists_query->bind_result( $replacement_name );

    if ( !$post_exists_query->fetch() )
    {
      $post_id = -1;
    }

    $post_exists_query->close();
  }

  if ( strlen( $post_url_url ) > 0 )
  {
    if ( Admin::isLoggedIn() )
    {
      $override = '';
    }

    $post_exists_query = $mysql->prepare( 'SELECT blogPostID, blogPostTitle FROM tblBlogPosts WHERE postURL = ? ' . $override );
    $post_exists_query->bind_param( 's', $post_url_url );
    $post_exists_query->execute();
    $post_exists_query->bind_result( $post_id, $replacement_name );

    if ( !$post_exists_query->fetch() )
    {
      // at this point, it *looks* like they've tried to access an old blog post. so let's pipe them to a current blog post.
      header( 'Location: /current.php' );
      exit;
    }

    $post_exists_query->close();
  }

  $comment_array = array(
    'name'          => '',
    'email'         => '',
    'site'          => '',
    'comments'      => '',
    'captcha_error' => ''
  );

  $action    = isset($_POST['action']) ? strtolower($_POST['action']) : '';
  $commented = false;
  if ( $action == 'comment' )
  {
    $name      = $_POST['name'];
    $email     = $_POST['email'];
    $site      = $_POST['site'];
    $post_id   = intval($_POST['p']);
    $comments  = kstripslashes( $_POST['comments'] );
    $commented = true;

    // 2020-12-30: algebra copied from contact-info.php; factor out?
    $captcha_id       = isset( $_POST['captcha_id'] ) && intval( $_POST['captcha_id'] ) == $_POST['captcha_id'] ? intval( $_POST['captcha_id'] ) : -1;
    $captcha_solution = isset( $_POST['captcha'] ) ? preg_replace( '/\s+/', '', $_POST['captcha'] ) : -1;
    $fraction_parts   = preg_split( '/\//', $captcha_solution );

    if ( count( $fraction_parts ) == 1 )
    {
      $captcha_solution = floatval( strlen( $fraction_parts[0] ) > 0 ? $fraction_parts[0] : -1 );
    }
    else if ( count( $fraction_parts ) == 2 )
    {
      if ( strlen( $fraction_parts[0] ) > 0 && floatval( $fraction_parts[1] ) )
      {
        $captcha_solution = floatval( $fraction_parts[0] ) / floatval( $fraction_parts[1] );
      }
      else
      {
        $captcha_solution = -1;
      }
    }
    else
    {
      $captcha_solution = -1;
    }

    $captcha_query = $mysql->prepare( 'SELECT capchaSolution FROM tblCapchas WHERE capchaURLID = ? AND NOW() < DATE_ADD( startTime, INTERVAL 12 HOUR )' );
    $captcha_query->bind_param( 'i', $captcha_id );
    $captcha_query->execute();
    $captcha_query->bind_result( $captcha_solution_db );

    if ( !$captcha_query->fetch() )
    {
      $comment_array['captcha_error'] = 'You took too long to comment; try the new CAPTCHA.';
    }
    else
    {
      if ( abs( $captcha_solution - floatval( $captcha_solution_db ) ) > 0.001 )
      {
        $comment_array['captcha_error'] = 'Sorry, your CAPTCHA response wasn\'t accurate enough; try the new CAPTCHA.';
      }
    }

    $captcha_query->close();


    
    $spam_error = 'Your post looks spammy; <a href="/about.php">contact me</a> if you feel wronged or would like more information.';
    if ( looksLikeSpam( $comments ) )
    {
      $comment_array['captcha_error'] = $spam_error;
    }

    //
    // input verification
    //
    
    //
    // strip tags from name, ensure length (63), escape for mysql
    //
    $name = strip_tags($name);
    if ( strlen($name) > 63 ) {
      $name = substr($name,0,63);
    }

    //
    // strip tags from email, ensure length (127), trim spaces, escape for mysql
    //
    $email = preg_replace('/[\s]/','',$email);
    $email = strip_tags($email);
    if ( strlen($email) > 127 ) {
      $email = substr($email,0,127);
    }

    //
    // strip tags from site, ensure length (255), add http://, escape for mysql
    //
    $site = strip_tags($site);
    if ( !preg_match('/(^ftp:\/\/)|(^https?:\/\/)/',$site) )
    {
      $site = 'http://'.$site;
    }
    if ( strlen($site) > 255 )
    {
      $site = substr($site,0,255);
    }

    //
    // strip tags from comments
    //
    $comments = kstripslashes( strip_tags( $comments ) );

    //
    // end input verification
    //

    if ( strlen( $comment_array['captcha_error'] ) > 0 )
    {
      $comment_array['name']     = kstripslashes( $name );
      $comment_array['email']    = $email;
      $comment_array['site']     = $site;
      $comment_array['comments'] = $comments;
    }
    else
    {
      //
      // form and run the query
      //
      $comment_insert_query = <<<EOQ
INSERT INTO tblBlogPostComments (
  blogPostID,
  commentDateTime,
  commentorName,
  commentorGravatar,
  commentorSite,
  comment
)
VALUES (
  ?,
  NOW(),
  ?,
  ?,
  ?,
  ?
)
EOQ;

      //
      // check: is commenting allowed?
      //

      $comment_allowed_query = $mysql->prepare( 'SELECT allowComments FROM tblBlogPosts WHERE blogPostID = ?' );
      $comment_allowed_query->bind_param( 'i', $post_id );
      $comment_allowed_query->execute();
      $comment_allowed_query->bind_result( $comment_allowed );

      if ( $comment_allowed_query->fetch() && $comment_allowed )
      {
        $comment_allowed_query->close();

        $comment_insert_query = $mysql->prepare( $comment_insert_query );
        $comment_insert_query->bind_param( 'issss', $post_id, $name, $email, $site, $comments );
        $comment_insert_query->execute();
        $comment_insert_query->close();

        if ( $comment_allowed == 1 )
        {
          if ( stripos( $_SERVER['REQUEST_URI'], 'current.php' ) !== false )
          {
            header( sprintf( 'Location: /current.php?p=%d', $post_id ) );
          }
          else
          {
            header( 'Location: ./' );
          }
        
          exit;
        }
      }
    }
  }
  
  
  
  $most_recent_post_query_string = <<<EOQ
SELECT
  blogPostID,
  blogPostTitle,
  postHTML,
  UNIX_TIMESTAMP( postDateTime ),
  postURL,
  allowComments
FROM tblBlogPosts
WHERE
  (
    ? < 0
    OR blogPostID = ?
  ) $override
ORDER BY postDateTime DESC
LIMIT 0, 1
EOQ;

  $most_recent_post_query = $mysql->prepare( $most_recent_post_query_string );
  $most_recent_post_query->bind_param( 'ii', $post_id, $post_id );
  $most_recent_post_query->execute();
  $most_recent_post_query->bind_result( $most_recent_post_id, $most_recent_post_title, $most_recent_post_html, $most_recent_post_timestamp, $most_recent_post_url, $most_recent_post_allow_comments );
  $most_recent_post_query->fetch();
  $most_recent_post_query->close();
        
  $post_title     = $most_recent_post_title;
  $unix_timestamp = $most_recent_post_timestamp;
  if ( $post_id < 0 )
  {
    $post_id = $most_recent_post_id;
  }
  
  $headers_query_string = <<<EOQ
SELECT
  bph.headerContent,
  ht.headerType
FROM
  tblBlogPostHeaders bph,
  luHeaderTypes ht
WHERE
  bph.headerTypeID = ht.headerTypeID
  AND bph.blogPostID = ?
EOQ;

  $addl_headers  = array();
  $addl_footers  = array();

  $headers_query = $mysql->prepare( $headers_query_string );
  $headers_query->bind_param( 'i', $post_id );
  $headers_query->execute();
  $headers_query->bind_result( $header_content, $header_type );
  
  while ( $headers_query->fetch() )
  {
    switch ( $header_type )
    {
      case 'Footer':
        array_push( $addl_footers, $header_content );
        break;

      case 'Header':
        array_push( $addl_headers, $header_content );
        break;
    }
  }

  $headers_query->close();



  $page = new Page();
  $page->setParentURL( 'current.php' );
  $page->setTitle( 'current' );
  foreach ( $addl_headers as $i => $h )
  {
    $page->addHeader( $h );
  }



  $page->addHeader( <<<EOJX
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
EOJX
  );

//  if ( strlen( $replacement_name ) > 0 )
//  {
//    wrapPageTop( 'current.php', null, $addl_headers );
//  }
//  else
//  {
//    wrapPageTop( null, null, $addl_headers );
//  }



  $page->printPageHeader();



?>
<h3 style="margin-bottom:0px;"><a href="/<?php print $most_recent_post_url;?>" target="_top"><?php print $post_title; ?></a></h3>
<span style="display:block;margin-bottom:2em;"><?php print date( 'j F Y', $unix_timestamp );?></span>
<?
        print $most_recent_post_html;
?>
        <p style="margin-top:2em;"><em>Included \(\LaTeX\) graphics are generated at <a href="/latex.php">LaTeX to png</a> or by <a href="https://mathjax.org" target="_new"><img alt="MathJax" style="border:0px;padding-left:0.5ex;" src="https://www.mathjax.org/badge/mj_logo_60x12.png" title="MathJax" /></a>.</em></p>
        <h3 style="margin-top:2em;">contemporary entries</h3>
        <ul class="blog_history">
<?php
          $adjacent_post_query_string = <<<EOQ
SELECT
  bp.blogPostID,
  bp.blogPostTitle,
  UNIX_TIMESTAMP( bp.postDateTime ),
  CONCAT( 'current/', bp.postURL )
FROM tblBlogPosts bp
WHERE
  bp.isVisible = 1
  AND (
    SELECT COUNT(*)
    FROM tblBlogPosts
    WHERE
      isVisible = 1
      AND (
        UNIX_TIMESTAMP( postDateTime ) BETWEEN ? AND UNIX_TIMESTAMP( bp.postDateTime )
        OR UNIX_TIMESTAMP( postDateTime ) BETWEEN UNIX_TIMESTAMP( bp.postDateTime ) AND ?
      )
      AND postDateTime <> bp.postDateTime
  ) < 3
ORDER BY bp.postDateTime DESC
EOQ;
          $adjacent_post_query = $mysql->prepare( $adjacent_post_query_string );
          $adjacent_post_query->bind_param( 'ii', $unix_timestamp, $unix_timestamp );
          $adjacent_post_query->execute();
          $adjacent_post_query->bind_result( $adjacent_post_id, $adjacent_post_title, $adjacent_post_timestamp, $adjacent_post_url );

          while ( $adjacent_post_query->fetch() )
          {
?>
          <li class="blog_history"><?php
            if ( $adjacent_post_id == $post_id )
            {
?><strong><?php
            }
?><a href="/<?php print $adjacent_post_url;?>" target="_top"><?php print $adjacent_post_title;?>
<?php
            if ( $adjacent_post_id == $post_id )
            {
?></strong><?php
            }
?></a> (<?php print date( 'j F Y', $adjacent_post_timestamp );?>)</li>
<?php
          }
?>
          <li class="blog_history"><br /><a href="/blog-archive.php" target="_top">view all entries &#x0bb;</a></li>
        </ul>
        <h3 style="margin-top:2em;">comments</h3>
<?php
          $comment_query_string = <<<EOQ
SELECT
  UNIX_TIMESTAMP( bpc.commentDateTime ),
  bpc.commentorName,
  bpc.commentorGravatar,
  bpc.commentorSite,
  bpc.comment
FROM
  tblBlogPostComments bpc,
  tblBlogPosts bp
WHERE
  bpc.blogPostID = ?
  AND bp.blogPostID = ?
  AND (
    bpc.isApproved = 1
    OR bp.allowComments <> 2
  )
ORDER BY bpc.commentDateTime
EOQ;
          $comment_query = $mysql->prepare( $comment_query_string );
          $comment_query->bind_param( 'ii', $post_id, $post_id );
          $comment_query->execute();
          $comment_query->bind_result( $comment_timestamp, $commentor_name, $commentor_gravatar, $commentor_site, $comment );
          $has_comments  = false;

          while ( $comment_query->fetch() )
          {
            $has_comments = true;
?>
        <div class="comment">
          <div class="comment_content">
            <?php print preg_replace( '/[\r\n]+/', '<br /><br />', $comment ); ?>
          </div><div class="comment_signature">
            <img src="http://www.gravatar.com/avatar.php?gravatar_id=<?php print md5( $commentor_gravatar ); ?>&amp;size=40" />
<?php
              if ( strlen( $commentor_site ) > 0 )
              {
?>
            <a href="<?php print $commentor_stie; ?>" target="_top"><?php print $commentor_name; ?></a>
<?php
              }
              else
              {
                print $commentor_name;
              }
?>
            (<?php print date( 'j F Y, g:ia', $comment_timestamp ); ?>)
          </div>
        </div>
<?php
          }

          $comment_query->close();

          if ( !$has_comments )
          {
?>
        <p style="margin-top:0em;">there are no comments on this post</p>
<?php
          }



          if ( strlen( $comment_array['captcha_error'] ) > 0 )
          {
?>
          <span style="color:red;"><?php print $comment_array['captcha_error'];?></span>
<?php
          }
          else if ( $commented && $most_recent_post_allow_comments == 2 )
          {
?>
          <span style="font-style:italic;">Comments on this post are pre-moderated; your comment will be approved or rejected shortly.</span>
<?php
          }

          if ( $most_recent_post_allow_comments || Admin::isLoggedIn() )
          {
            /*
            this was the code to shoot comments back to current.php; this breaks the url, so let's not enter an action and let the browser default to the same page.
            action="/current.php?p=<?php print $post_id.(isset($_GET['override'])?'&override':'');?>"*/
?>
        <form method="post" style="padding-top:1em;">
          <fieldset>
            <div class="input_element">
              <input id="name" name="name" type="text" value="<?php print $comment_array['name'];?>" /> <label for="name">name</label>
            </div>
            <div class="input_element">
            <input id="email" name="email" type="text" value="<?php print $comment_array['email'];?>" /> <label for="email">email (for <a href="http://gravatar.com/">gravatar</a>)</label>
            </div>
            <div class="input_element">
              <input id="site" name="site" type="text" value="<?php print $comment_array['site'];?>" /> <label for="site">site</label>
            </div>
<?php
          $captcha_id = time() . '_' . rand();
?>
            <div class="input_element">
              <input id="captcha" name="captcha" type="text" value="" />
              <label for="captcha"> = <img alt="captcha" src="/captcha.php?captchaid=<?php print $captcha_id;?>" style="vertical-align:text-bottom;" /> (3 decimal places)</label>
              <input id="captcha_id" name="captcha_id" type="hidden" value="<?php print $captcha_id;?>" />
            </div>
            <textarea id="comments" name="comments" rows="5" style="width:100%;"><?php print $comment_array['comments'];?></textarea>
            <input id="p" name="p" type="hidden" value="<?=$post_id;?>" />
            <input id="action" name="action" type="hidden" value="comment" />
            <input type="submit" value="comment" />
          </fieldset>
        </form>
<?php
          }
          else
          {
?>
        <p>Sorry, further commenting on this post has been disabled. For more information, <a href="/about.php">contact me</a>.</p>
<?php
          }



  foreach ( $addl_footers as $i => $f )
  {
    $page->addFooter( $f );
  }

  $page->printPageFooter();
  
  
  
?>