ifsvnadmin源码解析1--数据传输逻辑

问题1:在进行进程捕获的过程中,本身的subversion进程的操作是能够进行正确的捕获的,我们使用ifsvnadmin去创建版本库,却没有对相关的任何进程进行捕获
问题2:在问题1的基础上,使用ifsvnadmin创建的版本库,在服务器上没有启动正常的subversion服务,但是依然能够使用小乌龟工具进行文件上传下载

带着这两个问题,我们来看下ifsvnadmin的源码,正所谓源码之下无秘密。

1.由于要讨论版本库创建问题,先在svnadmin文件下找到repositorycreate.php文件,我们找到如下内容

//
// Actions
//

if (check_request_var('create'))
{
	$engine->handleAction('create_repository');
}

发现这里是创建版本库的,找到engine的定义

//
// Authentication
//

$engine = \svnadmin\core\Engine::getInstance();

2.去svnadmin\classes\core\目录下找到Engine.class.php文件,找到

/**
	 * Gets the singelton instance of this class.
	 *
	 * @return \svnadmin\core\Engine
	 */
	public static function getInstance()
	{
		if (self::$m_instance == null)
		{
			self::$m_instance = new Engine;
		}
		return self::$m_instance;
	}

这个接口就是自己的实例,然后我们在下面找到handleAction方法

/**
	 * Loads the given action file and handles it.
	 *
	 * @param string $action The name of the action
	 * @return void
	 */
	public function handleAction( $action )
	{
		global $appEngine;
		global $appTemplate;	// @todo Remove this variable from this place.
		global $appTR;			// @todo Remove this variable from this place.

		$appTR->loadModule("actions");

		if(!defined('ACTION_HANDLING'))
		{
			define('ACTION_HANDLING', true);
		}

		// Path to action implementation.
		$filename = 'actions/'.$action.'.php';

		if(file_exists($filename))
		{
			$code = file_get_contents($filename);
			eval(' ?>'.$code.'<?php ');
		}
		else
		{
			throw new Exception('Can not find implementation for action: '.$action);
		}
	}

这段代码意思加载了actions目录下的php文件,所以我们去找create_repository.php文件

3.在create_repository.php文件中,我们找到如下内容

// Create a initial repository structure.
		try {
			$repoPredefinedStructure = get_request_var("repostructuretype");
			if ($repoPredefinedStructure != NULL) {
				
				switch ($repoPredefinedStructure) {
					case "single":
						$engine->getRepositoryEditProvider()
							->mkdir($r, array('trunk', 'branches', 'tags'));
						break;

					case "multi":
						$projectName = get_request_var("projectname");
						if ($projectName != NULL) {
							$engine->getRepositoryEditProvider()
								->mkdir($r, array(
									$projectName . '/trunk',
									$projectName . '/branches',
									$projectName . '/tags'
								));
						}
						else {
							throw new ValidationException(tr("Missing project name"));
						}
						break;
				}
			}
		}
		catch (Exception $e3) {
			$engine->addException($e3);
		}

显然,这就是ifsvnadmin创建版本库的地方,在这段代码中,我们注意到有两个地方需要再细看一下,get_request_var和getRepositoryEditProvider这两个函数
4.通过全局查找发现,get_request_var函数再include\ifcorelib\globals.php中定义

function get_request_var( $varname )
{
	$method = null;
	if (check_request_var($varname, $method))
	{
		switch($method)
		{
			case 'get':
				if (is_array($_GET[$varname]))
				{
					if (count($_GET[$varname]) == 1 && empty($_GET[$varname][0]))
					{
						return null;
					}
				}
				return $_GET[$varname];

			case 'post':
				if (is_array($_POST[$varname]))
				{
					if (count($_POST[$varname]) == 1 && empty($_POST[$varname][0]))
					{
						return null;
					}
				}
				return $_POST[$varname];
		}
	}
	return null;
}

通过这个函数,我们可以看到,版本库的名字是通过http的方式传到后台的。

5.在Engine.class.php文件中找到getRepositoryEditProvider函数如下

public function isRepositoryEditActive()
  {
    return $this->m_repositoryEditProvider == null ? false : true;
  }

  public function setRepositoryEditProvider( $o )
  {
    $this->m_repositoryEditProvider = $o;
  }
public function getRepositoryEditProvider()
  {
    if( $this->m_repositoryEditProvider != null )
    {
      $this->m_repositoryEditProvider->init();
    }
    return $this->m_repositoryEditProvider;
  }

这种写法,面向对象常用的写法,所以我们去找在哪里调用了setRepositoryEditProvider这个函数
6.通过查找,我们发现调用出

# grep "setRepositoryEditProvider" ./* -r
./classes/core/Engine.class.php:  public function setRepositoryEditProvider( $o )
./include/config.inc.php:  $appEngine->setRepositoryEditProvider( $repoEdit );

所以去看下include/config.inc.php文件中是怎么样的,在其中找到了下面的代码

/**
 * Repository edit provider.
 */
