GitLab

+1 −1
Original line number Diff line number Diff line
@@ -208,7 +208,7 @@ class Post extends Base {
        header('HTTP/1.1 410 Gone');
        return '<article class="h-entry">'.
            '<div class="published">'.
              '<time class="dt-published" datetime="'.$atom_date.'">'.
              '<time class="dt-deleted" datetime="'.$atom_date.'">'.
                '<a href="'.$permalink_url.'" class="u-url">'.
                $formatted_date.'</a>'.
              '</time></div>'.
+182 −48
Original line number Diff line number Diff line
@@ -134,14 +134,44 @@ class Reader extends Base {
    $this->NotifyUsers($updated);

    $mysqli = connect_db();
    // Remove expired rssCloud subscribers every hour.
    $subscriptions = array();
    // Check hub expiry timestamps every hour.
    $xml_url_query = '';
    $query = 'SELECT hub, self, xml_url FROM reader_hub WHERE expiry != 0 AND '.
      'expiry < '.time();
    if ($result = $mysqli->query($query)) {
      while ($reader_hub = $result->fetch_assoc()) {
        $xml_url = $reader_hub['xml_url'];
        if ($xml_url_query !== '') {
          $xml_url_query .= ' OR ';
        }
        $xml_url_query .= 'xml_url = "'.$mysqli->escape_string($xml_url).'"';
        $subscriptions[] = $reader_hub;
      }
      $result->close();
    }
    else {
      $this->Log('Reader->Cron 1: '.$mysqli->error);
    }
    // Reset the registered flag, which will be set again when the endpoint
    // responds with a challenge parameter.
    if ($xml_url_query !== '') {
      $query = 'UPDATE reader_feeds SET registered = 0 WHERE '.$xml_url_query;
      if (!$mysqli->query($query)) {
        $this->Log('Reader->Cron 2: '.$mysqli->error);
      }
    }
    foreach ($subscriptions as $feed) {
      $this->RenewHub($feed['hub'], $feed['self'], $feed['xml_url']);
    }

    // Remove expired subscribers every hour, and renew our subscriptions at
    // midnight.
    $query = 'DELETE FROM reader_notify WHERE timestamp < '.
      strtotime('-25 hours');
    if (!$mysqli->query($query)) {
      $this->Log('Reader->Cron 1: '.$mysqli->error);
      $this->Log('Reader->Cron 3: '.$mysqli->error);
    }

    // Renew all subscriptions at midnight.
    if ($this->Run('12am')) {
      $subscriptions = array();
      $prev_register = '';
@@ -172,14 +202,14 @@ class Reader extends Base {
        $result->close();
      }
      else {
        $this->Log('Reader->Cron 2: '.$mysqli->error);
        $this->Log('Reader->Cron 4: '.$mysqli->error);
      }
      // Also want to reset the registered flag, which will be set again when
      // the endpoint responds with a challenge parameter for each feed.
      // Reset the registered flag, which will be set again when the endpoint
      // responds with a challenge parameter for each feed.
      if ($xml_url_query !== '') {
        $query = 'UPDATE reader_feeds SET registered = 0 WHERE '.$xml_url_query;
        if (!$mysqli->query($query)) {
          $this->Log('Reader->Cron 3: '.$mysqli->error);
          $this->Log('Reader->Cron 5: '.$mysqli->error);
        }
      }
      foreach ($subscriptions as $register => $feed_list) {
@@ -202,6 +232,11 @@ class Reader extends Base {
        $xml_url = $p[1];
        return $this->AddFeed($id, $xml_url, false);
      }
      if ($fn == 'RegisteredFeed' && $count == 2) {
        $xml_url = $p[0];
        $lease = $p[1];
        return $this->RegisteredFeed($xml_url, $lease);
      }
      return;
    }
    if ($fn == 'Following') {
@@ -263,6 +298,17 @@ class Reader extends Base {
      $this->Log('Reader->Install 3: '.$mysqli->error);
    }

    $query = 'CREATE TABLE IF NOT EXISTS reader_hub ('.
      'hub VARCHAR(200) NOT NULL,'.
      'self VARCHAR(200) NOT NULL,'.
      'expiry INT(10) UNSIGNED,'.
      'xml_url VARCHAR(200) NOT NULL,'.
      'PRIMARY KEY(xml_url)'.
      ') ENGINE=MyISAM';
    if (!$mysqli->query($query)) {
      $this->Log('Reader->Install 4: '.$mysqli->error);
    }

    $query = 'CREATE TABLE IF NOT EXISTS reader_notify ('.
      'register VARCHAR(200) NOT NULL,'.
      'xml_url VARCHAR(200) NOT NULL,'.
@@ -270,7 +316,7 @@ class Reader extends Base {
      'PRIMARY KEY(register, xml_url)'.
      ') ENGINE=MyISAM';
    if (!$mysqli->query($query)) {
      $this->Log('Reader->Install 4: '.$mysqli->error);
      $this->Log('Reader->Install 5: '.$mysqli->error);
    }
      
    $query = 'CREATE TABLE IF NOT EXISTS reader_items ('.
@@ -285,7 +331,7 @@ class Reader extends Base {
      'PRIMARY KEY(guid, xml_url)'.
      ') ENGINE=MyISAM';
    if (!$mysqli->query($query)) {
      $this->Log('Reader->Install 5: '.$mysqli->error);
      $this->Log('Reader->Install 6: '.$mysqli->error);
    }

    $format_with_title = '<h4 class="title">!title</h4>'.
@@ -396,23 +442,15 @@ class Reader extends Base {

  public function Update() {
    $mysqli = connect_db();
    $query = 'ALTER TABLE reader_items ADD COLUMN guid VARCHAR(200) AFTER '.
      'permalink';
    if (!$mysqli->query($query)) {
      $this->Log('Reader->Update 1: '.$mysqli->error);
    }
    // Need to populate guid column since it is becoming the new key.
    $query = 'UPDATE reader_items SET guid = permalink';
    if (!$mysqli->query($query)) {
      $this->Log('Reader->Update 2: '.$mysqli->error);
    }
    $query = 'ALTER TABLE reader_items DROP PRIMARY KEY';
    if (!$mysqli->query($query)) {
      $this->Log('Reader->Update 3: '.$mysqli->error);
    }
    $query = 'ALTER TABLE reader_items ADD PRIMARY KEY(guid, xml_url)';
    $query = 'CREATE TABLE IF NOT EXISTS reader_hub ('.
      'hub VARCHAR(200) NOT NULL,'.
      'self VARCHAR(200) NOT NULL,'.
      'expiry INT(10) UNSIGNED,'.
      'xml_url VARCHAR(200) NOT NULL,'.
      'PRIMARY KEY(xml_url)'.
      ') ENGINE=MyISAM';
    if (!$mysqli->query($query)) {
      $this->Log('Reader->Update 4: '.$mysqli->error);
      $this->Log('Reader->Update: '.$mysqli->error);
    }
    $mysqli->close();
  }
@@ -466,7 +504,7 @@ class Reader extends Base {
    if (count($all_feeds) <= 1) {
      // SimplePie will update the feed_url when the url given was
      // previously used for autodiscovery, or if there were redirects.
      $xml_url = $feed->subscribe_url();
      $xml_url = $mysqli->escape_string($feed->subscribe_url());
    }
    else {
      $mysqli->close();
@@ -531,8 +569,12 @@ class Reader extends Base {
      }
      // Leave call to SaveCloud until after the insert, as it triggers a call
      // to RegisteredFeed which will update reader_feeds for this entry.
      if ($cloud = $feed->get_channel_tags('', 'cloud')) {
        $this->SaveCloud($cloud, $xml_url);
      if ($us_cloud = $feed->get_channel_tags('', 'cloud')) {
        $this->SaveCloud($us_cloud, $xml_url);
      }
      else if ($us_hub = $feed->get_link(0, 'hub')) {
        $us_self = $feed->get_link(0, 'self');
        $this->SaveHub($us_hub, $us_self, $xml_url);
      }
      $config = HTMLPurifier_Config::createDefault();
      // Allow iframes from youtube and vimeo.
@@ -603,8 +645,12 @@ class Reader extends Base {
    $image_url = $mysqli->escape_string($feed->get_image_url());
    $image_title = $mysqli->escape_string($feed->get_image_title());
    $image_link = $mysqli->escape_string($feed->get_image_link());
    if ($cloud = $feed->get_channel_tags('', 'cloud')) {
      $this->SaveCloud($cloud, $xml_url);
    if ($us_cloud = $feed->get_channel_tags('', 'cloud')) {
      $this->SaveCloud($us_cloud, $xml_url);
    }
    else if ($us_hub = $feed->get_link(0, 'hub')) {
      $us_self = $feed->get_link(0, 'self');
      $this->SaveHub($us_hub, $us_self, $xml_url);
    }
    $query = 'UPDATE reader_feeds SET feed_title = "'.$title.'", '.
      'description = "'.$description.'", html_url = "'.$html_url.'", '.
@@ -1008,7 +1054,7 @@ class Reader extends Base {
    return $scheme.$this->user->config->ServerName().'/php/image.php';
  }

  protected function NotifyOthers($xml_url) {
  private function NotifyOthers($xml_url) {
    $mysqli = connect_db();
    $domain = substr($xml_url, strpos($xml_url, "//") + 2);
    $query = 'SELECT register FROM reader_notify WHERE '.
@@ -1074,8 +1120,10 @@ class Reader extends Base {
    $mysqli->close();
  }

  private function Register($xml_url, $register) {
  private function Register($us_xml_url, $us_register) {
    $mysqli = connect_db();
    $xml_url = $mysqli->escape_string($us_xml_url);
    $register = $mysqli->escape_string($us_register);
    $query = 'INSERT INTO reader_notify VALUES ("'.$register.'", '.
      '"'.$xml_url.'", '.time().') ON DUPLICATE KEY UPDATE timestamp = '.time();
    if (!$mysqli->query($query)) {
@@ -1084,12 +1132,12 @@ class Reader extends Base {
    $mysqli->close();
  }

  private function RegisteredFeed($xml_url) {
  private function RegisteredFeed($xml_url, $lease = false) {
    $registered = false;
    $mysqli = connect_db();
    $domain = substr($xml_url, strpos($xml_url, "//") + 2);
    $query = 'SELECT xml_url FROM reader_cloud WHERE '.
      'xml_url LIKE "%'.$domain.'"';
    $table = $lease === false ? 'reader_cloud' : 'reader_hub';
    $query = 'SELECT xml_url FROM '.$table.' WHERE xml_url LIKE "%'.$domain.'"';
    if ($result = $mysqli->query($query)) {
      $registered = $result->num_rows >= 1;
      $result->close();
@@ -1121,6 +1169,16 @@ class Reader extends Base {
          $this->Log('Reader->RegisteredFeed 3: '.$mysqli->error);
        }
      }
      // Also update the lease time for hub notifications.
      if ($lease !== false) {
        // Convert the lease in seconds to an expiry timestamp.
        if ($lease !== 0) $lease += time();
        $query = 'UPDATE reader_hub SET expiry = '.$lease.
          ' WHERE xml_url = "'.$xml_url.'"';
        if (!$mysqli->query($query)) {
          $this->Log('Reader->RegisteredFeed 4: '.$mysqli->error);
        }
      }
    }
    $mysqli->close();
    return $registered;
@@ -1229,10 +1287,14 @@ class Reader extends Base {
    if (!$mysqli->query($query)) {
      $this->Log('Reader->RemoveFromTables 2: '.$mysqli->error);
    }
    $query = 'DELETE FROM reader_items WHERE xml_url = "'.$xml_url.'"';
    $query = 'DELETE FROM reader_hub WHERE xml_url = "'.$xml_url.'"';
    if (!$mysqli->query($query)) {
      $this->Log('Reader->RemoveFromTables 3: '.$mysqli->error);
    }
    $query = 'DELETE FROM reader_items WHERE xml_url = "'.$xml_url.'"';
    if (!$mysqli->query($query)) {
      $this->Log('Reader->RemoveFromTables 4: '.$mysqli->error);
    }
    $mysqli->close();
  }

@@ -1251,6 +1313,37 @@ class Reader extends Base {
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
    curl_exec($ch);
    if (preg_match('/4[0-9][0-9]/', curl_getinfo($ch, CURLINFO_HTTP_CODE))) {
      $mysqli = connect_db();
      $query = 'DELETE FROM reader_cloud WHERE register = "'.$register.'"';
      if (!$mysqli->query($query)) {
        $this->Log('Reader->RenewCloud: '.$mysqli->error);
      }
      $mysqli->close();
    }
    curl_close($ch);
  }

  private function RenewHub($hub, $self, $xml_url) {
    $scheme = $this->user->config->Secure() ? 'https://' : 'http://';
    $callback = urlencode($scheme.$this->user->config->ServerName().
                          '/php/update.php?url='.$xml_url);
    $post_fields = 'hub.mode=subscribe&hub.topic='.urlencode($self).
      '&hub.callback='.$callback;
    $ch = curl_init($hub);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
    curl_exec($ch);
    if (preg_match('/4[0-9][0-9]/', curl_getinfo($ch, CURLINFO_HTTP_CODE))) {
      $mysqli = connect_db();
      $query = 'DELETE FROM reader_hub WHERE hub = "'.$hub.'" AND '.
        'self = "'.$self.'"';
      if (!$mysqli->query($query)) {
        $this->Log('Reader->RenewHub: '.$mysqli->error);
      }
      $mysqli->close();
    }
    curl_close($ch);
  }

@@ -1270,7 +1363,7 @@ class Reader extends Base {
    }
  }

  private function SaveCloud($cloud, $xml_url) {
  private function SaveCloud($us_cloud, $xml_url) {
    // If xml_url is on the same domain as the currently configured ServerName,
    // do nothing as UpdateFeed does notifications in this case.
    $matches = array();
@@ -1279,16 +1372,16 @@ class Reader extends Base {
      if ($domain === $this->user->config->ServerName()) return;
    }

    if ($attributes = $cloud[0]['attribs']['']) {
    if ($us_attributes = $us_cloud[0]['attribs']['']) {
      // Only supporting cloud notifications that specify 'http-post' as the
      // protocol here.
      if ($attributes['protocol'] != 'http-post') return;

      $domain = $attributes['domain'];
      if ($domain === '') return;
      if ($us_attributes['protocol'] != 'http-post' ||
          $us_attributes['domain'] === '') return;

      $mysqli = connect_db();
      $register = '';
      $port = $attributes['port'];
      $domain = $mysqli->escape_string($us_attributes['domain']);
      $port = $mysqli->escape_string($us_attributes['port']);
      // Use ports 80 and 443 to specify whether http or https should be
      // used to register, otherwise explicitly add the port number.
      if ($port == '80') {
@@ -1300,12 +1393,10 @@ class Reader extends Base {
      else {
        $register = 'http://'.$domain.':'.$port;
      }
      
      $register .= $attributes['path'];
      $existing = '';
      $mysqli = connect_db();
      $register .= $mysqli->escape_string($us_attributes['path']);
      // Check if there's an existing address to register for this feed, only
      // want to register again if it's changed.
      $existing = '';
      $query = 'SELECT register FROM reader_cloud WHERE '.
        'xml_url = "'.$xml_url.'"';
      if ($result = $mysqli->query($query)) {
@@ -1334,6 +1425,49 @@ class Reader extends Base {
    }
  }

  private function SaveHub($us_hub, $us_self, $xml_url) {
    // If xml_url is on the same domain as the currently configured ServerName,
    // do nothing as UpdateFeed does notifications in this case.
    $matches = array();
    if (preg_match('/\/\/([^\/]*)/', $xml_url, $matches)) {
      $domain = $matches[1];
      if ($domain === $this->user->config->ServerName()) return;
    }

    $existing = '';
    $mysqli = connect_db();
    // Check if there's an existing address to register for this feed, only
    // want to register again if it's changed.
    $query = 'SELECT hub FROM reader_hub WHERE xml_url = "'.$xml_url.'"';
    if ($result = $mysqli->query($query)) {
      if ($reader_hub = $result->fetch_assoc()) {
        $existing = $reader_hub['hub'];
      }
      $result->close();
    }
    else {
      $this->Log('Reader->SaveHub 1: '.$mysqli->error);
    }
    if ($existing !== $us_hub) {
      $hub = $mysqli->escape_string($us_hub);
      $self = $mysqli->escape_string($us_self);
      if ($self === '') $self = $xml_url;
      $query = 'INSERT INTO reader_hub VALUES ("'.$hub.'", "'.$self.'", 0, '.
        '"'.$xml_url.'") ON DUPLICATE KEY UPDATE hub = "'.$hub.'", '.
        'self = "'.$self.'"';
      if (!$mysqli->query($query)) {
        $this->Log('Reader->SaveHub 2: '.$mysqli->error);
      }
      $query = 'UPDATE reader_feeds SET registered = 0 WHERE '.
        'xml_url = "'.$xml_url.'"';
      if (!$mysqli->query($query)) {
        $this->Log('Reader->SaveHub 3: '.$mysqli->error);
      }
      $this->RenewHub($hub, $self, $xml_url);
    }
    $mysqli->close();
  }

  private function SaveItem($item, $xml_url, $purifier) {
    $us_title = strip_tags($item->get_title());
    $us_content = $purifier->purify($item->get_description());
+1 −0
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@ dobrado.editor=null;$(".editable").attr("contenteditable",false);$.each(CKEDITOR
if(extended.content.source){source=extended.content.source;}
notify=false;if(extended.content.notify){notify=extended.content.notify;}
var label=$(dobrado.current).data("label");if(label==="commenteditor"){label="Comment Manager";}
else if(label==="simple"){label="Text Area";}
else{label=label.charAt(0).toUpperCase()+label.slice(1);}
$("#extended-tabs li a").first().html(label);$("#extended-custom").html('<div class="custom">'+custom+'</div>');$("#extended-content").html('<div class="edit">'+source+'</div>');dobrado.oldPermalink=extended.content.permalink;$("#extended-custom-settings").find(":input").change(function(){customChange=true;});$("#extended-custom-settings :submit").button().click(function(){$(".extended").dialog("close");return false;});if(extended.content.editor===true){var edit=$("#extended-content > div.edit");var editorCssProperties=["azimuth","background","background-attachment","background-color","background-image","background-position","background-repeat","background-size","caption-side","clear","clip","color","content","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","empty-cells","filter","font","font-family","font-size","font-style","font-variant","font-weight","letter-spacing","line-height","list-style","list-style-image","list-style-position","list-style-type","opacity","orphans","outline","outline-color","outline-style","outline-width","padding","padding-bottom","padding-left","padding-right","padding-top","page","page-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","pitch","pitch-range","play-during","quotes","richness","size","speak","speak-header","speak-numeral","speak-punctuation","speech-rate","stress","text-align","text-decoration","text-indent","text-transform","unicode-bidi","vertical-align","voice-family","volume","white-space","widows","word-spacing"];var css=".cke_editable { ";var current=$(dobrado.current);for(var i=0;i<editorCssProperties.length;i++){var property=editorCssProperties[i];var value=current.css(property);if(value){css+=property+": "+value+";";}}
css+="}";CKEDITOR.addCss(css);dobrado.editor=CKEDITOR.replace(edit.get(0),{allowedContent:true,disableNativeSpellChecker:false,enterMode:CKEDITOR.ENTER_BR,filebrowserBrowseUrl:"/php/browse.php",height:240,removePlugins:"elementspath,contextmenu,tabletools,liststyle",resize_enabled:false,toolbar:[['Source','-','NewPage'],['Undo','Redo','-','SelectAll','RemoveFormat'],['Form','Checkbox','Radio','TextField','Textarea','Select','Button','ImageButton','HiddenField'],['Bold','Italic','Underline','Strike'],['Subscript','Superscript'],['NumberedList','BulletedList','-','Outdent','Indent','Blockquote','CreateDiv'],['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],['Link','Unlink','Anchor'],['Image','Table','HorizontalRule','Smiley','SpecialChar','PageBreak'],['Format','Font','FontSize'],['TextColor','BGColor'],['Maximize','ShowBlocks','-','About']]});}
+3 −0
Original line number Diff line number Diff line
@@ -516,6 +516,9 @@ if (!this.dobrado.extended) {
    if (label === "commenteditor") {
      label = "Comment Manager";
    }
    else if (label === "simple") {
      label = "Text Area";
    }
    else {
      // Otherwise capitalize the first letter to match other tabs.
      label = label.charAt(0).toUpperCase() + label.slice(1);
+44 −22
Original line number Diff line number Diff line
<?php
// Dobrado Content Management System
// Copyright (C) 2015 Malcolm Blaney
// Copyright (C) 2016 Malcolm Blaney
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
@@ -20,14 +20,55 @@ include 'config.php';
include 'module.php';
include 'user.php';

function challenge($register, $url, $challenge, $lease = false) {
  if ($lease !== false) {
    $ch = curl_init($register.'?hub.mode=subscribe&hub.topic='.$url.
                    '&hub.challenge='.$challenge.'&hub.lease_seconds='.$lease);
  }
  else {
    $ch = curl_init($register.'?url='.$url.'&challenge='.$challenge);
  }
  curl_setopt($ch, CURLOPT_HEADER, true);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $data = explode("\r\n", curl_exec($ch));
  $count = count($data);
  if ($count < 2) {
    echo 'Error: could not check notification URL.';
    return false;
  }
  // Check that the status code for the first header is in the 200's.
  if (!preg_match('/^HTTP\/1.1 2[0-9][0-9]/', $data[0])) {
    echo 'Error: incorrect status header.';
    return false;
  }
  // Then the last entry in the data array must be the challenge value.
  if ($data[$count - 1] !== $challenge) {
    echo 'Error: challenge does not match.';
    return false;
  }
  return true;
}

$user = new User();
$reader = new Module($user, '', 'reader');
if (!$reader->IsInstalled()) {
  header("HTTP/1.1 404 Not Found");
  echo 'Update notifications are not available.';
  return;
}

if (isset($_POST['hub.mode']) && $_POST['hub.mode'] == 'subscribe') {
  $url = $_POST['hub.topic'];
  $register = $_POST['hub.callback'];
  if (challenge($register, $url, md5(mt_rand()), 86400)) {
    $reader->Factory('Register', array($url, $register));
    echo 'Registration successful.';
  }
  return;
}

if (isset($_POST['protocol']) && $_POST['protocol'] != 'http-post') {
  header("HTTP/1.1 404 Not Found");
  echo 'Sorry only http-post protocol is supported.';
  return;
}
@@ -37,7 +78,6 @@ $path = isset($_POST['path']) ? $_POST['path'] : '/';
$domain = '';
$register = '';
$verify = false;
$challenge = md5(mt_rand());
$server = $user->config->ServerName();

if (isset($_POST['domain'])) {
@@ -68,29 +108,11 @@ while (isset($_POST['url'.$i])) {
    echo 'Error: cannot register for '.$url;
    continue;
  }
  if ($verify) {
    $ch = curl_init($register.'?url='.$url.'&challenge='.$challenge);
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $data = explode("\r\n", curl_exec($ch));
    $count = count($data);
    if ($count < 2) {
      echo 'Error: could not check notification URL.';
      continue;
    }
    // Check that the status code for the first header is in the 200's.
    if (preg_match('/^HTTP\/1.1 2[0-9][0-9]/', $data[0]) != 1) {
      echo 'Error: incorrect status header.';
  if ($verify && !challenge($register, $url, md5(mt_rand()))) {
    continue;
  }
    // Then the last entry in the data array must be the challenge value.
    if ($data[$count - 1] !== $challenge) {
      echo 'Error: challenge does not match.';
      continue;
    }
  }
  $reader->Factory('Register', array($url, $register));
  echo 'Registration successful.';
  echo 'Registration for '.$url.' successful.';
}

?>

Read the original on gitlab.com ↗