Merge branch '1.0.x' of gitorious.org:statusnet/mainline into 1.0.x

* '1.0.x' of gitorious.org:statusnet/mainline:
  more verbose, better checks for installforemail
  Check for unavailable site nicknames in DomainStatusNetwork
  Status_network::setupDB() sets up related tables too
  Class to store unavailable status network names
  First version of installer for domain-based status networks
  Ignore empty email whitelist string
  Don't redefine 'STATUSNET' in installer
  move registeremailuser.php back to email registration
  Move common domain-to-network mapping to the plugin module
  move external libs for domains here
  move domain status network scripts
  Revert "Add DomainStatusNetwork plugin"
  add DomainStatusNetwork plugin
  Add DomainStatusNetwork plugin
  utility functions for setting config options in memory
  get a free site nickname by email or domain
  TLD utilities
  script to determine site nickname by domain
  lookup a statusnetwork by tag
This commit is contained in:
Zach Copley 2011-04-28 15:54:13 -07:00
commit cb6b5b2cc6
13 changed files with 4619 additions and 4 deletions

View File

@ -74,7 +74,10 @@ class Status_network extends Safe_DataObject
$config['db']['database_'.$dbname] = "mysqli://$dbuser:$dbpass@$dbhost/$dbname";
$config['db']['ini_'.$dbname] = INSTALLDIR.'/classes/status_network.ini';
$config['db']['table_status_network'] = $dbname;
foreach (array('status_network', 'status_network_tag', 'unavailable_status_network') as $table) {
$config['db']['table_'.$table] = $dbname;
}
if (class_exists('Memcache')) {
self::$cache = new Memcache();

View File

@ -129,4 +129,15 @@ class Status_network_tag extends Safe_DataObject
$this->decache();
return $ret;
}
static function withTag($tag)
{
$snt = new Status_network_tag();
$snt->tag = $tag;
$snt->find();
return $snt;
}
}

View File

@ -0,0 +1,84 @@
<?php
/**
* Data class for unavailable status networks
*
* PHP version 5
*
* @category Data
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Keeps a list of unavailable status network names
*
* @category Data
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*
* @see Managed_DataObject
*/
class Unavailable_status_network extends Managed_DataObject
{
public $__table = 'unavailable_status_network'; // table name
public $nickname; // varchar(64) UUID
public $created; // datetime
/**
* Get an instance by key
*
* @param string $k Key to use to lookup (usually 'id' for this class)
* @param mixed $v Value to lookup
*
* @return Happening object found, or null for no hits
*
*/
function staticGet($k, $v=null)
{
return Memcached_DataObject::staticGet('Unavailable_status_network', $k, $v);
}
/**
* The One True Thingy that must be defined and declared.
*/
public static function schemaDef()
{
return array(
'description' => 'An unavailable status network nickname',
'fields' => array(
'nickname' => array('type' => 'varchar',
'length' => 64,
'not null' => true, 'description' => 'nickname not to use'),
'created' => array('type' => 'datetime',
'not null' => true),
),
'primary key' => array('nickname'),
);
}
}

View File

@ -44,7 +44,7 @@ abstract class Installer
/** Web site info */
public $sitename, $server, $path, $fancy;
/** DB info */
public $host, $dbname, $dbtype, $username, $password, $db;
public $host, $database, $dbtype, $username, $password, $db;
/** Administrator info */
public $adminNick, $adminPass, $adminEmail, $adminUpdates;
/** Should we skip writing the configuration file? */
@ -509,7 +509,9 @@ abstract class Installer
$this->updateStatus("Initializing...");
ini_set('display_errors', 1);
error_reporting(E_ALL);
define('STATUSNET', 1);
if (!defined('STATUSNET')) {
define('STATUSNET', 1);
}
require_once INSTALLDIR . '/lib/framework.php';
StatusNet::initDefaults($this->server, $this->path);

View File

