显示标签为“ViewVC”的博文。显示所有博文
显示标签为“ViewVC”的博文。显示所有博文

2007年9月10日星期一

viewvc 搭建笔记

viewvc 搭建笔记

Digg It!

关于svn+apache2的工作环境搭建,请参考我的这一篇笔记

这是一篇关于如何搭建viewvc来更好的通过WEB展示svn记录的笔记。

工作环境为RedHat EL4
Linux MYSERVER 2.6.9-5.ELsmp #1 SMP Wed Jan 5 19:30:39 EST 2005 i686 i686 i386 GNU/Linux

首先,viewvc同时支持CVS和Subversion,最开始是为CVS系统开发,不过由于SVN系统近来的流行,viewvc也开始支持svn系统了。

  1. 下载viewvc
    $ wget http://www.viewvc.org/nightly/viewvc-1.1-dev-20070307.tar.gz
  2. 安装viewvc
    $ ./viewvc-install
  3. 编辑viewvc配置文件 viewvc.conf
    root_parents = /home/userhome/zztest/svn_dir : svn,
    # /home/cvs-repositories : cvs

    同时你还可以在 viewvc.conf 中找到一些可选配置,比如PHP语法高亮等。
  4. 在 Apache 的配置文件中加入viewvc配置,并加如HTTP验证功能
    Alias /webview /usr/local/viewvc-dev/bin/cgi/

    DirectoryIndex viewvc.cgi
    AddHandler cgi-script .cgi
    Options ExecCGI FollowSymLinks
    AllowOverride None
    AuthType Basic
    AuthName “Subversion repository”
    AuthUserFile /usr/local/viewvc-dev/viewpasswd.conf
    Require valid-user
    Order allow,deny
    Allow from all
  5. 这时当你在WEB上查看viewvc时,可能会提示
    viewvcImportError: No module named svn
    这是因为 python 还需要一个和你工作的svn系统进行联系的库,在svn的原代码中可以进行编译并安装,前提是需要安装 swig 这个软件包,还好在大多数LINUX发行版中,这个软件包是默认安装的。你可以用
    $ which swig
    这样的命令来验证,如果没有安装上,可以安装一个。接下来你可能需要通过这样的方式,重新编译并安装svn软件包
    $ ./configure --prefix=/usr/local --with-apxs=/usr/local/bin/apxs --with-berkeley-db=/usr/local PYTHON=/usr/local/bin/python2.5
    $ make
    $ sudo make install
    $ make swig-py
    $ make install-swig-py

    现在你的viewvc就可以正常工作了。
  6. 最后你还可以对viewvc安装目录中的 python 代码和 template 模板文件进行修改来满足你的需求。

UPDATE 在编译的时候,使用VIEWVC可能会遇到象这里描述的问题,不过这里有解决方法,可以参考 ;)

Indexing repository
Failed to initialize environment. /usr/local/lib/libsvn_ra_dav-1.so.0: undefined symbol: gss_delete_sec_context
Traceback (most recent call last):
File “/usr/lib/python2.3/site-packages/trac/scripts/admin.py”, line 616, in do_initenv
repos = self.__env.get_repository()
File “/usr/lib/python2.3/site-packages/trac/env.py”, line 155, in get_repository
from trac.versioncontrol.svn_fs import SubversionRepository
File “/usr/lib/python2.3/site-packages/trac/versioncontrol/svn_fs.py”, line 25, in ?
from svn import fs, repos, core, delta
File “/usr/local/lib/svn-python/svn/fs.py”, line 19, in ?
from libsvn.fs import *
File “/usr/local/lib/svn-python/libsvn/fs.py”, line 5, in ?
import _fs
ImportError: /usr/local/lib/libsvn_ra_dav-1.so.0: undefined symbol: gss_delete_sec_context

把 Makefile 的

SVN_APR_LIBS = /home/rob/build/subversion-1.4.2/apr/libapr-0.la -lrt -lm -lcrypt -lnsl -lpthread -ldl

改为

SVN_APR_LIBS = /home/rob/build/subversion-1.4.2/apr/libapr-0.la -lrt -lm -lcrypt -lnsl -lpthread -ldl -L/usr/kerberos/lib -lgssapi_krb5 -lkrb5 -lk5crypto

