source: dns/do_domaines.sh @ 57

Revision 57, 15.4 KB checked in by anarcat, 9 years ago (diff)

[project @ alternc: changeset 2003-04-22 13:03:50 by benjamin]
Changement du système de redirection.

Original author: benjamin
Date: 2003-04-22 13:03:50

Line 
1#!/usr/bin/ksh
2#
3# $Id: do_domaines.sh,v 1.7 2003/04/22 13:03:50 benjamin Exp $
4# ----------------------------------------------------------------------
5# AlternC - Web Hosting System
6# Copyright (C) 2002 by the AlternC Development Team.
7# http://alternc.org/
8# ----------------------------------------------------------------------
9# Based on:
10# Valentin Lacambre's web hosting softwares: http://altern.org/
11# ----------------------------------------------------------------------
12# LICENSE
13#
14# This program is free software; you can redistribute it and/or
15# modify it under the terms of the GNU General Public License (GPL)
16# as published by the Free Software Foundation; either version 2
17# of the License, or (at your option) any later version.
18#
19# This program is distributed in the hope that it will be useful,
20# but WITHOUT ANY WARRANTY; without even the implied warranty of
21# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22# GNU General Public License for more details.
23#
24# To read the license please visit http://www.gnu.org/copyleft/gpl.html
25# ----------------------------------------------------------------------
26# Original Author of file: Jerome Moinet for l'Autre Net - 14/12/2000
27# Purpose of file: system level domain management
28# ----------------------------------------------------------------------
29#
30
31# ####################################################################
32# VARIABLES SETTINGS :
33# ####################################################################
34
35umask 022
36
37integer nb1
38integer nb2
39integer ORDRE
40
41DOM_ROOT=/usr/lib/alternc/system
42BIND2_ROOT=$DOM_ROOT/bind2
43FIC_TMP=/tmp/domaines.tmp
44FIC_TMP_SUB=/tmp/sub_domaines.tmp
45FIC_LOCK=/var/run/alternc/cron.lock
46FIC_LOG=/var/log/alternc/domaines.log
47FIC_LOG_SUB=/var/log/alternc/sub_domaines.log
48HTTP_DNS=/var/alternc/dns
49HTML_HOME=/var/alternc/html
50NAMED_HOME=/etc/bind
51NAMED_ETC=${NAMED_HOME}
52NAMED_MASTER=${NAMED_HOME}/master
53NAMED_TPL=domaines.template
54SLAVE_TPL=slave.template
55SECONDARY_LIST=secondary.list
56NAMED_MX_TPL=mx.template
57NAMED_CONF=automatic.conf
58RELOAD_NAMED=/etc/init.d/bind9
59WEBMAIL_ROOT=/var/alternc/dns/redir/mail
60DATA_ROOT=var/alternc
61
62ACTION_INSERT=0
63ACTION_UPDATE=1
64ACTION_DELETE=2
65TYPE_LOCAL=0
66TYPE_URL=1
67TYPE_IP=2
68TYPE_WEBMAIL=3
69SLAVE=2
70OUI=1
71NON=0
72
73mail=/usr/bin/mail
74wc=/usr/bin/wc
75awk=/usr/bin/awk
76echo=/bin/echo
77cut=/usr/bin/cut
78grep=/bin/grep
79tail=/usr/bin/tail
80head=/usr/bin/head
81rm=/bin/rm
82find=/usr/bin/find
83cat=/bin/cat
84sed=/bin/sed
85mv=/bin/mv
86ln=/bin/ln
87date=/bin/date
88printf=/usr/bin/printf
89cp=/bin/cp
90env=/usr/bin/env
91
92# récupération des passwd et autres à partir d'un fichier externe :
93FIC_CONF=/etc/alternc/local.sh
94if [ -x "$FIC_CONF" ]
95    then
96    . $FIC_CONF
97else
98    echo "Le fichier de configuration ($FIC_CONF) est absent ou n'est pas exécutable."
99    exit 1
100fi
101
102# On teste si les variables attendues sont renseignées :
103for variable in MYSQL_DATABASE MYSQL_USER MYSQL_PASS ROOT_MAIL DEFAULT_MX DEFAULT_IP
104do
105        var=""
106  var=`set | $grep $variable | $grep -v variable`
107  var=`$echo $var | $cut -d"=" -f2`
108  if [ -z "$var" ]
109  then
110    $echo "la variable \$$variable n'est pas renseignée."
111                exit 1
112  fi
113done
114
115
116WEBMAIL_ROOT=/$DATA_ROOT/bureau/admin/webmail/
117DOM_ROOT=/$DATA_ROOT/exec/system
118FIC_LOCK=/$DATA_ROOT/bureau/cron.lock
119HTTP_DNS=/$DATA_ROOT/dns
120HTML_HOME=/$DATA_ROOT/html
121DEST_MAILS_ERREUR=$ROOT_MAIL
122
123MYSQL_SELECT="/usr/bin/mysql -u${MYSQL_USER} -p${MYSQL_PASS} -Bs ${MYSQL_DATABASE} -e "
124MYSQL_DELETE="/usr/bin/mysql -u${MYSQL_USER} -p${MYSQL_PASS} ${MYSQL_DATABASE} -e "
125
126
127# ####################################################################
128# FUNCTIONS :
129# ####################################################################
130
131#---------------
132# A chaque modification du fichier named
133# d'un domaine, on incrémente son serial.
134# Le serial est de la forme YYYYYMMDDSS
135# où SS est le numéro d'ordre dans la journée.
136# On incrémente ce numéro d'ordre si la modif
137# est du même jour que la précédente, sinon on
138# met la date du jour avec 01 en numéro d'ordre.
139# Prend le nom du fic de conf en argument.
140#---------------
141function incrementer_serial
142{
143    if [ -f "$1" ]
144        then
145        SERIAL=`$grep "serial" $1 | $grep -v "^serial" | $awk {'print $1'}`
146        DATE=`$echo $SERIAL | $cut -c1-8`
147        ORDRE=`$echo $SERIAL | $sed s/"$DATE"/""/g`
148        DATE_JOUR=`$date +%Y%m%d`
149        if [ $DATE = $DATE_JOUR ]
150            then
151            ORDRE=$ORDRE+1
152        else
153            ORDRE=1
154            DATE=$DATE_JOUR
155        fi
156        NEW_SERIAL=$DATE`$printf "%.2d" $ORDRE`
157        $cat $1 | $awk -v NEW_SERIAL=$NEW_SERIAL '{if ($3 =="serial") print "           "NEW_SERIAL "   ; serial"; else print $0}' > $1.tmp
158        $mv -f $1.tmp $1
159    fi
160}
161
162
163#---------------
164# Modification de l'ip d'un sous_domaine.
165# dans la conf named.
166# Prend domaine, ip et
167# sous_domaine en arguments.
168#---------------
169function modifier_ip_sous_domaine
170{
171    if [ -f $NAMED_MASTER/$1 ]
172        then
173        nb1=`$wc -l $NAMED_MASTER/$1 | $awk {'print $1'}`
174        if [ "$3" = "" ]
175            then
176            nb2=`$grep -n "^[   ]*IN[   ]*A[    ]*" $NAMED_MASTER/$1 | $cut -d":" -f1`
177        else
178            nb2=`$grep -n "^$3" $NAMED_MASTER/$1 | $cut -d":" -f1`
179        fi
180        if [ $nb2 -eq 0 ]
181            then
182            nb1=$nb1-1
183            nb2=1
184        else
185            nb2=$nb1-$nb2
186            nb1=$nb1-$nb2-1
187        fi
188        $head -n$nb1 $NAMED_MASTER/$1 > $NAMED_MASTER/$1.tmp
189        $cat $NAMED_MASTER/ip.template | $sed s/"@@SUB@@"/"$3"/ | $sed s/"@@IP@@"/"$2"/ >> $NAMED_MASTER/$1.tmp
190        $tail -n$nb2 $NAMED_MASTER/$1 >> $NAMED_MASTER/$1.tmp
191        $mv -f $NAMED_MASTER/$1.tmp $NAMED_MASTER/$1
192    fi
193}
194
195
196#---------------
197# Crée un sous-domaine au niveau disque,
198# et dans les fichiers named.
199# prend domaine,
200# type, valeur, sous-domaine en argument.
201# Principe : la création est forcée,
202# si le sub existe déjà, il est remplacé.
203#---------------
204function creer_sous_domaine
205{
206    DOM=$1
207    TYP=$2
208    VAL=$4
209    SB=$3
210    POINT="."
211    if [ "$SB" = "" ]
212        then
213        POINT=""
214        modifier_ip_sous_domaine $DOM $DEFAULT_IP 
215    fi
216    detruire_sous_domaine $DOM $SB
217    if [ "$TYP" = "$TYPE_LOCAL" ]
218        then
219        # NOTE : ne pas virer le rm -f (le ln -sf est buggé)
220        $rm -f ${HTTP_DNS}/${INITIALE_DOM}/${SB}${POINT}${DOM}
221        $ln -s ${HTML_HOME}/${INITIALE_USER}/${USER}${VAL} ${HTTP_DNS}/${INITIALE_DOM}/${SB}${POINT}${DOM}
222    fi
223    if [ "$TYP" = "$TYPE_WEBMAIL" ]
224        then
225        # NOTE : ne pas virer le rm -f (le ln -sf est bugg?)
226        $rm -f ${HTTP_DNS}/${INITIALE_DOM}/${SB}${POINT}${DOM}
227        $ln -s ${WEBMAIL_ROOT} ${HTTP_DNS}/${INITIALE_DOM}/${SB}${POINT}${DOM}
228    fi
229    if [ "$TYP" = "$TYPE_URL" ]
230        then
231
232        mkdir -p ${HTTP_DNS}/redir/${INITIALE_DOM}/${SB}${POINT}${DOM}
233        $echo "RewriteEngine on
234RewriteRule (.*) ${VAL}/\$1 [R,L]" > ${HTTP_DNS}/redir/${INITIALE_DOM}/${SB}${POINT}${DOM}/.htaccess
235        # NOTE : ne pas virer le rm -f (le ln -sf est buggé)
236        $rm -f ${HTTP_DNS}/${INITIALE_DOM}/${SB}${POINT}${DOM}
237        $ln -s ${HTTP_DNS}/redir/${INITIALE_DOM}/${SB}${POINT}${DOM} ${HTTP_DNS}/${INITIALE_DOM}/${SB}${POINT}${DOM}
238    fi
239    if [ "$TYP" = "$TYPE_IP" ]
240        then
241        $rm -f ${HTTP_DNS}/${INITIALE_DOM}/${SB}${POINT}${DOM}
242        $rm -fr ${HTTP_DNS}/redir/${INITIALE_DOM}/${SB}${POINT}${DOM}
243        modifier_ip_sous_domaine $DOM $VAL $SB
244    fi
245}
246
247#---------------
248# Destruction d'un
249# sous-domaine
250#---------------
251function detruire_sous_domaine
252{
253    DOM=$1
254    SB=$2
255    if ! [ "$SB" = "" ]
256        then
257        if [ -f $NAMED_MASTER/$DOM.hosts ]
258            then
259            $grep -v "^$SB" $NAMED_MASTER/$DOM > $NAMED_MASTER/$DOM.tmp
260            $mv -f $NAMED_MASTER/$DOM.tmp $NAMED_MASTER/$DOM
261        fi
262    fi
263    $rm -f ${HTTP_DNS}/${INITIALE_DOM}/${SB}.${DOM}
264    $rm -fr ${HTTP_DNS}/redir/${INITIALE_DOM}/${SB}.${DOM}
265    incrementer_serial $NAMED_MASTER/$DOM
266}
267
268
269#---------------
270# création du fichier named
271# si il n'existe pas.
272# Prend le nom du domaine
273# en argument.
274#---------------
275function creer_fic_named
276{
277    if ! [ -f $NAMED_MASTER/$1 ]
278        then
279        SERIAL=`$date +%Y%m%d`00
280        $cat $NAMED_MASTER/$NAMED_TPL | $sed s/"@@DOMAINE@@"/${1}/g | $sed s/"@@SERIAL@@"/$SERIAL/g > $NAMED_MASTER/${1}
281        $cp -f $NAMED_ETC/$NAMED_CONF $NAMED_ETC/$NAMED_CONF.prec
282        $cat $NAMED_ETC/$NAMED_TPL | $sed s/"@@DOMAINE@@"/${1}/g >> $NAMED_ETC/$NAMED_CONF
283        $cp -f $BIND2_ROOT/$SECONDARY_LIST $BIND2_ROOT/$SECONDARY_LIST.prec
284        $cat $BIND2_ROOT/$NAMED_TPL | $sed s/"@@DOMAINE@@"/${1}/g >> $BIND2_ROOT/$SECONDARY_LIST
285        RESTART_NAMED="true"
286    fi
287}
288
289
290#---------------
291# Destruction des fichiers
292# de conf named pour
293# un domaine
294#---------------
295function detruire_fic_named
296{
297    if [ -f $NAMED_MASTER/$1 ]
298        then
299        $rm -f $NAMED_MASTER/$1
300        $cat $NAMED_ETC/$NAMED_CONF | $grep -v "\"$1\"" > $NAMED_ETC/$NAMED_CONF.tmp
301        $cp -f $NAMED_ETC/$NAMED_CONF $NAMED_ETC/$NAMED_CONF.prec
302        $mv -f $NAMED_ETC/$NAMED_CONF.tmp $NAMED_ETC/$NAMED_CONF
303        $cat $BIND2_ROOT/$SECONDARY_LIST | $grep -v "\"$1\"" > $BIND2_ROOT/$SECONDARY_LIST.tmp
304        $cp -f $BIND2_ROOT/$SECONDARY_LIST $BIND2_ROOT/$SECONDARY_LIST.prec
305        $mv -f $BIND2_ROOT/$SECONDARY_LIST.tmp $BIND2_ROOT/$SECONDARY_LIST
306    fi
307}
308
309#---------------
310# Modification du champ mx.
311# prend domaine et champ mx
312# en arguments.
313#---------------
314function modifier_mx_domaine
315{
316    nb1=`$grep -n "MX" $NAMED_MASTER/${1} | $cut -d":" -f1`
317    nb1=$nb1-1
318    $head -n$nb1 $NAMED_MASTER/${1} > $NAMED_MASTER/${1}.tmp
319    $cat $NAMED_MASTER/$NAMED_MX_TPL | $sed s/"@@MX@@"/${2}/g >> $NAMED_MASTER/${1}.tmp
320    nb2=`$wc -l $NAMED_MASTER/${1} | $awk {'print $1'}`
321    nb2=$nb2-$nb1-1
322    $tail -n$nb2 $NAMED_MASTER/${1} >> $NAMED_MASTER/${1}.tmp
323    $mv -f $NAMED_MASTER/${1}.tmp $NAMED_MASTER/${1}
324    incrementer_serial $NAMED_MASTER/${1}
325    RESTART_NAMED="true"
326}
327
328
329# ####################################################################
330# Main program
331# ####################################################################
332
333#------------------------------------
334# CALL WITH "do_domaines.sh --secondary"
335# It rebuilds the secondary.list file.
336if [ "$1" = "--secondary" ]
337    then
338    > $FIC_LOCK
339    $rm -f $FIC_TMP
340    echo "CALL --secondary, rebuilding secondary.list file." >> $FIC_LOG
341    SQL_RES=`$MYSQL_SELECT "SELECT b.domaine INTO OUTFILE '$FIC_TMP' FROM domaines b WHERE b.gesdns=1;" 2>&1` 
342    RES=$?
343    if [ "$RES" != 0 ]
344        then
345        $echo `$date` >> $FIC_LOG
346        $echo $SQL_RES >> $FIC_LOG
347        $echo "`$date` : $DOM_ROOT/do_domaines.sh : erreur à l'exécution de la requête de sélection des domaines à traiter : $SQL_RES" | $mail -s "erreur requête dans do_domaines.sh" $DEST_MAILS_ERREUR
348        $rm -f $FIC_LOCK >> $FIC_LOG 2>&1
349        $rm -f $FIC_TMP >> $FIC_LOG 2>&1
350        exit 1
351    else
352        # On traite les domaines
353        $mv -f $BIND2_ROOT/$SECONDARY_LIST $BIND2_ROOT/$SECONDARY_LIST.prec
354        for i in `$cat $FIC_TMP`
355          do
356          $cat $BIND2_ROOT/$NAMED_TPL | $sed s/"@@DOMAINE@@"/${i}/g >> $BIND2_ROOT/$SECONDARY_LIST
357        done
358    fi
359    $rm -f $FIC_LOCK >> $FIC_LOG 2>&1
360    $rm -f $FIC_TMP >> $FIC_LOG 2>&1
361    echo "CALL --secondary END" >> $FIC_LOG
362    exit 0
363fi
364
365
366# ------------------------------------------------------------
367# CALL with NO argument : process pending domains / subdomains
368#
369# si le cron précédent n'est pas
370# terminé, on attend le suivant.
371if [ -f $FIC_TMP ]
372    then
373    echo `$date` >> $FIC_LOG
374    echo "ERREUR : cron précédent inachevé." >> $FIC_LOG
375    $echo "`$date` : $DOM_ROOT/do_domaines.sh : cron précédent inachevé." | $mail -s "erreur do_domaines.sh" $DEST_MAILS_ERREUR
376    exit 1
377fi
378
379> $FIC_LOCK
380
381SQL_RES=`$MYSQL_SELECT "SELECT m.login,b.domaine,b.mx,b.gesdns,b.gesmx,b.action INTO OUTFILE '$FIC_TMP' FROM domaines_standby b INNER JOIN membres m ON m.uid=b.compte ORDER BY b.action;" 2>&1`
382RES=$?
383
384if [ "$RES" != 0 ]
385    then
386    $echo `$date` >> $FIC_LOG
387    $echo $SQL_RES >> $FIC_LOG
388    $echo "`$date` : $DOM_ROOT/do_domaines.sh : erreur à l'exécution de la requête de sélection des domaines à traiter : $SQL_RES" | $mail -s "erreur requête dans do_domaines.sh" $DEST_MAILS_ERREUR
389    $rm -f $FIC_LOCK >> $FIC_LOG 2>&1
390    $rm -f $FIC_TMP >> $FIC_LOG 2>&1
391    $rm -f $FIC_TMP_SUB >> $FIC_LOG 2>&1
392    exit 1
393else
394    SQL_RES=`$MYSQL_SELECT "SELECT m.login,b.domaine,b.sub,b.valeur,b.type,b.action INTO OUTFILE '$FIC_TMP_SUB' FROM sub_domaines_standby b INNER JOIN membres m ON m.uid=b.compte ORDER BY b.action desc;" 2>&1`
395    RES=$?
396    if [ "$RES" != 0 ]
397        then
398        $echo `$date` >> $FIC_LOG
399        $echo $SQL_RES >> $FIC_LOG
400        $echo "`$date` : $DOM_ROOT/do_domaines.sh : erreur à l'exécution de la requête de sélection des sous-domaines à traiter : $SQL_RES" | $mail -s "erreur requête dans do_domaines.sh" $DEST_MAILS_ERREUR
401        $rm -f $FIC_LOCK >> $FIC_LOG 2>&1
402        $rm -f $FIC_TMP >> $FIC_LOG 2>&1
403        $rm -f $FIC_TMP_SUB >> $FIC_LOG 2>&1
404        exit 1
405    else
406        $rm -f $FIC_LOCK
407        $MYSQL_DELETE "DELETE FROM domaines_standby;"
408        $MYSQL_DELETE "DELETE FROM sub_domaines_standby;"
409        RESTART_NAMED="false"
410        # On traite les domaines
411        $cat $FIC_TMP | $sed s/"        "/"@"/g > $FIC_TMP.tmp
412        $mv -f $FIC_TMP.tmp $FIC_TMP
413        if [ `$cat $FIC_TMP | $wc -l | $awk {'print $1'}` -gt 0 ]
414            then
415            $echo `$date` >> $FIC_LOG
416            $cat $FIC_TMP >> $FIC_LOG
417        fi
418        for i in `$cat $FIC_TMP`
419          do
420          USER=`$echo $i | $cut -d"@" -f1`
421          DOMAINE=`$echo $i | $cut -d"@" -f2`
422          MX=`$echo $i | $cut -d"@" -f3`
423          GESDNS=`$echo $i | $cut -d"@" -f4`
424          GESMX=`$echo $i | $cut -d"@" -f5`
425          ACTION=`$echo $i | $cut -d"@" -f6`
426          PASS=`$echo $i | $cut -d"@" -f7`
427          INITIALE_DOM=`$echo $DOMAINE | $awk '{z = split($0, intiale, "."); print substr(intiale[z - 1], 1, 1)}'`
428          INITIALE_USER=`$echo $USER | $awk {'print substr($1, 1, 1)'}`
429          if [ "$ACTION" = "$ACTION_INSERT" ]
430              then
431              # création des liens symboliques par défaut :
432              # NOTE : ne pas virer le rm -f (le ln -sf est buggé)
433              $rm -f ${HTTP_DNS}/${INITIALE_DOM}/$DOMAINE
434              $ln -s ${HTML_HOME}/${INITIALE_USER}/$USER ${HTTP_DNS}/${INITIALE_DOM}/$DOMAINE
435              $rm -f ${HTTP_DNS}/${INITIALE_DOM}/www.$DOMAINE
436              $ln -s ${HTML_HOME}/${INITIALE_USER}/$USER ${HTTP_DNS}/${INITIALE_DOM}/www.$DOMAINE
437              $rm -f ${HTTP_DNS}/${INITIALE_DOM}/mail.$DOMAINE
438              $ln -s ${WEBMAIL_ROOT} ${HTTP_DNS}/${INITIALE_DOM}/mail.$DOMAINE
439              if [ "$GESDNS" = "$OUI" ]
440                  then
441                  creer_fic_named $DOMAINE
442              fi
443          fi
444          if [ "$ACTION" = "$ACTION_UPDATE" ]
445              then
446              if [ "$GESDNS" = "$OUI" ]
447                  then
448                  creer_fic_named $DOMAINE
449                  modifier_mx_domaine $DOMAINE $MX
450              else
451                  detruire_fic_named $DOMAINE
452              fi
453          fi
454          if [ "$ACTION" = "$ACTION_DELETE" ]
455              then
456              detruire_fic_named $DOMAINE
457              # suppression des liens symboliques :
458              $rm -f ${HTTP_DNS}/${INITIALE_DOM}/*.$DOMAINE
459              $rm -f ${HTTP_DNS}/${INITIALE_DOM}/$DOMAINE
460              $rm -fr ${HTTP_DNS}/redir/${INITIALE_DOM}/*.$DOMAINE
461              $rm -fr ${HTTP_DNS}/redir/${INITIALE_DOM}/$DOMAINE
462          fi
463          RESTART_NAMED="true"
464        done
465        # on traite les sous-domaines
466        $cat $FIC_TMP_SUB | $sed s/"    "/"@"/g > $FIC_TMP_SUB.tmp
467        $mv -f $FIC_TMP_SUB.tmp $FIC_TMP_SUB
468       
469        if [ `$cat $FIC_TMP_SUB | $wc -l | $awk {'print $1'}` -gt 0 ]
470            then
471            $echo `$date` >> $FIC_LOG_SUB
472            $cat $FIC_TMP_SUB >> $FIC_LOG_SUB
473        fi
474        for i in `$cat $FIC_TMP_SUB`
475          do
476          USER=`$echo $i | $cut -d"@" -f1`
477          DOMAINE=`$echo $i | $cut -d"@" -f2`
478          SUB=`$echo $i | $cut -d"@" -f3`
479          VALEUR=`$echo $i | $cut -d"@" -f4`
480          TYPE=`$echo $i | $cut -d"@" -f5`
481          ACTION=`$echo $i | $cut -d"@" -f6`
482          PASS=`$echo $i | $cut -d"@" -f7`
483          INITIALE_DOM=`$echo $DOMAINE | $awk '{z = split($0, intiale, "."); print substr(intiale[z - 1], 1, 1)}'`
484          INITIALE_USER=`$echo $USER | $awk {'print substr($1, 1, 1)'}`
485          if [ "$ACTION" = "$ACTION_UPDATE" -o "$ACTION" = "$ACTION_INSERT" ]
486              then
487              creer_sous_domaine "$DOMAINE" "$TYPE" "$SUB" "$VALEUR" 
488
489          fi   
490          if [ "$ACTION" = "$ACTION_DELETE" ]
491              then
492              detruire_sous_domaine $DOMAINE $SUB
493          fi
494          RESTART_NAMED="true"
495        done
496        if [ "$RESTART_NAMED" = "true" ]
497            then
498            $RELOAD_NAMED reload
499        fi
500        $rm -f $FIC_TMP_SUB >> $FIC_LOG 2>&1
501        $rm -f $FIC_TMP >> $FIC_LOG 2>&1
502    fi
503fi     
504
505       
Note: See TracBrowser for help on using the repository browser.