if ($cfg->getValue("Engine:Providers", "RepositoryEditProviderType") == "svnclient")
{
  include_once( "./classes/providers/RepositoryEditProvider.class.php" );
  $repoEdit = \svnadmin\providers\RepositoryEditProvider::getInstance();
  $appEngine->setRepositoryEditProvider( $repoEdit );
}

从这段代码可以看到,repoEdit的值是从providers\RepositoryEditProvider::getInstance()中来的,我们再去看这个文件,providers文件夹在classes文件夹下面
7.在RepositoryEditProvider.class.php文件中,我们找到getInstance,如下

/**
	 * Gets the singelton instance of this object.
	 *
	 * @return svnadmin\providers\RepositoryEditProvider
	 */
	public static function getInstance()
	{
		if (self::$_instance == NULL) {
			self::$_instance = new self();
		}
		return self::$_instance;
}

创建一个自己的实例,所以我们要仔细的看看其中到底有什么?
8.在其中我们找到了如下代码

/**
	 * (non-PHPdoc)
	 * @see svnadmin\core\interfaces.IRepositoryEditProvider::mkdir()
	 */
	public function mkdir(\svnadmin\core\entities\Repository $oRepository, array $paths)
	{
		$svnParentPath = $this->getRepositoryConfigValue($oRepository, 'SVNParentPath');
		
		if ($svnParentPath == NULL) {
			throw new \Exception('Invalid parent-identifier: ' .
					$oRepository->getParentIdentifier());
		}
		
		// Create absolute paths.
		for ($i = 0; $i < count($paths); ++$i) {
			$paths[$i] = $svnParentPath . '/' . $oRepository->name . '/' . $paths[$i];
		}
		
		$this->_svnClient->svn_mkdir($paths);
		
    	return true;
}

因为我们关注的版本库的创建,所以看的是mkdir部分,可以看到,指向了_svnClient这个方法,看一下