del.icio.us Digg Furl Reddit BlinkList Google Windows Live Yahoo!

让 viewvc 默认显示GB2312编码

让 viewvc 默认显示GB2312编码

Digg It!

默认的viewvc是显示UTF-8,不过可能有时候你的项目都是GB2312编码或者BIG5编码,需要改变其实很简单,在viewvc的安装目录下找到这个文件
$VIEWVC_INSTALL_DIR/lib/sapi.py
只需要把 UTF-8 替换成 GB2312 就可以了,一共有三个地方,使用 vim 来就是这样
:%s/=UTF-8/=GB2312/g
或者你可以改一下原代码,更为方便的改动和使用。

注意我使用的 viewvc 版本为dev-1.1,关于viewvc的安装请参考这篇日志,关于svn的安装请参考这篇日志

一个生产环境使用的apache module– viewvc权限控制)

一个生产环境使用的apache module– viewvc权限控制)

from: [url]http://www.loveopensource.com/?p=19[/url]

Apache模块开发/用C语言扩展apache(4:一个生产环境使用的apache module– viewvc权限控制)
by linux_prog
下面公布一个目前在我们公司使用的apache module的源代码。
我们公司开发人员很多,使用了SVN和viewvc来进行版本控制和查看,通过web界面,SVN能够根据每个用户的权限来控制能够
浏览某个项目下的代码,但是viewvc只要你在SVN中有用户,你就可以看当前SVN中所有项目的代码。这个风险比较大,因此,我们
开发了一个apache module,用来读取SVN的权限配置文件,把相应的权限集成到VIEWVC中。
源代码:
#include “apr_strings.h”
#include “apr_hash.h”
#include “apr_tables.h”
#include “apr_md5.h” /* for apr_password_validate */
#include “apr_lib.h” /* for apr_isspace */
#include “apr_base64.h” /* for apr_base64_decode et al */
#define APR_WANT_STRFUNC /* for strcasecmp */
#include “apr_want.h”

#include “ap_config.h”
#include “httpd.h”
#include “http_config.h”
#include “http_core.h”
#include “http_log.h”
#include “http_protocol.h”
#include “http_request.h”
#include “ap_provider.h”
#include

#define ENABLED 1
#define DISABLED 0

/* data need by our module */
typedef struct
{
short enabled;
short debug;
char *dir;
// the starting path
char *prefixPath;
// the stop url pattern
char *stopPattern;
// the svn access file
char *accessFile;
} authSVN_rec;

struct access_rec
{
// 0: group
// 1: user
// 2: all
short type;
// the group or user name
char *name;
// 0: don’t have read access
// 1: have read access
short access;
// the next access record
struct access_rec *next;
};
module AP_MODULE_DECLARE_DATA authSVN_module;

// src starts with start
static short start_with(const char *src, const char *start)
{
int i = 0;
if(strlen(src) < strlen(start))
return 0;

i = strlen(start) - 1;

while(i >= 0)
{
if(src[i] != start[i])
return 0;
i–;
}

return 1;
}