@ -1860,6 +1860,30 @@ function common_config($main, $sub)
array_key_exists($sub, $config[$main])) ? $config[$main][$sub] : false;
}
function common_config_set($main, $sub, $value)
{
global $config;
if (!array_key_exists($main, $config)) {
$config[$main] = array();
}
$config[$main][$sub] = $value;
}
function common_config_append($main, $sub, $value)
{
global $config;
if (!array_key_exists($main, $config)) {
$config[$main] = array();
}
if (!array_key_exists($sub, $config[$main])) {
$config[$main][$sub] = array();
}
if (!is_array($config[$main][$sub])) {
$config[$main][$sub] = array($config[$main][$sub]);
}
array_push($config[$main][$sub], $value);
}
/**
* Pull arguments from a GET/POST/REQUEST array with first-level input checks:
* strips "magic quotes" slashes if necessary, and kills invalid UTF-8 strings.

View File

@ -0,0 +1,202 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* One status_network per email domain
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category DomainStatusNetwork
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
// This check helps protect against security problems;
// your code file can't be executed directly from the web.
exit(1);
}
$_dir = dirname(__FILE__);
require_once $_dir . '/extlib/effectiveTLDs.inc.php';
require_once $_dir . '/extlib/regDomain.inc.php';
/**
* Tools to map one status_network to one email domain in a multi-site
* installation.
*
* @category DomainStatusNetwork
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class DomainStatusNetworkPlugin extends Plugin
{
static $_thetree = null;
function initialize()
{
// For various reasons this gets squished
global $tldTree;
if (empty($tldTree)) {
if (!empty(self::$_thetree)) {
$tldTree = self::$_thetree;
}
}
$nickname = StatusNet::currentSite();
if (empty($nickname)) {
$this->log(LOG_WARNING, "No current site");
return;
}
try {
$sn = Status_network::staticGet('nickname', $nickname);
} catch (Exception $e) {
$this->log(LOG_ERR, $e->getMessage());
}
$tags = $sn->getTags();
foreach ($tags as $tag) {
if (strncmp($tag, 'domain=', 7) == 0) {
$domain = substr($tag, 7);
$this->log("Setting email domain to {$domain}");
common_config_append('email', 'whitelist', $domain);
}
}
}
function onAutoload($cls)
{
$dir = dirname(__FILE__);
switch ($cls)
{
case 'DomainStatusNetworkInstaller':
include_once $dir . '/' . strtolower($cls) . '.php';
return false;
default:
return true;
}
}
static function toDomain($raw)
{
$parts = explode('@', $raw);
if (count($parts) == 1) {
$domain = $parts[0];
} else {
$domain = $parts[1];
}
$domain = strtolower(trim($domain));
return $domain;
}
static function registeredDomain($domain)
{
return getRegisteredDomain($domain);
}
static function nicknameAvailable($nickname)
{
$sn = Status_network::staticGet('nickname', $nickname);
if (!empty($sn)) {
return false;
}
$usn = Unavailable_status_network::staticGet('nickname', $nickname);
if (!empty($usn)) {
return false;
}
return true;
}
static function nicknameForDomain($domain)
{
$registered = self::registeredDomain($domain);
$parts = explode('.', $registered);
$base = $parts[0];
if (self::nicknameAvailable($base)) {
return $base;
}
$domainish = str_replace('.', '-', $registered);
if (self::nicknameAvailable($domainish)) {
return $domainish;
}
$i = 1;
// We don't need to keep doing this forever
while ($i < 1024) {
$candidate = $domainish.'-'.$i;
if (self::nicknameAvailable($candidate)) {
return $candidate;
}
$i++;
}
return null;
}
static function siteForDomain($domain)
{
$snt = Status_network_tag::withTag('domain='.$domain);
while ($snt->fetch()) {
$sn = Status_network::staticGet('site_id', $snt->site_id);
if (!empty($sn)) {
return $sn;
}
}
return null;
}
function onPluginVersion(&$versions)
{
$versions[] = array('name' => 'DomainStatusNetwork',
'version' => STATUSNET_VERSION,
'author' => 'Evan Prodromou',
'homepage' => 'http://status.net/wiki/Plugin:DomainStatusNetwork',
'rawdescription' =>
_m('A plugin that maps a single status_network to an email domain.'));
return true;
}
}
// The way addPlugin() works, this global variable gets disappeared.
// So, we re-appear it.
DomainStatusNetworkPlugin::$_thetree = $tldTree;

View File

@ -0,0 +1,296 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* Installer class for domain-based multi-homing systems
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category DomainStatusNetwork
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
// This check helps protect against security problems;
// your code file can't be executed directly from the web.
exit(1);
}
/**
* Installer class for domain-based multi-homing systems
*
* @category DomainStatusNetwork
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class DomainStatusNetworkInstaller extends Installer
{
protected $domain = null;
protected $rootname = null;
protected $sitedb = null;
protected $rootpass = null;
protected $nickname = null;
protected $sn = null;
public $verbose = false;
function __construct($domain)
{
$this->domain = $domain;
}
/**
* Go for it!
* @return boolean success
*/
function main()
{
// We don't check prereqs. Check 'em before setting up a
// multi-home system, kthxbi
if ($this->prepare()) {
return $this->handle();
} else {
$this->showHelp();
return false;
}
}
/**
* Get our input parameters...
* @return boolean success
*/
function prepare()
{
$config = $this->getConfig();
$this->nickname = DomainStatusNetworkPlugin::nicknameForDomain($this->domain);
// XXX make this configurable
$this->sitename = sprintf('The %s Status Network', $this->domain);
$this->server = $this->nickname.'.'.$config['WILDCARD'];
$this->path = null;
$this->fancy = true;
$datanick = $this->databaseize($this->nickname);
$this->host = $config['DBHOSTNAME'];
$this->database = $datanick.$config['DBBASE'];
$this->dbtype = 'mysql'; // XXX: support others... someday
$this->username = $datanick.$config['USERBASE'];
// Max size for MySQL
if (strlen($this->username) > 16) {
$this->username = sprintf('%s%08x', substr($this->username, 0, 8), crc32($this->username));
}
$pwgen = $config['PWDGEN'];
$password = `$pwgen`;
$this->password = trim($password);
// For setting up the database
$this->rootname = $config['ADMIN'];
$this->rootpass = $config['ADMINPASS'];
$this->sitehost = $config['DBHOST'];
$this->sitedb = $config['SITEDB'];
// Explicitly empty
$this->adminNick = null;
$this->adminPass = null;
$this->adminEmail = null;
$this->adminUpdates = null;
/** Should we skip writing the configuration file? */
$this->skipConfig = true;
if (!$this->validateDb()) {
return false;
}
return true;
}
function handle()
{
return $this->doInstall();
}
function setupDatabase()
{
$this->updateStatus('Creating database...');
$this->createDatabase();
parent::setupDatabase();
$this->updateStatus('Creating file directories...');
$this->createDirectories();
$this->updateStatus('Saving status network...');
$this->saveStatusNetwork();
$this->updateStatus('Checking schema for plugins...');
$this->checkSchema();
}
function saveStatusNetwork()
{
Status_network::setupDB($this->sitehost,
$this->rootname,
$this->rootpass,
$this->sitedb, array());
$sn = new Status_network();
$sn->nickname = $this->nickname;
$sn->dbhost = $this->host;
$sn->dbuser = $this->username;
$sn->dbpass = $this->password;
$sn->dbname = $this->database;
$sn->sitename = $this->sitename;
$result = $sn->insert();
if (!$result) {
throw new ServerException("Couldn't create status_network: " . print_r($sn, true));
}
$sn->setTags(array('domain='.$this->domain));
$this->sn = $sn;
}
function checkSchema()
{
$config = $this->getConfig();
Status_network::$wildcard = $config['WILDCARD'];
StatusNet::switchSite($this->nickname);
Event::handle('CheckSchema');
}
function getStatusNetwork()
{
return $this->sn;
}
function createDirectories()
{
$config = $this->getConfig();
foreach (array('AVATARBASE', 'BACKGROUNDBASE', 'FILEBASE') as $key) {
$base = $config[$key];
mkdir($base.'/'.$this->nickname, 0777, true);
}
}
function createDatabase()
{
// Create the New DB
$res = mysql_connect($this->host, $this->rootname, $this->rootpass);
if (!$res) {
throw new ServerException("Can't connect to {$this->host} as {$this->rootname}");
}
mysql_query("CREATE DATABASE ". mysql_real_escape_string($this->database), $res);
$return = mysql_select_db($this->database, $res);
if (!$return) {
throw new ServerException("Unable to connect to {$this->database} on {$this->host}");
}
foreach (array('localhost', '%') as $src) {
mysql_query("GRANT ALL ON " .
mysql_real_escape_string($this->database).".* TO '" .
$this->username . "'@'".$src."' ".
"IDENTIFIED BY '".$this->password."'", $res);
}
mysql_close($res);
}
function getConfig()
{
static $config;
$cfg_file = "/etc/statusnet/setup.cfg";
if (empty($config)) {
$result = parse_ini_file($cfg_file);
$config = array();
foreach ($result as $key => $value) {
$key = str_replace('export ', '', $key);
$config[$key] = $value;
}
}
return $config;
}
function showHelp()
{
}
function warning($message, $submessage='')
{
print $this->html2text($message) . "\n";
if ($submessage != '') {
print " " . $this->html2text($submessage) . "\n";
}
print "\n";
}
function updateStatus($status, $error=false)
{
if ($this->verbose || $error) {
if ($error) {
print "ERROR: ";
}
print $this->html2text($status);
print "\n";
}
}
private function html2text($html)
{
// break out any links for text legibility
$breakout = preg_replace('/<a[^>+]\bhref="(.*)"[^>]*>(.*)<\/a>/',
'\2 &lt;\1&gt;',
$html);
return html_entity_decode(strip_tags($breakout), ENT_QUOTES, 'UTF-8');
}
function databaseize($nickname)
{
$nickname = str_replace('-', '_', $nickname);
return $nickname;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,83 @@
<?
/*
* Calculate the effective registered domain of a fully qualified domain name.
*
* <@LICENSE>
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* </@LICENSE>
*
* Florian Sager, 25.07.2008, sager@agitos.de
*/
/*
* Remove subdomains from a signing domain to get the registered domain.
*
* dkim-reputation.org blocks signing domains on the level of registered domains
* to rate senders who use e.g. a.spamdomain.tld, b.spamdomain.tld, ... under
* the most common identifier - the registered domain - finally.
*
* This function returns NULL if $signingDomain is TLD itself
*/
function getRegisteredDomain($signingDomain) {
global $tldTree;
$signingDomainParts = split('\.', $signingDomain);
$result = findRegisteredDomain($signingDomainParts, $tldTree);
if ($result===NULL || $result=="") {
// this is an invalid domain name
return NULL;
}
// assure there is at least 1 TLD in the stripped signing domain
if (!strpos($result, '.')) {
$cnt = count($signingDomainParts);
if ($cnt==1 || $signingDomainParts[$cnt-2]=="") return NULL;
return $signingDomainParts[$cnt-2].'.'.$signingDomainParts[$cnt-1];
}
return $result;
}
// recursive helper method
function findRegisteredDomain($remainingSigningDomainParts, &$treeNode) {
$sub = array_pop($remainingSigningDomainParts);
$result = NULL;
if (isset($treeNode['!'])) {
return '#';
} else if (is_array($treeNode) && array_key_exists($sub, $treeNode)) {
$result = findRegisteredDomain($remainingSigningDomainParts, $treeNode[$sub]);
} else if (is_array($treeNode) && array_key_exists('*', $treeNode)) {
$result = findRegisteredDomain($remainingSigningDomainParts, $treeNode['*']);
} else {
return $sub;
}
// this is a hack 'cause PHP interpretes '' as NULL
if ($result == '#') {
return $sub;
} else if (strlen($result)>0) {
return $result.'.'.$sub;
}
return NULL;
}
?>

View File

@ -0,0 +1,59 @@
#!/usr/bin/env php
<?php
/*
* StatusNet - a distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* Script to print out current version of the software
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
$helptext = <<<END_OF_INSTALLFOREMAIL_HELP
installforemail.php [options] <email address>
END_OF_INSTALLFOREMAIL_HELP;
require_once INSTALLDIR.'/scripts/commandline.inc';
$email = $args[0];
$domain = DomainStatusNetworkPlugin::toDomain($email);
$sn = DomainStatusNetworkPlugin::siteForDomain($domain);
if (empty($sn)) {
$installer = new DomainStatusNetworkInstaller($domain);
$installer->verbose = have_option('v', 'verbose');
// Do the thing
$installer->main();
$sn = $installer->getStatusNetwork();
$config = $installer->getConfig();
Status_network::$wildcard = $config['WILDCARD'];
}
StatusNet::switchSite($sn->nickname);
$confirm = EmailRegistrationPlugin::registerEmail($email);
$confirmUrl = common_local_url('register', array('code' => $confirm->code));
print $confirmUrl."\n";

View File

@ -0,0 +1,42 @@
#!/usr/bin/env php
<?php
/*
* StatusNet - a distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* Script to print out current version of the software
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
$helptext = <<<END_OF_SITEFORDOMAIN_HELP
sitefordomain.php [options] <email address|domain>
Prints site information for the domain given
END_OF_SITEFORDOMAIN_HELP;
require_once INSTALLDIR.'/scripts/commandline.inc';
$domain = DomainStatusNetworkPlugin::toDomain($args[0]);
$nickname = DomainStatusNetworkPlugin::nicknameForDomain($domain);
if (empty($nickname)) {
throw new ClientException("No candidate found.");
} else {
print $nickname;
print "\n";
}

View File

@ -0,0 +1,43 @@
#!/usr/bin/env php
<?php
/*
* StatusNet - a distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* Script to print out current version of the software
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
$helptext = <<<END_OF_SITEFORDOMAIN_HELP
sitefordomain.php [options] <email address|domain>
Prints site information for the domain given
END_OF_SITEFORDOMAIN_HELP;
require_once INSTALLDIR.'/scripts/commandline.inc';
$domain = DomainStatusNetworkPlugin::toDomain($args[0]);
$sn = DomainStatusNetworkPlugin::siteForDomain($domain);
if (empty($sn)) {
exit(1);
}
print $sn->nickname."\n";
exit(0);

View File

@ -98,7 +98,7 @@ class DomainWhitelistPlugin extends Plugin
{
$whitelist = $this->getWhitelist();
if (empty($whitelist)) {
if (empty($whitelist) || empty($whitelist[0])) {
return true;
}