/**
	 * Initializes the object by Engine configuration. 
	 */
	public function __construct()
	{
		$engine = \svnadmin\core\Engine::getInstance();
		$config = $engine->getConfig();
		
		// Subversion class for browsing.
		$this->_svnClient = new \IF_SVNClientC($engine->getConfig()
				->getValue('Repositories:svnclient', 'SvnExecutable'));
....

这里就不粘贴完了,又调用了getConfig这个函数再往下看
9.在Engine.class.php中又找到

/**
	 * Gets the global configuration object.
	 *
	 * @return \IF_IniFile
	 */
	public function getConfig()
	{
		return $this->m_config;
	}
	...

那么这个m_config又是什么,继续找

/**
	 * Private constructor, because the class is a singelton instance.
	 * @return \svnadmin\core\Engine
	 */
	private function __construct()
	{
		// Load the global configuration.
		$this->m_config = new \IF_IniFile();
		$this->m_config->loadFromFile("./data/config.ini");
	}

在这里可以看到,加载了data/config.ini这个配置文件,我们去看看这个配置文件
10.在data/目录下找到了config.ini文件,下面是配置文件的内容

[Common]
FirstStart=0
BackupFolder=./data/backup/

[Translation]
Directory=./translations/

[Engine:Providers]
AuthenticationStatus=basic
UserViewProviderType=passwd
UserEditProviderType=passwd
GroupViewProviderType=svnauthfile
GroupEditProviderType=svnauthfile
AccessPathViewProviderType=svnauthfile
AccessPathEditProviderType=svnauthfile
RepositoryViewProviderType=svnclient
RepositoryEditProviderType=svnclient

[ACLManager]
UserRoleAssignmentFile=./data/userroleassignments.ini

[Subversion]
SVNAuthFile=/var/www/html/svnadmin/accessfile

[Repositories:svnclient]
SVNParentPath=/usr/local/svn/svnrepos
SvnExecutable=/usr/bin/svn
SvnAdminExecutable=/usr/bin/svnadmin

[Users:passwd]
SVNUserFile=/var/www/html/svnadmin/passwdfile

[Users:digest]
SVNUserDigestFile=
SVNDigestRealm=SVN Privat

[Ldap]
HostAddress=ldap://192.168.136.130:389/
ProtocolVersion=3
BindDN=CN=Manuel Freiholz,CN=Users,DC=insanefactory,DC=com
BindPassword=root
CacheEnabled=false
CacheFile=./data/ldap.cache.json

[Users:ldap]
BaseDN=DC=insanefactory,DC=com
SearchFilter=(&(objectClass=person)(objectClass=user))
Attributes=sAMAccountName

[Groups:ldap]
BaseDN=DC=insanefactory,DC=com
SearchFilter=(objectClass=group)
Attributes=sAMAccountName
GroupsToUserAttribute=member
GroupsToUserAttributeValue=distinguishedName

[Update:ldap]
AutoRemoveUsers=true
AutoRemoveGroups=true

[GUI]
RepositoryDeleteEnabled=false
RepositoryDumpEnabled=false
AllowUpdateByGui=true

根据第8步知道,要找Repositories:svnclient,在配置文件中看到

[Repositories:svnclient]
SVNParentPath=/usr/local/svn/svnrepos
SvnExecutable=/usr/bin/svn
SvnAdminExecutable=/usr/bin/svnadmin

所以,到这里,我们找到了这里调用的mkdir命令是由/usr/bin/svn进程实现的,现在返回到我们的实际环境中看一下
11.在实际环境中,看到了所有相关的svn命令

# ls /usr/bin/svn
svn            svnadmin       svndumpfilter  svnlook        svnrdump       svnserve       svnsync        svnversion 

也就是说,直接调用svn命令,导致我的程序没有捕获相应的操作,下面来看看svn这个命令做了什么
12.在环境中发现我的目录结构和第3步的目录结构不一样,我的目录结构是

#ls
conf  dav  db  format  hooks  locks  README.txt

意识到我的出发点有错误,于是继续查找,在create_repository.php文件中找到如下内容

if ($reponame == NULL) {
	$engine->addException(new ValidationException(tr("You have to fill out all fields.")));
}
else {
	$r = new \svnadmin\core\entities\Repository($reponame, $varParentIdentifier);

	// Create repository.
	try {
		$engine->getRepositoryEditProvider()->create($r, $repotype);
		$engine->getRepositoryEditProvider()->save();
		$engine->addMessage(tr("The repository %0 has been created successfully", array($reponame)));
...

这里才是初始化创建库的地方,这里就可以发现,同样是调用的getRepositoryEditProvider函数,所以接下来就简单了,到RepositoryEditProvider.class.php文件中去看

13.在RepositoryEditProvider.class.php文件中找到

/**
     * (non-PHPdoc)
     * @see svnadmin\core\interfaces.IProvider::init()
     */
	public function init()
	{
		return true;
	}

	/**
	 * (non-PHPdoc)
	 * @see svnadmin\core\interfaces.IEditProvider::save()
	 */
	public function save()
	{
		return true;
	}

	/**
	 * (non-PHPdoc)
	 * @see svnadmin\core\interfaces.IRepositoryEditProvider::create()
	 */
	public function create(\svnadmin\core\entities\Repository $oRepository, $type = "fsfs")
	{
		$svnParentPath = $this->getRepositoryConfigValue($oRepository, 'SVNParentPath');
		
		if (!file_exists($svnParentPath)) {
			throw new \Exception("The repository parent path doesn't exists: " .
					$svnParentPath);
		}

		$path = $svnParentPath . '/' . $oRepository->name;
		$this->_svnAdmin->create($path, $type);

		return true;
	}

这个类中,做了一些封装,查看一下svnAdmin的定义,如下

// Subversion class for administration.
		$this->_svnAdmin = new \IF_SVNAdminC($engine->getConfig()
				->getValue('Repositories:svnclient', 'SvnAdminExecutable'));

同样是取配置,调用的是SvnAdminExecutable=/usr/bin/svnadmin

到此为止,我们弄清楚了ifsvnadmin的工作方式,数据流向以及程序逻辑。但是我的问题似乎并不在这里,还要继续查看,应该是其他问题。
14.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java实现的SVN远程管理WEB应用 源码地址:http://code.google.com/p/jsvnadmin Svn Admin是一个Java开发的管理Svn服务器的项目用户的web应用。安装好Svn服务器端好,把Svn Admin部署好,就可以通过web浏览器管理Svn的项目,管理项目的用户,管理项目的权限。使得管理配置Svn简便,再也不需要每次都到服务器手工修改配置文件。 有什么优点 多数据库:Svn项目配置数据保存在数据库,支持所有数据库(默认MySQL/Oracle/SQL Server)。 多操作系统:支持Window,Linux等操作系统。 权限控制:管理员可以随意分配权限、项目管理员可以管理项目成员、成员只能查看和修改自己的密码。 支持多项目、多用户、多用户组Group(默认带有“项目管理组”、“项目开发组”、“项目测试组”)。 安全:密码加密保存。 多协议:支持svn协议和http协议(从2.0开始支持Apache服务器单库方式,从3.0开始支持Apache多库方式) Svnadmin在Java 1.6、Tomcat 6、Subversion 1.6、MySQL 5.1、Apache 2.2、Windows 7上开发测试通过,同时支持其他操作系统和数据库。 支持svn协议 Svn的配置信息都在仓库目录的conf下的authz,passwd,svnserve.conf三个文件中,配置用户和权限都是通过修改passwd和authz,立刻就生效。Svn Admin的本质是对这3个文件进行管理,所有成员、权限的数据都保存在数据库中,一旦在Svn Admin的页面上修改,就会把配置信息输出到conf下的那3个配置文件中。 支持http协议 Apache+SVN配置成功后可以有两种方式: 多库方式:SVNParentPath 指定一个父目录,所有仓库在这个父目录下,使用一个密码文件和一个权限配置文件。优点是增加删除仓库不需要改apache的httpd.conf,不需要重启Apache。缺点是项目多会很混乱。 单库方式:SVNPath 每个仓库单独配置各自的密码和权限文件。优点是各自分开,互相不影响,维护方便。缺点是增加或删除仓库需要修改apache的httpd.conf后重启。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WenCoo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值