removed the top-level directory

This commit is contained in:
2022-10-17 23:31:51 +09:00
parent 1d4761c569
commit 64f2849676
766 changed files with 0 additions and 0 deletions

14
etc/Makefile.am Normal file
View File

@ -0,0 +1,14 @@
cfgdir=$(CFGDIR)
cfg_DATA = codepot.ini codepot.mysql codepot.pgsql codepot.sqlite codepot.a2ldap codepot.httpd
cfg_SCRIPTS = start-commit pre-commit post-commit pre-revprop-change post-revprop-change cloc.pl
perldir=$(CFGDIR)/perl/Codepot
perl_SCRIPTS=perl/Codepot/AccessHandler.pm perl/Codepot/AuthenHandler.pm
EXTRA_DIST = $(cfg_DATA) $(cfg_SCRIPTS) $(perl_SCRIPTS)
install-data-hook:
perl -e 'use Digest::SHA1' 2>/dev/null || \
perl -e 'use Digest::SHA' 2>/dev/null && \
sed -ri -e 's|Digest::SHA1|Digest::SHA|g' "$(DESTDIR)$(CFGDIR)/perl/Codepot/AccessHandler.pm"

580
etc/Makefile.in Normal file
View File

@ -0,0 +1,580 @@
# Makefile.in generated by automake 1.16.5 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2021 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__is_gnu_make = { \
if test -z '$(MAKELEVEL)'; then \
false; \
elif test -n '$(MAKE_HOST)'; then \
true; \
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
true; \
else \
false; \
fi; \
}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
*) echo "am__make_running_with_option: internal error: invalid" \
"target option '$${target_option-}' specified" >&2; \
exit 1;; \
esac; \
has_opt=no; \
sane_makeflags=$$MAKEFLAGS; \
if $(am__is_gnu_make); then \
sane_makeflags=$$MFLAGS; \
else \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
bs=\\; \
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
esac; \
fi; \
skip_next=no; \
strip_trailopt () \
{ \
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
}; \
for flg in $$sane_makeflags; do \
test $$skip_next = yes && { skip_next=no; continue; }; \
case $$flg in \
*=*|--*) continue;; \
-*I) strip_trailopt 'I'; skip_next=yes;; \
-*I?*) strip_trailopt 'I';; \
-*O) strip_trailopt 'O'; skip_next=yes;; \
-*O?*) strip_trailopt 'O';; \
-*l) strip_trailopt 'l'; skip_next=yes;; \
-*l?*) strip_trailopt 'l';; \
-[dEDm]) skip_next=yes;; \
-[JT]) skip_next=yes;; \
esac; \
case $$flg in \
*$$target_option*) has_opt=yes; break;; \
esac; \
done; \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
subdir = etc
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/ac/m4/as-ac-expand.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/./config.h
CONFIG_CLEAN_FILES = codepot.ini codepot.a2ldap codepot.httpd \
pre-commit start-commit post-commit pre-revprop-change \
post-revprop-change
CONFIG_CLEAN_VPATH_FILES =
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
am__install_max = 40
am__nobase_strip_setup = \
srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
am__nobase_strip = \
for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
am__nobase_list = $(am__nobase_strip_setup); \
for p in $$list; do echo "$$p $$p"; done | \
sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
$(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
if (++n[$$2] == $(am__install_max)) \
{ print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
END { for (dir in files) print dir, files[dir] }'
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
am__uninstall_files_from_dir = { \
test -z "$$files" \
|| { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
|| { echo " ( cd '$$dir' && rm -f" $$files ")"; \
$(am__cd) "$$dir" && rm -f $$files; }; \
}
am__installdirs = "$(DESTDIR)$(cfgdir)" "$(DESTDIR)$(perldir)" \
"$(DESTDIR)$(cfgdir)"
SCRIPTS = $(cfg_SCRIPTS) $(perl_SCRIPTS)
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
SOURCES =
DIST_SOURCES =
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
DATA = $(cfg_DATA)
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/codepot.a2ldap.in \
$(srcdir)/codepot.httpd.in $(srcdir)/codepot.ini.in \
$(srcdir)/post-commit.in $(srcdir)/post-revprop-change.in \
$(srcdir)/pre-commit.in $(srcdir)/pre-revprop-change.in \
$(srcdir)/start-commit.in
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
BINDIR = @BINDIR@
CACHEDIR = @CACHEDIR@
CFGDIR = @CFGDIR@
CSCOPE = @CSCOPE@
CTAGS = @CTAGS@
CYGPATH_W = @CYGPATH_W@
DATADIR = @DATADIR@
DATAROOTDIR = @DATAROOTDIR@
DEFS = @DEFS@
DEPOTDIR = @DEPOTDIR@
DOCDIR = @DOCDIR@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
ETAGS = @ETAGS@
GREP = @GREP@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LD = @LD@
LIBDIR = @LIBDIR@
LIBEXECDIR = @LIBEXECDIR@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LOCALSTATEDIR = @LOCALSTATEDIR@
LOGDIR = @LOGDIR@
LTLIBOBJS = @LTLIBOBJS@
MAKEINFO = @MAKEINFO@
MKDIR_P = @MKDIR_P@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
PHPEXTDIR = @PHPEXTDIR@
PHPEXTINIDIR = @PHPEXTINIDIR@
PREFIX = @PREFIX@
RM = @RM@
RMDIR = @RMDIR@
SBINDIR = @SBINDIR@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
SYSCONFDIR = @SYSCONFDIR@
VERSION = @VERSION@
WWWDIR = @WWWDIR@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
am__leading_dot = @am__leading_dot@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build_alias = @build_alias@
builddir = @builddir@
cachedir = @cachedir@
cfgdir = $(CFGDIR)
datadir = @datadir@
datarootdir = @datarootdir@
depotdir = @depotdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host_alias = @host_alias@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
logdir = @logdir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
phpextdir = @phpextdir@
phpextinidir = @phpextinidir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
runstatedir = @runstatedir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
wwwdir = @wwwdir@
cfg_DATA = codepot.ini codepot.mysql codepot.pgsql codepot.sqlite codepot.a2ldap codepot.httpd
cfg_SCRIPTS = start-commit pre-commit post-commit pre-revprop-change post-revprop-change cloc.pl
perldir = $(CFGDIR)/perl/Codepot
perl_SCRIPTS = perl/Codepot/AccessHandler.pm perl/Codepot/AuthenHandler.pm
EXTRA_DIST = $(cfg_DATA) $(cfg_SCRIPTS) $(perl_SCRIPTS)
all: all-am
.SUFFIXES:
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign etc/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign etc/Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
codepot.ini: $(top_builddir)/config.status $(srcdir)/codepot.ini.in
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
codepot.a2ldap: $(top_builddir)/config.status $(srcdir)/codepot.a2ldap.in
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
codepot.httpd: $(top_builddir)/config.status $(srcdir)/codepot.httpd.in
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
pre-commit: $(top_builddir)/config.status $(srcdir)/pre-commit.in
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
start-commit: $(top_builddir)/config.status $(srcdir)/start-commit.in
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
post-commit: $(top_builddir)/config.status $(srcdir)/post-commit.in
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
pre-revprop-change: $(top_builddir)/config.status $(srcdir)/pre-revprop-change.in
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
post-revprop-change: $(top_builddir)/config.status $(srcdir)/post-revprop-change.in
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
install-cfgSCRIPTS: $(cfg_SCRIPTS)
@$(NORMAL_INSTALL)
@list='$(cfg_SCRIPTS)'; test -n "$(cfgdir)" || list=; \
if test -n "$$list"; then \
echo " $(MKDIR_P) '$(DESTDIR)$(cfgdir)'"; \
$(MKDIR_P) "$(DESTDIR)$(cfgdir)" || exit 1; \
fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
done | \
sed -e 'p;s,.*/,,;n' \
-e 'h;s|.*|.|' \
-e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
{ d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
if ($$2 == $$4) { files[d] = files[d] " " $$1; \
if (++n[d] == $(am__install_max)) { \
print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
else { print "f", d "/" $$4, $$1 } } \
END { for (d in files) print "f", d, files[d] }' | \
while read type dir files; do \
if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
test -z "$$files" || { \
echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(cfgdir)$$dir'"; \
$(INSTALL_SCRIPT) $$files "$(DESTDIR)$(cfgdir)$$dir" || exit $$?; \
} \
; done
uninstall-cfgSCRIPTS:
@$(NORMAL_UNINSTALL)
@list='$(cfg_SCRIPTS)'; test -n "$(cfgdir)" || exit 0; \
files=`for p in $$list; do echo "$$p"; done | \
sed -e 's,.*/,,;$(transform)'`; \
dir='$(DESTDIR)$(cfgdir)'; $(am__uninstall_files_from_dir)
install-perlSCRIPTS: $(perl_SCRIPTS)
@$(NORMAL_INSTALL)
@list='$(perl_SCRIPTS)'; test -n "$(perldir)" || list=; \
if test -n "$$list"; then \
echo " $(MKDIR_P) '$(DESTDIR)$(perldir)'"; \
$(MKDIR_P) "$(DESTDIR)$(perldir)" || exit 1; \
fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
done | \
sed -e 'p;s,.*/,,;n' \
-e 'h;s|.*|.|' \
-e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
{ d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
if ($$2 == $$4) { files[d] = files[d] " " $$1; \
if (++n[d] == $(am__install_max)) { \
print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
else { print "f", d "/" $$4, $$1 } } \
END { for (d in files) print "f", d, files[d] }' | \
while read type dir files; do \
if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
test -z "$$files" || { \
echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(perldir)$$dir'"; \
$(INSTALL_SCRIPT) $$files "$(DESTDIR)$(perldir)$$dir" || exit $$?; \
} \
; done
uninstall-perlSCRIPTS:
@$(NORMAL_UNINSTALL)
@list='$(perl_SCRIPTS)'; test -n "$(perldir)" || exit 0; \
files=`for p in $$list; do echo "$$p"; done | \
sed -e 's,.*/,,;$(transform)'`; \
dir='$(DESTDIR)$(perldir)'; $(am__uninstall_files_from_dir)
install-cfgDATA: $(cfg_DATA)
@$(NORMAL_INSTALL)
@list='$(cfg_DATA)'; test -n "$(cfgdir)" || list=; \
if test -n "$$list"; then \
echo " $(MKDIR_P) '$(DESTDIR)$(cfgdir)'"; \
$(MKDIR_P) "$(DESTDIR)$(cfgdir)" || exit 1; \
fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
done | $(am__base_list) | \
while read files; do \
echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(cfgdir)'"; \
$(INSTALL_DATA) $$files "$(DESTDIR)$(cfgdir)" || exit $$?; \
done
uninstall-cfgDATA:
@$(NORMAL_UNINSTALL)
@list='$(cfg_DATA)'; test -n "$(cfgdir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
dir='$(DESTDIR)$(cfgdir)'; $(am__uninstall_files_from_dir)
tags TAGS:
ctags CTAGS:
cscope cscopelist:
distdir: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) distdir-am
distdir-am: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
check-am: all-am
check: check-am
all-am: Makefile $(SCRIPTS) $(DATA)
installdirs:
for dir in "$(DESTDIR)$(cfgdir)" "$(DESTDIR)$(perldir)" "$(DESTDIR)$(cfgdir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
clean-am: clean-generic mostlyclean-am
distclean: distclean-am
-rm -f Makefile
distclean-am: clean-am distclean-generic
dvi: dvi-am
dvi-am:
html: html-am
html-am:
info: info-am
info-am:
install-data-am: install-cfgDATA install-cfgSCRIPTS \
install-perlSCRIPTS
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) install-data-hook
install-dvi: install-dvi-am
install-dvi-am:
install-exec-am:
install-html: install-html-am
install-html-am:
install-info: install-info-am
install-info-am:
install-man:
install-pdf: install-pdf-am
install-pdf-am:
install-ps: install-ps-am
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-generic
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am: uninstall-cfgDATA uninstall-cfgSCRIPTS \
uninstall-perlSCRIPTS
.MAKE: install-am install-data-am install-strip
.PHONY: all all-am check check-am clean clean-generic cscopelist-am \
ctags-am distclean distclean-generic distdir dvi dvi-am html \
html-am info info-am install install-am install-cfgDATA \
install-cfgSCRIPTS install-data install-data-am \
install-data-hook install-dvi install-dvi-am install-exec \
install-exec-am install-html install-html-am install-info \
install-info-am install-man install-pdf install-pdf-am \
install-perlSCRIPTS install-ps install-ps-am install-strip \
installcheck installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
pdf-am ps ps-am tags-am uninstall uninstall-am \
uninstall-cfgDATA uninstall-cfgSCRIPTS uninstall-perlSCRIPTS
.PRECIOUS: Makefile
install-data-hook:
perl -e 'use Digest::SHA1' 2>/dev/null || \
perl -e 'use Digest::SHA' 2>/dev/null && \
sed -ri -e 's|Digest::SHA1|Digest::SHA|g' "$(DESTDIR)$(CFGDIR)/perl/Codepot/AccessHandler.pm"
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

17089
etc/cloc.pl Executable file

File diff suppressed because it is too large Load Diff

80
etc/codepot.a2ldap.in Normal file
View File

@ -0,0 +1,80 @@
#
# This file contains sample configuration to enable LDAP authentication
# for subversion webdav access. The actual configuration can be more
# complex than this file depending on your requirement.
#
<Location "/svn">
DAV svn
SVNParentPath "@DEPOTDIR@/svnrepo"
#SVNListParentPath on
Order deny,allow
#Deny from all
#Satisfy any
# Uncomment SSLRequireSSL below to disallow non-SSL access.
# Note that SSL must be enabled site-wide to use it.
# SSLRequireSSL
AuthType Basic
AuthName "@PACKAGE@"
#
# configure authentication below
#
#
# Apache version 2.1 or later
# mod_authnz_ldap
#
AuthBasicProvider ldap
# prevent other authenticatication moudles from authenticating users
# if this one fails. Set it to 'off' for 'require valid-user' as it
# is handled by mod_authz_user.
AuthzLDAPAuthoritative on
AuthLDAPGroupAttribute memberUid
AuthLDAPGroupAttributeIsDN off
AuthLDAPRemoteUserIsDN off
# ldap binding information
AuthLDAPURL ldap://127.0.0.1:389/ou=users,dc=sample,dc=net?uid
AuthLDAPBindDN cn=admin,dc=sample,dc=net
AuthLDAPBindPassword xxxxxxx
#Require ldap-group cn=users,ou=groups,dc=sample,dc=net
# Enable find-grained access control using the access file.
# The specified file must be located under the repository/conf subdirectory.
# AuthzSVNRespsRelativeAccessFile requried subversion 1.7 or later.
# If you're using a older version, there are no automatic repostory
# protection according to the project type (public/private)
# You may have to use AuthzSVNAccessFile for manual control globally
# in such a case.
# AuthzSVNReposRelativeAccessFile access.conf
# Satisfy All
# allow anynymous/guest for viewing and checking out
<Limit GET HEAD OPTIONS REPORT PROPFIND>
# Use 'Allow from all' to allow anonymous access.
#Allow from all
# 'Required valid-user' is more strict in that it requires a valid
# user name and password. You may create a guest account to supplement
# anonymous access.
Require valid-user
</Limit>
# require authentication for other operations
<LimitExcept GET HEAD OPTIONS REPORT PROPFIND>
Require ldap-group cn=coders,ou=groups,dc=sample,dc=net
</LimitExcept>
#
# Apache version 2.0.41
# mod_auth_ldap
#
</Location>

24
etc/codepot.httpd.in Normal file
View File

@ -0,0 +1,24 @@
#
# This file contains sample configuration to enable subversion
# webdav access. The actual configuration can be more complex
# than this file depending on your requirement.
#
<Location "/svn">
DAV svn
SVNParentPath "@DEPOTDIR@/svnrepo"
#SVNListParentPath on
PerlAccessHandler Codepot::AccessHandler
PerlAuthenHandler Codepot::AuthenHandler
PerlSetEnv CODEPOT_CONFIG_FILE /etc/codepot/codepot.ini
# Uncomment SSLRequireSSL below to disallow non-SSL access.
# Note that SSL must be enabled site-wide to use it.
# SSLRequireSSL
AuthType Basic
AuthName "@PACKAGE@"
require valid-user
</Location>

390
etc/codepot.ini.in Normal file
View File

@ -0,0 +1,390 @@
;------------------------------------------------------------------------------
; default site language
;------------------------------------------------------------------------------
default_site_language = "english"
;------------------------------------------------------------------------------
; default site name
;------------------------------------------------------------------------------
default_site_name = "@PACKAGE@"
;------------------------------------------------------------------------------
; database settings
;
; database_driver: mysql, mysqli, postgre, oci8, sqlite
;
; == PDO mysql ==
; database_hostname = "localhost"
; database_port = ""
; database_username = "codepot"
; database_password = "codepot"
; database_name = "codepot"
; database_driver = "mysql"
; database_use_pdo = "yes"
; database_prefix = ""
;
; == PDO sqlite ==
; database_hostname = "@DEPOTDIR@/codepot.db"
; database_port = ""
; database_username = ""
; database_password = ""
; database_name = ""
; database_driver = "sqlite"
; database_use_pdo = "yes"
; database_prefix = ""
;
; == MySQL ==
; database_hostname = "localhost"
; database_port = ""
; database_username = "codepot"
; database_password = "codepot"
; database_name = "codepot"
; database_driver = "mysql"
; database_use_pdo = "no"
; database_prefix = ""
;
; == PostgresSQL ==
; database_hostname = ""
; database_port = ""
; database_username = "codepot"
; database_password = "codepot"
; database_name = "codepot"
; database_driver = "postgre"
; database_use_pdo = "no"
; database_prefix = ""
;
; == Oracle
; database_hostname = "(DESCRIPTION=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(HOST=192.168.1.126)(PORT=1521))(CONNECT_DATA=(SID=ORCL)))"
; database_port = ""
; database_username = "scott"
; database_password = "tiger"
; database_name = ""
; database_driver = "oci8"
; database_use_pdo = "no"
; database_prefix = "cpot_"
;------------------------------------------------------------------------------
database_hostname = "localhost"
database_port = ""
database_username = ""
database_password = ""
database_name = ""
database_driver = ""
database_use_pdo = "no"
database_prefix = ""
database_store_gmt = "yes"
;------------------------------------------------------------------------------
; ldap settings
;------------------------------------------------------------------------------
; The userid format and the password format can contain
; ${userid} and ${password} to represent the actual user ID
; and the password respectively.
;
; If ldap_auth_mode is 1, authentication is performed by binding to
; a LDAP server specified using ldap_userid_format as a binddn and
; ldap_password_format as a password.
;
; If ldap_auth_mode is 2, it finds a binddn by searching a subtree
; under ldap_userid_search_base using ldap_userid_search_filter
; after having bound with ldap_admin_binddn and ldap_admin_password.
; The binddn found is used for subsequent binding for authentication.
; ldap_userid_format is unused in this mode.
;
; if you want to specify multiple attributes in ldap_insider_attribute_names
; separate them with a space.
;------------------------------------------------------------------------------
ldap_server_uri = "ldap://127.0.0.1:389"
ldap_server_protocol_version = "3"
ldap_auth_mode = 2
ldap_userid_format = "cn=${userid},ou=users,dc=codepot,dc=org"
ldap_password_format = "${password}"
ldap_admin_binddn = "cn=admin,dc=codepot,dc=org"
ldap_admin_password = "admin-password"
ldap_userid_search_filter = "(uid=${userid})"
ldap_userid_search_base = "ou=users,dc=codepot,dc=org"
ldap_mail_attribute_name = ""
ldap_insider_attribute_names = "mssfu30posixmemberof memberof"
ldap_insider_attribute_value = ""
;------------------------------------------------------------------------------
; default langage to use. set it to 'auto' to detect it automatically.
; available languages are:
; english, korean, indonesian
;------------------------------------------------------------------------------
language = "auto"
;------------------------------------------------------------------------------
; Name of the index page. If you want to hide the index page name from
; the URL by rewriting it (e.g. mod_rewrite), you have to change this
; properly to match the rewriting rule
;------------------------------------------------------------------------------
index_page = "index.php"
;------------------------------------------------------------------------------
; When set to yes, viewing pages requires a user to sign in.
;------------------------------------------------------------------------------
signin_compulsory = "no"
;------------------------------------------------------------------------------
; Code read access is limited to the specified user type. The types
; include anonymous, authenticated, authenticated-insider, member.
; This applies to a public project only. Write access to any projects
; and read access to a non-public project require membership regardless
; of this item.
;------------------------------------------------------------------------------
code_read_access = "anonymous"
;------------------------------------------------------------------------------
; File read access is limited to the specified user type. The types
; include anonymous, authenticated, authenticated-insider, member.
; This applies to a public project only. Write access to any projects
; and read access to a non-public project require membership regardless
; of this item.
;------------------------------------------------------------------------------
file_read_access = "anonymous"
;------------------------------------------------------------------------------
; When set to yes, non-http access is diverted to https using
; the base URL specified in https_base_url. api calls are not affected.
;------------------------------------------------------------------------------
https_compulsory = "no"
https_url = "https://${SERVER_NAME}${REQUEST_URI}"
;------------------------------------------------------------------------------
; Command-separated list of subversion base URLs
;------------------------------------------------------------------------------
; This URL depends on the webdav configuration for subversion repository.
; If you have your repository enabled with SSL under /svn, you can set this to
; https://${SERVER_NAME}/svn.
; You may specify multiple URLs separated with a comma as shown below:
; "http://${SERVER_NAME}/svn, https://${SERVER_NAME}/svn"
; All specified URLs are shown in the repository list of the project overview
; page.
; You may use various items available in $_SERVER when composing the URL.
; For example, ${HTTP_HOST} maps to $_SERVER{'HTTP_HOST'}.
; Additionally, $_SERVER{'REQUEST_PROTOCOL'} added by config/config.php
; can be referenced with ${REQUEST_PROTOCOL} and it is resolved to either
; http or https.
;------------------------------------------------------------------------------
;svn_base_url = "${REQUEST_PROTOCOL}://${SERVER_NAME}:${SERVER_PORT}/svn"
svn_base_url = "${REQUEST_PROTOCOL}://${HTTP_HOST}/svn"
;------------------------------------------------------------------------------
; Login model to use. LdapLoginModel or DbLoginModel.
;------------------------------------------------------------------------------
;login_model = "LdapLoginModel"
login_model = "DbLoginModel"
;------------------------------------------------------------------------------
; Comma separated list of system administrator IDs
;------------------------------------------------------------------------------
; sysadmin_userids = "tom, dick, jane"
;------------------------------------------------------------------------------
sysadmin_userids = ""
;------------------------------------------------------------------------------
; Maximum file upload size in Kbytes. You may have to adjust PHP settings
; for a large value to work.
;------------------------------------------------------------------------------
max_upload_size = "10000"
;------------------------------------------------------------------------------
; Maximum number of latest projects to show in the front page
;------------------------------------------------------------------------------
max_latest_projects = "10"
;------------------------------------------------------------------------------
; Maximum number of projects to show in the top projects graph (0 to hide)
;------------------------------------------------------------------------------
max_top_projects = "10"
;------------------------------------------------------------------------------
; Maximum number of committers to show in the top committers graph (0 to hide)
;------------------------------------------------------------------------------
max_top_committers = "10"
;------------------------------------------------------------------------------
; Maximum number of projects to show
;------------------------------------------------------------------------------
max_projects_per_page = "100"
;------------------------------------------------------------------------------
; Maximum number of issues to show
;------------------------------------------------------------------------------
max_issues_per_page = "50"
;------------------------------------------------------------------------------
; Maximum number of log entries to show per details page
;------------------------------------------------------------------------------
max_logs_per_page = "50"
;------------------------------------------------------------------------------
; Maximum number of log entries to show in the front page
;------------------------------------------------------------------------------
max_logs_in_site_home = "20"
;------------------------------------------------------------------------------
; Maximum number of log entries to show in the project home page
;------------------------------------------------------------------------------
max_logs_in_project_home = "10"
;------------------------------------------------------------------------------
; directory to accomodate subversion repositories
;------------------------------------------------------------------------------
svnrepo_dir = "@DEPOTDIR@/svnrepo"
;------------------------------------------------------------------------------
; directory to store uploaded files
;------------------------------------------------------------------------------
file_dir = "@DEPOTDIR@/files"
;------------------------------------------------------------------------------
; directory to store issue attachments
;------------------------------------------------------------------------------
issue_file_dir = "@DEPOTDIR@/issuefiles"
;------------------------------------------------------------------------------
; directory to store wiki attachments
;------------------------------------------------------------------------------
attachment_dir = "@DEPOTDIR@/attachments"
;------------------------------------------------------------------------------
; directory to store user icons
;------------------------------------------------------------------------------
usericon_dir = "@DEPOTDIR@/usericons"
;------------------------------------------------------------------------------
; log threshold
;------------------------------------------------------------------------------
; 0 = Disables logging, Error logging TURNED OFF
; 1 = Error Messages (including PHP errors)
; 2 = Debug Messages
; 3 = Informational Messages
; 4 = All Messages
;------------------------------------------------------------------------------
log_threshold = 0
;------------------------------------------------------------------------------
; When yes, open a wiki creation page if a non-existent wiki pages is
; requested
;------------------------------------------------------------------------------
create_missing_wiki = "no"
;------------------------------------------------------------------------------
; When yes, a project member can delete a non-empty project containing
; wiki pages, file uploads, etc. An empty project can be deleted any time
; regardless of this option. A system administrator(sysadmin_userids) is
; allowed to delete a non-empty project also regardless of this option.
;------------------------------------------------------------------------------
force_project_delete = "no"
;------------------------------------------------------------------------------
; When yes, a codepot page can use set_time_limit() to adjust the maximum
; execution time to override the global value. For example, the code search
; page can take very long if the code repository is very large. You can
; set this item to yes to allow such a page to complete its processing.
;------------------------------------------------------------------------------
allow_set_time_limit = "no"
;------------------------------------------------------------------------------
; When yes, a user must sign in to be able to search code.
; When no, an anonymous user can search code.
;------------------------------------------------------------------------------
signin_for_code_search = "yes"
;------------------------------------------------------------------------------
; customized footer
;------------------------------------------------------------------------------
; Leave this empty for the default footer message. You can use a HTML tag as
; well as a plain text. For example,
; footer = "<u>Code Repository</u>"
;------------------------------------------------------------------------------
footer = ""
;------------------------------------------------------------------------------
; CLOC command
;------------------------------------------------------------------------------
; Full path to the CLOC command
;------------------------------------------------------------------------------
cloc_command_path = "@CFGDIR@/cloc.pl"
;------------------------------------------------------------------------------
; List of README files to show in the code folder view.
; The first file found is shown. You can specify multiple file name
; separated by a comma.
;------------------------------------------------------------------------------
code_folder_readme = "README.md,README.wc,README.txt,README"
;------------------------------------------------------------------------------
; Email address to use when sending notification emails
;------------------------------------------------------------------------------
email_sender = ""
;------------------------------------------------------------------------------
; Send notification upon a new commit if yes
;------------------------------------------------------------------------------
commit_notification = "yes"
;------------------------------------------------------------------------------
; Send commit review notification if yes
;------------------------------------------------------------------------------
commit_review_notification = "yes"
;------------------------------------------------------------------------------
; URL to include when sending a commit notification message.
; You can specify multiple urls. in fact, it's a free text.
; Replacement is performed for ${REV}, ${AUTHOR}, ${PROJECTID}.
;------------------------------------------------------------------------------
commit_notification_url = ""
;------------------------------------------------------------------------------
; Send notification upon new issue registration if yes
;------------------------------------------------------------------------------
issue_notification = "yes"
;------------------------------------------------------------------------------
; Codepot sets this revision property to assign a tag to a specific revision.
;------------------------------------------------------------------------------
svn_tag_property = "codepot:tag"
;------------------------------------------------------------------------------
; Subversion read access is limited to the specified user type. The types
; include anonymous, authenticated, authenticated-insider, member.
; This applies to a public project only. Write access to any projects
; and read access to a non-public project require membership regardless
; of this item.
;------------------------------------------------------------------------------
svn_read_access = "member"
;------------------------------------------------------------------------------
; Grant subversion read access to public repository if the user id and the
; password match this special credential configured. The value must be
; a userid and a password separated by a colon. For example,
; svnuser:aab08d13-942c-49bc-b6a7-5ca4408b08d6
; This credentical takes precedence over non-anonymous svn_read_access mode.
;------------------------------------------------------------------------------
svn_read_credential = ""
;------------------------------------------------------------------------------
; The length of a commit message must be as long as this value.
;------------------------------------------------------------------------------
svn_min_commit_message_length = "0"
;------------------------------------------------------------------------------
; Operations on the files under one of svn_restricted_topdirs
; are limited. The value can be separated by a comma.
; To allow 1 subdirectory under the tags directory, set the followings:
; svn_restricted_topdirs = "tags"
; svn_restriction_allowed_subdir_depth_min = "0"
; svn_restriction_allowed_subdir_depth_max = "1"
;------------------------------------------------------------------------------
svn_restricted_topdirs = ""
svn_restriction_allowed_subdir_depth_min = "0"
svn_restriction_allowed_subdir_depth_max = "0"
;------------------------------------------------------------------------------
; Command separated list of user names who can execute /usr/sbin/codepot-user.
; Set it to an empty string to allow all users.
;------------------------------------------------------------------------------
codepot_user_executor = "root"

281
etc/codepot.mysql Normal file
View File

@ -0,0 +1,281 @@
-- ---------------------------------------------------------
-- This file is the Codepot database schema file for MySQL.
-- Note this file doesn't mandate which database to use.
-- ---------------------------------------------------------
CREATE TABLE site (
id VARCHAR(32) PRIMARY KEY,
name VARCHAR(128) NOT NULL,
summary VARCHAR(255) NOT NULL,
text TEXT NOT NULL,
createdon DATETIME NOT NULL,
updatedon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL
) charset=utf8 engine=InnoDB;
CREATE TABLE project (
id VARCHAR(32) PRIMARY KEY,
name VARCHAR(255) UNIQUE NOT NULL,
summary VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
webhooks TEXT NOT NULL,
commitable CHAR(1) NOT NULL DEFAULT 'Y',
public CHAR(1) NOT NULL DEFAULT 'Y',
codecharset VARCHAR(32),
createdon DATETIME NOT NULL,
updatedon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL
) charset=utf8 engine=InnoDB;
CREATE TABLE project_membership (
projectid VARCHAR(32) NOT NULL,
userid VARCHAR(32) NOT NULL,
priority INTEGER NOT NULL,
UNIQUE KEY membership (projectid, userid),
KEY userid_index (userid),
KEY projectid_index (projectid),
CONSTRAINT membership_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE CASCADE ON UPDATE CASCADE
) charset=utf8 engine=InnoDB;
CREATE TABLE wiki (
projectid VARCHAR(32) NOT NULL,
name VARCHAR(255) NOT NULL,
text TEXT NOT NULL,
doctype CHAR(1) NOT NULL DEFAULT 'C',
columns INT NOT NULL DEFAULT 1,
createdon DATETIME NOT NULL,
updatedon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
UNIQUE KEY wiki_id (projectid, name),
CONSTRAINT wiki_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE
) charset=utf8 engine=InnoDB;
CREATE TABLE wiki_attachment (
projectid VARCHAR(32) NOT NULL,
wikiname VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
encname VARCHAR(255) NOT NULL,
createdon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
UNIQUE KEY wiki_attachment_id (projectid, wikiname, name),
CONSTRAINT wiki_attachment_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT wiki_attachment_wikiid FOREIGN KEY (projectid,wikiname) REFERENCES wiki(projectid,name)
ON DELETE RESTRICT ON UPDATE CASCADE
) charset=utf8 engine=InnoDB;
CREATE TABLE issue (
projectid VARCHAR(32) NOT NULL,
id BIGINT NOT NULL,
summary VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
type VARCHAR(32) NOT NULL,
status VARCHAR(32) NOT NULL,
owner VARCHAR(255) NOT NULL,
priority VARCHAR(32) NOT NULL,
createdon DATETIME NOT NULL,
updatedon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
PRIMARY KEY (projectid, id),
KEY issue_status_type_summary (projectid, status, type, summary),
KEY issue_summary (projectid, summary),
CONSTRAINT issue_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE
) charset=utf8 engine=InnoDB;
CREATE TABLE issue_file_list (
projectid VARCHAR(32) NOT NULL,
issueid BIGINT NOT NULL,
filename VARCHAR(255) NOT NULL,
encname VARCHAR(255) NOT NULL,
md5sum CHAR(32) NOT NULL,
description VARCHAR(255) NOT NULL,
createdon DATETIME NOT NULL,
updatedon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
UNIQUE KEY issue_file_list_id (projectid, issueid, filename),
UNIQUE KEY (encname),
CONSTRAINT issue_file_list_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT issue_file_list_issueid FOREIGN KEY (projectid,issueid) REFERENCES issue(projectid,id)
ON DELETE RESTRICT ON UPDATE CASCADE
) charset=utf8 engine=InnoDB;
CREATE TABLE issue_change (
projectid VARCHAR(32) NOT NULL,
id BIGINT NOT NULL,
sno BIGINT NOT NULL,
type VARCHAR(32) NOT NULL,
status VARCHAR(32) NOT NULL,
owner VARCHAR(255) NOT NULL,
priority VARCHAR(32) NOT NULL,
comment TEXT NOT NULL,
createdon DATETIME NOT NULL,
updatedon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
PRIMARY KEY (projectid, id, sno),
KEY issue_update_time (projectid, id, updatedon),
CONSTRAINT issue_change_id FOREIGN KEY (projectid,id) REFERENCES issue(projectid,id)
ON DELETE RESTRICT ON UPDATE CASCADE
) charset=utf8 engine=InnoDB;
CREATE TABLE issue_change_file_list (
projectid VARCHAR(32) NOT NULL,
issueid BIGINT NOT NULL,
issuesno BIGINT NOT NULL,
filename VARCHAR(255) NOT NULL,
encname VARCHAR(255) NOT NULL,
createdon DATETIME NOT NULL,
updatedon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
UNIQUE KEY issue_change_file_list_id (projectid, issueid, filename),
CONSTRAINT issue_change_file_list_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT issue_change_file_list_issueidsno FOREIGN KEY (projectid,issueid,issuesno) REFERENCES issue_change(projectid,id,sno)
ON DELETE RESTRICT ON UPDATE CASCADE
) charset=utf8 engine=InnoDB;
CREATE TABLE issue_coderev (
projectid VARCHAR(32) NOT NULL,
issueid BIGINT NOT NULL,
codeproid VARCHAR(32) NOT NULL,
coderev VARCHAR(64) NOT NULL, -- git commit id is 40 characters. subversion revision is a number.
UNIQUE KEY issue_coderev_uid(projectid, issueid, codeproid, coderev),
KEY issue_coderev_codekey (codeproid, coderev),
KEY issue_coderev_issuekey (projectid, issueid),
CONSTRAINT issue_coderev_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT issue_coderev_codeproid FOREIGN KEY (codeproid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE
-- Commit message is typically a free text. Its issue reference could be error-prone.
-- So i won't have this constraint enforced.
-- CONSTRAINT issue_coderev_issueid FOREIGN KEY (projectid,issueid) REFERENCES issue(projectid,id)
-- ON DELETE RESTRICT ON UPDATE CASCADE
) charset=utf8 engine=InnoDB;
CREATE TABLE file (
projectid VARCHAR(32) NOT NULL,
name VARCHAR(255) NOT NULL,
tag VARCHAR(54) NOT NULL,
description TEXT NOT NULL,
createdon DATETIME NOT NULL,
updatedon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
UNIQUE KEY file_id (projectid, name),
INDEX file_tagged_name (projectid, tag, name),
CONSTRAINT file_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE
) charset=utf8 engine=InnoDB;
CREATE TABLE file_list (
projectid VARCHAR(32) NOT NULL,
name VARCHAR(255) NOT NULL,
filename VARCHAR(255) NOT NULL,
encname VARCHAR(255) NOT NULL,
md5sum CHAR(32) NOT NULL,
description VARCHAR(255) NOT NULL,
createdon DATETIME NOT NULL,
updatedon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
INDEX file_list_id (projectid, name),
UNIQUE KEY file_list_fileid (projectid, filename),
UNIQUE KEY (encname),
CONSTRAINT file_list_projectid FOREIGN KEY (projectid,name) REFERENCES file(projectid,name)
ON DELETE RESTRICT ON UPDATE CASCADE
) charset=utf8 engine=InnoDB;
CREATE TABLE code_review (
projectid VARCHAR(32) NOT NULL,
rev BIGINT NOT NULL,
sno BIGINT NOT NULL,
comment TEXT NOT NULL,
createdon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedon DATETIME NOT NULL,
updatedby VARCHAR(32) NOT NULL,
UNIQUE KEY code_review_id (projectid, rev, sno),
CONSTRAINT code_review_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE
) charset=utf8 engine=InnoDB;
CREATE TABLE log (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
projectid VARCHAR(32) NOT NULL,
type VARCHAR(16) NOT NULL,
action VARCHAR(16) NOT NULL,
userid VARCHAR(32) NOT NULL,
message TEXT NOT NULL,
createdon DATETIME NOT NULL,
INDEX timed_project_type_action (createdon, projectid, type, action)
) charset=utf8 engine=InnoDB;
CREATE TABLE user_settings (
userid VARCHAR(32) PRIMARY KEY,
code_hide_line_num CHAR(1) NOT NULL,
code_hide_metadata CHAR(1) NOT NULL,
icon_name VARCHAR(255) UNIQUE NULL,
user_summary VARCHAR(255) NULL
) charset=utf8 engine=InnoDB;
CREATE TABLE user_account (
userid VARCHAR(32) PRIMARY KEY,
passwd VARCHAR(255) NOT NULL,
email VARCHAR(255),
enabled CHAR(1) NOT NULL DEFAULT 'N' CHECK(enabled in ('Y', 'N'))
) charset=utf8 engine=InnoDB;

295
etc/codepot.oracle Normal file
View File

@ -0,0 +1,295 @@
-- ------------------------------------------------------------
-- This file is the Codepot database schema file for PostreSQL.
-- Note this file doesn't mandate which database to use.
--
-- Assumining "local all all password" in /var/lib/pgsql/data/pg_hba.conf
--
-- $ sqlplus "scott/tiger@(DESCRIPTION=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(HOST=192.168.1.126)(PORT=1521))(CONNECT_DATA=(SID=ORCL)))"
-- SQL> @codepot.oracle
--
--
-- DROP SEQUENCE "cpot_log_id_seq";
-- DROP TABLE "cpot_user_account";
-- DROP TABLE "cpot_user_settings";
-- DROP TABLE "cpot_log";
-- DROP TABLE "cpot_code_review";
-- DROP TABLE "cpot_file";
-- DROP TABLE "cpot_issue_change_file_list";
-- DROP TABLE "cpot_issue_change";
-- DROP TABLE "cpot_issue_file_list";
-- DROP TABLE "cpot_issue";
-- DROP TABLE "cpot_wiki_attachment";
-- DROP TABLE "cpot_wiki";
-- DROP TABLE "cpot_project_membership";
-- DROP TABLE "cpot_project";
-- DROP TABLE "cpot_site";
-- ------------------------------------------------------------
CREATE TABLE "cpot_site" (
"id" VARCHAR(32) PRIMARY KEY,
"name" VARCHAR(128) NOT NULL,
"summary" VARCHAR(255) NOT NULL,
"text" CLOB NOT NULL,
"createdon" TIMESTAMP NOT NULL,
"updatedon" TIMESTAMP NOT NULL,
"createdby" VARCHAR(32) NOT NULL,
"updatedby" VARCHAR(32) NOT NULL
);
CREATE TABLE "cpot_project" (
"id" VARCHAR(32) PRIMARY KEY,
"name" VARCHAR(255) UNIQUE NOT NULL,
"summary" VARCHAR(255) NOT NULL,
"description" CLOB NOT NULL,
"webhooks" CLOB NOT NULL,
"commitable" CHAR(1) DEFAULT 'Y' NOT NULL,
"public" CHAR(1) DEFAULT 'Y' NOT NULL,
"codecharset" VARCHAR(32),
"createdon" TIMESTAMP NOT NULL,
"updatedon" TIMESTAMP NOT NULL,
"createdby" VARCHAR(32) NOT NULL,
"updatedby" VARCHAR(32) NOT NULL
);
CREATE TABLE "cpot_project_membership" (
"projectid" VARCHAR(32) NOT NULL,
"userid" VARCHAR(32) NOT NULL,
"priority" INTEGER NOT NULL,
UNIQUE ("projectid", "userid"),
CONSTRAINT membership_projectid FOREIGN KEY ("projectid") REFERENCES "cpot_project"("id") ON DELETE CASCADE
);
CREATE INDEX cpot_projectid_index ON "cpot_project_membership"("projectid");
CREATE INDEX cpot_userid_index ON "cpot_project_membership"("userid");
CREATE TABLE "cpot_wiki" (
"projectid" VARCHAR(32) NOT NULL,
"name" VARCHAR(255) NOT NULL,
"text" CLOB NOT NULL,
"doctype" CHAR(1) DEFAULT 'C' NOT NULL,
"columns" INT DEFAULT 1 NOT NULL,
"createdon" TIMESTAMP NOT NULL,
"updatedon" TIMESTAMP NOT NULL,
"createdby" VARCHAR(32) NOT NULL,
"updatedby" VARCHAR(32) NOT NULL,
UNIQUE ("projectid", "name"),
CONSTRAINT wiki_projectid FOREIGN KEY ("projectid") REFERENCES "cpot_project"("id")
);
-- [ NOTE ] oracle defaults to ON DELETE RESTRICT if it is not specified.
CREATE TABLE "cpot_wiki_attachment" (
"projectid" VARCHAR(32) NOT NULL,
"wikiname" VARCHAR(255) NOT NULL,
"name" VARCHAR(255) NOT NULL,
"encname" VARCHAR(255) NOT NULL,
"createdon" TIMESTAMP NOT NULL,
"createdby" VARCHAR(32) NOT NULL,
UNIQUE ("projectid", "wikiname", "name"),
CONSTRAINT wiki_attachment_projectid FOREIGN KEY ("projectid") REFERENCES "cpot_project"("id"),
CONSTRAINT wiki_attachment_wikiid FOREIGN KEY ("projectid","wikiname") REFERENCES "cpot_wiki"("projectid","name")
);
CREATE TABLE "cpot_issue" (
"projectid" VARCHAR(32) NOT NULL,
"id" NUMBER(20,0) NOT NULL,
"summary" VARCHAR(255) NOT NULL,
"description" CLOB NOT NULL,
"type" VARCHAR(32) NOT NULL,
"status" VARCHAR(32) NOT NULL,
"owner" VARCHAR(255) NOT NULL,
"priority" VARCHAR(32) NOT NULL,
"createdon" TIMESTAMP NOT NULL,
"updatedon" TIMESTAMP NOT NULL,
"createdby" VARCHAR(32) NOT NULL,
"updatedby" VARCHAR(32) NOT NULL,
PRIMARY KEY ("projectid", "id"),
CONSTRAINT issue_projectid FOREIGN KEY ("projectid") REFERENCES "cpot_project"("id")
);
CREATE INDEX cpot_issue_index_1 ON "cpot_issue"("projectid", "status", "type", "summary");
CREATE INDEX cpot_issue_index_2 ON "cpot_issue"("projectid", "summary");
CREATE TABLE "cpot_issue_file_list" (
"projectid" VARCHAR(32) NOT NULL,
"issueid" NUMBER(20,0) NOT NULL,
"filename" VARCHAR(255) NOT NULL,
"encname" VARCHAR(255) NOT NULL,
"md5sum" CHAR(32) NOT NULL,
"description" CLOB NOT NULL,
"createdon" TIMESTAMP NOT NULL,
"updatedon" TIMESTAMP NOT NULL,
"createdby" VARCHAR(32) NOT NULL,
"updatedby" VARCHAR(32) NOT NULL,
UNIQUE ("projectid", "issueid", "filename"),
UNIQUE ("encname"),
CONSTRAINT issue_file_list_projectid FOREIGN KEY ("projectid") REFERENCES "cpot_project"("id"),
CONSTRAINT issue_file_list_issueid FOREIGN KEY ("projectid","issueid") REFERENCES "cpot_issue"("projectid","id")
);
CREATE TABLE "cpot_issue_change" (
"projectid" VARCHAR(32) NOT NULL,
"id" NUMBER(20,0) NOT NULL,
"sno" NUMBER(20,0) NOT NULL,
"type" VARCHAR(32) NOT NULL,
"status" VARCHAR(32) NOT NULL,
"owner" VARCHAR(255) NOT NULL,
"priority" VARCHAR(32) NOT NULL,
"comment" CLOB NOT NULL,
"createdon" TIMESTAMP NOT NULL,
"updatedon" TIMESTAMP NOT NULL,
"createdby" VARCHAR(32) NOT NULL,
"updatedby" VARCHAR(32) NOT NULL,
PRIMARY KEY ("projectid", "id", "sno"),
CONSTRAINT issue_update_id FOREIGN KEY ("projectid","id") REFERENCES "cpot_issue"("projectid","id")
);
CREATE INDEX cpot_issue_change_index_1 ON "cpot_issue_change"("projectid", "id", "updatedon");
CREATE TABLE "cpot_issue_change_file_list" (
"projectid" VARCHAR(32) NOT NULL,
"issueid" NUMBER(20,0) NOT NULL,
"issuesno" NUMBER(20,0) NOT NULL,
"filename" VARCHAR(255) NOT NULL,
"encname" VARCHAR(255) NOT NULL,
"createdon" TIMESTAMP NOT NULL,
"updatedon" TIMESTAMP NOT NULL,
"createdby" VARCHAR(32) NOT NULL,
"updatedby" VARCHAR(32) NOT NULL,
UNIQUE ("projectid", "issueid", "filename"),
CONSTRAINT issue_change_file_list_c1 FOREIGN KEY ("projectid") REFERENCES "cpot_project"("id"),
CONSTRAINT issue_change_file_list_c2 FOREIGN KEY ("projectid","issueid","issuesno") REFERENCES "cpot_issue_change"("projectid","id","sno")
);
CREATE TABLE "cpot_issue_coderev" (
"projectid" VARCHAR(32) NOT NULL,
"issueid" NUMBER(20,0) NOT NULL,
"codeproid" VARCHAR(32) NOT NULL,
"coderev" VARCHAR(64) NOT NULL,
UNIQUE ("projectid", "issueid", "codeproid", "coderev"),
CONSTRAINT issue_coderev_projectid FOREIGN KEY ("projectid") REFERENCES "project"("id"),
CONSTRAINT issue_coderev_codeproid FOREIGN KEY ("codeproid") REFERENCES "project"("id")
);
CREATE INDEX issue_coderev_index_1 ON "issue_coderev"("codeproid", "coderev");
CREATE INDEX issue_coderev_index_2 ON "issue_coderev"("projectid", "issueid");
CREATE TABLE "cpot_file" (
"projectid" VARCHAR(32) NOT NULL,
"name" VARCHAR(255) NOT NULL,
"tag" VARCHAR(54) NOT NULL,
"description" CLOB NOT NULL,
"createdon" TIMESTAMP NOT NULL,
"updatedon" TIMESTAMP NOT NULL,
"createdby" VARCHAR(32) NOT NULL,
"updatedby" VARCHAR(32) NOT NULL,
UNIQUE ("projectid", "name"),
CONSTRAINT file_projectid FOREIGN KEY ("projectid") REFERENCES "cpot_project"("id")
);
CREATE INDEX cpot_file_index_1 ON "cpot_file"("projectid", "tag", "name");
CREATE TABLE "cpot_file_list" (
"projectid" VARCHAR(32) NOT NULL,
"name" VARCHAR(255) NOT NULL,
"filename" VARCHAR(255) NOT NULL,
"encname" VARCHAR(255) NOT NULL,
"md5sum" CHAR(32) NOT NULL,
"description" CLOB NOT NULL,
"createdon" TIMESTAMP NOT NULL,
"updatedon" TIMESTAMP NOT NULL,
"createdby" VARCHAR(32) NOT NULL,
"updatedby" VARCHAR(32) NOT NULL,
UNIQUE ("projectid", "filename"),
UNIQUE ("encname"),
CONSTRAINT file_list_projectid FOREIGN KEY ("projectid","name") REFERENCES "cpot_file"("projectid","name")
);
CREATE INDEX cpot_file_list_index_1 ON "cpot_file_list"("projectid", "name");
CREATE TABLE "cpot_code_review" (
"projectid" VARCHAR(32) NOT NULL,
"rev" NUMBER(20,0) NOT NULL,
"sno" NUMBER(20,0) NOT NULL,
"comment" CLOB NOT NULL,
"createdon" TIMESTAMP NOT NULL,
"createdby" VARCHAR(32) NOT NULL,
"updatedon" TIMESTAMP NOT NULL,
"updatedby" VARCHAR(32) NOT NULL,
UNIQUE ("projectid", "rev", "sno"),
CONSTRAINT code_review_projectid FOREIGN KEY ("projectid") REFERENCES "cpot_project"("id")
);
CREATE TABLE "cpot_log" (
"id" NUMBER(20,0) PRIMARY KEY,
"projectid" VARCHAR(32) NOT NULL,
"type" VARCHAR(16) NOT NULL,
"action" VARCHAR(16) NOT NULL,
"userid" VARCHAR(32) NOT NULL,
"message" CLOB NOT NULL,
"createdon" TIMESTAMP NOT NULL
);
CREATE INDEX codepopt_log_index_1 ON "cpot_log"("createdon", "projectid", "type", "action");
CREATE SEQUENCE "cpot_log_id_seq";
CREATE OR REPLACE TRIGGER cpot_inc_log_id BEFORE INSERT ON "cpot_log" FOR EACH ROW WHEN (new."id" IS NULL)
BEGIN
SELECT "cpot_log_id_seq".NEXTVAL INTO :new."id" FROM dual;
END;
/
CREATE TABLE "cpot_user_settings" (
"userid" VARCHAR(32) PRIMARY KEY,
"code_hide_line_num" CHAR(1) NOT NULL,
"code_hide_metadata" CHAR(1) NOT NULL,
"icon_name" VARCHAR(255) UNIQUE NULL,
"user_summary" VARCHAR(255) NULL
);
CREATE TABLE "cpot_user_account" (
"userid" VARCHAR(32) PRIMARY KEY,
"passwd" VARCHAR(255) NOT NULL,
"email" VARCHAR(255),
"enabled" CHAR(1) DEFAULT 'N' NOT NULL CHECK("enabled" in ('Y', 'N'))
);
CREATE OR REPLACE TRIGGER cpot_upon_project_id_update AFTER UPDATE OF "id" ON "cpot_project" FOR EACH ROW
BEGIN
UPDATE "cpot_project_membership" SET "projectid" = :new."id" WHERE "projectid" = :old."id";
UPDATE "cpot_wiki" SET "projectid" = :new."id" WHERE "projectid" = :old."id";
UPDATE "cpot_wiki_attachment" SET "projectid" = :new."id" WHERE "projectid" = :old."id";
UPDATE "cpot_issue" SET "projectid" = :new."id" WHERE "projectid"= :old."id";
UPDATE "cpot_issue_file_list" SET "projectid" = :new."id" WHERE "projectid" = :old."id";
UPDATE "cpot_issue_change" SET "projectid" = :new."id" WHERE "projectid" = :old."id";
UPDATE "cpot_issue_change_file_list" SET "projectid" = :new."id" WHERE "projectid" = :old."id";
UPDATE "cpot_issue_coderev" SET "projectid" = :new."id" WHERE "projectid" = :old."id";
UPDATE "cpot_issue_coderev" SET "codeproid" = :new."id" WHERE "codeproid" = :old."id";
UPDATE "cpot_file" SET "projectid" = :new."id" WHERE "projectid" = :old."id";
UPDATE "cpot_code_review" SET "projectid" = :new."id" WHERE "projectid" = :old."id";
END;
/
CREATE OR REPLACE TRIGGER cpot_upon_wiki_name_update AFTER UPDATE OF "name" ON "cpot_wiki" FOR EACH ROW
BEGIN
UPDATE "cpot_wiki_attachment" SET "wikiname" = :new."name" WHERE "projectid" = :old."projectid" AND "wikiname" = :old."name";
END;
/
CREATE OR REPLACE TRIGGER cpot_upon_issue_id_update AFTER UPDATE OF "id" ON "cpot_issue" FOR EACH ROW
BEGIN
UPDATE "cpot_issue_file_list" SET "issueid" = :new."id" WHERE "projectid" = :old."projectid" AND "issueid" = :old."id";
UPDATE "cpot_issue_change" SET "id" = :new."id" WHERE "projectid" = :old."projectid" AND "id" = :old."id";
UPDATE "cpot_issue_change_file_list" SET "issueid" = :new."id" WHERE "projectid" = :old."projectid" AND "issueid" = :old."id";
UPDATE "cpot_issue_coderev" SET "issueid" = :new."id" WHERE WHERE "projectid" = :old."projectid" AND "issueid" = :old."id";
END;
/
CREATE OR REPLACE TRIGGER cpot_upon_issue_chsno_update AFTER UPDATE OF "id" ON "cpot_issue_change" FOR EACH ROW
BEGIN
UPDATE "cpot_issue_change_file_list" SET "issuesno" = :new."sno" WHERE "projectid" = :old."projectid" AND "issueid" = :old."id" AND "issuesno" = :old."sno";
END;
/
CREATE OR REPLACE TRIGGER cpot_upon_file_name_update AFTER UPDATE OF "name" ON "cpot_file" FOR EACH ROW
BEGIN
UPDATE "cpot_file_list" SET "name" = :new."name" WHERE "projectid" = :old."projectid" AND "name" = :old."name";
END;
/

297
etc/codepot.pgsql Normal file
View File

@ -0,0 +1,297 @@
-- ------------------------------------------------------------
-- This file is the Codepot database schema file for PostreSQL.
-- Note this file doesn't mandate which database to use.
--
-- Assumining "local all all password" in /var/lib/pgsql/data/pg_hba.conf
--
-- $ sudo -u postgres psql
-- postgres=# CREATE USER codepot WITH PASSWORD 'codepot';
-- postgres=# \du
-- postgres=# CREATE DATABASE codepot;
-- postgres=# \l
-- postgres=# ALTER DATABASE "codepot" OWNER TO codepot;
-- postgres=# \l
-- postgres=# \q
--
-- $ psql -U codepot -W codepot
-- postgres=# \i codepot.pgsql
-- postgres=# \dt
-- postgres=# \q
-- ------------------------------------------------------------
CREATE TABLE site (
id VARCHAR(32) PRIMARY KEY,
name VARCHAR(128) NOT NULL,
summary VARCHAR(255) NOT NULL,
text TEXT NOT NULL,
createdon TIMESTAMP NOT NULL,
updatedon TIMESTAMP NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL
);
CREATE TABLE project (
id VARCHAR(32) PRIMARY KEY,
name VARCHAR(255) UNIQUE NOT NULL,
summary VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
webhooks TEXT NOT NULL,
commitable CHAR(1) NOT NULL DEFAULT 'Y',
public CHAR(1) NOT NULL DEFAULT 'Y',
codecharset VARCHAR(32),
createdon TIMESTAMP NOT NULL,
updatedon TIMESTAMP NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL
);
CREATE TABLE project_membership (
projectid VARCHAR(32) NOT NULL,
userid VARCHAR(32) NOT NULL,
priority INTEGER NOT NULL,
UNIQUE (projectid, userid),
CONSTRAINT membership_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE INDEX projectid_index ON project_membership(projectid);
CREATE INDEX userid_index ON project_membership(userid);
CREATE TABLE wiki (
projectid VARCHAR(32) NOT NULL,
name VARCHAR(255) NOT NULL,
text TEXT NOT NULL,
doctype CHAR(1) NOT NULL DEFAULT 'C',
columns INT NOT NULL DEFAULT 1,
createdon TIMESTAMP NOT NULL,
updatedon TIMESTAMP NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
UNIQUE (projectid, name),
CONSTRAINT wiki_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE TABLE wiki_attachment (
projectid VARCHAR(32) NOT NULL,
wikiname VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
encname VARCHAR(255) NOT NULL,
createdon TIMESTAMP NOT NULL,
createdby VARCHAR(32) NOT NULL,
UNIQUE (projectid, wikiname, name),
CONSTRAINT wiki_attachment_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT wiki_attachment_wikiid FOREIGN KEY (projectid,wikiname) REFERENCES wiki(projectid,name)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE TABLE issue (
projectid VARCHAR(32) NOT NULL,
id BIGINT NOT NULL,
summary VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
type VARCHAR(32) NOT NULL,
status VARCHAR(32) NOT NULL,
owner VARCHAR(255) NOT NULL,
priority VARCHAR(32) NOT NULL,
createdon TIMESTAMP NOT NULL,
updatedon TIMESTAMP NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
PRIMARY KEY (projectid, id),
CONSTRAINT issue_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE INDEX issue_index_1 ON issue(projectid, status, type, summary);
CREATE INDEX issue_index_2 ON issue(projectid, summary);
CREATE TABLE issue_file_list (
projectid VARCHAR(32) NOT NULL,
issueid BIGINT NOT NULL,
filename VARCHAR(255) NOT NULL,
encname VARCHAR(255) NOT NULL,
md5sum CHAR(32) NOT NULL,
description VARCHAR(255) NOT NULL,
createdon TIMESTAMP NOT NULL,
updatedon TIMESTAMP NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
UNIQUE (projectid, issueid, filename),
UNIQUE (encname),
CONSTRAINT issue_file_list_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT issue_file_list_issueid FOREIGN KEY (projectid,issueid) REFERENCES issue(projectid,id)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE TABLE issue_change (
projectid VARCHAR(32) NOT NULL,
id BIGINT NOT NULL,
sno BIGINT NOT NULL,
type VARCHAR(32) NOT NULL,
status VARCHAR(32) NOT NULL,
owner VARCHAR(255) NOT NULL,
priority VARCHAR(32) NOT NULL,
comment TEXT NOT NULL,
createdon TIMESTAMP NOT NULL,
updatedon TIMESTAMP NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
PRIMARY KEY (projectid, id, sno),
CONSTRAINT issue_update_id FOREIGN KEY (projectid,id) REFERENCES issue(projectid,id)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE INDEX issue_change_index_1 ON issue_change(projectid, id, updatedon);
CREATE TABLE issue_change_file_list (
projectid VARCHAR(32) NOT NULL,
issueid BIGINT NOT NULL,
issuesno BIGINT NOT NULL,
filename VARCHAR(255) NOT NULL,
encname VARCHAR(255) NOT NULL,
createdon TIMESTAMP NOT NULL,
updatedon TIMESTAMP NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
UNIQUE (projectid, issueid, filename),
CONSTRAINT issue_change_file_list_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT issue_change_file_list_issueidsno FOREIGN KEY (projectid,issueid,issuesno) REFERENCES issue_change(projectid,id,sno)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE TABLE issue_coderev (
projectid VARCHAR(32) NOT NULL,
issueid BIGINT NOT NULL,
codeproid VARCHAR(32) NOT NULL,
coderev VARCHAR(64) NOT NULL,
UNIQUE (projectid, issueid, codeproid, coderev),
CONSTRAINT issue_coderev_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT issue_coderev_codeproid FOREIGN KEY (codeproid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE INDEX issue_coderev_index_1 ON issue_coderev(codeproid, coderev);
CREATE INDEX issue_coderev_index_2 ON issue_coderev(projectid, issueid);
CREATE TABLE file (
projectid VARCHAR(32) NOT NULL,
name VARCHAR(255) NOT NULL,
tag VARCHAR(54) NOT NULL,
description TEXT NOT NULL,
createdon TIMESTAMP NOT NULL,
updatedon TIMESTAMP NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
UNIQUE (projectid, name),
CONSTRAINT file_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE INDEX file_index_1 ON file(projectid, tag, name);
CREATE TABLE file_list (
projectid VARCHAR(32) NOT NULL,
name VARCHAR(255) NOT NULL,
filename VARCHAR(255) NOT NULL,
encname VARCHAR(255) NOT NULL,
md5sum CHAR(32) NOT NULL,
description VARCHAR(255) NOT NULL,
createdon TIMESTAMP NOT NULL,
updatedon TIMESTAMP NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
UNIQUE (projectid, filename),
UNIQUE (encname),
CONSTRAINT file_list_projectid FOREIGN KEY (projectid,name) REFERENCES file(projectid,name)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE INDEX file_list_index_1 ON file(projectid, name);
CREATE TABLE code_review (
projectid VARCHAR(32) NOT NULL,
rev BIGINT NOT NULL,
sno BIGINT NOT NULL,
comment TEXT NOT NULL,
createdon TIMESTAMP NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedon TIMESTAMP NOT NULL,
updatedby VARCHAR(32) NOT NULL,
UNIQUE (projectid, rev, sno),
CONSTRAINT code_review_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE TABLE log (
id BIGSERIAL PRIMARY KEY,
projectid VARCHAR(32) NOT NULL,
type VARCHAR(16) NOT NULL,
action VARCHAR(16) NOT NULL,
userid VARCHAR(32) NOT NULL,
message TEXT NOT NULL,
createdon TIMESTAMP NOT NULL
);
CREATE INDEX log_index_1 ON log(createdon, projectid, type, action);
CREATE TABLE user_settings (
userid VARCHAR(32) PRIMARY KEY,
code_hide_line_num CHAR(1) NOT NULL,
code_hide_metadata CHAR(1) NOT NULL,
icon_name VARCHAR(255) UNIQUE NULL,
user_summary VARCHAR(255) NULL
);
CREATE TABLE user_account (
userid VARCHAR(32) PRIMARY KEY,
passwd VARCHAR(255) NOT NULL,
email VARCHAR(255),
enabled CHAR(1) NOT NULL DEFAULT 'N' CHECK(enabled in ('Y', 'N'))
);

276
etc/codepot.sqlite Normal file
View File

@ -0,0 +1,276 @@
-- ---------------------------------------------------------
-- This file is the Codepot database schema file for SQLITE.
-- ---------------------------------------------------------
PRAGMA journal_mode=WAL;
PRAGMA foreign_keys=ON;
CREATE TABLE site (
id VARCHAR(32) PRIMARY KEY,
name VARCHAR(128) NOT NULL,
summary VARCHAR(255) NOT NULL,
text TEXT NOT NULL,
createdon DATETIME NOT NULL,
updatedon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL
);
CREATE TABLE project (
id VARCHAR(32) PRIMARY KEY,
name VARCHAR(255) UNIQUE NOT NULL,
summary VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
webhooks TEXT NOT NULL,
commitable CHAR(1) NOT NULL DEFAULT 'Y',
public CHAR(1) NOT NULL DEFAULT 'Y',
codecharset VARCHAR(32),
createdon DATETIME NOT NULL,
updatedon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL
);
CREATE TABLE project_membership (
projectid VARCHAR(32) NOT NULL,
userid VARCHAR(32) NOT NULL,
priority INTEGER NOT NULL,
CONSTRAINT membership_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE UNIQUE INDEX project_membership_index_1 on project_membership(projectid, userid);
CREATE INDEX project_membership_index_2 on project_membership(userid);
CREATE INDEX project_membership_index_3 on project_membership(projectid);
CREATE TABLE wiki (
projectid VARCHAR(32) NOT NULL,
name VARCHAR(255) NOT NULL,
text TEXT NOT NULL,
doctype CHAR(1) NOT NULL DEFAULT 'C',
columns INT NOT NULL DEFAULT 1,
createdon DATETIME NOT NULL,
updatedon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
CONSTRAINT wiki_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE UNIQUE INDEX wiki_index on wiki(projectid, name);
CREATE TABLE wiki_attachment (
projectid VARCHAR(32) NOT NULL,
wikiname VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
encname VARCHAR(255) NOT NULL,
createdon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
CONSTRAINT wiki_attachment_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT wiki_attachment_wikiid FOREIGN KEY (projectid,wikiname) REFERENCES wiki(projectid,name)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE UNIQUE INDEX wiki_attachment_index_1 on wiki_attachment(projectid, wikiname, name);
CREATE TABLE issue (
projectid VARCHAR(32) NOT NULL,
id BIGINT NOT NULL,
summary VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
type VARCHAR(32) NOT NULL,
status VARCHAR(32) NOT NULL,
owner VARCHAR(255) NOT NULL,
priority VARCHAR(32) NOT NULL,
createdon DATETIME NOT NULL,
updatedon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
PRIMARY KEY (projectid, id),
CONSTRAINT issue_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE INDEX issue_index_1 ON issue(projectid, status, type, summary);
CREATE INDEX issue_index_2 ON issue(projectid, summary);
CREATE TABLE issue_file_list (
projectid VARCHAR(32) NOT NULL,
issueid BIGINT NOT NULL,
filename VARCHAR(255) NOT NULL,
encname VARCHAR(255) NOT NULL,
md5sum CHAR(32) NOT NULL,
description VARCHAR(255) NOT NULL,
createdon DATETIME NOT NULL,
updatedon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
CONSTRAINT issue_file_list_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT issue_file_list_issueid FOREIGN KEY (projectid,issueid) REFERENCES issue(projectid,id)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE UNIQUE INDEX issue_file_list_index_1 on issue_file_list(projectid, issueid, filename);
CREATE UNIQUE INDEX issue_file_list_index_2 on issue_file_list(encname);
CREATE TABLE issue_change (
projectid VARCHAR(32) NOT NULL,
id BIGINT NOT NULL,
sno BIGINT NOT NULL,
type VARCHAR(32) NOT NULL,
status VARCHAR(32) NOT NULL,
owner VARCHAR(255) NOT NULL,
priority VARCHAR(32) NOT NULL,
comment TEXT NOT NULL,
createdon DATETIME NOT NULL,
updatedon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
PRIMARY KEY (projectid, id, sno),
CONSTRAINT issue_change_id FOREIGN KEY (projectid,id) REFERENCES issue(projectid,id)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE INDEX issue_change_index_1 ON issue_change(projectid, id, updatedon);
CREATE TABLE issue_change_file_list (
projectid VARCHAR(32) NOT NULL,
issueid BIGINT NOT NULL,
issuesno BIGINT NOT NULL,
filename VARCHAR(255) NOT NULL,
encname VARCHAR(255) NOT NULL,
createdon DATETIME NOT NULL,
updatedon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
CONSTRAINT issue_change_file_list_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT issue_change_file_list_issueidsno FOREIGN KEY (projectid,issueid,issuesno) REFERENCES issue_change(projectid,id,sno)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE UNIQUE INDEX issue_change_file_list_index_1 on issue_change_file_list(projectid, issueid, filename);
CREATE TABLE issue_coderev (
projectid VARCHAR(32) NOT NULL,
issueid BIGINT NOT NULL,
codeproid VARCHAR(32) NOT NULL,
coderev VARCHAR(64) NOT NULL, -- git commit id is 40 characters. subversion revision is a number.
CONSTRAINT issue_coderev_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT issue_coderev_codeproid FOREIGN KEY (codeproid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE
-- Commit message is typically a free text. Its issue reference could be error-prone.
-- So i won't have this constraint enforced.
-- CONSTRAINT issue_coderev_issueid FOREIGN KEY (projectid,issueid) REFERENCES issue(projectid,id)
-- ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE UNIQUE INDEX issue_coderev_index_1 ON issue_coderev(projectid, issueid, codeproid, coderev);
CREATE INDEX issue_coderev_index_2 ON issue_coderev(codeproid, coderev);
CREATE INDEX issue_coderev_index_3 ON issue_coderev(projectid, issueid);
CREATE TABLE file (
projectid VARCHAR(32) NOT NULL,
name VARCHAR(255) NOT NULL,
tag VARCHAR(54) NOT NULL,
description TEXT NOT NULL,
createdon DATETIME NOT NULL,
updatedon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
CONSTRAINT file_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE UNIQUE INDEX file_index_1 ON file(projectid, name);
CREATE INDEX file_index_2 ON file(projectid, tag, name);
CREATE TABLE file_list (
projectid VARCHAR(32) NOT NULL,
name VARCHAR(255) NOT NULL,
filename VARCHAR(255) NOT NULL,
encname VARCHAR(255) NOT NULL,
md5sum CHAR(32) NOT NULL,
description VARCHAR(255) NOT NULL,
createdon DATETIME NOT NULL,
updatedon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedby VARCHAR(32) NOT NULL,
CONSTRAINT file_list_projectid FOREIGN KEY (projectid,name) REFERENCES file(projectid,name)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE INDEX file_list_index_1 ON file_list(projectid, name);
CREATE UNIQUE INDEX file_list_index_2 ON file_list(projectid, filename);
CREATE UNIQUE INDEX file_list_index_3 ON file_list(encname);
CREATE TABLE code_review (
projectid VARCHAR(32) NOT NULL,
rev BIGINT NOT NULL,
sno BIGINT NOT NULL,
comment TEXT NOT NULL,
createdon DATETIME NOT NULL,
createdby VARCHAR(32) NOT NULL,
updatedon DATETIME NOT NULL,
updatedby VARCHAR(32) NOT NULL,
CONSTRAINT code_review_projectid FOREIGN KEY (projectid) REFERENCES project(id)
ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE UNIQUE INDEX code_review_index_1 ON code_review(projectid, rev, sno);
CREATE TABLE log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
projectid VARCHAR(32) NOT NULL,
type VARCHAR(16) NOT NULL,
action VARCHAR(16) NOT NULL,
userid VARCHAR(32) NOT NULL,
message TEXT NOT NULL,
createdon DATETIME NOT NULL
);
CREATE INDEX log_index_1 ON log(createdon, projectid, type, action);
CREATE TABLE user_settings (
userid VARCHAR(32) PRIMARY KEY,
code_hide_line_num CHAR(1) NOT NULL,
code_hide_metadata CHAR(1) NOT NULL,
icon_name VARCHAR(255) UNIQUE NULL,
user_summary VARCHAR(255) NULL
);
CREATE TABLE user_account (
userid VARCHAR(32) PRIMARY KEY,
passwd VARCHAR(255) NOT NULL,
email VARCHAR(255),
enabled CHAR(1) NOT NULL DEFAULT 'N' CHECK(enabled in ('Y', 'N'))
);

View File

@ -0,0 +1,482 @@
#
# This file is not desinged to be used in conjuntion with other AAA providers.
# This file requires to be used alone as shown below for apache httpd2.
# You may change AuthName or SVNParentPath.
#
# <Location "/svn">
# DAV svn
# SVNParentPath "/var/lib/codepot/svnrepo"
# PerlAccessHandler Codepot::AccessHandler
# PerlAuthenHandler Codepot::AuthenHandler
# PerlSetEnv CODEPOT_CONFIG_FILE /etc/codepot/codepot.ini
# AuthType Basic
# AuthName "codepot"
# require valid-user
# </Location>
#
# If you do not move the handler files to the default library directory,
# a switch to indicate the location of the files are needed when loading
# the mod_perl module. Somewhere in your httpd configuration, specify
# the -Mlib switch.
#
# LoadModule perl_module modules/mod_perl.so
# PerlSwitches -Mlib=/etc/codepot/perl
#
package Codepot::AccessHandler;
use strict;
use warnings;
use Apache2::Access ();
use Apache2::RequestUtil ();
use Apache2::RequestRec ();
use Apache2::Log;
use APR::Table;
use APR::Base64;
use Config::Simple;
use Net::LDAP;
use URI;
use DBI;
use Digest::SHA;
use Apache2::Const -compile => qw(OK DECLINED FORBIDDEN HTTP_UNAUTHORIZED HTTP_INTERNAL_SERVER_ERROR PROXYREQ_PROXY AUTH_REQUIRED);
sub get_config
{
my $cfg = new Config::Simple();
if (!$cfg->read($ENV{'CODEPOT_CONFIG_FILE'}))
{
return undef;
}
my $config = {
login_model => $cfg->param('login_model'),
ldap_server_uri => $cfg->param('ldap_server_uri'),
ldap_server_protocol_version => $cfg->param('ldap_server_protocol_version'),
ldap_auth_mode => $cfg->param('ldap_auth_mode'),
ldap_userid_format => $cfg->param('ldap_userid_format'),
ldap_password_format => $cfg->param('ldap_password_format'),
ldap_admin_binddn => $cfg->param('ldap_admin_binddn'),
ldap_admin_password => $cfg->param('ldap_admin_password'),
ldap_userid_search_base => $cfg->param('ldap_userid_search_base'),
ldap_userid_search_filter => $cfg->param('ldap_userid_search_filter'),
ldap_insider_attribute_names => $cfg->param('ldap_insider_attribute_names'),
ldap_insider_attribute_value => $cfg->param('ldap_insider_attribute_value'),
database_hostname => $cfg->param('database_hostname'),
database_port => $cfg->param("database_port"),
database_username => $cfg->param('database_username'),
database_password => $cfg->param('database_password'),
database_name => $cfg->param('database_name'),
database_driver => $cfg->param('database_driver'),
database_prefix => $cfg->param('database_prefix'),
svn_read_access => $cfg->param('svn_read_access'),
svn_read_credential => $cfg->param('svn_read_credential')
};
return $config;
}
sub format_string
{
my ($fmt, $userid, $password) = @_;
my $out = $fmt;
$out =~ s/\$\{userid\}/$userid/g;
$out =~ s/\$\{password\}/$password/g;
return $out;
}
sub authenticate_ldap
{
my ($r, $cfg, $userid, $password) = @_;
my $binddn;
my $passwd;
my $uri = URI->new($cfg->{ldap_server_uri});
my $ldap = Net::LDAP->new(
$uri->host,
scheme => $uri->scheme,
port => $uri->port,
version => $cfg->{ldap_server_protocol_version}
);
if (!defined($ldap))
{
$r->log_error ('Cannot create LDAP');
return -1;
}
if ($cfg->{ldap_auth_mode} == 2)
{
my $f_rootdn = format_string($cfg->{ldap_admin_binddn}, $userid, $password);
my $f_rootpw = format_string($cfg->{ldap_admin_password}, $userid, $password);
my $f_basedn = format_string($cfg->{ldap_userid_search_base}, $userid, $password);
my $f_filter = format_string($cfg->{ldap_userid_search_filter}, $userid, $password);
my $res = $ldap->bind($f_rootdn, password => $f_rootpw);
if ($res->code != Net::LDAP::LDAP_SUCCESS)
{
$r->log_error ("Cannot bind LDAP as $f_rootdn - " . $res->error());
$ldap->unbind();
return -1;
}
$res = $ldap->search(base => $f_basedn, scope => 'sub', filter => $f_filter);
if ($res->code != Net::LDAP::LDAP_SUCCESS)
{
$ldap->unbind();
return 0;
}
my $entry = $res->entry(0); # get the first entry only
if (!defined($entry))
{
$ldap->unbind();
return 0;
}
$binddn = $entry->dn ();
}
else
{
$binddn = format_string ($cfg->{ldap_userid_format}, $userid, $password);
}
$passwd = format_string ($cfg->{ldap_password_format}, $userid, $password);
my $res = $ldap->bind ($binddn, password => $passwd);
if ($res->code != Net::LDAP::LDAP_SUCCESS)
{
#$r->log_error ("Cannot bind LDAP as $binddn - " . $res->error());
$ldap->unbind();
return 0;
}
my $authenticated = 1;
if ($cfg->{ldap_insider_attribute_names} ne '' && $cfg->{ldap_insider_attribute_value} ne '')
{
my $attr_str = $cfg->{ldap_insider_attribute_names};
$attr_str =~ s/^\s+|\s+$//g;
my @attrs = split(/\s+/, $attr_str);
if (scalar(@attrs) > 0)
{
#my $f_filter = '(' . $cfg->{ldap_insider_attribute_name} . '=*)';
my $f_filter = '(objectClass=*)';
$res = $ldap->search(base => $binddn, scope => 'base', filter => $f_filter, @attrs);
if ($res->code == Net::LDAP::LDAP_SUCCESS)
{
search_loop:
foreach my $entry ($res->entries)
{
foreach my $a (@attrs)
{
my @va = $entry->get_value($a);
foreach my $v (@va)
{
if (lc($v) eq lc($cfg->{ldap_insider_attribute_value}))
{
$authenticated = 2;
last search_loop;
}
}
}
}
$res->abandon();
}
}
}
$ldap->unbind();
return $authenticated;
}
sub authenticate_database
{
my ($dbh, $prefix, $userid, $password, $qc) = @_;
my $query = $dbh->prepare("SELECT ${qc}userid${qc},${qc}passwd${qc} FROM ${qc}${prefix}user_account${qc} WHERE ${qc}userid${qc}=? and ${qc}enabled${qc}='Y'");
if (!$query || !$query->execute ($userid))
{
return (-1, $dbh->errstr());
}
my @row = $query->fetchrow_array;
$query->finish ();
if (scalar(@row) <= 0) { return (0, undef); }
my $db_pw = $row[1];
if (length($db_pw) < 10) { return (0, undef); }
my $hexsalt = substr($db_pw, -10);
my $binsalt = pack('H*', $hexsalt);
my $fmt_pw = '{ssha1}' . Digest::SHA::sha1_hex($password . $binsalt) . $hexsalt;
return (($fmt_pw eq $db_pw? 1: 0), undef);
}
sub open_database
{
my ($cfg) = @_;
my $dbtype = $cfg->{database_driver};
my $dbname = $cfg->{database_name};
my $dbhost = $cfg->{database_hostname};
my $dbport = $cfg->{database_port};
if ($dbtype eq 'postgre') { $dbtype = 'Pg'; }
elsif ($dbtype eq 'oci8') { $dbtype = 'Oracle'; }
elsif ($dbtype eq 'mysqli') { $dbtype = 'mysql'; }
elsif ($dbtype eq 'sqlite') { $dbtype = 'SQLite'; }
my $dbstr;
my $dbuser;
my $dbpass;
if ($dbtype eq 'Oracle')
{
$dbstr = "DBI:$dbtype:";
$dbuser = $cfg->{database_username} . '/' . $cfg->{database_password} . '@' . $dbhost;
$dbpass = '';
}
elsif ($dbtype eq 'SQLite')
{
$dbstr = "DBI:$dbtype:database=$dbhost;";
$dbuser = $cfg->{database_username};
$dbpass = $cfg->{database_password};
}
else
{
$dbstr = "DBI:$dbtype:database=$dbname;";
if (length($dbhost) > 0) { $dbstr .= "host=$dbhost;"; }
if (length($dbport) > 0) { $dbstr .= "port=$dbport;"; }
$dbuser = $cfg->{database_username};
$dbpass = $cfg->{database_password};
}
my $dbh = DBI->connect(
$dbstr, $dbuser, $dbpass,
{ RaiseError => 0, PrintError => 0, AutoCommit => 0 }
);
return $dbh;
}
sub close_database
{
my ($dbh) = @_;
$dbh->disconnect ();
}
sub is_project_member
{
my ($dbh, $prefix, $projectid, $userid, $qc) = @_;
my $query = $dbh->prepare("SELECT ${qc}projectid${qc} FROM ${qc}${prefix}project_membership${qc} WHERE ${qc}userid${qc}=? AND ${qc}projectid${qc}=?");
if (!$query || !$query->execute ($userid, $projectid))
{
return (-1, $dbh->errstr());
}
my @row = $query->fetchrow_array;
$query->finish ();
return (((scalar(@row) > 0)? 1: 0), undef);
}
sub is_project_public
{
my ($dbh, $prefix, $projectid, $qc) = @_;
my $query = $dbh->prepare("SELECT ${qc}public${qc} FROM ${qc}${prefix}project${qc} WHERE ${qc}id${qc}=?");
if (!$query || !$query->execute ($projectid))
{
return (-1, $dbh->errstr());
}
my @row = $query->fetchrow_array;
$query->finish ();
return (((scalar(@row) > 0 && $row[0] eq 'Y')? 1: 0), undef);
}
sub is_read_method
{
my ($method) = @_;
return $method eq "GET" || $method eq "HEAD" ||
$method eq "OPTIONS" || $method eq "REPORT" ||
$method eq "PROPFIND";
}
sub __handler
{
my ($r, $cfg, $dbh) = @_;
my $method = uc($r->method());
my $is_method_r = is_read_method($method);
#my ($empty, $base, $repo, $dummy) = split('/', $r->uri(), 4);
my @urisegs = split('/', $r->uri());
my $repo = $urisegs[2];
my $author;
my $userid = undef;
my $password = undef;
my $public = undef;
my $member = undef;
my $errmsg = undef;
my $qc = '';
if ($cfg->{database_driver} eq 'oci8') { $qc = '"'; }
if ($r->proxyreq() == Apache2::Const::PROXYREQ_PROXY)
{
$author = $r->headers_in->{'Proxy-Authorization'};
}
else
{
$author = $r->headers_in->{'Authorization'};
}
if (defined($author))
{
my ($rc, $pass) = $r->get_basic_auth_pw();
if ($rc != Apache2::Const::OK) { return $rc; }
#$author = APR::Base64::decode((split(/ /,$author))[1]);
#($userid,$password) = split(/:/, $author);
$userid = $r->user();
$password = $pass;
}
if (!defined($userid)) { $userid = ""; }
if (!defined($password)) { $password = ""; }
if ($is_method_r)
{
($public, $errmsg) = is_project_public($dbh, $cfg->{database_prefix}, $repo, $qc);
if ($public <= -1)
{
# failed to contact the authentication server
$r->log_error ("Cannot check if a project is public - $errmsg");
return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
}
elsif ($public >= 1)
{
if (lc($cfg->{svn_read_access}) eq 'anonymous')
{
# grant an anonymous user the read access.
if (!defined($userid) || $userid eq '')
{
# httpd 2.4 emits the following message if the user is not set
# AH00027: No authentication done but request not allowed
# without authentication for /xxx/xxx. Authentication not configured?
$r->user('<codepot-anonymous-user>');
}
return Apache2::Const::OK;
}
elsif (defined($cfg->{svn_read_credential}) && $cfg->{svn_read_credential} ne '')
{
# security loop hole here.
my ($c_user, $c_pass) = split(/:/, $cfg->{svn_read_credential});
if ($c_user ne '' && $c_pass ne '' && $c_user eq $userid && $c_pass eq $password)
{
return Apache2::Const::OK;
}
}
}
}
my $auth = -3;
if ($cfg->{login_model} eq 'LdapLoginModel')
{
$auth = authenticate_ldap($r, $cfg, $userid, $password);
}
elsif ($cfg->{login_model} eq 'DbLoginModel')
{
($auth, $errmsg) = authenticate_database($dbh, $cfg->{database_prefix}, $userid, $password, $qc);
if ($auth <= -1)
{
$r->log_error ("Database error - $errmsg");
}
}
if ($auth <= -1)
{
# failed to contact the authentication server
return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
}
elsif ($auth == 0)
{
# authentication denied
$r->note_basic_auth_failure ();
return Apache2::Const::HTTP_UNAUTHORIZED;
}
# authentication successful.
if ($is_method_r && $public >= 1)
{
if (lc($cfg->{svn_read_access}) eq 'authenticated')
{
# grant read access to an authenticated user regardless of membership
# this applies to a public project only
return Apache2::Const::OK;
}
elsif (lc($cfg->{svn_read_access}) eq 'authenticated-insider')
{
if ($auth >= 2) { return Apache2::Const::OK; }
}
}
($member, $errmsg) = is_project_member($dbh, $cfg->{database_prefix}, $repo, $userid, $qc);
if ($member <= -1)
{
$r->log_error ("Cannot check project membership - $errmsg");
return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
}
elsif ($member == 0)
{
# access denined
return Apache2::Const::FORBIDDEN;
}
else
{
# the user is a member of project. access granted.
return Apache2::Const::OK;
}
}
sub handler: method
{
my ($class, $r) = @_;
my $res;
my $cfg;
$cfg = get_config();
if (!defined($cfg))
{
$r->log_error ('Cannot load configuration');
return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
}
my $dbh = open_database($cfg);
if (!defined($dbh))
{
$r->log_error ('Cannot open database - ' . $DBI::errstr);
return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
}
$res = __handler($r, $cfg, $dbh);
close_database ($dbh);
return $res;
}
1;

View File

@ -0,0 +1,19 @@
package Codepot::AuthenHandler;
use strict;
use warnings;
use Apache2::Access ();
use Apache2::RequestUtil ();
use Apache2::RequestRec ();
use Apache2::Log;
use Apache2::Const -compile => qw(OK DECLINED FORBIDDEN HTTP_UNAUTHORIZED HTTP_INTERNAL_SERVER_ERROR AUTH_REQUIRED);
use Data::Dumper;
sub handler: method
{
my ($class, $r) = @_;
return Apache2::Const::OK;
}
1;

565
etc/post-commit.in Normal file
View File

@ -0,0 +1,565 @@
#!/usr/bin/perl
use strict;
use Config::Simple;
use DBI;
use File::Basename;
use POSIX;
use SVN::Core;
use SVN::Repos;
use SVN::Fs;
use Net::LDAP;
use Net::LDAP qw(LDAP_SUCCESS);
use URI;
use Mail::Sendmail;
use LWP::UserAgent;
my $CFG_FILE = '@CFGDIR@/codepot.ini';
my $REPOFS = $ARGV[0];
my $REPOBASE = basename($REPOFS);
my $REV = $ARGV[1];
my $QC = '';
sub get_config
{
my $cfg = new Config::Simple();
if (!$cfg->read($CFG_FILE))
{
return undef;
}
my $config = {
login_model => $cfg->param('login_model'),
ldap_server_uri => $cfg->param('ldap_server_uri'),
ldap_server_protocol_version => $cfg->param('ldap_server_protocol_version'),
ldap_auth_mode => $cfg->param('ldap_auth_mode'),
ldap_userid_format => $cfg->param('ldap_userid_format'),
ldap_password_format => $cfg->param('ldap_password_format'),
ldap_admin_binddn => $cfg->param('ldap_admin_binddn'),
ldap_admin_password => $cfg->param('ldap_admin_password'),
ldap_userid_search_base => $cfg->param('ldap_userid_search_base'),
ldap_userid_search_filter => $cfg->param('ldap_userid_search_filter'),
ldap_mail_attribute_name => $cfg->param('ldap_mail_attribute_name'),
database_hostname => $cfg->param("database_hostname"),
database_port => $cfg->param("database_port"),
database_username => $cfg->param("database_username"),
database_password => $cfg->param("database_password"),
database_name => $cfg->param("database_name"),
database_driver => $cfg->param("database_driver"),
database_prefix => $cfg->param("database_prefix"),
database_store_gmt => $cfg->param("database_store_gmt"),
email_sender => $cfg->param("email_sender"),
commit_notification => $cfg->param("commit_notification"),
commit_notification_url => $cfg->param("commit_notification_url")
};
return $config;
}
sub open_database
{
my ($cfg) = @_;
my $dbtype = $cfg->{database_driver};
my $dbname = $cfg->{database_name};
my $dbhost = $cfg->{database_hostname};
my $dbport = $cfg->{database_port};
if ($dbtype eq 'postgre') { $dbtype = 'Pg'; }
elsif ($dbtype eq 'oci8') { $dbtype = 'Oracle'; }
elsif ($dbtype eq 'mysqli') { $dbtype = 'mysql'; }
elsif ($dbtype eq 'sqlite') { $dbtype = 'SQLite'; }
my $dbstr;
my $dbuser;
my $dbpass;
if ($dbtype eq 'Oracle')
{
$QC = '"';
$dbstr = "DBI:$dbtype:";
$dbuser = $cfg->{database_username} . '/' . $cfg->{database_password} . '@' . $dbhost;
$dbpass = '';
}
elsif ($dbtype eq 'SQLite')
{
$dbstr = "DBI:$dbtype:database=$dbhost;";
$dbuser = $cfg->{database_username};
$dbpass = $cfg->{database_password};
}
else
{
$dbstr = "DBI:$dbtype:database=$dbname;";
if (length($dbhost) > 0) { $dbstr .= "host=$dbhost;"; }
if (length($dbport) > 0) { $dbstr .= "port=$dbport;"; }
$dbuser = $cfg->{database_username};
$dbpass = $cfg->{database_password};
}
my $dbh = DBI->connect(
$dbstr, $dbuser, $dbpass,
{ RaiseError => 0, PrintError => 0, AutoCommit => 0 }
);
return $dbh;
}
sub close_database
{
my ($dbh) = @_;
$dbh->disconnect ();
}
sub find_issue_reference_in_commit_message
{
my ($dbh, $prefix, $projectid, $revision, $commit_message) = @_;
# find [[#IXXXX]]
my @issue_ids = ($commit_message =~ /\[\[#I(\d+)\]\]/g);
# find #XXXX
my @issue_ids2 = ($commit_message =~ /(^|[^#])#(\d+)(\D|$)/g);
# find unique issue ids in the findings.
my %tmp;
@tmp{@issue_ids} = 1;
for (my $i = 0; $i < scalar(@issue_ids2); $i += 3)
{
my $id = @issue_ids2[$i + 1];
@tmp{$id} = 1;
}
my @unique_issue_ids = keys(%tmp);
$dbh->begin_work ();
my $query = $dbh->prepare("DELETE FROM ${QC}${prefix}issue_coderev${QC} WHERE ${QC}codeproid${QC}=? AND ${QC}coderev${QC}=?");
if (!$query || !$query->execute($projectid, $revision))
{
my $errstr = $dbh->errstr();
if ($query) { $query->finish (); }
$dbh->rollback ();
return (-1, $errstr);
}
$query->finish ();
for my $issue_id(@unique_issue_ids)
{
my $query = $dbh->prepare("INSERT INTO ${QC}${prefix}issue_coderev${QC} (${QC}projectid${QC},${QC}issueid${QC},${QC}codeproid${QC},${QC}coderev${QC}) VALUES (?,?,?,?)");
if ($query)
{
# ignore errors
$query->execute ($projectid, $issue_id, $projectid, $revision);
$query->finish ();
}
}
$dbh->commit ();
return (0, undef);
}
sub write_commit_log
{
my ($dbh, $prefix, $projectid, $revision, $userid, $store_gmt) = @_;
#+------+---------+-----------+---------------------------+---------------------+---------------+-----------------+
#| id | type | projectid | message | createdon | action | userid |
#+------+---------+-----------+---------------------------+---------------------+---------------+-----------------+
#| 895 | code | codepot | svn,codepot,72 | 2011-10-10 14:26:43 | commit | hyunghwan.chung |
my $message = "svn,$projectid,$revision";
my $timestamp;
if (($store_gmt =~ /^\d+?$/ && int($store_gmt) != 0) || (lc($store_gmt) eq 'yes'))
{
$timestamp = POSIX::strftime('%Y-%m-%d %H:%M:%S', gmtime());
}
else
{
$timestamp = POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime());
}
# the PHP side is executing ALTER SESSION SET NLS_TIMESTAMP_FORMAT='YYYY-MM-DD HH24:MI:SS.FF.
# do i have to do it here or use the database time (CURRENT_TIMESTAMP) instead?
# make sure that you have the same time between the app server and the data server.
# to minize side-effect of using the time of data server.
#my $createdon = POSIX::strftime ('%Y-%m-%d %H:%M:%S', localtime());
$dbh->begin_work ();
#my $query = $dbh->prepare ("INSERT INTO ${QC}${prefix}log${QC} (${QC}type${QC},${QC}projectid${QC},${QC}message${QC},${QC}createdon${QC},${QC}action${QC},${QC}userid${QC}) VALUES (?,?,?,?,?,?)");
#if (!$query || !$query->execute('code', $projectid, $message, $createdon, 'commit', $userid))
my $query = $dbh->prepare ("INSERT INTO ${QC}${prefix}log${QC} (${QC}type${QC},${QC}projectid${QC},${QC}message${QC},${QC}createdon${QC},${QC}action${QC},${QC}userid${QC}) VALUES (?,?,?,?,?,?)");
if (!$query || !$query->execute('code', $projectid, $message, $timestamp, 'commit', $userid))
{
my $errstr = $dbh->errstr();
if ($query) { $query->finish (); }
$dbh->rollback ();
return (-1, $errstr);
}
$query->finish ();
$dbh->commit ();
return (0, undef);
}
sub get_author
{
my $pool = SVN::Pool->new(undef);
my $svn = eval { SVN::Repos::open ($REPOFS, $pool) };
if (!defined($svn))
{
print (STDERR "Cannot open svn - $REPOFS\n");
return undef;
}
my $fs = $svn->fs();
if (!defined($fs))
{
print (STDERR "Cannot open fs - $REPOFS\n");
return undef;
}
my $author = $fs->revision_prop ($REV, 'svn:author');
return $author;
}
sub get_commit_message
{
my $pool = SVN::Pool->new(undef);
my $svn = eval { SVN::Repos::open ($REPOFS, $pool) };
if (!defined($svn))
{
print (STDERR "Cannot open svn - $REPOFS\n");
return undef;
}
my $fs = $svn->fs();
if (!defined($fs))
{
print (STDERR "Cannot open fs - $REPOFS\n");
return undef;
}
my $logmsg = $fs->revision_prop($REV, 'svn:log');
return $logmsg;
}
sub format_string
{
my ($fmt, $userid, $password) = @_;
my $out = $fmt;
$out =~ s/\$\{userid\}/$userid/g;
$out =~ s/\$\{password\}/$password/g;
return $out;
}
sub get_author_email_ldap
{
my ($cfg, $userid) = @_;
my $uri = URI->new ($cfg->{ldap_server_uri});
my $ldap = Net::LDAP->new (
$uri->host,
scheme => $uri->scheme,
port => $uri->port,
version => $cfg->{ldap_server_protocol_version}
);
if (!defined($ldap))
{
print (STDERR 'Cannot create LDAP');
return (-1, undef);
}
my $f_rootdn = format_string($cfg->{ldap_admin_binddn}, $userid, '');
my $f_rootpw = format_string($cfg->{ldap_admin_password}, $userid, '');
my $res = $ldap->bind($f_rootdn, password => $f_rootpw);
if ($res->code != LDAP_SUCCESS)
{
print (STDERR "Cannot bind LDAP as $f_rootdn - " . $res->error());
$ldap->unbind();
return (-1, undef);
}
my $f_basedn = '';
my $f_filter = '';
if ($cfg->{ldap_auth_mode} == 2)
{
$f_basedn = format_string($cfg->{ldap_userid_search_base}, $userid, '');
$f_filter = format_string($cfg->{ldap_userid_search_filter}, $userid, '');
$res = $ldap->search(base => $f_basedn, scope => 'sub', filter => $f_filter);
if ($res->code != LDAP_SUCCESS)
{
$ldap->unbind();
return (-1, undef);
}
my $entry = $res->entry(0); # get the first entry only
if (!defined($entry))
{
$ldap->unbind();
return (0, undef);
}
$f_basedn = $entry->dn();
}
else
{
$f_basedn = format_string($cfg->{ldap_userid_format}, $userid, '');
}
$f_filter = '(' . $cfg->{ldap_mail_attribute_name} . '=*)';
$res = $ldap->search(base => $f_basedn, scope => 'sub', filter => $f_filter);
if ($res->code != LDAP_SUCCESS)
{
$ldap->unbind();
return (0, undef);
}
my $entry = $res->entry(0); # get the first entry only
if (!defined($entry))
{
$ldap->unbind();
return (0, undef);
}
my $xret = 0;
my $email = '';
my @attrs = $entry->attributes ();
foreach my $attr (@attrs)
{
if ($attr eq $cfg->{ldap_mail_attribute_name})
{
$email = $entry->get_value ($attr);
$xret = 1;
last;
}
}
$ldap->unbind();
return ($xret, $email);
}
sub get_author_email_db
{
my ($cfg, $dbh, $prefix, $userid) = @_;
my $query = $dbh->prepare("SELECT ${QC}email${QC} FROM ${QC}${prefix}user_account${QC} WHERE ${QC}userid${QC}=?");
if (!$query || !$query->execute($userid))
{
return (-1, $dbh->errstr());
}
if (my @row = $query->fetchrow_array())
{
return (1, @row[0]);
}
return (0, undef);
}
sub email_message_to_project_members
{
my ($cfg, $dbh, $prefix, $projectid, $subject, $message) = @_;
my $query = $dbh->prepare("SELECT ${QC}userid${QC} FROM ${QC}${prefix}project_membership${QC} WHERE ${QC}projectid${QC}=?");
if (!$query || !$query->execute($projectid))
{
if ($query) { $query->finish (); }
return (-1, $dbh->errstr());
}
my @members;
while (my @row = $query->fetchrow_array())
{
push (@members, $row[0]);
}
$query->finish ();
my $recipients = '';
foreach my $member (@members)
{
my $xret;
my $email;
if ($cfg->{login_model} eq 'LdapLoginModel')
{
($xret, $email) = get_author_email_ldap($cfg, $member)
}
elsif ($cfg->{login_model} eq 'DbLoginModel')
{
($xret, $email) = get_author_email_db($cfg, $dbh, $prefix, $member);
}
else
{
$xret = -2;
$email = '';
}
if ($xret >= 1 && defined($email) && length($email) > 0)
{
if (length($recipients) > 0) { $recipients .= ', '; }
$recipients .= $email;
}
}
if (length($recipients) <= 0) { return (0, undef); }
my %mail = (
To => $recipients,
Subject => $subject,
Message => $message
);
if (length($cfg->{email_sender}) > 0)
{
$mail{From} .= $cfg->{email_sender};
}
Mail::Sendmail::sendmail (%mail);
return (1, undef);
}
sub format_commit_url
{
my ($fmt, $projectid, $author, $rev) = @_;
my $out = $fmt;
$out =~ s/\$\{PROJECTID\}/$projectid/g;
$out =~ s/\$\{AUTHOR\}/$author/g;
$out =~ s/\$\{REV\}/$rev/g;
return $out;
}
sub trigger_webhooks
{
my ($cfg, $dbh, $prefix, $projectid, $commit_message) = @_;
# find [skip ci], [no ci] or something similar
my @skip = ();
while ($commit_message =~ /\[(skip|no)[[:space:]]+([[:alpha:]]+)\]/g)
{
push (@skip, $2);
}
my $query = $dbh->prepare("SELECT ${QC}webhooks${QC} FROM ${QC}${prefix}project${QC} WHERE ${QC}id${QC}=?");
if (!$query || !$query->execute($projectid))
{
if ($query) { $query->finish (); }
return (-1, $dbh->errstr());
}
my $webhooks = '';
if (my @row = $query->fetchrow_array())
{
$webhooks = $row[0];
}
my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 });
$ua->timeout (5);
foreach my $webhook (split(/\n/ ,$webhooks))
{
$webhook =~ s/^\s+|\s+$//g;
if ($webhook ne '')
{
my @tmp = split(/[[:space:]]+/, $webhook);
my $type = 'ci';
my $url = '';
if (scalar(@tmp) == 1) { $url = @tmp[0]; }
elsif (scalar(@tmp) == 2) { $type = @tmp[0]; $url = @tmp[1]; }
else { next; }
if (grep(/^$type$/, @skip)) { next; }
## TODO: some formatting on url?
my $res = $ua->get($url);
if ($res->is_success)
{
##print $res->decoded_content . "\n";
}
else
{
##print $res->status_line . "\n";
}
}
}
$query->finish ();
return (0, undef);
}
#------------------------------------------------------------
# MAIN
#------------------------------------------------------------
my $AUTHOR = get_author();
if (!defined($AUTHOR))
{
print (STDERR "Cannot get author for $REPOBASE $REV\n");
exit (1);
}
chomp ($AUTHOR);
my $cfg = get_config();
if (!defined($cfg))
{
print (STDERR "Cannot load codepot configuration file\n");
exit (1);
}
my $dbh = open_database($cfg);
if (!defined($dbh))
{
printf (STDERR "Cannot open database - %s\n", $DBI::errstr);
exit (1);
}
my $raw_commit_message = get_commit_message();
find_issue_reference_in_commit_message ($dbh, $cfg->{database_prefix}, $REPOBASE, $REV, $raw_commit_message);
write_commit_log ($dbh, $cfg->{database_prefix}, $REPOBASE, $REV, $AUTHOR, $cfg->{database_store_gmt});
if (lc($cfg->{commit_notification}) eq 'yes')
{
my $commit_subject = "Commit r$REV by $AUTHOR in $REPOBASE";
my $commit_message = '';
if ($cfg->{commit_notification_url} eq '')
{
$commit_message = $commit_subject;
}
else
{
$commit_message = format_commit_url($cfg->{commit_notification_url}, $REPOBASE, $AUTHOR, $REV);
}
if (defined($raw_commit_message))
{
$commit_message = $commit_message . "\n" . $raw_commit_message;
}
email_message_to_project_members ($cfg, $dbh, $cfg->{database_prefix}, $REPOBASE, $commit_subject, $commit_message);
}
trigger_webhooks ($cfg, $dbh, $cfg->{database_prefix}, $REPOBASE, $raw_commit_message);
close_database ($dbh);
exit (0);

233
etc/post-revprop-change.in Normal file
View File

@ -0,0 +1,233 @@
#!/usr/bin/perl
use strict;
use Config::Simple;
use DBI;
use File::Basename;
use POSIX;
use SVN::Core;
use SVN::Repos;
use SVN::Fs;
my $CFG_FILE = '@CFGDIR@/codepot.ini';
my $REPOFS = $ARGV[0];
my $REPOBASE = basename($REPOFS);
my $REV = $ARGV[1];
my $USER = $ARGV[2];
my $PROPNAME = $ARGV[3];
my $ACTION = $ARGV[4];
my $QC = '';
# [STDIN] PROPVAL ** the old property value is passed via STDIN.
sub get_config
{
my $cfg = new Config::Simple();
if (!$cfg->read ($CFG_FILE))
{
return undef;
}
my $config = {
database_hostname => $cfg->param ("database_hostname"),
database_port => $cfg->param ("database_port"),
database_username => $cfg->param ("database_username"),
database_password => $cfg->param ("database_password"),
database_name => $cfg->param ("database_name"),
database_driver => $cfg->param ("database_driver"),
database_prefix => $cfg->param ("database_prefix"),
database_store_gmt => $cfg->param ("database_store_gmt")
};
return $config;
}
sub open_database
{
my ($cfg) = @_;
my $dbtype = $cfg->{database_driver};
my $dbname = $cfg->{database_name};
my $dbhost = $cfg->{database_hostname};
my $dbport = $cfg->{database_port};
if ($dbtype eq 'postgre') { $dbtype = 'Pg'; }
elsif ($dbtype eq 'oci8') { $dbtype = 'Oracle'; }
elsif ($dbtype eq 'mysqli') { $dbtype = 'mysql'; }
elsif ($dbtype eq 'sqlite') { $dbtype = 'SQLite'; }
my $dbstr;
my $dbuser;
my $dbpass;
if ($dbtype eq 'Oracle')
{
$QC = '"';
$dbstr = "DBI:$dbtype:";
$dbuser = $cfg->{database_username} . '/' . $cfg->{database_password} . '@' . $dbhost;
$dbpass = '';
}
elsif ($dbtype eq 'SQLite')
{
$dbstr = "DBI:$dbtype:database=$dbhost;";
$dbuser = $cfg->{database_username};
$dbpass = $cfg->{database_password};
}
else
{
$dbstr = "DBI:$dbtype:database=$dbname;";
if (length($dbhost) > 0) { $dbstr .= "host=$dbhost;"; }
if (length($dbport) > 0) { $dbstr .= "port=$dbport;"; }
$dbuser = $cfg->{database_username};
$dbpass = $cfg->{database_password};
}
my $dbh = DBI->connect(
$dbstr, $dbuser, $dbpass,
{ RaiseError => 0, PrintError => 0, AutoCommit => 0 }
);
return $dbh;
}
sub close_database
{
my ($dbh) = @_;
$dbh->disconnect ();
}
sub find_issue_reference_in_commit_message
{
my ($dbh, $prefix, $projectid, $revision, $commit_message) = @_;
# find [[#IXXXX]]
my @issue_ids = ($commit_message =~ /\[\[#I(\d+)\]\]/g);
# find #XXXX
my @issue_ids2 = ($commit_message =~ /(^|[^#])#(\d+)(\D|$)/g);
# find unique issue ids in the findings.
my %tmp;
@tmp{@issue_ids} = 1;
for (my $i = 0; $i < scalar(@issue_ids2); $i += 3)
{
my $id = @issue_ids2[$i + 1];
@tmp{$id} = 1;
}
my @unique_issue_ids = keys(%tmp);
$dbh->begin_work ();
my $query = $dbh->prepare ("DELETE FROM ${QC}${prefix}issue_coderev${QC} WHERE ${QC}codeproid${QC}=? AND ${QC}coderev${QC}=?");
if (!$query || !$query->execute ($projectid, $revision))
{
my $errstr = $dbh->errstr();
if ($query) { $query->finish (); }
$dbh->rollback ();
return (-1, $errstr);
}
$query->finish ();
for my $issue_id(@unique_issue_ids)
{
my $query = $dbh->prepare ("INSERT INTO ${QC}${prefix}issue_coderev${QC} (${QC}projectid${QC},${QC}issueid${QC},${QC}codeproid${QC},${QC}coderev${QC}) VALUES (?,?,?,?)");
if ($query)
{
# ignore errors
$query->execute ($projectid, $issue_id, $projectid, $revision);
$query->finish ();
}
}
$dbh->commit ();
return (0, undef);
}
sub write_revprop_change_log
{
my ($dbh, $prefix, $projectid, $revision, $userid, $propname, $action, $store_gmt) = @_;
#+------+---------+-----------+---------------------------+---------------------+---------------+-----------------+
#| id | type | projectid | message | createdon | action | userid |
#+------+---------+-----------+---------------------------+---------------------+---------------+-----------------+
#| 1610 | code | codepot | svn,codepot,98,svn:log,M | 2014-05-16 22:27:36 | revpropchange | hyunghwan.chung |
my $message = "svn,$projectid,$revision,$propname,$action";
my $timestamp;
if (($store_gmt =~ /^\d+?$/ && int($store_gmt) != 0) || (lc($store_gmt) eq 'yes'))
{
$timestamp = POSIX::strftime ('%Y-%m-%d %H:%M:%S', gmtime());
}
else
{
$timestamp = POSIX::strftime ('%Y-%m-%d %H:%M:%S', localtime());
}
$dbh->begin_work ();
my $query = $dbh->prepare ("INSERT INTO ${QC}${prefix}log${QC} (${QC}type${QC},${QC}projectid${QC},${QC}message${QC},${QC}createdon${QC},${QC}action${QC},${QC}userid${QC}) VALUES (?,?,?,?,?,?)");
if (!$query || !$query->execute ('code', $projectid, $message, $timestamp, 'revpropchange', $userid))
{
my $errstr = $dbh->errstr();
if ($query) { $query->finish (); }
$dbh->rollback ();
return (-1, $errstr);
}
$query->finish ();
$dbh->commit ();
return (0, undef);
}
sub get_commit_message
{
my $pool = SVN::Pool->new(undef);
my $svn = eval { SVN::Repos::open ($REPOFS, $pool) };
if (!defined($svn))
{
print (STDERR "Cannot open svn - $REPOFS\n");
return undef;
}
my $fs = $svn->fs ();
if (!defined($fs))
{
print (STDERR "Cannot open fs - $REPOFS\n");
return undef;
}
my $logmsg = $fs->revision_prop ($REV, 'svn:log');
return $logmsg;
}
#------------------------------------------------------------
# MAIN
#------------------------------------------------------------
my $cfg = get_config ();
if (!defined($cfg))
{
print (STDERR "Cannot load codepot configuration file\n");
exit (1);
}
my $dbh = open_database ($cfg);
if (!defined($dbh))
{
printf (STDERR "Cannot open database - %s\n", $DBI::errstr);
exit (1);
}
my $raw_commit_message = get_commit_message();
find_issue_reference_in_commit_message ($dbh, $cfg->{database_prefix}, $REPOBASE, $REV, $raw_commit_message);
write_revprop_change_log ($dbh, $cfg->{database_prefix}, $REPOBASE, $REV, $USER, $PROPNAME, $ACTION, $cfg->{database_store_gmt});
close_database ($dbh);
exit (0);

725
etc/pre-commit.in Normal file
View File

@ -0,0 +1,725 @@
#!/usr/bin/perl
use strict;
use Config::Simple;
use DBI;
use File::Basename;
use SVN::Core;
use SVN::Repos;
use SVN::Fs;
my $CFG_FILE = '@CFGDIR@/codepot.ini';
my $REPOFS = $ARGV[0];
my $REPOBASE = basename($REPOFS);
my $TRANSACTION = $ARGV[1];
my $QC = '';
my %SVN_ACTIONS =
(
'A ' => 'add',
'U ' => 'update',
'D ' => 'delete',
'_U' => 'propset',
'UU' => 'update/propset'
);
my %SVN_ACTION_VERBS =
(
$SVN::Fs::PathChange::modify => 'modify',
$SVN::Fs::PathChange::add => 'add',
$SVN::Fs::PathChange::delete => 'delete',
$SVN::Fs::PathChange::replace => 'replace'
);
sub get_config
{
my $cfg = new Config::Simple();
if (!$cfg->read ($CFG_FILE))
{
return undef;
}
my $config = {
database_hostname => $cfg->param ('database_hostname'),
database_port => $cfg->param ("database_port"),
database_username => $cfg->param ('database_username'),
database_password => $cfg->param ('database_password'),
database_name => $cfg->param ('database_name'),
database_driver => $cfg->param ('database_driver'),
database_prefix => $cfg->param ('database_prefix'),
svn_min_commit_message_length => $cfg->param ('svn_min_commit_message_length'),
svn_restricted_topdirs => $cfg->param('svn_restricted_topdirs'),
svn_restriction_allowed_subdir_depth_min => $cfg->param('svn_restriction_allowed_subdir_depth_min'),
svn_restriction_allowed_subdir_depth_max => $cfg->param('svn_restriction_allowed_subdir_depth_max')
};
return $config;
}
sub open_database
{
my ($cfg) = @_;
my $dbtype = $cfg->{database_driver};
my $dbname = $cfg->{database_name};
my $dbhost = $cfg->{database_hostname};
my $dbport = $cfg->{database_port};
if ($dbtype eq 'postgre') { $dbtype = 'Pg'; }
elsif ($dbtype eq 'oci8') { $dbtype = 'Oracle'; }
elsif ($dbtype eq 'mysqli') { $dbtype = 'mysql'; }
elsif ($dbtype eq 'sqlite') { $dbtype = 'SQLite'; }
my $dbstr;
my $dbuser;
my $dbpass;
if ($dbtype eq 'Oracle')
{
$QC = '"';
$dbstr = "DBI:$dbtype:";
$dbuser = $cfg->{database_username} . '/' . $cfg->{database_password} . '@' . $dbhost;
$dbpass = '';
}
elsif ($dbtype eq 'SQLite')
{
$dbstr = "DBI:$dbtype:database=$dbhost;";
$dbuser = $cfg->{database_username};
$dbpass = $cfg->{database_password};
}
else
{
$dbstr = "DBI:$dbtype:database=$dbname;";
if (length($dbhost) > 0) { $dbstr .= "host=$dbhost;"; }
if (length($dbport) > 0) { $dbstr .= "port=$dbport;"; }
$dbuser = $cfg->{database_username};
$dbpass = $cfg->{database_password};
}
my $dbh = DBI->connect(
$dbstr, $dbuser, $dbpass,
{ RaiseError => 0, PrintError => 0, AutoCommit => 0 }
);
return $dbh;
}
sub close_database
{
my ($dbh) = @_;
$dbh->disconnect ();
}
sub is_project_member
{
my ($dbh, $prefix, $projectid, $userid) = @_;
my $query = $dbh->prepare ("SELECT ${QC}projectid${QC} FROM ${QC}${prefix}project_membership${QC} WHERE ${QC}userid${QC}=? AND ${QC}projectid${QC}=?");
if (!$query || !$query->execute ($userid, $projectid))
{
return (-1, $dbh->errstr());
}
my @row = $query->fetchrow_array;
$query->finish ();
return (((scalar(@row) > 0)? 1: 0), undef);
}
sub is_project_commitable
{
my ($dbh, $prefix, $projectid) = @_;
my $query = $dbh->prepare ("SELECT ${QC}commitable${QC} FROM ${QC}${prefix}project${QC} WHERE ${QC}id${QC}=?");
if (!$query || !$query->execute ($projectid))
{
return (-1, $dbh->errstr());
}
my @row = $query->fetchrow_array;
$query->finish ();
return (((scalar(@row) > 0 && $row[0] eq 'Y')? 1: 0), undef);
}
sub contains_repeated_chars
{
my ($str, $limit) = @_;
my $len = length($str);
my $lc = '';
my $count = 1;
for (my $i = 0; $i < $len; $i++)
{
my $c = substr($str, $i, 1);
if ($lc eq $c)
{
$count++;
if ($count > $limit) { return 1; }
}
else
{
$count = 1;
$lc = $c;
}
}
return 0;
}
sub check_commit_message
{
my ($minlen) = @_;
my $pool = SVN::Pool->new(undef);
#my $config = SVN::Core::config_get_config(undef);
#my $fs = eval { SVN::Fs::open ($REPOFS, $config, $pool) };
my $svn = eval { SVN::Repos::open ($REPOFS, $pool) };
if (!defined($svn))
{
print (STDERR "Cannot open svn - $REPOFS\n");
return -1; # error
}
my $fs = $svn->fs ();
if (!defined($fs))
{
print (STDERR "Cannot open fs - $REPOFS\n");
return -1; # error
}
my $txn = eval { $fs->open_txn ($TRANSACTION) };
if (!defined($txn))
{
print (STDERR "Cannot open transaction - $TRANSACTION\n");
return -1;
}
my $log = $txn->prop ('svn:log');
# TODO: block a certain message patterns. create a configuration item
# for this
#if ($log =~ /[[:punct:]]{5,}/ || $log =~ /[[:alpha:]]{40,}/ || contains_repeated_chars($log, 4))
#{
# print (STDERR "Commit message rejected\n");
# return 0;
#}
$log =~ s/\s{2,}/ /g;
$log =~ s/([[:punct:]]{1,2}\s+){3,}/ /g;
$log =~ s/[[:punct:]]{3,}/ /g;
$log =~ s/^\s+|\s+$//g; # trim leading spaces and trailing spaces
if (length($log) < $minlen)
{
print (STDERR "Commit message too short. meaningful part must be >= $minlen\n");
return 0;
}
if ($log =~ /^[[:punct:][:space:]]+$/)
{
print (STDERR "Commit message meaningless\n");
return 0;
}
return 1;
}
sub restrict_changes_in_directory_old
{
my ($dir, $min_level, $max_level) = @_;
my @change_info = `svnlook changed --copy-info -t "${TRANSACTION}" "${REPOFS}"`;
# 'A ' Item added to repository
# 'D ' Item deleted from repository
# 'U ' File contents changed
# '_U' Properties of item changed; note the leading underscore
# 'UU' File contents and properties changed
# ------------------------------------------------------------
# + on the third column to indicate copy
# fourth column is empty.
# ------------------------------------------------------------
# When copy-info is used, the source of the copy is shown
# on the next line aligned at the file name part and
# begins with spaces.
#
# A + y/t/
# (from c/:r2)
# ------------------------------------------------------------
#
# Renaming a file in the copied directory looks like this.
# D tags/xxx-1.2.3/2/screenrc
# A + tags/xxx-1.2.3/2/screenrc.x
# (from tags/xxx-1.2.3/2/screenrc:r10)
#
# If the deletion of the file is disallowed, the whole
# transaction is blocked. so I don't need to care about
# copied addition.
# ------------------------------------------------------------
foreach my $line(@change_info)
{
chomp ($line);
print (STDERR "... CHANGE INFO => $line\n");
}
my $disallowed = 0;
while (@change_info) #foreach my $line(@change_info)
{
my $line = shift (@change_info);
chomp ($line);
if ($line =~ /^(A |U |D |_U|UU) ${dir}\/(.*)$/)
{
my $action = "${1}";
my $affected_file = "${dir}/${2}";
my $affected_file_nodir = "${2}";
my $action_verb = $SVN_ACTIONS{$action};
if (rindex($affected_file, '/') + 1 == length($affected_file))
{
# the last character is a slash. so it's a directory.
# let's allow most of the operations on a directory.
#if ($action eq 'D ')
#{
my @segs = split ('/', $affected_file_nodir);
my $num_segs = scalar(@segs);
# NOTE: for a string like abc/def/, split() seems to return 2 segments only.
if ($affected_file_nodir eq '')
{
# it is the main directory itself.
# allow operation on it.
}
elsif ($num_segs < $min_level || $num_segs > $max_level)
{
# disallow deletion if the directory name to be deleted
# matches a tag pattern
print (STDERR "Disallowed to ${action_verb} a directory - ${affected_file}\n");
$disallowed++;
}
#}
}
else
{
print (STDERR "Disallowed to ${action_verb} a file - ${affected_file}\n");
$disallowed++;
}
}
elsif ($line =~ /^(A )\+ ${dir}\/(.*)$/)
{
my $action = "${1}";
my $affected_file = "${dir}/${2}";
# copying
#
# A + tags/xxx-1.2.3/2/smi.conf.2
# (from tags/xxx-1.2.3/2/smi.conf:r10)
#
my $source_line = shift (@change_info);
chomp ($source_line);
if ($source_line =~ /
^ # beginning of string
\W* # 0 or more white-spaces
\( # opening parenthesis
\S+ # 1 or more non-space characters
\W+ # 1 or more space characters
(.+) # 1 or more characters
:r[0-9]+ # :rXXX where XXX is digits
\) # closing parenthesis
$ # end of string
/x)
{
my $source_file = "${1}";
if (rindex($affected_file, '/') + 1 != length($affected_file))
{
# the file beging added by copyiung is not a directory.
# it disallows individual file copying.
# copy a whole directory at one go.
print (STDERR "Disallowed to copy $source_file to $affected_file\n");
$disallowed++;
}
elsif ($source_file =~ /^${dir}\/(.*)$/)
{
# i don't want to be a copied file or directory to be
# a source of another copy operation.
print (STDERR "Disallowed to copy $source_file to $affected_file\n");
$disallowed++;
}
else
{
# Assume xxx is a directory.
# Assume min_level is 1 and max_level is 2.
#
# If the following two commans are executed,
# svn copy trunk/xxx tags/my-4.0.0
# svn copy trunk/xxx tags/my-4.0.0/1
#
# svnlook returns the following text.
# A + tags/my-4.0.0/
# (from trunk/xxx/:r16)
# A + tags/my-4.0.0/1/
# (from trunk/xxx/:r16)
#
# if the script knows that tags/my-4.0.0 is created via copying,
# i want this script to prevent copying other sources into it.
# this case is not fully handled by this script.
# TODO: DISALLOW THIS if the parent directory is a copied directory
my $pardir = dirname ($affected_file);
}
}
}
#else
#{
# print (STDERR "OK ... ${line}\n");
#}
}
return ($disallowed > 0)? -1: 0;
}
sub restrict_changes_in_topdirs
{
my ($min_level, $max_level, @topdirs) = @_;
my $disallowed = 0;
my $pool = SVN::Pool->new(undef);
#my $config = SVN::Core::config_get_config(undef);
#my $fs = eval { SVN::Fs::open ($REPOFS, $config, $pool) };
my $svn = eval { SVN::Repos::open ($REPOFS, $pool) };
if (!defined($svn))
{
print (STDERR "Cannot open svn - $REPOFS\n");
return -1; # error
}
my $fs = $svn->fs ();
if (!defined($fs))
{
print (STDERR "Cannot open fs - $REPOFS\n");
return -1; # error
}
my $txn = eval { $fs->open_txn ($TRANSACTION) };
if (!defined($txn))
{
print (STDERR "Cannot open transaction - $TRANSACTION\n");
return -1;
}
my $root = eval { $txn->root() };
if (!defined($root))
{
print (STDERR "Cannot open root of transaction - $TRANSACTION\n");
return -1;
}
my $paths_changed = eval { $root->paths_changed() };
if (!defined($paths_changed))
{
# no change information found. return ok
$root->close_root ();
return 0;
}
foreach my $affected_file(keys(%$paths_changed))
{
my $chg = $paths_changed->{$affected_file};
my $action = $chg->change_kind();
my $action_verb = $SVN_ACTION_VERBS{$action};
if (length($action_verb) <= 0) { $action_verb = "work on"; }
my $is_source_file_dir = 0;
my $is_affected_file_dir = eval { $root->is_dir($affected_file) };
#$chg->text_mod(), $chg->prop_mod()
#my $affected_rev_id = eval { SVN::Fs::unparse_id($chg->node_rev_id()) };
my $source_file = undef;
#my $source_id = undef;
if ($action == $SVN::Fs::PathChange::add)
{
$source_file = eval { $root->copied_from($affected_file) };
#if ($source_file)
#{
# $source_id = eval { SVN::Fs::unparse_id($root->node_id($source_file)) };
#}
}
elsif ($action == $SVN::Fs::PathChange::delete)
{
# when a file is deleted, $root->is_dir() doesn't seem to
# return the right type. use the revision root to determine it.
my $rev_root = $fs->revision_root($fs->youngest_rev());
$is_affected_file_dir = eval { $rev_root->is_dir ($affected_file) };
$rev_root->close_root();
}
#print STDERR "@@@@@ [$affected_file] [$action_verb] [$source_file] [$is_source_file_dir] [$is_affected_file_dir]\n";
foreach my $topdir(@topdirs)
{
if ($affected_file =~ /^\/${topdir}\/(.*)$/)
{
# the affected file is located under the given directory.
my $affected_file_nodir = "${1}";
if (defined($source_file))
{
# it's being copied.
if (!$is_affected_file_dir)
{
# the file beging added by copying is not a directory.
# it disallows individual file copying.
# copy a whole directory at one go.
print (STDERR "Disallowed to copy ${source_file} to ${affected_file}\n");
$disallowed++;
}
elsif ($source_file =~ /^\/${topdir}\/(.*)$/)
{
# i don't want to be a copied file or directory to be
# a source of another copy operation.
print (STDERR "Disallowed to copy ${source_file} to ${affected_file}\n");
$disallowed++;
}
else
{
# TODO: DISALLOW THIS if the parent directory is a copied directory
#my $pardir = dirname ($affected_file);
}
}
else
{
if ($is_affected_file_dir)
{
my @segs = split ('/', $affected_file_nodir);
my $num_segs = scalar(@segs);
# NOTE: for a string like abc/def/, split() seems to return 2 segments only.
if ($affected_file_nodir eq '')
{
# it is the main directory itself.
# allow operation on it.
}
elsif ($num_segs < $min_level || $num_segs > $max_level)
{
# disallow deletion if the directory name to be deleted
# matches a tag pattern
print (STDERR "Disallowed to ${action_verb} a directory - ${affected_file}\n");
$disallowed++;
}
}
else
{
print (STDERR "Disallowed to ${action_verb} a file - ${affected_file}\n");
$disallowed++;
}
}
}
}
}
# 'svn rename' within the restricted directory is disallowed because
# it splits to deletion and addition. for this reason, you're supposed
# to copy from the trunk or branch source again.
#
# $ svn rename tags/my-1.0.0 tags/my-2.0.0
# $ svn commit -m "XXXXXXXXXXXX"
# Deleting tags/my-1.0.0
# Adding tags/my-2.0.0
#
$root->close_root ();
return ($disallowed > 0)? -1: 0;
}
sub restrict_read_only_files()
{
my $disallowed = 0;
my $pool = SVN::Pool->new(undef);
#my $config = SVN::Core::config_get_config(undef);
#my $fs = eval { SVN::Fs::open($REPOFS, $config, $pool) };
my $svn = eval { SVN::Repos::open($REPOFS, $pool) };
if (!defined($svn))
{
print (STDERR "Cannot open svn - $REPOFS\n");
return -1; # error
}
my $fs = $svn->fs();
if (!defined($fs))
{
print (STDERR "Cannot open fs - $REPOFS\n");
return -1; # error
}
my $txn = eval { $fs->open_txn ($TRANSACTION) };
if (!defined($txn))
{
print (STDERR "Cannot open transaction - $TRANSACTION\n");
return -1;
}
my $root = eval { $txn->root() };
if (!defined($root))
{
print (STDERR "Cannot open root of transaction - $TRANSACTION\n");
return -1;
}
my $paths_changed = eval { $root->paths_changed() };
if (!defined($paths_changed))
{
# no change information found. return ok
$root->close_root ();
return 0;
}
my $fs_root = $fs->revision_root($fs->youngest_rev());
foreach my $affected_file(keys(%$paths_changed))
{
my $chg = $paths_changed->{$affected_file};
my $action = $chg->change_kind();
my $action_verb = $SVN_ACTION_VERBS{$action};
if (length($action_verb) <= 0) { $action_verb = "work on"; }
## check codepot:readonly only if there is content change.
## property-only change is always allowed.
## directory addition is probably allowed. ## TODO: prevent this?
next if ($action != $SVN::Fs::PathChange::delete && !$chg->text_mod());
my $file = $affected_file;
my $readonly = eval { $fs_root->node_prop($file, 'codepot:readonly') };
if ($readonly eq 'yes')
{
$disallowed = 1;
print (STDERR "Unable to $action_verb $file - read-only\n");
}
elsif ($readonly eq 'no')
{
## no is set explicitly on the node itself.
## don't proceed to check the parent side.
## change is granted immediately.
## DO NOTHING HERE
}
else
{
## check permission in the parent side
while ((my $slash = rindex($file, "/")) >= 0)
{
$file = substr($file, 0, $slash);
my $tmp = $file;
$tmp = '/' if ($tmp eq '');
$readonly = eval { $fs_root->node_prop($tmp, 'codepot:readonly') };
if ($readonly eq 'yes')
{
$disallowed = 1;
print (STDERR "Unable to $action_verb $affected_file - $tmp set to read-only\n");
}
elsif ($readonly eq 'no')
{
last;
}
}
}
}
$root->close_root ();
return ($disallowed > 0)? -1: 0;
}
#------------------------------------------------------------
# MAIN
#------------------------------------------------------------
my $cfg = get_config ();
if (!defined($cfg))
{
print (STDERR "Cannot load codepot configuration file\n");
exit (1);
}
if (check_commit_message ($cfg->{svn_min_commit_message_length}) <= 0)
{
exit (1);
}
# TODO: enable per-project settings for topdir restriction
my $min_level = $cfg->{svn_restriction_allowed_subdir_depth_min};
if (!defined($min_level)) { $min_level = 0; }
my $max_level = $cfg->{svn_restriction_allowed_subdir_depth_max};
if (!defined($max_level)) { $max_level = 0; }
my $topdirs = $cfg->{svn_restricted_topdirs};
if (defined($topdirs))
{
my @topdir_array = split (/\s*,\s*/, $topdirs);
if (scalar(@topdir_array) > 0)
{
if (restrict_changes_in_topdirs ($min_level, $max_level, @topdir_array) <= -1)
{
exit (1);
}
}
}
if (restrict_read_only_files() <= -1)
{
exit (1);
}
#my $dbh = open_database ($cfg);
#if (!defined($dbh))
#{
# printf (STDERR "Cannot open database - %s\n", $DBI::errstr);
# exit (1);
#}
#
#my $member;
#my $commitable;
#my $errstr;
#
#($member, $errstr) = is_project_member (
# $dbh, $cfg->{database_prefix}, $REPOBASE, $USER);
#if ($member <= -1)
#{
# print (STDERR "Cannot check membership - $errstr\n");
# close_database ($dbh);
# exit (1);
#}
#
#($commitable, $errstr) = is_project_commitable (
# $dbh, $cfg->{database_prefix}, $REPOBASE);
#if ($commitable <= -1)
#{
# print (STDERR "Cannot check commitability - $errstr\n");
# close_database ($dbh);
# exit (1);
#}
#
#close_database ($dbh);
#
#if ($member == 0)
#{
# print (STDERR "$USER doesn't belong to the $REPOBASE project\n");
# exit (1);
#}
#
#if ($commitable == 0)
#{
# print (STDERR "The $REPOBASE project is not commitable\n");
# exit (1);
#}
#
exit (0);

237
etc/pre-revprop-change.in Normal file
View File

@ -0,0 +1,237 @@
#!/usr/bin/perl
use strict;
use Config::Simple;
use DBI;
use File::Basename;
#use SVN::Core;
#use SVN::Repos;
#use SVN::Fs;
my $CFG_FILE = '@CFGDIR@/codepot.ini';
my $REPOFS = $ARGV[0];
my $REPOBASE = basename($REPOFS);
my $REVISION= $ARGV[1];
my $USER = $ARGV[2];
my $PROPNAME = $ARGV[3];
my $ACTION = $ARGV[4];
my $QC = '';
sub get_config
{
my $cfg = new Config::Simple();
if (!$cfg->read ($CFG_FILE))
{
return undef;
}
my $config = {
database_hostname => $cfg->param ("database_hostname"),
database_port => $cfg->param ("database_port"),
database_username => $cfg->param ("database_username"),
database_password => $cfg->param ("database_password"),
database_name => $cfg->param ("database_name"),
database_driver => $cfg->param ("database_driver"),
database_prefix => $cfg->param ("database_prefix"),
svn_min_commit_message_length => $cfg->param ('svn_min_commit_message_length')
};
return $config;
}
sub open_database
{
my ($cfg) = @_;
my $dbtype = $cfg->{database_driver};
my $dbname = $cfg->{database_name};
my $dbhost = $cfg->{database_hostname};
my $dbport = $cfg->{database_port};
if ($dbtype eq 'postgre') { $dbtype = 'Pg'; }
elsif ($dbtype eq 'oci8') { $dbtype = 'Oracle'; }
elsif ($dbtype eq 'mysqli') { $dbtype = 'mysql'; }
elsif ($dbtype eq 'sqlite') { $dbtype = 'SQLite'; }
my $dbstr;
my $dbuser;
my $dbpass;
if ($dbtype eq 'Oracle')
{
$QC = '"';
$dbstr = "DBI:$dbtype:";
$dbuser = $cfg->{database_username} . '/' . $cfg->{database_password} . '@' . $dbhost;
$dbpass = '';
}
elsif ($dbtype eq 'SQLite')
{
$dbstr = "DBI:$dbtype:database=$dbhost;";
$dbuser = $cfg->{database_username};
$dbpass = $cfg->{database_password};
}
else
{
$dbstr = "DBI:$dbtype:database=$dbname;";
if (length($dbhost) > 0) { $dbstr .= "host=$dbhost;"; }
if (length($dbport) > 0) { $dbstr .= "port=$dbport;"; }
$dbuser = $cfg->{database_username};
$dbpass = $cfg->{database_password};
}
my $dbh = DBI->connect(
$dbstr, $dbuser, $dbpass,
{ RaiseError => 0, PrintError => 0, AutoCommit => 0 }
);
return $dbh;
}
sub close_database
{
my ($dbh) = @_;
$dbh->disconnect ();
}
sub is_project_member
{
my ($dbh, $prefix, $projectid, $userid) = @_;
my $query = $dbh->prepare ("SELECT ${QC}projectid${QC} FROM ${QC}${prefix}project_membership${QC} WHERE ${QC}userid${QC}=? AND ${QC}projectid${QC}=?");
if (!$query || !$query->execute ($userid, $projectid))
{
return (-1, $dbh->errstr());
}
my @row = $query->fetchrow_array;
$query->finish ();
return (((scalar(@row) > 0)? 1: 0), undef);
}
#sub check_commit_message
#{
# my ($minlen, $newmsg) = @_;
#
# my $pool = SVN::Pool->new(undef);
# my $svn = eval { SVN::Repos::open ($REPOFS, $pool) };
# if (!defined($svn))
# {
# print (STDERR "Cannot open svn - $REPOFS\n");
# return -1; # error
# }
#
# my $fs = $svn->fs ();
# if (!defined($fs))
# {
# print (STDERR "Cannot open fs - $REPOFS\n");
# return -1; # error
# }
#
# my $log = $fs->revision_prop ($REVISION, 'svn:log');
# $log =~ s/^\s+|\s+$//g; # trim leading spaces and trailing spaces
# if (length($log) < $minlen)
# {
# print (STDERR "[$log] Commit message too short. must be >= $minlen\n");
# return 0;
# }
#
# return 1;
#}
#------------------------------------------------------------
# MAIN
#------------------------------------------------------------
my $newauthor = undef;
my $cfg = get_config ();
if (!defined($cfg))
{
print (STDERR "Cannot load codepot configuration file\n");
exit (1);
}
if ($ACTION eq 'D')
{
if ($PROPNAME eq 'svn:log' || $PROPNAME eq 'svn:author' || $PROPNAME eq 'svn:date')
{
print (STDERR "Not allowed to delete $PROPNAME\n");
exit (1);
}
}
elsif ($ACTION eq 'M' || $ACTION eq 'A')
{
if ($PROPNAME eq 'svn:author')
{
$newauthor = do { local $/; <STDIN> }; # read <STDIN> as a whole
$newauthor =~ s/^\s+|\s+$//g; # trim leading spaces and trailing spaces
if ($newauthor eq '')
{
print (STDERR "Not allowed to empty the author\n");
exit (1);
}
}
elsif ($PROPNAME eq 'svn:log')
{
my $minlen = $cfg->{svn_min_commit_message_length};
my $newmsg = do { local $/; <STDIN> }; # read <STDIN> as a whole
$newmsg =~ s/^\s+|\s+$//g; # trim leading spaces and trailing spaces
if (length($newmsg) < $minlen)
{
print (STDERR "Commit message too short. must be >= $minlen\n");
exit (1);
}
}
}
my $dbh = open_database ($cfg);
if (!defined($dbh))
{
printf (STDERR "Cannot open database - %s\n", $DBI::errstr);
exit (1);
}
my ($member, $errstr) = is_project_member ($dbh, $cfg->{database_prefix}, $REPOBASE, $USER);
if ($member <= -1)
{
close_database ($dbh);
print (STDERR "Cannot check membership of [$USER] in the $REPOBASE project - $errstr\n");
exit (1);
}
elsif ($member == 0)
{
close_database ($dbh);
print (STDERR "[$USER] doesn't belong to the $REPOBASE project\n");
exit (1);
}
if (defined($newauthor))
{
# the new author to set must be a member of the project.
my ($member, $errstr) = is_project_member ($dbh, $cfg->{database_prefix}, $REPOBASE, $newauthor);
if ($member <= -1)
{
close_database ($dbh);
print (STDERR "Cannot check membership of [$newauthor] in the $REPOBASE project - $errstr\n");
exit (1);
}
elsif ($member == 0)
{
close_database ($dbh);
print (STDERR "[$newauthor] doesn't belong to the $REPOBASE project\n");
exit (1);
}
}
close_database ($dbh);
exit (0);

175
etc/start-commit.in Normal file
View File

@ -0,0 +1,175 @@
#!/usr/bin/perl
use strict;
use Config::Simple;
use DBI;
use File::Basename;
my $CFG_FILE = '@CFGDIR@/codepot.ini';
my $REPOBASE = basename($ARGV[0]);
my $USER = $ARGV[1];
my $QC = '';
sub get_config
{
my $cfg = new Config::Simple();
if (!$cfg->read ($CFG_FILE))
{
return undef;
}
my $config = {
database_hostname => $cfg->param ("database_hostname"),
database_port => $cfg->param ("database_port"),
database_username => $cfg->param ("database_username"),
database_password => $cfg->param ("database_password"),
database_name => $cfg->param ("database_name"),
database_driver => $cfg->param ("database_driver"),
database_prefix => $cfg->param ("database_prefix")
};
return $config;
}
sub open_database
{
my ($cfg) = @_;
my $dbtype = $cfg->{database_driver};
my $dbname = $cfg->{database_name};
my $dbhost = $cfg->{database_hostname};
my $dbport = $cfg->{database_port};
if ($dbtype eq 'postgre') { $dbtype = 'Pg'; }
elsif ($dbtype eq 'oci8') { $dbtype = 'Oracle'; }
elsif ($dbtype eq 'mysqli') { $dbtype = 'mysql'; }
elsif ($dbtype eq 'sqlite') { $dbtype = 'SQLite'; }
my $dbstr;
my $dbuser;
my $dbpass;
if ($dbtype eq 'Oracle')
{
$QC = '"';
$dbstr = "DBI:$dbtype:";
$dbuser = $cfg->{database_username} . '/' . $cfg->{database_password} . '@' . $dbhost;
$dbpass = '';
}
elsif ($dbtype eq 'SQLite')
{
$dbstr = "DBI:$dbtype:database=$dbhost;";
$dbuser = $cfg->{database_username};
$dbpass = $cfg->{database_password};
}
else
{
$dbstr = "DBI:$dbtype:database=$dbname;";
if (length($dbhost) > 0) { $dbstr .= "host=$dbhost;"; }
if (length($dbport) > 0) { $dbstr .= "port=$dbport;"; }
$dbuser = $cfg->{database_username};
$dbpass = $cfg->{database_password};
}
my $dbh = DBI->connect(
$dbstr, $dbuser, $dbpass,
{ RaiseError => 0, PrintError => 0, AutoCommit => 0 }
);
return $dbh;
}
sub close_database
{
my ($dbh) = @_;
$dbh->disconnect ();
}
sub is_project_member
{
my ($dbh, $prefix, $projectid, $userid) = @_;
my $query = $dbh->prepare ("SELECT ${QC}projectid${QC} FROM ${QC}${prefix}project_membership${QC} WHERE ${QC}userid${QC}=? AND ${QC}projectid${QC}=?");
if (!$query || !$query->execute ($userid, $projectid))
{
return (-1, $dbh->errstr());
}
my @row = $query->fetchrow_array;
$query->finish ();
return (((scalar(@row) > 0)? 1: 0), undef);
}
sub is_project_commitable
{
my ($dbh, $prefix, $projectid) = @_;
my $query = $dbh->prepare ("SELECT ${QC}commitable${QC} FROM ${QC}${prefix}project${QC} WHERE ${QC}id${QC}=?");
if (!$query || !$query->execute ($projectid))
{
return (-1, $dbh->errstr());
}
my @row = $query->fetchrow_array;
$query->finish ();
return (((scalar(@row) > 0 && $row[0] eq 'Y')? 1: 0), undef);
}
#------------------------------------------------------------
# MAIN
#------------------------------------------------------------
my $cfg = get_config ();
if (!defined($cfg))
{
print (STDERR "Cannot load codepot configuration file\n");
exit (1);
}
my $dbh = open_database ($cfg);
if (!defined($dbh))
{
printf (STDERR "Cannot open database - %s\n", $DBI::errstr);
exit (1);
}
my $member;
my $commitable;
my $errstr;
($member, $errstr) = is_project_member (
$dbh, $cfg->{database_prefix}, $REPOBASE, $USER);
if ($member <= -1)
{
print (STDERR "Cannot check membership - $errstr\n");
close_database ($dbh);
exit (1);
}
($commitable, $errstr) = is_project_commitable (
$dbh, $cfg->{database_prefix}, $REPOBASE);
if ($commitable <= -1)
{
print (STDERR "Cannot check commitability - $errstr\n");
close_database ($dbh);
exit (1);
}
close_database ($dbh);
if ($member == 0)
{
print (STDERR "$USER doesn't belong to the $REPOBASE project\n");
exit (1);
}
if ($commitable == 0)
{
print (STDERR "The $REPOBASE project is not commitable\n");
exit (1);
}
exit (0);