// parse the SVN access file
static short parse_access_file(request_rec *r, const char* file,
const authSVN_rec *conf,
apr_hash_t* ugMap,
apr_hash_t* accessMap)
{
ap_configfile_t *f = NULL;
apr_status_t status;
char l[MAX_STRING_LEN], dir[256];
status = ap_pcfg_openfile(&f, r->pool, file);
short flag = 0;

if (status != APR_SUCCESS)
return 0;

while(!(ap_cfg_getline(l, MAX_STRING_LEN, f)))
{
const char *w = NULL;
char *last = NULL;
apr_table_t *apt = NULL;
struct access_rec *arec = NULL, *arecp = NULL;

if ((l[0] == ‘#’) || (!l[0])) {
continue;
}

if(start_with(l, “[groups]”)) {
flag = 1;
continue;
}

if(l[0] == ‘[’) {
flag = 2;
w = apr_strtok(l, “[]:\n”, &last);

if(w && w[0] == ‘/’) {
// the root directory
snprintf(dir, sizeof(dir), “%s”, conf->prefixPath);
dir[strlen(dir) - 1] = ‘\0′;
}
else if(w && w[0] != ‘/’)
{
const char *project = w;
w = apr_strtok(NULL, “[]:\n”, &last);
if(w)
{
snprintf(dir, sizeof(dir), “%s%s%s”, conf->prefixPath, project, w);
// make sure the dir is not end with /
int len = strlen(dir);
if(dir[len - 1] == ‘/’) dir[len - 1] = ‘\0′;
}
else
dir[0] = ‘\0′;
}
else
{
dir[0] = ‘\0′;
}

continue;
}

if(flag == 1) {
// this is the groups and users definition
w = apr_strtok(l, “=, \n”, &last);
if(w == NULL)
// group name not found
continue;

apt = (apr_table_t *)apr_hash_get(ugMap, (const void *)w, APR_HASH_KEY_STRING);
if(apt == NULL) {
apt = apr_table_make(r->pool, 10);
apr_hash_set(ugMap, (const void *)apr_pstrdup(r->pool, w),
APR_HASH_KEY_STRING, (const void *)apt);
}

while((w = apr_strtok(NULL, “=, \n”, &last)) != NULL) {
// this is group name or user name
if(w[0] == ‘@’) {
w++;
if(w) {
apr_table_setn(apt, apr_pstrdup(r->pool, w), “0″);
}
}
else
{
// this is user name
apr_table_setn(apt, apr_pstrdup(r->pool, w), “1″);
}
}

}

if(flag == 2) {
if(dir[0] == ‘\0′) continue;
w = apr_strtok(l, “= \n”, &last);
if(w)
{
arec = (struct access_rec *)apr_pcalloc(r->pool, sizeof(struct access_rec));
arec->access = 0;

if(w[0] == ‘@’) {
w++;
if(w) {
arec->name = apr_pstrdup(r->pool, w);
arec->type = 0;
}
else continue;
}
else if(w[0] == ‘*’)
{
arec->name = apr_pstrdup(r->pool, “*”);
// this is all
arec->type = 2;
}
else
{
arec->name = apr_pstrdup(r->pool, w);
// this is user name
arec->type = 1;
}
}
else continue;

w = apr_strtok(NULL, “= \n”, &last);
if(!w)
{
arec->access = 0;
}
else
{
arec->access = 1;
}

arecp = (struct access_rec *)apr_hash_get(accessMap, (const void *)dir, APR_HASH_KEY_STRING);
if(arecp == NULL) {
arec->next = NULL;
apr_hash_set(accessMap, (const void *)apr_pstrdup(r->pool, dir),
APR_HASH_KEY_STRING, (const void *)arec);
}
else
{
while(arecp->next != NULL) arecp = arecp->next;
arecp->next = arec;
}

}

}

ap_cfg_closefile(f);

return 1;
}
/* init per dir */
static void *create_authSVN_dir_config(apr_pool_t *p, char *d)
{
authSVN_rec *conf = (authSVN_rec *)apr_pcalloc(p, sizeof(*conf));
if(conf == NULL) return NULL;

conf->enabled = DISABLED;
conf->debug = DISABLED;
conf->dir = d;
conf->prefixPath = NULL;
conf->stopPattern = NULL;
conf->accessFile = NULL;

return conf;
}

/* hex to int */
static char hex2int(char c)
{
if( c>=’0′ && c<='9' ) return (c - '0');
return (c - 'A' + 10);
}

/* url decode */
static void url_decode(char *url)
{
char *p = url;
char *p1 = NULL;

while(*p)
{
if(*p == '+') *p = ' ';

/* %AB */
if(*p=='%' && *(p+1) && *(p+2))
{
*p = hex2int(toupper(*(p+1))) * 16 + hex2int(toupper(*(p+ 2)));
strcpy(p + 1, p + 3);
}
/* \xAB */
if(*p=='\\' && *(p+1) && *(p+2) && *(p+3))
{
p1 = p + 1;
if(*p1 && *p1=='x')
{
*p = hex2int(toupper(*(p+2))) * 16 + hex2int(toupper(*(p+3)));
strcpy(p+1, p+4);
}
}
p++;
}

return;
}

static void parent_path(char *url)
{
char *p = url + strlen(url) - 1;

while(p != url && *p != '/') { *p = '\0'; p--; }
if(p != url && *p=='/') *p = '\0';

return;
}

// return
// 0: the user don't belong to this group
// 1: the user belong to this group
static short find_user_in_group(const char* user, const char *group, apr_hash_t* ugMap)
{
apr_table_t *apt = (apr_table_t *)apr_hash_get(ugMap,
(const void *)group,
APR_HASH_KEY_STRING);
if(apt == NULL) return 0;
apr_array_header_t *arr;
apr_table_entry_t *elts;
int i;
arr = (apr_array_header_t *)apr_table_elts(apt);
elts = (apr_table_entry_t *)arr->elts;

for(i=0; inelts; i++)
{
if(elts[i].key == NULL || elts[i].val == NULL) continue;

if(elts[i].val[0] == ‘1′ && strcmp(elts[i].key, user) == 0)
{
return 1;
}

if(elts[i].val[0] == ‘0′)
{
if(find_user_in_group(user, elts[i].key, ugMap))
return 1;
}
}

return 0;
}

// return
// 0:don’t have access
// 1:have read access
// 2:access not found
static short find_access(const char* user, const char* url,
apr_hash_t* ugMap, apr_hash_t* accessMap)
{
struct access_rec *arec= (struct access_rec *)apr_hash_get(accessMap,
(const void *)url, APR_HASH_KEY_STRING);

short access = 2;

while(arec != NULL)
{
if(strcmp(arec->name, “*”) == 0)
{
// specified access to all users and groups on this url
access = arec->access;
}

if(arec->type == 1 && strcmp(arec->name, user) == 0)
{
// specified user access on this url
access = arec->access;
}

if(arec->type == 0)
{
// this is group access
if(find_user_in_group(user, arec->name, ugMap))
access = arec->access;
}

// if this user have access, we return
if(access == 1) return access;

arec = arec->next;
}

return access;
}
static short estimate_access( request_rec *r, const authSVN_rec* conf,
char* url, apr_hash_t* ugMap,
apr_hash_t* accessMap )
{
const char* user = r->user;
// unauthorized
if(!user || !user[0]) return 0;

short access = find_access(user, url, ugMap, accessMap);
if(access < 2) return access;

if(url[0] == '/' && url[1] == '\0') return 0;

parent_path(url);

return estimate_access(r, conf, url, ugMap, accessMap);
}

// do regexp matching
static short regexp_match(char *str, char *pattern)
{
regex_t reg;
regmatch_t pm[1];
const size_t nmatch = 1;
int res = 0;
short r = 0;
char ebuf[MAX_STRING_LEN];

res = regcomp(®, pattern, REG_EXTENDED);

if(res != 0)
{
regfree(®);
return 0;
}

res = regexec(®, str, nmatch, pm, 0);

if(res == REG_NOMATCH)
r = 0;
else
r = 1;

regfree(®);

return r;
}

/* all pages need to pass from this handler */
static int authSVN_handler(request_rec *r)
{
authSVN_rec *conf = ap_get_module_config(r->per_dir_config,
&authSVN_module);
if(!conf || !conf->enabled)
return DECLINED;

if(conf->prefixPath == NULL || !start_with(r->uri, conf->prefixPath))
return DECLINED;

if(conf->stopPattern !=NULL && regexp_match(r->uri, conf->stopPattern))
return DECLINED;

apr_hash_t* ugMap = apr_hash_make(r->pool);
apr_hash_t* accessMap = apr_hash_make(r->pool);

if(!parse_access_file(r, conf->accessFile, conf, ugMap, accessMap))
return 403;

if(conf->debug)
{
// run in debug mode
// print all users/groups and access information
apr_hash_index_t* hi;
char *key;
apr_table_t *val;
struct access_rec *arec;
apr_array_header_t *arr;
apr_table_entry_t *elts;
int i;

r->content_type=”text/plain”;
ap_rprintf(r, “Parsed Users and Groups:\n”);

hi = apr_hash_first(r->pool, ugMap);
while(hi != NULL)
{
apr_hash_this(hi, (void *)&key, NULL, (void *)&val);

ap_rprintf(r, “%s: “, key);
arr = (apr_array_header_t *)apr_table_elts(val);
elts = (apr_table_entry_t *)arr->elts;
for(i=0; inelts; i++)
{
if(elts[i].key == NULL || elts[i].val == NULL) continue;
if(elts[i].val[0] == ‘0′)
{
ap_rprintf(r, “@”);
}
ap_rprintf(r, “%s “, elts[i].key);
}

ap_rprintf(r, “\n”);

hi = apr_hash_next(hi);
}

ap_rprintf(r, “Parsed Path Access:\n”);
hi = apr_hash_first(r->pool, accessMap);
while(hi != NULL)
{
apr_hash_this(hi, (void *)&key, NULL, (void *)&arec);
ap_rprintf(r, “%s:\n”, key);
while(arec != NULL)
{
if(arec->type == 0)
ap_rprintf(r, “group:%s “, arec->name);
else if(arec->type == 1)
ap_rprintf(r, “user:%s “, arec->name);
else
ap_rprintf(r, “all “);

ap_rprintf(r, “access:%d “, arec->access);
ap_rprintf(r, “\n”);
arec = arec->next;
}

ap_rprintf(r, “\n”);
hi = apr_hash_next(hi);
}
}

char *url = apr_pstrdup(r->pool, r->uri);
// decode the url for some chinese characters
url_decode(url);

// analyze the access
if(estimate_access(r, conf, url, ugMap, accessMap))
{
if(conf->debug)
{
ap_rprintf(r, “%s have access on:%s\n”, r->user, r->uri);
return OK;
}
return DECLINED;
}
else
{
if(conf->debug)
{
ap_rprintf(r, “%s don’t have access on:%s\n”, r->user, r->uri);
return OK;
}
}

return 403;
}

/* enable this module or not */
static const char *set_authSVN_enable(cmd_parms *cmd,
void *mconfig,
int arg)
{
authSVN_rec *conf = (authSVN_rec *) mconfig;
conf->enabled = arg;
return NULL;
}

/* debug this module or not */
static const char *set_authSVN_debug( cmd_parms *cmd,
void *mconfig,
int arg)
{
authSVN_rec *conf = (authSVN_rec *) mconfig;
conf->debug = arg;
return NULL;
}

/* setting prefix path */
static const char *set_prefix_path(cmd_parms *cmd,
void *mconfig,
const char *name)
{
authSVN_rec *conf = (authSVN_rec *) mconfig;

if(strlen(name) <= 0)
return "AuthSVNPrefixPath can not be null.";

if(name[0] != '/' || name[strlen(name) - 1] != '/')
return "AuthSVNPrefixPath must start and end with '/'.";

conf->prefixPath = apr_pstrdup(cmd->pool, name);

return NULL;
}

/* setting stop url pattern */
static const char *set_stop_pattern(cmd_parms *cmd,
void *mconfig,
const char *name)
{
authSVN_rec *conf = (authSVN_rec *) mconfig;

if(strlen(name) <= 0)
return "AuthSVNStopPattern can not be null.";

conf->stopPattern = apr_pstrdup(cmd->pool, name);

return NULL;
}

/* setting SVN access file */
static const char *set_authSVN_accessFile(cmd_parms *cmd,
void *mconfig,
const char *name)
{
authSVN_rec *conf = (authSVN_rec *) mconfig;
ap_configfile_t *f = NULL;
apr_status_t status;

if(strlen(name) <= 0)
return "SVNAccessFile can not be null.";

status = ap_pcfg_openfile(&f, cmd->pool, name);

if (status != APR_SUCCESS) {
return “Can not open given SVN access file.”;
}
ap_cfg_closefile(f);

conf->accessFile = apr_pstrdup(cmd->pool, name);

return NULL;
}
static const command_rec auth_cmds[] =
{
AP_INIT_FLAG(”EnableAuthSVN”, set_authSVN_enable, NULL, OR_FILEINFO,
“enable authSVN or not.”),
AP_INIT_FLAG(”DebugAuthSVN”, set_authSVN_debug, NULL, OR_FILEINFO,
“debug authSVN or not.”),
AP_INIT_TAKE1(”AuthSVNPrefixPath”, set_prefix_path, NULL, OR_FILEINFO,
“set prefix path.”),
AP_INIT_TAKE1(”AuthSVNStopPattern”, set_stop_pattern, NULL, OR_FILEINFO,
“the url pattern we do not do the access checking.”),
AP_INIT_TAKE1(”SVNAccessFile”, set_authSVN_accessFile, NULL, OR_FILEINFO,
“set SVN access file.”),
{ NULL }
};

static void register_hooks(apr_pool_t *p)
{
ap_hook_handler(authSVN_handler, NULL, NULL, APR_HOOK_FIRST);
}

module AP_MODULE_DECLARE_DATA authSVN_module =
{
STANDARD20_MODULE_STUFF,
create_authSVN_dir_config, /* dir config creater */
NULL, /* dir merger — default is to override */
NULL, /* server config */
NULL, /* merge server config */
auth_cmds, /* command apr_table_t */
register_hooks /* register hooks */
};

安装方法:
install the module:
./bin/apachectl stop
./bin/apxs -c mod_authSVN.c
./bin/apxs -a -i -n authSVN mod_authSVN.la
./bin/apachectl start

add the following to httpd.conf(insite the viewvc location directory):
EnableAuthSVN on
DebugAuthSVN off
SVNAccessFile /usr/local/apache2.2.4/access
AuthSVNPrefixPath /viewvc/
AuthSVNStopPattern ^\/viewvc\/\*docroot\*\/

2007年9月9日星期日

ViewVC 集成 Subversion, 要说爱你不容易!!! [原创]

ViewVC 集成 Subversion, 要说爱你不容易!!! [原创]
2006年10月27日 星期五 22:35

先谈感受,一个字:“累”。俺从来没见过在WINDOWS平台搭建环境有这么累的,够烦,够专业,TMD真是弄死人。不过终于在21点51分完成在自己电脑上的“超级建设”,成就感没得说的爽。

(此文谨献给还在奋斗中的战友们…… 还有,向Shirley致敬!!)

开始傻瓜式的全图片教程。

本人环境介绍:WindowsXp Sp2 中文版,Subversion 1.4.0,Python 2.3.5

第一步:安装IIS

第二步:下载所有需要的组件。Subversion,ViewVC,Python,和SVN-Python集成包。

第三步:安装Subversion。为了便于记忆和稍后的命令行操作,我把它装在了C盘。

第四步:(重要)设置Subversion的命令行编辑器,请看图片,如此设置即可。

第五步:(重要)为Subversion添加日志储存点。如图,在命令行中输入这个命令。

第六步:为Subversion设置用户和权限,请注意看图,路径和文件名。(重要

第七步:(重要)开启Subversion的服务。如图输入命令,解除阻止就可以了。
第八步:(非常重要)在不关闭刚才的命令行的情况下,输入这个命令,以检验Subversion的服务启用是否正常,如果这里显示Failed,那么恭喜你,从头来过,俺真不知道那里出错了,反正弄干净就对了。(从这一步开始,我真TMD感觉这个配置起来不但麻烦,还要看造化。)
第九步:如图,把这个外挂程序复制到Subversion的主程序目录。(非非非常重要
第十步:正式启动Subversion的系统服务。(先输入命令,再到“服务”中去启动)如图:
第十一步:安装Python 2.3.5
第十二步:安装SVN-Python集成包。注意:这里会自动识别路径,如果没有被正确识别,再次恭喜你,又要重来了。俺就有过一次,原因不明。。这个臭东西真古怪!~~
第十三步:设置环境变量AGAIN,这次把Python的安装路径设置为系统路径。~(你看它“作”不,什么东西都要你亲自打点~~)
第十四步:(决定胜负的一步)到VIEWVC的安装包目录,进入命令行,输入安装指令。根据英语提示,把VIEWVC安装完毕。如果这里出现“包”错误,或IMPORT ERROR,那么意味着你机器的JAVA环境绝对的有问题。问题如此深奥,自己解决,放过我吧。。
第十五步:(最后的攻坚战)如图,建立你的虚拟目录和IIS配置。WIN2000和XP的用户参照我的教程,2003的会不一样,请不要参照。
最后,验证一下。这里如果出现失败,请把失败提示或症状告诉我,因为按照上面的顺序,我的确安装成功了。不过我也知道,这么复杂的东西,妖怪还是很喜欢钻进去的。
这里能浏览,就表示成功了。这次真的恭喜你~~
PS:如果这里显示包错误或IMPORT ERROR,请看下图,手动更改类库的地址指向,可以尝试的。
因为我在Subversion中没有添加过什么仓库,所以打开VIEWVC看不见什么实质性的东西。
最后再感叹一下,这个东西真********
牛人玩这个,说实话,我真不喜欢。。