From c81b7f986867db292d62a1757123723b5ef66518 Mon Sep 17 00:00:00 2001 From: Jonas Smedegaard Date: Wed, 12 Dec 2001 19:52:40 +0000 Subject: Initial revision --- adduser.local | 33 + aptdpkgclean.sh | 19 + aptdpkgro.sh | 16 + deluser.local | 23 + faxrcvd | 124 ++ faxrcvd-mail | 147 ++ htdig-init | 13 + htdig-init-all | 5 + htdig-run | 25 + htdig-run-all | 10 + init-mailmanarchive | 28 + localdumpsql | 78 + localezcreate | 178 +++ localmoffycreate | 70 + localwebbbscreate | 40 + localwebcalcreate | 47 + localwebstats | 181 +++ localwebstats-all | 24 + mkmailcerts | 38 + rm-toobig | 14 + smbadduser | 73 + user-init | 175 +++ user-mailadr | 6 + userforward | 29 + usergroupadd | 33 + usergroupinfo | 13 + volinit-netatalk | 63 + volinit-public | 63 + volinit-samba | 59 + volinit-std-all | 45 + volinit-xchange | 63 + vpopmail2postfixvirtual | 53 + wcald | 3688 +++++++++++++++++++++++++++++++++++++++++++++++ webuser | 66 + webuser-refresh-all | 7 + 35 files changed, 5549 insertions(+) create mode 100755 adduser.local create mode 100755 aptdpkgclean.sh create mode 100755 aptdpkgro.sh create mode 100755 deluser.local create mode 100755 faxrcvd create mode 100755 faxrcvd-mail create mode 100755 htdig-init create mode 100755 htdig-init-all create mode 100755 htdig-run create mode 100755 htdig-run-all create mode 100755 init-mailmanarchive create mode 100755 localdumpsql create mode 100755 localezcreate create mode 100755 localmoffycreate create mode 100755 localwebbbscreate create mode 100755 localwebcalcreate create mode 100755 localwebstats create mode 100755 localwebstats-all create mode 100755 mkmailcerts create mode 100755 rm-toobig create mode 100755 smbadduser create mode 100755 user-init create mode 100755 user-mailadr create mode 100755 userforward create mode 100755 usergroupadd create mode 100755 usergroupinfo create mode 100755 volinit-netatalk create mode 100755 volinit-public create mode 100755 volinit-samba create mode 100755 volinit-std-all create mode 100755 volinit-xchange create mode 100755 vpopmail2postfixvirtual create mode 100755 wcald create mode 100755 webuser create mode 100755 webuser-refresh-all diff --git a/adduser.local b/adduser.local new file mode 100755 index 0000000..d6bbbce --- /dev/null +++ b/adduser.local @@ -0,0 +1,33 @@ +#!/bin/sh + +set -e + +NEWUSERNAME=$1 +NEWUID=$2 +NEWGID=$3 +NEWHOMEDIR=$4 + +[ "x$NEWHOMEDIR" = "x/home/$NEWUSERNAME" ] || exit 0 + +#su $1 -c 'echo "$realuser\@$host">~/.forward' + +addgroup $NEWUSERNAME users + +if [ -x /usr/local/sbin/user-init ]; then + /usr/local/sbin/user-init $NEWUSERNAME + fi + +test -e /etc/samba/smbpasswd && \ + /usr/local/sbin/smbadduser $NEWUSERNAME:$NEWUSERNAME \ + || true + +test -x /usr/sbin/add_members || exit 0 + +if [ -x /var/lib/mailman/lists/users ]; then + /bin/echo $NEWUSERNAME@`/bin/dnsdomainname` \ + | /usr/sbin/add_members -n - users + fi +#if [ -x /var/lib/mailman/lists/friends ]; then +# /bin/echo $NEWUSERNAME@`/bin/dnsdomainname` \ +# | /usr/sbin/add_members -n - friends +# fi diff --git a/aptdpkgclean.sh b/aptdpkgclean.sh new file mode 100755 index 0000000..7de9b77 --- /dev/null +++ b/aptdpkgclean.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +pathmatch="^/usr" + +cat /var/lib/my_ro_hack.todel | while read file; do + [ -f "$file" ] || continue + N1=`find "$file" -printf "%i\n"` + + b=`basename $file`; d=`dirname $file` + XF="${b#.}"; XF="$d/${XF%.dpkg-ro-used.*}" + N2=`find "$XF" -printf "%i\n"` + + if [ "$N1" != "$N2" ] && ! fuser -s "$file"; then + rm -f "$file" + else + echo "$file" + fi +done >/var/lib/my_ro_hack.todel.new +mv /var/lib/my_ro_hack.todel.new /var/lib/my_ro_hack.todel diff --git a/aptdpkgro.sh b/aptdpkgro.sh new file mode 100755 index 0000000..71dc44d --- /dev/null +++ b/aptdpkgro.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +pathmatch="^/usr" + +while read debname; do + pkg=$(dpkg --info $debname | sed -n 's/^ Package: *//p' | head -1) + (dpkg -L "$pkg" 2>/dev/null || true) | grep "$pathmatch" | + while read file; do + [ -f "$file" -a ! -L "$file" ] || continue + dir=`dirname "$file"`; + base=`basename "$file"`; + inode=`find "$file" -printf "%i\n"` + (cd "$dir" && ln "$base" ".${base}.dpkg-ro-used.$inode") + echo "$dir/.${base}.dpkg-ro-used.$inode" + done >>/var/lib/my_ro_hack.todel +done diff --git a/deluser.local b/deluser.local new file mode 100755 index 0000000..b5fd6f9 --- /dev/null +++ b/deluser.local @@ -0,0 +1,23 @@ +#!/bin/sh + +OLDUSERNAME=$1 +OLDUID=$2 +OLDGID=$3 +OLDHOMEDIR=$4 + +[ "x$OLDHOMEDIR" = "x/home/$OLDUSERNAME" ] || exit 0 + +test -e /etc/samba/smbpasswd && \ + /usr/bin/smbpasswd -x $OLDUSERNAME \ + || true + +test -x /usr/sbin/remove_members || exit 0 + +if [ -x /var/lib/mailman/lists/users ]; then + /bin/echo $OLDUSERNAME@`/bin/dnsdomainname` \ + | /usr/sbin/remove_members -f - users + fi +if [ -x /var/lib/mailman/lists/friends ]; then + /bin/echo $OLDUSERNAME@`/bin/dnsdomainname` \ + | /usr/sbin/remove_members -f - friends + fi diff --git a/faxrcvd b/faxrcvd new file mode 100755 index 0000000..9b968d7 --- /dev/null +++ b/faxrcvd @@ -0,0 +1,124 @@ +#! /bin/bash +# +# /var/spool/fax/bin/faxrcvd +# Noel Burton-Krahn +# Sept 4, 1999 +# +# +# faxrcvd file devID commID error-msg +# +# modified faxrcvd from hylafax which calls faxrcvd-mail +# +if [ $# != 4 ]; then + echo "Usage: $0 file devID commID error-msg" + exit 1 +fi +FILE="$1" +DEVICE="$2" +COMMID="$3" +MSG="$4" + +test -f etc/setup.cache || { + SPOOL=`pwd` + cat<" + echo "Subject: facsimile not received" + echo "" + echo "An attempt to receive facsimile on $DEVICE failed because:" + echo "" + echo " $ERRORMSG" + echo "" + ) | 2>&1 $SENDMAIL -f$SENDFROM -oi $TOADDR + fi + +else + # + # Generate notification mail for a failed attempt. + # + (echo "To: $TOADDR" + echo "From: The HylaFAX Receive Agent <$SENDFROM>" + echo "Subject: facsimile not received" + echo "" + echo "An attempt to receive facsimile on $DEVICE failed because:" + echo "" + echo " $MSG" + echo "" + echo " ---- Transcript of session follows ----" + echo "" + if [ -f log/c$COMMID ]; then + $SED -e '/-- data/d' \ + -e '/start.*timer/d' -e '/stop.*timer/d' \ + log/c$COMMID + elif [ -n "$COMMID" ]; then + echo " No transcript available (CommID c$COMMID)." + else + echo " No transcript available." + fi + ) | 2>&1 $SENDMAIL -f$SENDFROM -oi $TOADDR +fi diff --git a/faxrcvd-mail b/faxrcvd-mail new file mode 100755 index 0000000..d7ff1ee --- /dev/null +++ b/faxrcvd-mail @@ -0,0 +1,147 @@ +#! /usr/bin/perl -w +# +# /var/spool/fax/bin/faxrcvd-mail +# Noel Burton-Krahn +# Sept 4, 1999 +# +# Tweaks by Jonas Smedegaard +# fall 2001 +# +# a replacement for hylafax's faxrcvd which sends the whole fax by email + +use strict; + +my($filebase, $filetype, $device, $commid, $msg, $toaddr, $fromaddr, $sentaddr) = @ARGV; +if( !defined $fromaddr ) { die("Usage: faxrcvd-mail []") }; +if( !defined $sentaddr ) { $sentaddr="" }; + +open(STDOUT, "|/usr/sbin/sendmail -oi -f$fromaddr $toaddr") || die("/usr/sbin/sendmail -oi -f$fromaddr $toaddr: $!"); + +my(%info, $info); +my($boundary); + +$boundary=join('---', + "=Boundary=", + $$, + sprintf('%x', rand(0xffffffff))); + +open(IN, "/usr/sbin/faxinfo $filebase.tif|") || die("/usr/sbin/faxinfo $filebase.tif: $!"); +while() { + $info .= $_; + $info{lc($1)} = $2 if( /^\s*(\S+): (.*)$/ ); +} +close(IN) || die("/usr/sbin/faxinfo: $?"); + +my($subject) = "FAX from $info{sender}"; +if( $sentaddr ne "" ) { $subject .= " (dispatched to $sentaddr)" }; + +print < +To: $toaddr +Subject: $subject +Mime-Version: 1.0 +Content-Type: Multipart/Mixed; Boundary=\"$boundary\" +Content-Transfer-Encoding: 7bit + +This is a multi-part message in MIME format. + +--$boundary +Content-Type: text/plain; charset=us-ascii +Content-Description: FAX information +Content-Transfer-Encoding: 7bit + +File on host is: $info + +EOF +; + +if( $msg ne "" ) { + print <); + close(IN) || die("mimencode: $?"); + next; + }; + /^ps$/ and -f "$filebase.ps" and do { + print <); + close(IN) || die("mimencode: $?"); + next; + }; + /^tif$/ and -f "$filebase.tif" and do { + print <); + close(IN) || die("mimencode: $?"); + next; + }; +# default: output the tif file + die "Unsupported filetype \"$filetype\"!"; + } + + if( open(IN, "); + close(IN); + } + +}; # end of part possibly dispatched + +print < [...]" + +for list in $@; do + mboxdir="$basedir/$list.mbox" + mboxfile="$mboxdir/$list.mbox" + htmldir="$basedir/$list" + + if [ "x$1" != "" -a -d $mboxdir ]; then + echo "Renewing list \"$list\"..." + + # Make sure the mbox file exists even if no mails are archived yet + touch $mboxfile && chown list.list $mboxfile && chmod 664 $mboxfile + rm -rf $htmldir + +# $bindir/arch $list + mkdir -p $htmldir && chown list.list $htmldir && chmod 2755 $htmldir + su list "mhonarc -outdir $htmldir -idxfname index.html -rcfile /etc/mailman/mhonarc.mrc -title \"$list Archive (by Date)\" -ttitle \"$list Archive (Threaded)\" $mboxfile" + + echo "Done with list \"$list\"!" + else + echo "Problem renewing list \"$list\"." + fi +done diff --git a/localdumpsql b/localdumpsql new file mode 100755 index 0000000..9639911 --- /dev/null +++ b/localdumpsql @@ -0,0 +1,78 @@ +#!/bin/bash +# /etc/cron.daily/localdumpsql: MySQL/PostgreSQL maintenance script +# Written by Jonas Smedegaard + +# halt on errors +set -e + +function usage() { + echo "Usage: `basename $0` daily|weekly|monthly| [mysql|postgres]" + echo " If sqltype is missing, all are attempted" + echo " Tip: Automagically runs when symlinked to /etc/cron.{daily,weekly,monthly}/" + exit 1 +} + +# automagically configure when run from cron dirs +case `dirname $0` in + /etc/cron.daily) + stamp=daily + ;; + /etc/cron.weekly) + stamp=weekly + ;; + /etc/cron.monthly) + stamp=monthly + ;; + *) + if [ $# -lt 1 -o $# -gt 2 ]; then + usage + fi + stamp=$1 + sqltypes=$2 + ;; +esac + +# Default is all sqltypes +[ -z $sqltypes ] && sqltypes="mysql postgres" + +# Define paths +targetdir=/var/local/backups/localhost +mysql_bin=/usr/bin/mysqldump +postgres_bin=/usr/lib/postgresql/bin/pg_dump + +# Define routines +function mysql_valid() { [ -f $mysql_bin ]; } +function mysql_get_pw() { grep password /root/.my.cnf | awk -F= '{print $2}' | head -1 | sed 's/^ //g'; } +function mysql_list_db() { echo "show databases"|mysql -uroot -p`mysql_get_pw`|grep -v '^Database$'; } +function mysql_dump_db() { $mysql_bin -c --add-drop-table -uroot -p`mysql_get_pw` $1; } + +function postgres_valid() { [ -f $postgres_bin ]; } +function postgres_list_db() { su -s /bin/sh postgres -c "/usr/bin/psql -t -c 'select datname from pg_database order by datname' -d template1 | sed -e 's/ //' | grep -v '^template[01]$'"; } +function postgres_dump_db() { su -s /bin/sh postgres -c "$postgres_bin $1"; } + +# Exit if the directory isn't there +if [ ! -d "$targetdir" ]; then + echo "Directory $targetdir doesn't exist!" + exit 1 +fi + +# Check for valid input +for sqltype in $sqltypes; do + case "$sqltype" in + mysql|postgres) + ;; + *) + usage + ;; + esac +done + +for sqltype in $sqltypes; do + if ${sqltype}_valid; then + for db in `${sqltype}_list_db`; do + targetfile=$targetdir/dump_${stamp}_${db}.${sqltype} + [ -f $targetfile ] && savelog -c 3 $targetfile >/dev/null + ${sqltype}_dump_db $db > $targetfile + done + fi +done diff --git a/localezcreate b/localezcreate new file mode 100755 index 0000000..09c270f --- /dev/null +++ b/localezcreate @@ -0,0 +1,178 @@ +#!/bin/bash +# /usr/local/sbin/localezcreate: create local eZ Publish site +# Written by Jonas Smedegaard + +set -e + +# Defaults +defaultmaildomain=`dnsdomainname` +defaultdomain="ez.$defaultmaildomain" +defaultuid="www-data" + +# Input +dbuser="$1" +host="$2" +fqdn="$3" +db="$4" +mailuser="$5" +maildomain="$6" +uid="$7" +gid="$8" +dbpasswd="$9" +ezpasswd="$10" + +if [ $# \< 2 -o $# \> 8 ]; then + echo "\$#: $#" + echo "Usage: `basename $0` dbuser host [fqdn [db [mailuser [maildomain [ uid [ gid [dbpasswd [ezpasswd]]]]]]]]" + echo + echo " dbuser: MySQL database user" + echo " host: hostname (and only that - no dots!)" + echo " fqdn: Fully qualified domain name (default is .$defaultdomain)" + echo " db: MySQL database name" + echo " mailuser: User part of mail address (default is )" + echo " maildomain: Domain part of mail address (default is $defaultmailname)" + echo " uid: System user id (default is $defaultuid)" + echo " gid: System group id (default is " + echo " dbpasswd: MySQL database password (default is to ask for it)" + echo " ezpasswd: Web password (default is to ask for it)" + + exit 1 +fi + +if [ -f /root/.my.cnf ]; then + dbrootpw=$(grep password /root/.my.cnf | awk -F= '{print $2}' | head -1 | sed 's/^ //g') +else + echo -n "Enter MySQL root password: " + read -s dbrootpw + echo +fi + +pkg="ezpublish" +#src="/usr/local/src/$pkg/ezpublish_2_2_beta_1.tar.gz" +srcdir="/usr/share/ezpublish" +sqldir="$srcdir/sql" +webdir="$srcdir/lib" +tarballs="data install www" +sqlfile="publish_mysql.sql" +sqlfile2="data_mysql.sql" + +[ "x$uid" == "x" ] && uid="$defaultuid" +gid="$uid" +[ "x$fqdn" == "x" ] && fqdn="$host.$defaultdomain" +[ "x$db" == "x" ] && db="ez_2_2_$host" +[ "x$mailuser" == "x" ] && mailuser="$uid" +[ "x$maildomain" == "x" ] && maildomain="$defaultmaildomain" +if [ "x$dbpasswd" == "x" ]; then + echo -n "Enter MySQL password for $dbuser: " + read -s dbpasswd + echo +fi +[ "x$ezpasswd" == "x" ] && ezpasswd="$dbpasswd" +home=`getent passwd $uid | awk -F: '{print $6}';` +basedir="$home/web_$fqdn" +adminhost="admin.$fqdn" +adminbasedir="$basedir/admin" +webcfg="/etc/apache/vhosts/$fqdn" + +#TODO: Make sure $basedir, $db, $dbuser and $webcfg doesn't exist + +if [ "$(basename $0)" = "${pkg}betarecreate" ]; then + mysqladmin -uroot -p$dbrootpw drop $db + rm -rf $basedir +fi + +echo "Creating database $db" +mysqladmin -uroot -p$dbrootpw create $db +echo "GRANT SELECT,INSERT,UPDATE,DELETE ON $db.* TO '$dbuser'@localhost IDENTIFIED BY '$dbpasswd';" | mysql -uroot -p$dbrootpw + +echo "Copying initial files to $basedir" +su $uid -c "cp -a $webdir $basedir" + +echo "Adding initial data to database" +#dbstring="INSERT INTO eZUser_User VALUES (1,'$dbuser',password('$ezpasswd'),'$dbuser@$maildomain','admin','user','false','',0,0);" +#cat $sqldir/$sqlfile | sed "s/^INSERT INTO eZUser_User VALUES.*\$/$dbstring/" | mysql -uroot -p$dbrootpw $db +#cat $sqldir/$sqlfile | sed "s/'0c947f956f7aa781','postmaster@yourdomain'/password('$ezpasswd'),'$dbuser@$maildomain'/" | mysql -uroot -p$dbrootpw $db +cat $sqldir/$sqlfile | mysql -uroot -p$dbrootpw $db +#cat $basedir/sql/publish.sql | sed -e "s/^INSERT INTO eZUser_User VALUES.*\$/$dbstring/" -e 's/ PACK_KEYS=1//' | mysql -uroot -p$dbrootpw $db +#su $uid -c "zcat $basedir/data.tar.gz | tar -xv -C $basedir" + +echo -n "Unpacking tarballs:" +su $uid -c " + for tarball in $tarballs; do + echo -n \" \$tarball\" + tar -xz -C $basedir -f $srcdir/\$tarball.tar.gz + done + echo +" + +echo "Adding demo data to database" +#su - $uid -c "mysql -u$dbuser -p$dbpasswd $db < $sqldir/$sqlfile2" +mysql -u$dbuser -p$dbpasswd $db < $sqldir/$sqlfile2 + +echo "Configuring website" +su $uid -c " + cd $basedir + $srcdir/bin/modfix.sh >/dev/null + $srcdir/bin/clearcache.sh >/dev/null + mkdir -p override + echo \"[site] +SiteURL=$fqdn +AdminSiteURL=admin.$fqdn +UserSiteURL=$fqdn +SiteTitle=$host eZ site +Keywords=$host $maildomain +SiteAuthor=someone at $maildomain +SiteCopyright=someone © 2001 +SiteDescription=a web application suite +SiteKeywords=Content Management System, CMS, e-commerce +Database=$db +User=$dbuser +Password=$dbpasswd + +[eZUserMain] +DefaultCountry=58 + +[eZTradeMain] +OrderSenderEmail=$mailuser@$maildomain +OrderReceiverEmail=$mailuser@$maildomain +mailToAdmin=$mailuser@$maildomain +ForceSSL=disabled +ApacheUser=UserApacheRunsAs + +[eZForumMain] +ReplyAddress=$mailuser@$maildomain +\" > override/site.ini.append +" + +echo "Configuring Apache" +echo "# Generated for $pkg by $(basename $0) + + ServerName $fqdn + ServerAdmin $mailuser@$maildomain + DocumentRoot $basedir + user $uid + group $gid + php_flag magic_quotes_gpc off + RewriteEngine On + RewriteRule ^/stats/store/(.*).gif\$ $basedir/ezstats/user/storestats.php [S=2] + RewriteRule ^/filemanager/filedownload/([^/]+)/(.*)\$ $basedir/ezfilemanager/files/\$1 [T="application/oct-stream",S=1] + RewriteRule !\.(gif|css|jpg|png)\$ $basedir/index.php + + + ServerName $adminhost + ServerAdmin $mailuser@$maildomain + DocumentRoot $basedir + user $uid + group $gid + php_flag magic_quotes_gpc off + RewriteEngine On + RewriteRule !\.(gif|css|jpg|png)\$ $basedir/index_admin.php + + + ServerName www.$fqdn + RewriteEngine On + RewriteRule / http://$fqdn/ [R] +" > $webcfg + +echo "Done!" +echo "Remember to reload Apache..." diff --git a/localmoffycreate b/localmoffycreate new file mode 100755 index 0000000..ca969bc --- /dev/null +++ b/localmoffycreate @@ -0,0 +1,70 @@ +#!/bin/bash + +set -e + +user="moffy" +passwd="oscerciv" +db="moffy" + +mysqladmin -uroot -p create $db +echo "GRANT ALL ON $db.* TO '$user@localhost' IDENTIFIED BY '$passwd' WITH GRANT OPTION;" | mysql -uroot -p + +#mysql -u$user -p$passwd $db < $basedir/sql/publish.sql +dbstring="INSERT INTO eZUser_User VALUES (1,'$user',password('$passwd'),'$user@$domain','admin','user','false','',0,0);" +cat $basedir/sql/publish.sql | sed "s/^INSERT INTO eZUser_User VALUES.*\$/$dbstring/" | mysql -u$user -p$passwd $db +su $user -c "zcat $basedir/data.tar.gz | tar -xv -C $basedir" +mysql -u$user -p$passwd $db < $basedir/sql/data.sql + +#su $user -c "cd $basedir && ./modfix.sh" +#su $user -c "cd $basedir && ./secure_clearcache.sh" +#su $user -c "echo \" +#RewriteEngine On +#RewriteRule ^/stats/store/(.*).gif\\\$ $basedir/ezstats/user/storestats.php [S=2] +#RewriteRule ^/filemanager/filedownload/([^/]+)/(.*)\\\$ $basedir/ezfilemanager/files/\\\$1 [T=\\\"application/oct-stream\\\",S=1] +#RewriteRule \!\.(gif|css|jpg|png)\\\$ $basedir/index.php +#\" > $basedir/.htaccess" + +#su $user -c "echo \" +#RewriteEngine On +#RewriteRule \!\.(gif|css|jpg|png)\\\$ $basedir/index_admin.php +#\" > $adminbasedir/.htaccess" + +su $user -c " + cd $basedir + ./modfix.sh + ./secure_modfix.sh $user $group + ./clearcache.sh + ./secure_clearcache.sh + cat site.ini | sed 's/^SiteURL=publish.ez.no\$/SiteURL=$host/' > site.ini.tmp && mv site.ini.tmp site.ini + cat site.ini | sed 's/^Database=publish\$/Database=$db/' > site.ini.tmp && mv site.ini.tmp site.ini + cat site.ini | sed 's/^User=publish\$/User=$user/' > site.ini.tmp && mv site.ini.tmp site.ini + cat site.ini | sed 's/^Password=publish\$/Password=$passwd/' > site.ini.tmp && mv site.ini.tmp site.ini + cat site.ini | sed 's/^Database=publish\$/Database=$db/' > site.ini.tmp && mv site.ini.tmp site.ini +" + +echo " + + ServerName $host + ServerAdmin $user@$domain + DocumentRoot $basedir + user $user + group $group + php_flag magic_quotes_gpc off + RewriteEngine On + RewriteRule ^/stats/store/(.*).gif\$ $basedir/ezstats/user/storestats.php [S=2] + RewriteRule ^/filemanager/filedownload/([^/]+)/(.*)\$ $basedir/ezfilemanager/files/\$1 [T="application/oct-stream",S=1] + RewriteRule !\.(gif|css|jpg|png)\$ $basedir/index.php + + + ServerName $adminhost + ServerAdmin $user@$domain + DocumentRoot $basedir + user $user + group $group + php_flag magic_quotes_gpc off + RewriteEngine On + RewriteRule !\.(gif|css|jpg|png)\$ $basedir/index_admin.php + +" > $webcfg + +echo "Remember to reload Apache..." diff --git a/localwebbbscreate b/localwebbbscreate new file mode 100755 index 0000000..07406c1 --- /dev/null +++ b/localwebbbscreate @@ -0,0 +1,40 @@ +#!/bin/sh + +NAME=webbbs +PATH=$PATH:/usr/local/sbin +USER=$2 +BBS=$3 +HOME=/web/$USER +CGI=$HOME/www/cgi-local + +test "x$USER" = "x" && $1="" + +case "$1" in + add) + test "x$BBS" = "x" && exit 1 + echo -n "Adding bbs $BBS to webserver $USER: " + test -d $HOME/webbbs || mkdir $HOME/webbbs + mkdir $HOME/webbbs/$BBS + test -d $CGI || mkdir $CGI + chown $USER.www-data $CGI + cp /usr/local/lib/scripts/darryl/webbbs/config.pl $CGI/$BBS.cgi + chown $USER.www-data $CGI/$BBS.cgi + chown -R $USER.www-data $HOME/webbbs + chmod g+w $HOME/webbbs/$BBS + chmod g+x,o= $CGI/$BBS.cgi + echo "Done!" + ;; + del|delete|remove) + test -d $HOME/webbbs/$BBS || exit 1 + echo -n "Deleting bbs $BBS from webserver $USER: " + rm -rf $HOME/webbbs/$BBS + rm -rf $CGI/$BBS.cgi + echo "Done!" + ;; + *) + echo "Usage: $NAME [ add | delete ] WEBSERVER BBS" + exit 1 + ;; +esac + +exit 0 diff --git a/localwebcalcreate b/localwebcalcreate new file mode 100755 index 0000000..f057d21 --- /dev/null +++ b/localwebcalcreate @@ -0,0 +1,47 @@ +#!/bin/sh + +NAME=webcal +PATH=$PATH:/usr/local/sbin +USER=$2 +HOME=/web/$USER +CGI=$HOME/www/cgi-local +NEWNAME=kalender +CAL=$HOME/www/$NEWNAME + +test "x$USER" = "x" && $1="" + +case "$1" in + add) + test "x$USER" = "x" && exit 1 + echo -n "Adding calendar to webserver $USER: " + test -d $CGI || mkdir $CGI + chown $USER.www-data $CGI + cp /usr/local/lib/scripts/darryl/webcal/webcal.pl $CGI/$NEWNAME.cgi + cp /usr/local/lib/scripts/darryl/webcal/webcal.add.pl $CGI/$NEWNAME.cgi + cp /usr/local/lib/scripts/darryl/webcal/webcal.config.pl $CGI/$NEWNAME.cgi + cp /usr/local/lib/scripts/darryl/webcal/webcal.delete.pl $CGI/$NEWNAME.cgi + chown $USER.www-data $CGI/NEWNAME*.cgi + chmod g+x,o= $CGI/$NEWNAME*.cgi + mkdir $HOME/webcal + cp /usr/local/lib/scripts/darryl/webcal/webcal.data $HOME/webcal/ + chown -R $USER.www-data $HOME/webcal + chmod g+w $HOME/webcal/webcal.data + mkdir $CAL + chown -R $USER.www-data $CAL + echo "Done!" + ;; + del|delete|remove) + test -d $HOME/webcal || exit 1 + echo -n "Deleting calendar from webserver $USER: " + rm -rf $CGI/$NEWNAME*.cgi + rm -rf $HOME/webcal + rm -rf $CAL + echo "Done!" + ;; + *) + echo "Usage: $NAME [ add | delete ] WEBSERVER" + exit 1 + ;; +esac + +exit 0 diff --git a/localwebstats b/localwebstats new file mode 100755 index 0000000..7de9106 --- /dev/null +++ b/localwebstats @@ -0,0 +1,181 @@ +#!/bin/bash +# /etc/cron.daily/localstats: Webstats maintenance script +# Written by Jonas Smedegaard + +# halt on errors +#set -e + +function usage() { + echo "Usage: `basename $0` update|prelogrotate|postlogrotate|reset [...]" + echo " If no website is given, all are attempted" + echo " Tip: Automagically runs when symlinked to /etc/cron.{daily,weekly,monthly}/" + exit 1 +} + +# automagically configure when run from cron dirs +case `dirname $0` in + /etc/cron.daily) + stamp=ignore + ;; + /etc/cron.weekly) + stamp=update + ;; + /etc/cron.monthly) + stamp=ignore + ;; + *) + stamp=$1 + shift || usage + ;; +esac + +case "$stamp" in + update|prelogrotate|postlogrotate|reset|ignore) + ;; + *) + usage + ;; +esac + +if [ "$stamp" == "ignore" ]; then + if [ $DEBUG ]; then + echo "Asked to ignore - exiting silently..." + fi + exit 0 +fi + +WEBROOT=/var/www +LOGROOT=/var/log/apache +WEBALIZER_OPTIONS="-q" + +function webdir() { echo $1/VIRTUAL/$2; } +function webdirs() { echo `find /var/log/apache -type d -print`; } +#function webdirs() { for dir in $(find ${LOGROOT} -type d -print); do echo $dir; done; } +#function logdir() { echo ${LOGROOT}/${WEBSITE}; } +#function logfiles() { $(ls -r $LOGDIR/*-access*.gz) $(ls -r $LOGDIR/access*.??.gz) $(ls -r $LOGDIR/access*.?.gz) $(ls -r $LOGDIR/access*.?); } +function logfiles() { ls $1/$2/????.??.??; } +function logfilesgz() { ls $1/$2/????.??.??.gz; } +function host() { cat $1/VIRTUAL/$2/hostname || exit 1; } +function domain() { cat $1/VIRTUAL/$2/domainname || exit 1; } + +# The above can be overridden in /etc/local/www + +. /etc/local/www || exit 1 + +# variables too boring to be configurable +JDRESOLVE_DB="/var/cache/jdresolve/hosts.db" +#JDRESOLVE_OPTIONS="-r -t 5 --database=${JDRESOLVE_DB} --dbfirst --expiredb=48" +JDRESOLVE_OPTIONS="-r --database=${JDRESOLVE_DB} --dbfirst --expiredb=48" +JDRESOLVE_MERGE_OPTIONS="--mergedb --database=${JDRESOLVE_DB}" + +test -d ${WEBROOT} -a -d ${LOGROOT} || exit 1 + +WEBSITES=$@ +if [ "$WEBSITES" == "" ]; then + WEBSITES=$(webdirs) +fi + +for WEBSITE in $WEBSITES; do + WEBDIR=$(webdir $WEBROOT ${WEBSITE}) || exit 1 + + test -d ${WEBDIR} || exit 1 + + HOST=$(host ${WEBROOT} ${WEBSITE}) || exit 1 + DOMAIN=$(domain ${WEBROOT} ${WEBSITE}) || exit 1 + LOGFILES=$(logfiles ${LOGROOT} ${WEBSITE}) || exit 1 + LOGFILESGZ=$(logfilesgz ${LOGROOT} ${WEBSITE}) || exit 1 + + + if [ -x /usr/bin/jdresolve ]; then + touch ${JDRESOLVE_DB} || exit 1 + fi + + WEBALIZER_OPTIONS="-c /etc/webalizer.conf -c $WEBDIR/webalizer.conf -o $WEBDIR/stats/webalizer -f" + ANALOG_OPTIONS="-G +g/etc/analog.conf +g$WEBDIR/analog.conf -a" + AWSTATS_OPTIONS="-update" + FQDN_ESC=`echo "$HOST.$DOMAIN" | sed -e 's/\./\\\./g'` # needed for awstats config + + if [ ${DEBUG} ]; then + echo "Making stats for ${WEBSITE} in ${WEBDIR}:" + JDRESOLVE_OPTIONS="${JDRESOLVE_OPTIONS} -p" + ANALOG_OPTIONS="${ANALOG_OPTIONS} +q" + WEBALIZER_OPTIONS="${WEBALIZER_OPTIONS} -T" + AWSTATS_OPTIONS="${AWSTATS_OPTIONS} -showsteps" + else + JDRESOLVE_OPTIONS="${JDRESOLVE_DB} -n" + ANALOG_OPTIONS="${ANALOG_OPTIONS} -q" + WEBALIZER_OPTIONS="${WEBALIZER_OPTIONS} -Q" + AWSTATS_OPTIONS="${AWSTATS_OPTIONS}" + fi + + if [ $stamp == "reset" ]; then + rm -rf $WEBDIR/stats + mkdir $WEBDIR/stats + fi + + LOGDATA="${WEBDIR}/stats/rawlog.txt" + if [ -x /usr/bin/jdresolve ]; then + zcat ${LOGFILESGZ} | jdresolve ${JDRESOLVE_OPTIONS} - > ${LOGDATA} + cat ${LOGFILES} | jdresolve ${JDRESOLVE_OPTIONS} - >> ${LOGDATA} + jdresolve ${JDRESOLVE_MERGE_OPTIONS} ${LOGDATA} + elif [ -x /usr/sbin/logresolve ]; then + zcat ${LOGFILESGZ} | logresolve > ${LOGDATA} + cat ${LOGFILES} | logresolve >> ${LOGDATA} + else + zcat ${LOGFILESGZ} > ${LOGDATA} + cat ${LOGFILES} >> ${LOGDATA} + fi + + if [ -x /usr/bin/analog ]; then + if [ $stamp == "reset" ]; then + test ! -f $WEBDIR/analog.conf && echo "\ +HOSTNAME $HOST.$DOMAIN +"\ + >$WEBDIR/analog.conf + mkdir ${WEBDIR}/stats/analog + cat ${LOGDATA} | analog ${ANALOG_OPTIONS} -C"LOGFILE none" -C"LOGFILE stdin" > $WEBDIR/stats/analog/index.html + fi +# cat ${LOGDATA} | analog ${ANALOG_OPTIONS} -C"LOGFILE none" -C"LOGFILE stdin" > $WEBDIR/stats/analog/index.html + fi + if [ -x /usr/bin/webalizer ]; then + + if [ $stamp == "reset" ]; then + test ! -f $WEBDIR/webalizer.conf && echo "\ +HostName $HOST.$DOMAIN +HideSite *$DOMAIN +HideReferrer $DOMAIN/ +"\ + > $WEBDIR/webalizer.conf + mkdir $WEBDIR/stats/webalizer + cat ${LOGDATA} | webalizer $WEBALIZER_OPTIONS -N 0 - + fi +# cat ${LOGDATA} | webalizer $WEBALIZER_OPTIONS -N 0 - + fi + if [ -x /usr/lib/cgi-bin/awstats.pl ]; then + if [ $stamp == "reset" ]; then + test ! -f /etc/awstats/awstats.$WEBSITE.conf && echo "\ +LogFile=\"/var/www/VIRTUAL/$WEBSITE/stats/rawlog.txt\" +LogFormat=4 +DNSLookup=0 +DirData=\"/var/cache/awstats\" +DirIcons=\"/awstats-icon\" +SiteDomain=\"$FQDN_ESC\" +HostAliases=\"$FQDN_ESC localhost 127\.0\.0\.1\" +Lang=\"dk\" +DirLang=\"/usr/share/awstats/lang\" +DefaultFile=\"index.html\" +ShowFlagLinks=0 +AllowToUpdateStatsFromBrowser=0 +ShowLinksOnUrl=0 +DirCgi=\"http://cgi.jones.dk/cgi-bin\" +DirIcons=\"http://stats.jones.dk/awstats-icon\" +"\ + >/etc/awstats/awstats.$WEBSITE.conf + mkdir $WEBDIR/stats/awstats + /usr/lib/cgi-bin/awstats.pl -config=${WEBSITE} $AWSTATS_OPTIONS -output > $WEBDIR/stats/awstats/index.html + fi + /usr/lib/cgi-bin/awstats.pl -config=${WEBSITE} $AWSTATS_OPTIONS -output > $WEBDIR/stats/awstats/index.html + fi + test -L $WEBDIR/www/stats || ln -s $WEBDIR/stats $WEBDIR/www/stats + test -L $WEBDIR/stats/stats && rm $WEBDIR/stats/stats +done diff --git a/localwebstats-all b/localwebstats-all new file mode 100755 index 0000000..c402d28 --- /dev/null +++ b/localwebstats-all @@ -0,0 +1,24 @@ +#!/bin/bash + +WEBROOT=/var/www +LOGROOT=/var/log/apache + +function webdirs() +{ + echo `find /var/log/apache -type d -print` +# for dir in $(find ${LOGROOT} -type d -print) +# do +# echo $dir +# done +} + +. /etc/local/www || exit 1 + +#for WEBSITE in $(ls $WWWROOT) +#for WEBSITE in $(find $WWWROOT/* -type d -mindepth 1 -maxdepth 1 -print) +for WEBSITE in $(webdirs); do + if test $WEBSITE != "lost+found" + then + /usr/local/sbin/stats-init $WEBSITE + fi +done diff --git a/mkmailcerts b/mkmailcerts new file mode 100755 index 0000000..7a80eb2 --- /dev/null +++ b/mkmailcerts @@ -0,0 +1,38 @@ +#!/bin/sh + +CWD=`pwd` +PATH=$PATH:/usr/bin/ssl +COUNTRY='.' +STATE='.' +LOCALITY='.' +DOMAINNAME=`hostname -d` +#HOSTNAME=`hostname -s` +HOSTNAME="mail.$DOMAINNAME" +ISSUER="postmaster@$DOMAINNAME" +DAYS2EXPIRE=365 +DAEMONS="imapd ipop3d" + +cd /etc/ssl/certs +for DAEMON in $DAEMONS; do + if [ -f $DAEMON.pem ]; then + echo "You already have /etc/ssl/certs/$DAEMON.pem - ignoring certificate generation" + else + echo -n "Generating $DAEMON certificate..." + openssl req -new -x509 -nodes -out $DAEMON.pem -keyout $DAEMON.pem -days $DAYS2EXPIRE > /dev/null 2>&1 <<+ +$COUNTRY +$STATE +$LOCALITY +$HOSTNAME +$HOSTNAME +$HOSTNAME +$ISSUER ++ + ln -sf $DAEMON.pem `openssl x509 -noout -hash < $DAEMON.pem`.0 + echo "Done!" + fi + + chown root.root /etc/ssl/certs/$DAEMON.pem + chmod 0640 /etc/ssl/certs/$DAEMON.pem +done + +cd $CWD diff --git a/rm-toobig b/rm-toobig new file mode 100755 index 0000000..77889b1 --- /dev/null +++ b/rm-toobig @@ -0,0 +1,14 @@ +#!/bin/sh + +# It seems netatalk has problems dealing with Eudora: +# Occasionally files in the "Attachments Folder" explodes +# to sizes in the area 80-100 Megabytes. +# +# This script is the simple "solution": delete huge files... +# +# I put it in a cron job once a month just before an +# rsync script to mirror all homes to another machine. + +BASE=/home + +find $BASE -path '*Attachments*' -size +15000k -exec rm '{}' ';' diff --git a/smbadduser b/smbadduser new file mode 100755 index 0000000..7ac7681 --- /dev/null +++ b/smbadduser @@ -0,0 +1,73 @@ +#!/bin/csh +# +# smbadduser - Written by Mike Zakharoff +# +unalias * +#set path = ($path /usr/local/samba/bin) + +set smbpasswd = /etc/samba/smbpasswd +set user_map = /etc/samba/users.map +# +# Set to site specific passwd command +# +set passwd = "cat /etc/passwd" +#set passwd = "niscat passwd.org_dir" +#set passwd = "ypcat passwd" + +set line = "----------------------------------------------------------" +if ($#argv == 0) then + echo $line + echo "Written: Mike Zakharoff email: michael.j.zakharoff@boeing.com" + echo "" + echo " 1) Updates $smbpasswd" + echo " 2) Updates $user_map" + echo " 3) Executes smbpasswd for each new user" + echo "" + echo "smbadduser unixid:ntid unixid:ntid ..." + echo "" + echo "Example: smbadduser zak:zakharoffm johns:smithj" + echo $line + exit 1 +endif + +touch $smbpasswd $user_map +set new = () +foreach one ($argv) + echo $one | grep ':' >& /dev/null + if ($status != 0) then + echo "ERROR: Must use unixid:ntid like -> zak:zakharoffm" + continue + endif + set unix = `echo $one | awk -F: '{print $1}'` + set ntid = `echo $one | awk -F: '{print $2}'` + + set usr = `eval $passwd | awk -F: '$1==USR {print $1}' USR=$unix` + if ($#usr != 1) then + echo "ERROR: $unix Not in passwd database SKIPPING..." + continue + endif + set tmp = `cat $smbpasswd | awk -F: '$1==USR {print $1}' USR=$unix` + if ($#tmp != 0) then + echo "ERROR: $unix is already in $smbpasswd SKIPPING..." + continue + endif + + echo "Adding: $unix to $smbpasswd" + eval $passwd | \ + awk -F: '$1==USR { \ + printf( "%s:%s:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:%s:%s:%s\n", $1, $3, $5, $6, $7) }' USR=$unix >> $smbpasswd + if ($unix != $ntid) then + echo "Adding: {$unix = $ntid} to $user_map" + echo "$unix = $ntid" >> $user_map + endif + set new = ($new $unix) +end + +# +# Enter password for new users +# +foreach one ($new) + echo $line + echo "ENTER password for $one" + smbpasswd $one +end diff --git a/user-init b/user-init new file mode 100755 index 0000000..c2b4ad8 --- /dev/null +++ b/user-init @@ -0,0 +1,175 @@ +#!/bin/sh + +set -e + +. /etc/local/volumes || exit 0 +XDIRREAL="$XDIR/users/root" + +if [ $XCHANGE ]; then + if [ ! -d "$XDIR" ]; then + echo "XDIR doesn't exist. Ignoring XCHANGE!" + XCHANGE="" + fi +fi + +if [ $# -gt 0 ]; then + USERS=$* +else +# TODO: scan /etc/passwd and use `getent passwd postgres | awk -F: '{print $6}' | head -1` or similar + USERS=$(ls /home) +fi + +mac=".mac" +pc=".pc" +xchange=".xchange" +[ $NETATALK_HOME ] && mac=$NETATALK_HOME +[ $SAMBA_HOME ] && pc=$SAMBA_HOME +[ $XCHANGE_HOME ] && xchange=$XCHANGE_HOME + +for USER in $USERS; do + HOME="/home/$USER" + + test -d $HOME || continue + test -L $HOME && continue +# [ $USER != "lost+found" ] || continue + id -u $USER >/dev/null 2>&1 || continue +# echo $UID +# [ $UID gt 1000 ] || continue + + echo -n $USER + +# if [ -x /etc/local/quota.sh ]; then +# /etc/local/quota.sh $USER +# fi + [ $QUOTASOFT ] || QUOTASOFT="0" + [ $QUOTAHARD ] || QUOTAHARD="0" + for QUOTAHOME in $QUOTAHOMES; do + if [ $NEW_QUOTA ]; then + setquota $USER $QUOTASOFT $QUOTAHARD 0 0 $QUOTAHOME + else + setquota $USER $QUOTAHOME $QUOTASOFT $QUOTAHARD 0 0 + fi + done + +# groupadd $USER +# usermod -g $USER $USER +# rm -f $HOME/.profile $HOME/.bashrc $HOME/.bash_history +# cp /etc/skel/.bash_profile /etc/skel/.bashrc $HOME/ + + if [ $NETATALK ]; then + mkdir -p $HOME/$mac + fi + if [ $SAMBA ]; then + mkdir -p $HOME/$pc + fi +# if [ $XCHANGE ]; then +# mkdir -p $HOME/$xchange +# chown -R $USER: $HOME/$xchange +# chmod -R u=rw,go=,g+r,ug+X $HOME/$xchange +# fi + if [ $XCHANGE ]; then + [ -d $XDIRREAL/$USER ] || mkdir -p $XDIRREAL/$USER + chown -R $USER:users $XDIRREAL/$USER + chmod -R g=r,g+X $XDIRREAL/$USER + if [ -e "x$HOME/$xchange" ]; then + rm -rf $HOME/$xchange + ln -dfs $XDIRREAL/$USER $HOME/$xchange + fi + fi + if [ $PUBLIC ]; then + mkdir -p $HOME/public_html + fi + + chown $USER: $HOME + chmod u=rwX,go=rX $HOME + +# chown -R $USER: $HOME +# chmod -R u=rw,g=r,o=,ug+X $HOME +# chmod o+rX $HOME + + if [ -f $HOME/.forward ]; then + chown $USER: $HOME/.forward + chmod 0640 $HOME/.forward + fi + + mkdir -p $HOME/mail + touch $HOME/mail/mbox + chown -R $USER: $HOME/mail + chmod -R u=rw,go=,u+X $HOME/mail + if [ -f $HOME/.mailboxlist ]; then + chown $USER: $HOME/.mailboxlist + chmod 0640 $HOME/.mailboxlist + fi + if [ -f /var/spool/mail/$USER ]; then + chown $USER:mail /var/spool/mail/$USER + chmod ug=rw,o= /var/spool/mail/$USER + fi + + if [ -d $HOME/$mac ]; then + chown -R $USER: $HOME/$mac + chmod -R u=rw,g=r,o=,ug+X $HOME/$mac + rm -rf $HOME/$mac/Network\ Trash\ Folder + mkdir $HOME/$mac/Network\ Trash\ Folder + chown nobody: $HOME/$mac/Network\ Trash\ Folder + chmod a= $HOME/$mac/Network\ Trash\ Folder + fi + + # Access to Mac folders + for macdir in `find $HOME -mindepth 1 -maxdepth 1 -type d | egrep "^$HOME/mac_$USER$"`; do + chgrp -R $USER $macdir + chmod -R u=rw,g=rw,o=,ug+X,g+s $macdir + done + for macdir in `find $HOME -mindepth 1 -maxdepth 1 -type d | egrep "^$HOME/mac_${USER}_ro$"`; do + chown -R $USER: $macdir + chmod -R u=rw,g=r,o=,ug+X $macdir + done + + # Content cleanup of Mac folders + for macdir in `find $HOME -mindepth 2 -maxdepth 2 -type d | egrep "^$HOME/mac_$USER/"`; do + rm -rf $macdir/Network\ Trash\ Folder + mkdir $macdir/Network\ Trash\ Folder + chown nobody: $macdir/Network\ Trash\ Folder + chmod a= $macdir/Network\ Trash\ Folder + done + + if [ -d $HOME/$pc ]; then + chown -R $USER: $HOME/$pc + chmod -R u=rw,g=r,o=,ug+X $HOME/$pc + fi + +# if test -d $HOME/$xchange; then +# chown -R $USER:users $HOME/$xchange +# chmod -R g=r,g+X $HOME/$xchange +# if test "x$XCHANGEDIR" != "x"; then +# rm -rf $XCHANGEDIR/$USER +# ln -dfs $HOME/$xchange $XCHANGEDIR/$USER +# fi +# fi + + if [ -d $HOME/public_html ]; then + chown -R $USER: $HOME/public_html + chmod -R u+rX,go=r,go+X $HOME/public_html + if [ $NETATALK ]; then + if [ -d "$HOME/public_html/Network\ Trash\ Folder" ]; then + rm -rf $HOME/public_html/Network\ Trash\ Folder + mkdir $HOME/public_html/Network\ Trash\ Folder + chown nobody: $HOME/public_html/Network\ Trash\ Folder + chmod a= $HOME/public_html/Network\ Trash\ Folder + fi + fi + fi + +## Needs more work (not all websites are equally handled) +# for dir in $(cd $HOME && find -maxdepth 1 -type d -name 'web_*' -exec sh -c 'cd {} && basename `pwd`' \;); do +# chown -R $USER: $HOME/$dir +# chmod -R u+rX,go=r,go+X $HOME/$dir +# done + + echo "." +done + +if [ $XCHANGE ]; then + for USER in $(ls $XDIRREAL); do + id $USER >/dev/null 2>&1 || rm -rf $XDIRREAL/$USER + done +fi diff --git a/user-mailadr b/user-mailadr new file mode 100755 index 0000000..edc5f7f --- /dev/null +++ b/user-mailadr @@ -0,0 +1,6 @@ +#!/bin/sh + +for uid in $@; do + grep "[[:space:]]$uid\$" `ls /etc/postfix/virtual* | egrep -v '\.(bak|db|dpkg-new|dpkg-old|old)$'` | sed -e 's/.*://' -e 's/[[:space:]].*//' | sort + echo "$uid@`cat /etc/mailname`" +done diff --git a/userforward b/userforward new file mode 100755 index 0000000..5ca172f --- /dev/null +++ b/userforward @@ -0,0 +1,29 @@ +#!/bin/sh + +if [ $# -ne 2 ]; then + echo "Usage: userforward {account} {forward@address.somewhere}" + exit 1 +fi + +uid=$1 +addr=$2 + +set -e + +homedir=`getent passwd $uid | awk -F: '{print $6}';` + +if [ -e $homedir/.forward ]; then + echo "User \"$uid\" already has a .forward - exiting..." + exit 1 +fi + +if [ -d $homedir ]; then + touch $homedir/.forward + echo "echo $addr>$homedir/.forward" + echo $addr>$homedir/.forward + chown $uid. $homedir/.forward + echo "Done!" +else + echo "Problem with user \"$uid\": Homedir \"$homedir\" doesn't exist - exiting..." + exit 1 +fi diff --git a/usergroupadd b/usergroupadd new file mode 100755 index 0000000..b7611a4 --- /dev/null +++ b/usergroupadd @@ -0,0 +1,33 @@ +#!/usr/bin/perl +($newgroup, @ARGV) = @ARGV; +if ($newgroup eq "") +{ + print "Syntax: usergroupadd newgroup PATTERN\|*\n"; + exit; +} +@input = "| grep -i \"@ARGV\"" if @ARGV; +@users = `cat /etc/passwd @input`; + +print "Users modified: "; +foreach $n (@users) +{ + $user =~ /(\S+)/; + ($a, $b, $c, $d, $name) = split (/:/, $n); +# ($name) = split(/,/,$name); + next if ($c < 100); + +# $output = ""; +# @id = split /\s/, `id -Gn $a`; +# foreach $o (@id) +# { +# $output .= "$o,"; +# } +# $output .= "$newgroup"; +# +# `usermod -G $output $a`; + `adduser $a $newgroup`; + print "$a"; +# print "($output)"; + print ", "; +} +print "Done!\n"; diff --git a/usergroupinfo b/usergroupinfo new file mode 100755 index 0000000..f9fa9b8 --- /dev/null +++ b/usergroupinfo @@ -0,0 +1,13 @@ +#!/usr/bin/perl +@input = "| grep -i \"@ARGV\"" if @ARGV; +@users = `cat /etc/passwd @input`; +#print @input; +foreach $n (@users) +{ + $user =~ /(\S+)/; + ($a, $b, $c, $d, $name) = split (/:/, $n); + ($name) = split(/,/,$name); + next if ($c < 100); + print "$a\t($name)\t"; + print `id -Gn $a`; +} diff --git a/volinit-netatalk b/volinit-netatalk new file mode 100755 index 0000000..a1e355a --- /dev/null +++ b/volinit-netatalk @@ -0,0 +1,63 @@ +#!/bin/bash + +[ -f /etc/local/volumes ] || exit 1 +. /etc/local/volumes +[ "x$NDIR" = "x" ] && exit 1 + +if [ $# -eq 0 ]; then +# echo "$0: directory name(s) required" + echo "Usage: $0 [create]" + exit 1 + fi + +if [ "x$1" = "x" ]; then + echo "$0: base directory missing" + exit 1 + fi +VOL=$NDIR/$1 + +if [ "x$2" = "x" ]; then + echo "$0: read group missing" + exit 1 + fi +ALL=$2 + +if [ "x$3" = "x" ]; then + echo "$0: write group missing" + exit 1 + fi +GRP=$3 + +#GRPUSER="nobody" +GRPUSER="admin" +#if [ "x$4" != "x" ]; then +# GRPUSER=$4 +#else + id $GRP >/dev/null && GRPUSER=$GRP +#fi + +if [ "x$4" != "x" ]; then + if [ "x$4" == "xcreate" ]; then + mkdir -p $VOL/$ALL/$GRP || exit + else + echo "Usage: $0 [create]" + exit 1 + fi +fi + +test -d $VOL/$ALL/$GRP || exit +test -d $VOL/$ALL/$GRP/.AppleDouble || mkdir $VOL/$ALL/$GRP/.AppleDouble +test -d $VOL/$ALL/$GRP/.AppleDesktop || mkdir $VOL/$ALL/$GRP/.AppleDesktop +#chown $ALLUSER.$ALL $VOL/$ALL +chown root.$ALL $VOL/$ALL +chown $GRPUSER.$ALL $VOL/$ALL/$GRP +chmod u=rwX,g=rXs,o= $VOL/$ALL +chmod u=rwX,g=rXs,o= $VOL/$ALL/$GRP +echo "People in group $GRP has read/write access through AppleShare" +test $GRP = $ALL || echo "People in group $ALL has read access through AppleShare" +echo "Others have no access" +echo -n "Checking permissions..." +chown -R $GRPUSER.$GRP $VOL/$ALL/$GRP \ + && chmod -R u=rwX,g=rwXs,o=rX $VOL/$ALL/$GRP \ + && chmod u=rwX,g=rXs,o=rX $VOL/$ALL/$GRP +echo "." diff --git a/volinit-public b/volinit-public new file mode 100755 index 0000000..0a0049d --- /dev/null +++ b/volinit-public @@ -0,0 +1,63 @@ +#!/bin/bash + +[ -f /etc/local/volumes ] || exit 1 +. /etc/local/volumes +[ "x$PDIR" = "x" ] && exit 1 + +if [ $# -eq 0 ]; then +# echo "$0: directory name(s) required" + echo "Usage: $0 [create]" + exit 1 + fi + +if [ "x$1" = "x" ]; then + echo "$0: base directory missing" + exit 1 + fi +VOL=$PDIR/$1 + +if [ "x$2" = "x" ]; then + echo "$0: read group missing" + exit 1 + fi +ALL=$2 + +if [ "x$3" = "x" ]; then + echo "$0: write group missing" + exit 1 + fi +GRP=$3 + +#GRPUSER="nobody" +GRPUSER="admin" +#if [ "x$4" != "x" ]; then +# GRPUSER=$4 +#else + id $GRP >/dev/null && GRPUSER=$GRP +#fi + +if [ "x$4" != "x" ]; then + if [ "x$4" == "xcreate" ]; then + mkdir -p $VOL/$ALL/$GRP || exit + else + echo "Usage: $0 [create]" + exit 1 + fi +fi + +test -d $VOL/$ALL/$GRP || exit +test -d $VOL/$ALL/$GRP/.AppleDouble || mkdir $VOL/$ALL/$GRP/.AppleDouble +test -d $VOL/$ALL/$GRP/.AppleDesktop || mkdir $VOL/$ALL/$GRP/.AppleDesktop +#chown $ALLUSER.$ALL $VOL/$ALL +chown root.$ALL $VOL/$ALL +chown $GRPUSER.$ALL $VOL/$ALL/$GRP +chmod u=rwX,g=rXs,o= $VOL/$ALL +chmod u=rwX,g=rXs,o= $VOL/$ALL/$GRP +echo "People in group $GRP has read/write access through AppleShare" +test $GRP = $ALL || echo "People in group $ALL has read access through AppleShare" +echo "Others have no access" +echo -n "Checking permissions..." +chown -R $GRPUSER.$GRP $VOL/$ALL/$GRP \ + && chmod -R u=rwX,g=rwXs,o=rX $VOL/$ALL/$GRP \ + && chmod u=rwX,g=rXs,o=rX $VOL/$ALL/$GRP +echo "." diff --git a/volinit-samba b/volinit-samba new file mode 100755 index 0000000..d33ad00 --- /dev/null +++ b/volinit-samba @@ -0,0 +1,59 @@ +#!/bin/bash + +[ -f /etc/local/volumes ] || exit 1 +. /etc/local/volumes +[ "x$SDIR" = "x" ] && exit 1 + +if [ $# -eq 0 ]; then +# echo "$0: directory name(s) required" + echo "Usage: $0 [create]" + exit 1 + fi + +if [ "x$1" = "x" ]; then + echo "$0: base directory missing" + exit 1 + fi +VOL=$SDIR/$1 + +if [ "x$2" = "x" ]; then + echo "$0: read group missing" + exit 1 + fi +ALL=$2 + +if [ "x$3" = "x" ]; then + echo "$0: write group missing" + exit 1 + fi +GRP=$3 + +#GRPUSER="nobody" +GRPUSER="admin" +#if [ "x$4" != "x" ]; then +# GRPUSER=$4 +#else + id $GRP >/dev/null && GRPUSER=$GRP +#fi + +if [ "x$4" != "x" ]; then + if [ "x$4" == "xcreate" ]; then + mkdir -p $VOL/$ALL/$GRP || exit + else + echo "Usage: $0 [create]" + exit 1 + fi +fi + +test -d $VOL/$ALL/$GRP || exit +chown root.$ALL $VOL/$ALL +chown $GRPUSER.$ALL $VOL/$ALL/$GRP +chmod u=rwX,g=rX,o= $VOL/$ALL +chmod u=rwX,g=rX,o= $VOL/$ALL/$GRP +echo "People in group $GRP has read/write access through SMB networking" +test $GRP = $ALL || echo "People in group $ALL has read access through SMB networking" +echo "Others have no access" +echo -n "Checking permissions..." +chown -R $GRPUSER.$GRP $VOL/$ALL/$GRP \ + && chmod -R u=rwX,g=rwX,o=rX $VOL/$ALL/$GRP +echo "." diff --git a/volinit-std-all b/volinit-std-all new file mode 100755 index 0000000..e5e0a3b --- /dev/null +++ b/volinit-std-all @@ -0,0 +1,45 @@ +#!/bin/bash + +#TODO: generate based on actual config files +#cat AppleVolumes-xayide.jones.dk | egrep '^/' | sed 's!/var/local/netatalk/\(.*\)/\([[:alnum:]]*\)/\([[:alnum:]]*\)/[[:space:]].*!\1 \2 \3!' + +[ -f /etc/local/volumes ] || exit 1 +. /etc/local/volumes + +[ $DISTRIB ] && volinit-netatalk COMMON/C/intro users jonas create +[ $DISTRIB ] && volinit-netatalk COMMON/C/admin admin jonas create +[ $DISTRIB ] && volinit-netatalk COMMON/C/software users jonas create +[ $DISTRIB ] && volinit-netatalk COMMON/da/admin admin jonas create +[ $DISTRIB ] && volinit-netatalk COMMON/da/software users jonas create +[ $DISTRIB ] && volinit-netatalk COMMON/en/admin admin jonas create +[ $DISTRIB ] && volinit-netatalk COMMON/en/software users jonas create +[ $DISTRIB ] && volinit-samba COMMON/C/admin admin jonas create +[ $DISTRIB ] && volinit-samba COMMON/C/software users jonas create +[ $DISTRIB ] && volinit-samba COMMON/da/admin admin jonas create +[ $DISTRIB ] && volinit-samba COMMON/da/software users jonas create +[ $DISTRIB ] && volinit-samba COMMON/en/admin admin jonas create +[ $DISTRIB ] && volinit-samba COMMON/en/software users jonas create + +[ $MANAGED ] && volinit-netatalk tmpl users admin create +[ $MANAGED ] && volinit-netatalk software users admin create +[ $MANAGED ] && volinit-netatalk admin admin admin create +[ $MANAGED ] && volinit-samba tmpl users admin create +[ $MANAGED ] && volinit-samba software users admin create +[ $MANAGED ] && volinit-samba admin admin admin create + +[ $ANARCHY ] && volinit-netatalk doc users users create +#[ $ANARCHY ] && volinit-netatalk misc users users create +[ $ANARCHY ] && volinit-samba doc users users create +#[ $ANARCHY ] && volinit-samba misc users users create + +[ $PRIVATE ] && volinit-netatalk COMMON/private/C/private jonas jonas create +[ $PRIVATE ] && volinit-netatalk COMMON/private/da/private jonas jonas create +[ $PRIVATE ] && volinit-netatalk COMMON/private/en/private jonas jonas create +[ $PRIVATE ] && volinit-samba COMMON/private/C/private jonas jonas create +[ $PRIVATE ] && volinit-samba COMMON/private/da/private jonas jonas create +[ $PRIVATE ] && volinit-samba COMMON/private/en/private jonas jonas create + +[ $XCHANGE ] && volinit-xchange ./ users root create + +[ $PUBLIC ] && volinit-public ./ users users create + diff --git a/volinit-xchange b/volinit-xchange new file mode 100755 index 0000000..ea9128c --- /dev/null +++ b/volinit-xchange @@ -0,0 +1,63 @@ +#!/bin/bash + +[ -f /etc/local/volumes ] || exit 1 +. /etc/local/volumes +[ "x$XDIR" = "x" ] && exit 1 + +if [ $# -eq 0 ]; then +# echo "$0: directory name(s) required" + echo "Usage: $0 [create]" + exit 1 + fi + +if [ "x$1" = "x" ]; then + echo "$0: base directory missing" + exit 1 + fi +VOL=$XDIR/$1 + +if [ "x$2" = "x" ]; then + echo "$0: read group missing" + exit 1 + fi +ALL=$2 + +if [ "x$3" = "x" ]; then + echo "$0: write group missing" + exit 1 + fi +GRP=$3 + +#GRPUSER="nobody" +GRPUSER="admin" +#if [ "x$4" != "x" ]; then +# GRPUSER=$4 +#else + id $GRP >/dev/null && GRPUSER=$GRP +#fi + +if [ "x$4" != "x" ]; then + if [ "x$4" == "xcreate" ]; then + mkdir -p $VOL/$ALL/$GRP || exit + else + echo "Usage: $0 [create]" + exit 1 + fi +fi + +test -d $VOL/$ALL/$GRP || exit +test -d $VOL/$ALL/$GRP/.AppleDouble || mkdir $VOL/$ALL/$GRP/.AppleDouble +test -d $VOL/$ALL/$GRP/.AppleDesktop || mkdir $VOL/$ALL/$GRP/.AppleDesktop +#chown $ALLUSER.$ALL $VOL/$ALL +chown root.$ALL $VOL/$ALL +chown $GRPUSER.$ALL $VOL/$ALL/$GRP +chmod u=rwX,g=rXs,o= $VOL/$ALL +chmod u=rwX,g=rXs,o= $VOL/$ALL/$GRP +echo "People in group $GRP has read/write access through AppleShare" +test $GRP = $ALL || echo "People in group $ALL has read access through AppleShare" +echo "Others have no access" +echo -n "Checking permissions..." +chown -R $GRPUSER.$GRP $VOL/$ALL/$GRP \ + && chmod -R u=rwX,g=rwXs,o=rX $VOL/$ALL/$GRP \ + && chmod u=rwX,g=rXs,o=rX $VOL/$ALL/$GRP +echo "." diff --git a/vpopmail2postfixvirtual b/vpopmail2postfixvirtual new file mode 100755 index 0000000..7c37f4d --- /dev/null +++ b/vpopmail2postfixvirtual @@ -0,0 +1,53 @@ +#!/bin/bash + +# Generate virtual file for postfix +# +# Each user should have space-separated hints like "mailname1@ mailname2@gid1 mailname3@gid2" +# stored in the "Other" field (using chfn). +# +# The user of each mailgroup should have hints like "@domain1 @domain2" for each hosted domain +# stored in the "Other" field (using chfn). + +#TODO: reuse getent requests (drastically improves speed with multiple domains) + +function get_fullname_field() { getent passwd $1 | awk -F: '{print $5}' | awk -F, '{print $1}'; } +function get_roomnumber_field() { getent passwd $1 | awk -F: '{print $5}' | awk -F, '{print $2}'; } +function get_other_field() { getent passwd $1 | awk -F: '{print $5}' | awk -F, '{print $5}'; } +function get_domain() { echo $1 | egrep "^@[\.[:alnum:]]+$" | sed -e 's/@//'; } +function get_account() { echo $1 | egrep "^[\.[:alnum:]]+@($gid)?$" | sed -e 's/@.*//'; } + +loop="" +for gid in $@; do + for maildomainchunk in `get_other_field $gid`; do + for maildomain in `get_domain $maildomainchunk`; do + if [ $loop ]; then + echo + echo "##################################################################" + echo + fi + loop=true + echo "$maildomain VIRTUAL" + echo "postmaster@$maildomain root" + echo + mailusers="" + for mailgroup in `get_roomnumber_field $gid`; do + mailusers="$mailusers `members $mailgroup`" + done +# for uid in `members $gid | sort`; do + for uid in `echo $mailusers | tsort | uniq | sort`; do + echo "# `get_fullname_field $uid` (`get_roomnumber_field $uid`)" + uid_seen="" + for mailaccountchunk in `get_other_field $uid`; do + for mailaccount in `get_account $mailaccountchunk`; do + echo "$mailaccount@$maildomain $uid" + uid_seen=true + done + done + if [ ! $uid_seen ]; then + echo "#WARNING: No addresses for $uid" + fi + echo + done + done + done +done diff --git a/wcald b/wcald new file mode 100755 index 0000000..0d7b8d2 --- /dev/null +++ b/wcald @@ -0,0 +1,3688 @@ +#!/usr/bin/perl -w +# +# Wcal 2.0 copyright by Joël Savignon . +# +# Released under GNU General Public License (GPL). +# +# TAB size 4 +# + +require 5.004; +use Data::Dumper; +use Date::Manip; +use Socket; +use strict; +no strict 'refs'; + +# These you can edit +$::CONF_FILE = '/etc/wcal.conf'; +$::MSG_FILE = '/etc/wcal.msg'; +$::DIRECTORY = '/var/www/VIRTUAL/www.homebase.dk/www/wcal'; +$::DB_DIR = '/var/wcal'; # settable in wcal.conf in 1.x versions +$::REFRESH_DELAY = 900; # view refreshed every 900 seconds = 15 minutes +$::PATH_BASENAME = 'wcal'; # last component of the wcal directory name + +# Values below affect the proportions of the frames +$::FIRST_HOUR = 8; +$::LAST_HOUR = 23; +$::MANY_WEEKS_VERT = 2; +$::MANY_WEEKS_HOR = 7; +$::FIRST_DAY = 'monday'; + +# Colors +$::THCOLOR = '#E2E3FC'; +$::TDCOLOR = '#D9F4F4'; +$::NOW_THCOLOR = '#CAFFD0'; +$::NOW_TDCOLOR = '#CAFFD0'; +$::REPEAT_WEEK_TEXTCOLOR = '#C02090'; +$::REPEAT_MONTH_TEXTCOLOR = '#008844'; +$::BG_TDCOLOR = '#F2F3FC'; +$::ERROR_COLOR = '#e06060'; +@::EVENT_COLS = ('#D9F4F4','#FFFFAA','#AAFFFF','#FFAAFF','#FFAAAA','#AAAAFF','#AAFFAA','#80FFE0','#E080E0','#C0DCC0','#C0C0C0','#FF0080','#A4C8F0','#FF8000','#FF80C0','#8080C0','#FFFFFF'); + +# No need to touch these +$::MAX_DURATION = 21; # maximum duration of an event in days +$::CURRENT_DB_VERSION = 2; # Wcal 1.00 had version 1 databases +$::HTTP_HEADER = "Content-Type: text/html\n\n"; + +# internationalization +sub __ +{ + my $str = shift; + return $::msgs->{$str}->{$::REMOTE_LANG} || $str; +} + +# fusion +sub strcal +{ + if ($::query->{'cal'}) + { + return "&cal=".join("&cal=",split(/\n/,$::query->{'cal'})); + } + else + { + return ""; + } +} + +sub lncal +{ + my $year = shift; + my $week = shift; + + if ($::query->{'cal'}) + { + my $str = $::BASE_URL.'/fusion.cgi?t=links&cal='.join("&cal=",split(/\n/,$::query->{'cal'})); + if ($year && $week) + { + $str .= '&year='.$year.'&week='.$week; + } + return "$::LONG_NAME"; + } + else + { + return $::LONG_NAME; + } +} + +# Query string decoder, ripped from CGI_Lite +sub decode_url_encoded_data ($) { + my ($reference_data) = @_; + my ($code, $self); + + $code = <<'End_of_URL_Decode'; + + my (@key_value_pairs, $delimiter, $key_value, $key, $value); + + @key_value_pairs = (); + + return unless ($$reference_data); + + $delimiter = '&'; + + $$reference_data =~ tr/+/ /; + @key_value_pairs = split (/$delimiter/, $$reference_data); + + foreach $key_value (@key_value_pairs) { + ($key, $value) = split (/=/, $key_value, 2); + + $key =~ s/%([\da-fA-F]{2})/chr (hex ($1))/eg; + $value =~ s/%([\da-fA-F]{2})/chr (hex ($1))/eg; + +# print Socket "'$key':'$value'\n"; + + if ($self->{$key}) + { + $self->{$key} .= "\n$value"; + } + else + { + $self->{$key} = $value; + } + } + +End_of_URL_Decode + + eval ($code); + return $self; +} + +# And encoder, ripped too +sub url_encode +{ + my $string = shift; + my $str1 = '([\x00-\x20"#%;<>?{}|\\\\^~`\[\]\x7F-\xFF])'; + $string =~ s/$str1/ + sprintf ('%%%x', ord ($1))/eg; + + return $string; +} + +sub strip_space ($) { + my ($s) = @_; + $s =~ s/^\s*(.*?)\s*$/$1/; + return $s; +} + +sub wday_to_dmwday ($) { + my ($wday) = @_; + + if ($::FIRST_DAY eq 'monday') { + return ($wday + 1); + } else { + if ($wday == 0) { + return 7; + } else { + return $wday; + } + } +} + +sub dmwday_to_wday ($) { + my ($dmwday) = @_; + + return ($::FIRST_DAY eq 'monday' ? + $dmwday - 1 : + $dmwday % 7); +} + +sub get_now () { + if (! defined $::now_cache) { + my (@n); + @n = &UnixDate (&ParseDate ('now'), $::FIRST_DAY eq 'monday' ? "%G": "%L", "%m", "%d", $::FIRST_DAY eq 'monday' ? "%W" : "%U", "%w"); + $::now_cache = { + 'year' => $n[0] + 0, + 'month' => $n[1] + 0, + 'day' => $n[2] + 0, + 'week' => $n[3] + 0, + 'wday' => dmwday_to_wday ($n[4]) }; + } + return $::now_cache; +} + +sub get_next_year_week ($$) { + my ($year, $week) = @_; + my ($cacheid); + + $cacheid = sprintf ("%04d%02d", $year, $week); + if (! defined $::nyw_cache{$cacheid}) { + my ($y, $w); + ($y, $w) = &UnixDate (&DateCalc ("$::FIRST_DAY week $week in $year", "+ 7 days"), $::FIRST_DAY eq 'monday' ? "%G": "%L", $::FIRST_DAY eq 'monday' ? "%W" : "%U"); + $::nyw_cache{$cacheid} = [$y + 0, $w + 0]; + } + return @{$::nyw_cache{$cacheid}}; +} + +sub get_prev_year_week ($$) { + my ($year, $week) = @_; + my ($cacheid); + + $cacheid = sprintf ("%04d%02d", $year, $week); + if (! defined $::pyw_cache{$cacheid}) { + my ($y, $w); + ($y, $w) = &UnixDate (&DateCalc ("$::FIRST_DAY week $week in $year", "- 7 days"), $::FIRST_DAY eq 'monday' ? "%G": "%L", $::FIRST_DAY eq 'monday' ? "%W" : "%U"); + $::pyw_cache{$cacheid} = [$y + 0, $w + 0]; + } + return @{$::pyw_cache{$cacheid}}; +} + +sub get_month_day_by_firstday_year_week ($$) { + my ($year, $week) = @_; + my ($cacheid); + + $cacheid = sprintf ("%04d%02d", $year, $week); + if (! defined $::md_cache{$cacheid}) { + my ($month, $day); + ($month, $day) = &UnixDate (&ParseDate ("$::FIRST_DAY week $week in $year"), "%m", "%d"); + $::md_cache{$cacheid} = [$month + 0, $day + 0]; + } + return @{$::md_cache{$cacheid}}; +} + +sub get_month_day_by_wday_year_week ($$$) { + my ($wday, $year, $week) = @_; + my ($cacheid); + + $cacheid = sprintf ("%04d%02d%1d", $year, $week, $wday); + if (! defined $::md2_cache{$cacheid}) { + my ($month, $day); + ($month, $day) = &UnixDate (&ParseDate ($::weekdays2[$wday] . " week $week in $year"), "%m", "%d"); + $::md2_cache{$cacheid} = [$month + 0, $day + 0]; + } + return @{$::md2_cache{$cacheid}}; +} + +sub get_year_week_by_firstday_year_week_minus_days ($$$) { + my ($year, $week, $days) = @_; + my ($cacheid); + + $cacheid = sprintf ("%04d%02d%10d", $year, $week, $days); + if (! defined $::yw_cache{$cacheid}) { + my ($ryear, $rweek); + ($ryear, $rweek) = &UnixDate (&DateCalc ("$::FIRST_DAY week $week in $year", "- $days days"), $::FIRST_DAY eq 'monday' ? "%G": "%L", $::FIRST_DAY eq 'monday' ? "%W" : "%U"); + $::yw_cache{$cacheid} = [$ryear + 0, $rweek + 0]; + } + return @{$::yw_cache{$cacheid}}; +} + +sub week_wday_by_year_month_day ($$$) { + my ($year, $month, $day) = @_; + my ($cacheid); + + $cacheid = sprintf ("%04d%02d%2d", $year, $month, $day); + if (! defined $::ww_cache{$cacheid}) { + my ($week, $wday); + ($week, $wday) = &UnixDate (&ParseDate ("$month/$day/$year"), $::FIRST_DAY eq 'monday' ? "%W" : "%U", "%w"); + $::ww_cache{$cacheid} = [$week + 0, dmwday_to_wday ($wday)]; + } + return @{$::ww_cache{$cacheid}}; +} + +sub get_start_end_by_year_week_wday_duration ($$$$) { + my ($startyear, $startweek, $startwday, $duration) = @_; + my ($cacheid); + + $cacheid = sprintf ("%04d%02d%2d%03d", $startyear, $startweek, $startwday, $duration); + if (! defined $::se_cache{$cacheid}) { + my ($startmonth, $startday, $endyear, $endweek, $endwday, $endmonth, $endday); + + ($startmonth, $startday) = get_month_day_by_wday_year_week ($startwday, $startyear, $startweek); + + ($endyear, $endweek, $endwday) = + &UnixDate (&DateCalc (&ParseDate (sprintf ("%04d-W%02d-%1d", $startyear, $startweek, wday_to_dmwday ($startwday))), + "+ " . ($duration - 1) . " days"), + $::FIRST_DAY eq 'monday' ? "%G": "%L", $::FIRST_DAY eq 'monday' ? "%W" : "%U", "%w"); + $endwday = dmwday_to_wday ($endwday); + + ($endmonth, $endday) = get_month_day_by_wday_year_week ($endwday, $endyear, $endweek); + + $::se_cache{$cacheid} = + [ + $startmonth + 0, $startday + 0, + $endyear + 0, $endweek + 0, dmwday_to_wday ($endwday), + $endmonth + 0, $endday + 0 + ]; + } + return @{$::se_cache{$cacheid}}; +} + +sub get_year_week_wday_by_year_week_wday_plus_days ($$$$) { + my ($year, $week, $wday, $days) = @_; + my ($cacheid); + + $cacheid = sprintf ("%04d%02d%02d%10d", $year, $week, $wday, $days); + if (! defined $::yww_cache{$cacheid}) { + my ($nyear, $nweek, $nwday); + ($nyear, $nweek, $nwday) = + &UnixDate (&DateCalc (sprintf ("%04d-W%02d-%1d", $year, $week, wday_to_dmwday ($wday)), + "+ $days days"), + $::FIRST_DAY eq 'monday' ? "%G": "%L", + $::FIRST_DAY eq 'monday' ? "%W" : "%U", + "%w"); + $::yww_cache{$cacheid} = [$nyear + 0, $nweek + 0, dmwday_to_wday ($nwday)]; + } + return @{$::yww_cache{$cacheid}}; +} + +sub fit_in_week ($$$$) { + my ($year, $week, $wday, $duration) = @_; + my ($endyear, $endweek, $endwday); + + ($endyear, $endweek, $endwday) = + get_year_week_wday_by_year_week_wday_plus_days + ($year, $week, $wday, $duration-1); + if ($endyear != $year or $endweek != $week) { + return 0; + } else { + return 1; + } +} + +sub fit_in_month ($$$$) { + my ($year, $week, $wday, $duration) = @_; + my ($month, $day); + my ($endyear, $endweek, $endwday); + my ($endmonth, $endday); + + ($month, $day) = get_month_day_by_wday_year_week ($wday, $year, $week); + ($endyear, $endweek, $endwday) = + get_year_week_wday_by_year_week_wday_plus_days + ($year, $week, $wday, $duration-1); + ($endmonth, $endday) = get_month_day_by_wday_year_week ($endwday, $endyear, $endweek); + if ($endyear != $year or $endmonth != $month) { + return 0; + } else { + return 1; + } +} + +sub clipboard_set ($$) { + my ($key, $data_ref) = @_; + $::clipboard{$key} = $data_ref; + return; +} + +sub clipboard_get ($) { + my ($key) = @_; + if (defined $::clipboard{$key}) { + return $::clipboard{$key}; + } else { + return ''; + } +} + +sub format_hour ($) { + my ($hour) = @_; + + if ($::CLOCK eq '24-hour') { + return ($hour + 0); + } else { + my ($newhour); + + $newhour = $hour; + $newhour = 24 if $hour == 0; + $newhour -= 12 if $hour > 12; + return ($newhour + 0) . ($hour < 12 ? "am" : "pm"); + } +} + +sub format_hour_padded ($) { + my ($hour) = @_; + + if ($::CLOCK eq '24-hour') { + return sprintf ("%02d", $hour); + } else { + my ($newhour); + + $newhour = $hour; + $newhour = 24 if $hour == 0; + $newhour -= 12 if $hour > 12; + return sprintf ("%02d", $newhour) . ($hour < 12 ? "am" : "pm"); + } +} + +sub format_time ($$) { + my ($hour, $min) = @_; + + if ($::CLOCK eq '24-hour') { + return ($hour + 0) . ":" . sprintf ("%02d", $min); + } else { + my ($newhour); + + $newhour = $hour; + $newhour = 24 if $hour == 0; + $newhour -= 12 if $hour > 12; + return ($newhour + 0) . ":" . sprintf ("%02d", $min) . ($hour < 12 ? "am" : "pm"); + } +} + +sub format_time_padded ($$) { + my ($hour, $min) = @_; + + if ($::CLOCK eq '24-hour') { + return sprintf ("%02d", $hour) . ":" . sprintf ("%02d", $min); + } else { + my ($newhour); + + $newhour = $hour; + $newhour = 24 if $hour == 0; + $newhour -= 12 if $hour > 12; + return sprintf ("%02d", $newhour) . ":" . sprintf ("%02d", $min) . ($hour < 12 ? "am" : "pm"); + } +} + +sub format_alarm_type { + my ($type, $single) = @_; + + if ($type == 1) + { + if ($single) + { + return __("minute"); + } + else + { + return __("minutes"); + } + } + elsif ($type == 2) + { + if ($single) + { + return __("heure"); + } + else + { + return __("heures"); + } + } + elsif ($type == 3) + { + if ($single) + { + return __("jour"); + } + else + { + return __("jours"); + } + } + else + { + return ""; + } +} + +sub img_alarm { + my ($val, $type, $chk) = @_; + if ($val) + { + my $img = ($chk ? "alarm2" : "alarm"); + return ' '.__('; + } + else + { + return ""; + } +} +sub img_note { + my ($data) = @_; + if ($data) + { + $data =~ s/
/ /mg; + return ' '.__('; + } + else + { + return ""; + } +} + +sub txt_alarm { + my ($val, $type, $chk) = @_; + if ($val) + { + my $col = ($chk ? "#00AA00" : "#FF0000"); + return '
'.__("Alarme")." : ".$val." ".format_alarm_type($type, ($val == 1)).''; + } + else + { + return ""; + } +} + +sub has_html ($) { + my ($text) = @_; + return $text =~ /[<>&]/; +} + + +sub newline_to_html ($) { + my ($text) = @_; + $text =~ s/\015\012/
/sg; + return $text; +} + +sub html_to_newline ($) { + my ($text) = @_; + $text =~ s/
/\015\012/sg; + return $text; +} + +sub events_version ($) { + my ($events) = @_; + my ($version); + $version = $events->[0]; + if (ref $version) { + $version = 1; + } + return $version; +} + +sub convert_events_from_v1_to_v2 ($$$) { + my ($events, $year, $week) = @_; + my ($dayid, $eventhour, $eventid); + for $dayid (0 .. $#$events) { + for $eventhour (keys %{$events->[$dayid]}) { + for $eventid (0 .. $#{$events->[$dayid]->{$eventhour}}) { + my (@old_event, %new_event); + @old_event = @{$events->[$dayid]->{$eventhour}->[$eventid]}; + %new_event = ( + 'id' => $old_event[0], + 'min' => $old_event[1], + 'lengthmin' => $old_event[2], + 'title' => $old_event[3], + 'data' => $old_event[4], + 'rt' => $old_event[5], + 'startyear' => $year, + 'startweek' => $week, + 'startwday' => $dayid, + 'duration' => 1 + ); + $events->[$dayid]->{$eventhour}->[$eventid] = \%new_event; + } + } + } + # insert version number into beginning of the array + unshift (@$events, $::CURRENT_DB_VERSION); + return; +} + +sub read_events ($;$$) { + my ($rt, $year, $week) = @_; + my ($filename, $now); + + my @filenames; + my $id; + my ($refs, $ref, $old_slash); + + if ($main::DATA_ID) + { + if ($rt eq 'n') { + $filename = sprintf "$::DB_DIR/w-$main::DATA_ID-%04d%02d.db", $year, $week; + } else { + $filename = "$::DB_DIR/r$rt-$main::DATA_ID.db"; + } + if (open F, $filename) { + $old_slash = $/; + undef $/; + $refs = eval (); + $/ = $old_slash; + close F; + # backwards compatibility + if (events_version ($refs) < 2) { + $now = get_now (); + convert_events_from_v1_to_v2 ($refs, $year || $now->{'year'}, $week || $now->{'week'}); + } + shift @$refs; + } + } + elsif ($main::DATA_IDS) + { + my $cl = 0; + foreach $id (split(/\n/,$main::DATA_IDS)) + { + $cl++; + if ($rt eq 'n') { + $filename = sprintf "$::DB_DIR/w-$id-%04d%02d.db", $year, $week; + } else { + $filename = "$::DB_DIR/r$rt-$id.db"; + } + if (open F, $filename) { + $old_slash = $/; + undef $/; + $ref = eval (); + $/ = $old_slash; + close F; + # backwards compatibility + if (events_version ($ref) < 2) { + $now = get_now (); + convert_events_from_v1_to_v2 ($ref, $year || $now->{'year'}, $week || $now->{'week'}); + } + shift @$ref; + + # add cal index + my $i; + my $key; + my $val; + my $vl; + for ($i=0; $i < ($rt eq 'm' ? 32 : 7); $i++) + { + while (($key, $val) = each (%{@{$ref}[$i]})) + { + foreach $vl (@{$val}) + { + %{$vl}->{cal} = $cl; + } + } + } + + if (!$refs) + { + $refs = $ref; + next; + } + # fusion + for ($i=0; $i < ($rt eq 'm' ? 32 : 7); $i++) + { + while (($key, $val) = each (%{@{$ref}[$i]})) + { + if (%{@{$refs}[$i]}->{$key}) + { + push @{%{@{$refs}[$i]}->{$key}}, @{$val}; + } + else + { + @{%{@{$refs}[$i]}->{$key}} = @{$val}; + } + } + } + } + } + } + if ($refs) + { + return $refs; + } elsif ($rt eq 'n' or $rt eq 'w') { + return [ {}, {}, {}, {}, {}, {}, {} ]; + } elsif ($rt eq 'm') { + # let's give month 32 days to make sure + return [ {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} ] + } + # can't happen +} + + +sub write_events ($$;$$) { + my ($weekdata, $rt, $year, $week) = @_; + my ($filename); + + if ($rt eq 'n') { + if (! defined $year or ! $year or ! defined $week or ! $week) { + return "The file type is no-repeat, but week or year not given, or zero."; + } + $filename = sprintf "$::DB_DIR/w-${main::DATA_ID}-%04d%02d.db", $year, $week; + # Remove cached week file + unlink sprintf ("$::DB_DIR/cache/w-${main::DATA_ID}-%04d%02d.html", $year, $week); + } else { + my (@files); + $filename = "$::DB_DIR/r$rt-${main::DATA_ID}.db"; + # Remove all cached week files + if (opendir (TDH, "$::DB_DIR/cache")) { + @files = grep { /^w-${main::DATA_ID}-\d{6}\.html$/ } readdir (TDH); + closedir TDH; + for (@files) { + unlink "$::DB_DIR/cache/$_"; + } + } +} + +# add the version identifier +unshift (@$weekdata, $::CURRENT_DB_VERSION); + +if (open F, ">$filename") { + $Data::Dumper::Terse = 1; + flock (F, 2); + print F Dumper ($weekdata); + flock (F, 8); + close F; + return ''; +} else { + return "File $filename can't be opened for writing: $!"; +} + +# and remove the version identifier again +shift @$weekdata; +} + +sub read_general () { + my ($week) = @_; + if (-f "$::DB_DIR/general.db" and ! -r "$::DB_DIR/general.db") { + return "No write permission for file $::DB_DIR/general.db"; + } + if (open F, "$::DB_DIR/general.db") { + my ($ref, $old_slash); + $old_slash = $/; + undef $/; + $ref = eval (); + $/ = $old_slash; + close F; + if ($@) { + return "Error in processing file $::DB_DIR/general.db: $@"; + } else { + return $ref; + } + } else { + return { 'highid' => 56 }; + } +} + +sub write_general ($) { + my ($gendata) = @_; + if (open F, ">$::DB_DIR/general.db") { + $Data::Dumper::Terse = 1; + flock (F, 2); + print F Dumper ($gendata); + flock (F, 8); + close F; + return ''; + } else { + return "File $::DB_DIR/general.db can't be opened for writing: $!"; + } +} + +# Print year, month and day in chosen date format +sub pd_year_month_day ($$$) { + my ($year, $month, $day) = @_; + my ($t); + + $t = $::DATE_FORMAT; + if ($t == 1) { + return "$day.$month.$year"; + } elsif ($t == 2) { + return "$month/$day/$year"; + } elsif ($t == 3) { + return "$day/$month/$year"; + } elsif ($t == 4) { + return "$year/$month/$day"; + } elsif ($t == 5) { + return "$year-$month-$day"; + } elsif ($t == 5) { + return sprintf ("%04d%02d%02d", $year, $month, $day); + } else { + return "[DATE TYPE $t]"; + } +} + +sub pd_month_day ($$) { + my ($month, $day) = @_; + my ($t); + + $t = $::DATE_FORMAT; + if ($t == 1) { + return "$day.$month"; + } elsif ($t == 2) { + return "$month/$day"; + } elsif ($t == 3) { + return "$day/$month"; + } elsif ($t == 4) { + return "$month/$day"; + } elsif ($t == 5) { + return "$month-$day"; + } elsif ($t == 6) { + return sprintf ("%02d%02d", $month, $day); + } else { + return "[DATE TYPE $t]"; + } +} + +sub pd_month_day_padded ($$) { + my ($month, $day) = @_; + my ($t); + + $t = $::DATE_FORMAT; + if ($t == 1) { + return sprintf "%02d.%02d", $day, $month; + } elsif ($t == 2) { + return sprintf "%02d/%02d", $month, $day; + } elsif ($t == 3) { + return sprintf "%02d/%02d", $day, $month; + } elsif ($t == 4) { + return sprintf "%02d/%02d", $month, $day; + } elsif ($t == 5) { + return sprintf "%02d-%02d", $month, $day; + } elsif ($t == 6) { + return sprintf "%02d%02d", $month, $day; + } else { + return "[DATE TYPE $t]"; + } +} + +sub pd_single_event_date ($$$$) { + my ($rt, $year, $week, $wday) = @_; + my ($month, $day); + + ($month, $day) = get_month_day_by_wday_year_week ($wday, $year, $week); + if ($rt eq 'n') { + return pd_year_month_day ($year, $month, $day); + } elsif ($rt eq 'w') { + return __($::weekdays[$wday]); + } else { + return $day . "."; + } +} + +sub pd_event_date ($$$$$) { + my ($rt, $startyear, $startweek, $startwday, $duration) = @_; + my ($ret); + + $ret = ''; + $ret .= pd_single_event_date ($rt, $startyear, $startweek, $startwday); + if ($duration > 1) { + ($startyear, $startweek, $startwday) = + get_year_week_wday_by_year_week_wday_plus_days + ($startyear, $startweek, $startwday, $duration - 1); + $ret .= " to " . pd_single_event_date ($rt, $startyear, $startweek, $startwday); + } + if ($rt eq 'w') { + $ret .= " ".__("hebdomadaire"); + } elsif ($rt eq 'm') { + $ret .= " ".__("mensuelle"); + } + return $ret; +} + +sub join_days (@) { + my (@daylist) = @_; + my (%sum_day, $day_ref, $hour, $event_ref); + + %sum_day = (); + for $day_ref (@daylist) { + for $hour (keys %$day_ref) { + for $event_ref (@{$day_ref->{$hour}}) { + push @{$sum_day{$hour}}, $event_ref; + } + } + } + return \%sum_day; +} + +sub build_day ($$$$) { + my ($year, $week, $wday, $day) = @_; + my ($e_n, $e_rw, $e_rm, $sum_day_ref); + + $e_n = read_events ('n', $year, $week); + $e_rw = read_events ('w'); + $e_rm = read_events ('m'); + + $sum_day_ref = join_days ($e_n->[$wday], $e_rw->[$wday], $e_rm->[$day]); + return $sum_day_ref; +} + +sub build_week ($$) { + my ($year, $week) = @_; + my ($e_n, $e_rw, $e_rm, @sum_week, $wday); + + $e_n = read_events ('n', $year, $week); + $e_rw = read_events ('w'); + $e_rm = read_events ('m'); + + for $wday (0 .. 6) { + my ($month, $day); + ($month, $day) = get_month_day_by_wday_year_week ($wday, $year, $week); + $sum_week[$wday] = join_days ($e_n->[$wday], $e_rw->[$wday], $e_rm->[$day]); + } + return \@sum_week; +} + +sub build_week_list ($$) { + my ($start_year, $start_week) = @_; + my ($week_filename, @weeks, @ret, %weeks); + my ($e_n, $e_rw, $e_rm, @sum_week); + my ($cyear, $cweek, $cdate); + my ($end_year, $end_week, $end_date); + + $e_rw = read_events ('w'); + $e_rm = read_events ('m'); + + if ($main::DATA_ID) + { + if (! opendir (DIR, $::DB_DIR)) { + return "can't open $::DB_DIR for reading: $!"; + } + + # gather all week filenames, 199805, 199806, 199807 ... 199851 + @weeks = map { /^w-$main::DATA_ID-(\d{4}\d{2})\.db$/ && $1 } + grep { /^w-$main::DATA_ID-(\d{4}\d{2})\.db$/ && + $1 >= sprintf ("%04d%02d", $start_year, $start_week) } + sort readdir(DIR); + close DIR; + } + else + { + my $id; + my @wks; + foreach $id (split(/\n/,$main::DATA_IDS)) + { + if (! opendir (DIR, $::DB_DIR)) { + return "can't open $::DB_DIR for reading: $!"; + } + + # gather all week filenames, 199805, 199806, 199807 ... 199851 + @wks = map { /^w-$id-(\d{4}\d{2})\.db$/ && $1 } + grep { /^w-$id-(\d{4}\d{2})\.db$/ && + $1 >= sprintf ("%04d%02d", $start_year, $start_week) } + sort readdir(DIR); + close DIR; + push @weeks, @wks; + } + } + + # return immediately if no events + if (scalar @weeks == 0) { + return []; + } + + # figure out the filename of the last week file + $weeks[$#weeks] =~ /^(....)(..)$/; + ($end_year, $end_week) = ($1, $2); + $end_date = sprintf ("%04d%02d", $end_year, $end_week); + + # generate an array of filenames between first and last week filename into %weeks + $cyear = $start_year; $cweek = $start_week; + $cdate = sprintf "%04d%02d", $cyear, $cweek; + do { + $weeks{$cdate} = $weeks[0] == $cdate ? shift @weeks : 0; + ($cyear, $cweek) = get_next_year_week ($cyear, $cweek); + $cdate = sprintf "%04d%02d", $cyear, $cweek; + } while ($cdate le $end_date); + + @ret = (); + for $week_filename (sort keys %weeks) { + my ($year, $week, @sum_week); + + $week_filename =~ /^(\d\d\d\d)(\d\d)/; + ($year, $week) = ($1, $2); + + # if $weeks{$week_filename} has true value, then there are week + # events for that week - only in that case we will use the repeat events + + if ($weeks{$week_filename}) { + my ($e_n, $wday); + + $e_n = read_events ('n', $year, $week); + # Combine week files and repeat files + @sum_week = (); + for $wday (0 .. 6) { + my ($day, $month); + ($month, $day) = get_month_day_by_wday_year_week ($wday, $year, $week); + $sum_week[$wday] = join_days ($e_n->[$wday], $e_rw->[$wday], $e_rm->[$day]); + } + } else { + @sum_week = ( {}, {}, {}, {}, {}, {}, {} ); # ignore repeat events too + } + + push @ret, [\@sum_week, $year, $week]; + } + return \@ret; +} + +sub week_cache_read ($$) { + my ($year, $week) = @_; + my ($filename, $data, $old_slash); + local (*CRFH); + + if (!$main::DATA_ID) + { + return undef; + } + $filename = sprintf "$::DB_DIR/cache/w-%s-%04d%02d.html", $main::DATA_ID, $year, $week; + if (! open (CRFH, $filename)) { + return undef; + } + $old_slash = $/; + undef $/; + $data = ; + $/ = $old_slash; + close CRFH; + return \$data; +} + +sub week_cache_write_open ($$) { + my ($year, $week) = @_; + my ($dirname, $filename, $cfh); + $cfh = 'this_is_a_filehandle'; + + $dirname = "$::DB_DIR/cache"; + if ($main::DATA_ID) + { + $filename = sprintf "%s/w-%s-%04d%02d.html", $dirname, $main::DATA_ID, $year, $week; + } + else + { + $filename = sprintf "%s/w-fusion-%04d%02d.html", $dirname, $year, $week; + } +if (! -e $dirname) { + if (! mkdir ($dirname, 0770)) { + my $str1 = "can't create directory $dirname: $!"; + return \$str1; + } +} +if (! open ($cfh, ">$filename")) { + my $str2 = "can't open file $filename for writing: $!"; + return \$str2; +} +flock ($cfh, 2); +return $cfh; +} + +sub header_body ($) { + return \$::H_BODY; +} + +sub week_cache_write_close ($) { + my ($fh) = @_; + if (fileno(F)) + { + flock (F, 8); + } + close $fh; +} + +sub week_split_events_noon ($) { + my ($events_ref) = @_; + my ($e1, $e2, $e3, $day, $hour); + + for (@$events_ref) { + for $day (0 .. 6) { + my (%hours); + %hours = %{$events_ref->[$day]}; + $e1->[$day] = {}; $e2->[$day] = {}; $e3->[$day] = {}; + if (%hours) { + for $hour (keys %hours) { + if ($hour == -1) + { + $e3->[$day]->{$hour} = $events_ref->[$day]->{$hour}; + } + elsif ($hour < 12) + { + $e1->[$day]->{$hour} = $events_ref->[$day]->{$hour}; + } else + { + $e2->[$day]->{$hour} = $events_ref->[$day]->{$hour}; + } + } + } + } + } + return ($e1, $e2, $e3); +} + +sub week_print_events ($$$$$$) { + my ($fh, $events_ref, $use_now_wday, $year, $week, $weekdays_ref) = @_; + my ($day_offset, $day_ref, $hour, $event_ref); + + $day_offset = 0; + for $day_ref (@$events_ref) { + if (%$day_ref) { + print $fh "\n"; + print $fh "\n"; + for $hour (sort { $a <=> $b } keys %$day_ref) { + for $event_ref (@{$day_ref->{$hour}}) { + print $fh '\n"; + } + } + print $fh "
'; + my ($id, $min, $title, $data, $lengthmin, $rt, $color, $color_end); + my ($startyear, $startweek, $startwday, $duration); + my ($alval, $altype, $alchk, $strike); + + $id = $event_ref->{'id'}; + $min = $event_ref->{'min'}; + $lengthmin = $event_ref->{'lengthmin'}; + $title = $event_ref->{'title'}; + $data = $event_ref->{'data'}; + $rt = $event_ref->{'rt'}; + $startyear = $event_ref->{'startyear'}; + $startweek = $event_ref->{'startweek'}; + $startwday = $event_ref->{'startwday'}; + $duration = $event_ref->{'duration'}; + $alval = $event_ref->{'alval'} || 0; + $altype = $event_ref->{'altype'} || 0; + $alchk = $event_ref->{'alchk'} || ""; + $strike = $event_ref->{'strike'} || 0; + + if ($rt eq 'w') { + $color = ""; + } elsif ($rt eq 'm') { + $color = ""; + } else { + $color = ''; + } + $color_end = ""; + my $st = strcal(); + print $fh "$color", + $strike ? "" : "", + $hour == -1 ? "" : ("",format_time ($hour, $min)," - ",format_time (int ($hour + ($min + $lengthmin)/60) % 24, ($min + $lengthmin) % 60)," "), + $title, + $strike ? "" : "", + $color_end, "", + img_note($data), + img_alarm($alval, $altype, $alchk), + "
\n"; + } else { + print $fh " \n"; + } + $day_offset++; + } + return; +} + +# CGI input: year, week OR nothing, in which case current date is used +sub show_week (;$) { + my ($cacheonly) = @_; + my ($week, $month, $day, $year, $first_day); + my ($weekdata, $i, @weekdays, $cached_week_ref, $cache_fh); + my ($now_date, $now_year, $now_week, $now_wday, $use_now_wday); + my ($events1, $events2, $events3, $wday_name, $now_ref); + + $year = $::query->{'year'}; + $week = $::query->{'week'}; + if (! defined $year or ! defined $week) { + my ($ref); + $ref = get_now (); + ($year, $week) = ($ref->{'year'}, $ref->{'week'}); + } + + # If the week is in cache, return it + sw_check_cache: + $cached_week_ref = week_cache_read ($year, $week); + if (defined $cached_week_ref) { + $$cached_week_ref =~ s/X_USER_X/$main::USER/g; + print Socket $$cached_week_ref; + return; + } + + # Find the current day to mark it in the output + $now_ref = get_now (); + ($now_year, $now_week, $now_wday) = ($now_ref->{'year'}, $now_ref->{'week'}, $now_ref->{'wday'}); + + if ($year == $now_year and $week == $now_week) { + $use_now_wday = $now_wday + 0; + } else { + $use_now_wday = -1; + } + + if (defined $main::DATA_ID && $main::DATA_ID) + { + # Start writing the week into cache + $cache_fh = week_cache_write_open ($year, $week); + if (ref $cache_fh) { + print Socket "Internal error: $$cache_fh\n"; + return; + } + } + else # fusion + { + $cache_fh = "Socket"; + } + + # Build year/month/day information of the required week + ($month, $day) = get_month_day_by_firstday_year_week ($year, $week); + $weekdays[0] = [$month, $day]; + for $i (1..6) { + my ($month, $day); + ($month, $day) = get_month_day_by_wday_year_week ($i, $year, $week); + $weekdays[$i] = [$month, $day]; + } + + my $st = strcal(); + print $cache_fh "\n"; + print $cache_fh $::H_BODY; + + # Determine previous and next year & week + my ($prev_week, $next_week, $prev_year, $next_year); + $prev_week = $week - 1; $next_week = $week + 1; + $prev_year = $next_year = $year; + if ($prev_week < 2) { + ($prev_year, $prev_week )= get_prev_year_week ($year, $week); + } elsif ($next_week > 50) { + ($next_year, $next_week )= get_next_year_week ($year, $week); + } + + # cal links for fusion + my $stl = lncal($year, $week); + + # Output title line + print $cache_fh "\n"; + print $cache_fh "\n"; + print $cache_fh "\n"; + print $cache_fh "\n"; + print $cache_fh "
$stl\"".__("Semaine\n"; + print $cache_fh " ",sprintf(__("%s Semaine %s Année %s"),__($::months[$month]),$week,$year)," \n"; + print $cache_fh "\"".__("Semaine".__("Autres vues")."
\n\n"; + + print $cache_fh "\n"; + print $cache_fh "\n"; + + # Print weekday names + $i = 0; + for $wday_name (@main::weekdays_short) { + my ($color); + if ($i == $use_now_wday) { + $color = $::NOW_THCOLOR; + } else { + $color = $::THCOLOR; + } + if ($i <= 4) { + print $cache_fh "\n"; + week_print_events ($cache_fh, $events1, $use_now_wday, $year, $week, \@weekdays); + print $cache_fh "\n"; + week_print_events ($cache_fh, $events2, $use_now_wday, $year, $week, \@weekdays); + print $cache_fh "\n"; + week_print_events ($cache_fh, $events3, $use_now_wday, $year, $week, \@weekdays); + + print $cache_fh "
"; + } else { + print $cache_fh ""; + } + my $st = strcal(); + print $cache_fh "", pd_month_day ($weekdays[$i]->[0], $weekdays[$i]->[1]), " ", __($wday_name), "\n"; + $i++; + } + + $weekdata = build_week ($year, $week); + ($events1, $events2, $events3) = week_split_events_noon ($weekdata); + + print $cache_fh "
\n\n"; + + if (defined $main::DATA_ID && $main::DATA_ID) + { + # Now the cache should exist, so retry (someone may have removed it meanwhile, but then we just retry) + week_cache_write_close ($cache_fh); + goto sw_check_cache; + } +} + +sub show_day () { + my ($day, $month, $week, $year, $wday, $wday_name, $eventsdata, @hours, @thours, $day_ref, $rt); + my ($now_ref, $now_thcolor, $is_now_day, $hour, $event_ref); + + # see if we are given the day or not - if year exists, assume yes + $year = $::query->{'year'}; + if (! defined $year) { + my ($now_ref); + $now_ref = get_now (); + ($year, $month, $day, $week, $wday) = + ($now_ref->{'year'}, $now_ref->{'month'}, $now_ref->{'day'}, $now_ref->{'week'}, $now_ref->{'wday'}); + } else { + $week = $::query->{'week'}; + $wday = $::query->{'wday'}; + } + $wday_name = $::weekdays[$wday]; + + ($month, $day) = get_month_day_by_wday_year_week ($wday, $year, $week); + + $day_ref = build_day ($year, $week, $wday, $day); + + # Build list of events on each hour + @hours = (); + for $hour (sort { $a <=> $b } keys %$day_ref) { + if ($hour == -1) + { + for $event_ref (@{$day_ref->{$hour}}) { + push @{$hours[24]}, $event_ref; + } + } + else + { + for $event_ref (@{$day_ref->{$hour}}) { + push @{$hours[$hour]}, $event_ref; + } + } + } + + # Count how many events will be on each row + my ($max_c, $cols, @rows); + $max_c = 0; + @rows = (0) x ($::LAST_HOUR + 1); + for $hour ($::FIRST_HOUR .. $::LAST_HOUR) { + if ($hours[$hour]) { + for $event_ref (@{$hours[$hour]}) { + my ($lengthmin, $min, $end_hour, $h); + $lengthmin = $event_ref->{'lengthmin'}; + $min = $event_ref->{'min'}; + $end_hour = $hour + int (($lengthmin + $min - 1) / 60) % 24; + for $h ($hour .. $end_hour) { + my ($c); + $c = ++$rows[$h]; + if ($c > $max_c) { + $max_c = $c; + } + } + } + } + } + $max_c = 1 if $max_c == 0; + $cols = $max_c + 1; + + $now_ref = get_now (); + if ($now_ref->{'year'} == $year and $now_ref->{'month'} == $month and $now_ref->{'day'} == $day) { + $is_now_day = 1; + } else { + $is_now_day = 0; + } + if ($is_now_day) { + $now_thcolor = $::NOW_THCOLOR; + } else { + $now_thcolor = $::THCOLOR; + } + + my $st = strcal(); + print Socket "\n"; + print Socket "\n"; + print Socket $::H_BODY; + print Socket "\n"; + print Socket "\n"; + + print Socket "
\n"; + print Socket "
", pd_year_month_day ($year, $month, $day), " ", __("$wday_name")."\n"; + if (defined $::READ_ONLY and $::READ_ONLY ne 'true' and clipboard_get ($::REMOTE_USER)) { + print Socket "  [".__("Coller")."]\n"; + } + print Socket "
\n"; + if ($::READ_ONLY ne 'true') { + print Socket ""; + print Socket ">\n"; + } + else + { + print Socket ">\n"; + } + print Socket " 1) + { + print Socket "COLSPAN=\"$max_c\""; + } + print Socket ">\n"; + my $isev = 0; + my $sthour; + my $sttime; + for $hour (0 .. $::FIRST_HOUR-1, $::LAST_HOUR+1 .. 25) + { + if ($hours[$hour]) + { + $isev = 1; + my (@events, $event_ref); + @events = @{$hours[$hour]}; + for $event_ref (@events) { + my ($id, $min, $title, $data, $lengthmin, $rcolor, $rcolor_end); + my ($startyear, $startweek, $startwday, $duration); + my ($alval, $altype, $alchk, $strike); + $id = $event_ref->{'id'}; + $min = $event_ref->{'min'}; + $lengthmin = $event_ref->{'lengthmin'}; + $title = $event_ref->{'title'}; + $data = $event_ref->{'data'}; + $rt = $event_ref->{'rt'}; + $startyear = $event_ref->{'startyear'}; + $startweek = $event_ref->{'startweek'}; + $startwday = $event_ref->{'startwday'}; + $duration = $event_ref->{'duration'}; + $alval = $event_ref->{'alval'} || 0; + $altype = $event_ref->{'altype'} || 0; + $alchk = $event_ref->{'alchk'} || ""; + $strike = $event_ref->{'strike'} || 0; + + if ($rt eq 'w') { + $rcolor = ""; + $rcolor_end = ""; + } elsif ($rt eq 'm') { + $rcolor = ""; + $rcolor_end = ""; + } else { + $rcolor = $rcolor_end = ''; + } + + if ($hour == 24) + { + $sthour = -1; + $sttime = ""; + } + else + { + $sthour = $hour; + $sttime = "".format_time($hour, $min)." - ".format_time(int($hour + ($min + $lengthmin)/60) % 24, ($min + $lengthmin) % 60)." "; + } + + print Socket '
"; + } + print Socket format_hour_padded ($hour); + if ($::READ_ONLY ne 'true') { + print Socket "\n"; + } + + # + # Print events for this hour + # + if ($hours[$hour]) { + my (@events, $event_ref); + @events = @{$hours[$hour]}; + for $event_ref (@events) { + my ($id, $min, $title, $data, $lengthmin, $rcolor, $rcolor_end); + my ($startyear, $startweek, $startwday, $duration); + my ($alval, $altype, $alchk, $strike); + $id = $event_ref->{'id'}; + $min = $event_ref->{'min'}; + $lengthmin = $event_ref->{'lengthmin'}; + $title = $event_ref->{'title'}; + $data = $event_ref->{'data'}; + $rt = $event_ref->{'rt'}; + $startyear = $event_ref->{'startyear'}; + $startweek = $event_ref->{'startweek'}; + $startwday = $event_ref->{'startwday'}; + $duration = $event_ref->{'duration'}; + $alval = $event_ref->{'alval'} || 0; + $altype = $event_ref->{'altype'} || 0; + $alchk = $event_ref->{'alchk'} || ""; + $strike = $event_ref->{'strike'} || 0; + + if ($rt eq 'w') { + $rcolor = ""; + $rcolor_end = ""; + } elsif ($rt eq 'm') { + $rcolor = ""; + $rcolor_end = ""; + } else { + $rcolor = $rcolor_end = ''; + } + print Socket '{'year'}; + $week = $::query->{'week'}; + $wday = $::query->{'wday'} || 0; + $wday_name = $::weekdays[$wday]; + $hour = $::query->{'hour'} || 0; + $min = $::query->{'min'} || 0; + $lengthmin = $::query->{'lengthmin'} || 0; + $title = $::query->{'title'}; + $data = $::query->{'data'}; + $rt = $::query->{'rt'}; + $is_now = $::query->{'is_now'}; + $startyear = $::query->{'startyear'}; + $startweek = $::query->{'startweek'}; + $startwday = $::query->{'startwday'}; + $duration = $::query->{'duration'}; + $alval = $::query->{'alval'} || 0; + $altype = $::query->{'altype'} || 0; + $alchk = $::query->{'alchk'} || ""; + $strike = $::query->{'strike'} || 0; + + if (! defined $year or ! defined $title) { + my ($n); + $n = get_now (); + show_other_views ($n->{'year'}, $n->{'week'}); + return; + } + + ($month, $day) = get_month_day_by_wday_year_week ($wday, $year, $week); + + $endhour = int ($hour + ($min + $lengthmin) / 60) % 24; + $endmin = int (($min + $lengthmin) % 60); + + print Socket $::H_BODY; + + if ($is_now) { + $now_thcolor = $::NOW_THCOLOR; + } else { + $now_thcolor = $::THCOLOR; + } + if ($rt eq 'w') { + $rcolor = "COLOR=\"$::REPEAT_WEEK_TEXTCOLOR\""; + } elsif ($rt eq 'm') { + $rcolor = "COLOR=\"$::REPEAT_MONTH_TEXTCOLOR\""; + } else { + $rcolor = ''; + } + + print Socket "
\n\n"; + print Socket "
".__("Tâche le")." ", pd_event_date ($rt, $startyear, $startweek, $startwday, $duration); + print Socket "\n
\n"; + my ($stk1, $stk2) = $strike ? ("","") : ("",""); + if ($hour == -1) + { + printf Socket "

$stk1$title$stk2"; + } + else + { + printf Socket "

$stk1%s - %s $title$stk2", format_time ($hour, $min), format_time ($endhour, $endmin); + } + print Socket (($rt eq 'w') ? ' ('.__("hebdomadaire").')' : ( ( $rt eq 'm' ) ? ' ('.__("mensuelle").')' : '' )); + print Socket "", txt_alarm($alval, $altype, $alchk), "
\n"; + print Socket "

$data\n"; + print Socket "

\n"; + my $st = strcal(); + + if ($::READ_ONLY ne 'true') { + my ($enc_title, $enc_data); + $enc_title = url_encode ($title); + $enc_data = url_encode ($data); + my $stk3 = $strike ? __("Activer") : __("Désactiver"); + print Socket "", + "[".__("Editer")."]\n"; + print Socket "   ", + "[".__("Couper")."]\n"; + print Socket "   ", + "[".__("Copier")."]\n"; + print Socket "   ", + "[".__("Supprimer")."]\n"; + print Socket "   ", + "[$stk3]\n"; + } + print Socket "
\n"; +} + +sub add_edit_event_ask () { + my ($id, $year, $month, $day, $week, $wday, $wday_name, $weekdata, @hours, $day_ref); + my ($hour, $min, $lengthmin, $title, $data, $endhour, $endmin, $rt, $duration); + my ($startyear, $startweek, $startwday, $startmonth, $startday); + my ($endyear, $endweek, $endwday, $endmonth, $endday); + + $id = $::query->{'id'} || ''; + $year = $::query->{'year'}; + $week = $::query->{'week'}; + $wday = $::query->{'wday'}; + $wday_name = $::weekdays[$wday]; + $hour = $::query->{'hour'}; + $min = $::query->{'min'} || 0; + $lengthmin = $::query->{'lengthmin'}; + $rt = $::query->{'rt'} || 'n'; + $title = $::query->{'title'} || ''; + $data = $::query->{'data'} || ''; + $startyear = $::query->{'startyear'} || $year; + $startweek = $::query->{'startweek'} || $week; + $startwday = defined $::query->{'startwday'} ? $::query->{'startwday'} : $wday; + $duration = $::query->{'duration'} || 1; + my $strike = $::query->{'strike'} || 0; + + ($month, $day) = get_month_day_by_wday_year_week ($wday, $year, $week); + + + if ($id) { + if ($hour == -1) + { + $endhour = -1; + $endmin = -1; + } + else + { + $endhour = int ($hour + ($min + $lengthmin) / 60) % 24; + $endmin = int (($min + $lengthmin) % 60); + } + } else { + $endhour = $hour+1; + $endmin = 0; + } + + ($startmonth, $startday, $endyear, $endweek, $endwday, $endmonth, $endday) = + get_start_end_by_year_week_wday_duration ($startyear, $startweek, $startwday, $duration); + + print Socket $::H_BODY; + print Socket "
\n"; + print Socket "\n"; + print Socket "\n"; + print Socket "
"; + if ($id) { + print Socket __("Editer tâche"); + } else { + print Socket __("Ajouter tâche"); + } + print Socket " ".pd_event_date ($rt, $startyear, $startweek, $startwday, $duration); + print Socket "
\n"; + + print Socket "
\n\n"; + + print Socket "\n"; + print Socket "\n"; + print Socket "\n"; + print Socket "\n"; + print Socket "\n"; + print Socket "\n"; + print Socket "\n"; + print Socket "\n"; + print Socket "\n"; + print Socket "\n"; + + print Socket "\n"; + print Socket "\n"; + print Socket "\n"; + print Socket "\n"; + + # Event time + print Socket "\n"; + + # Duration (days) + print Socket "\n"; + + # Alarm + print Socket ""; + my $alval = $::query->{'alval'} || 0; + print Socket "\n"; + + # Title + print Socket ""; + + # Data + print Socket ""; + print Socket "
\n"; + print Socket __("Heure début")."\n"; + print Socket "\n"; + print Socket " - \n"; + print Socket "\n"; + print Socket "\n
".__("Durée")."\n"; + + # Repeat + my ($sn, $sw, $sm); + $sn = $sw = $sm = ''; + if ($rt eq 'n') { + $sn = 'SELECTED'; + } elsif ($rt eq 'w') { + $sw = 'SELECTED'; + } elsif ($rt eq 'm') { + $sm = 'SELECTED'; + } + print Socket "\n
".__("Alarme")."\n"; + my $altype = $::query->{'altype'} || 0; + print Socket "\n"; + print Socket ' ".__("lancée"); + print Socket "
".__("Titre").""; + print Socket "
\n"; + + # Submit + my ($submit_value); + $submit_value = ($id ? __("Valider") : __("Ajouter")); + print Socket "\n"; + print Socket "
\n"; + + print Socket "
\n"; + + print Socket "
\n"; + print Socket "\n"; + +} + +sub print_event_error_start () { + print Socket $::H_BODY; + print Socket "
\n"; + print Socket "\n


\n\n"; +} + +sub print_event_error_end () { + print Socket "

\n\n"; +} + +sub day_index ($$$$) { + my ($rt, $year, $week, $wday) = @_; + my ($index); + + # Index by day or wday, depending on repeat type + if ($rt eq 'n' or $rt eq 'w') { + $index = $wday + 0; + } else { + my ($month, $day); + ($month, $day) = get_month_day_by_wday_year_week ($wday, $year, $week); + $index = $day + 0; + } + + return $index; +} + +# Read, update, and write new highest event id into general database +# Return the (id, possible error or false) +sub next_id () { + my ($id, $gendata, $err); + + $gendata = read_general (); + if (! ref $gendata eq 'HASH') { + $err = $gendata; + return ('', $err); + } + + $id = $gendata->{'highid'} + 1; + $gendata->{'highid'} = $id; + + $err = write_general ($gendata); + if ($err) { + return ('', $err); + } + return ($id, ''); +} + +sub remove_events ($$$$$$$) { + my ($rt, $year, $week, $wday, $duration, $hour, $id) = @_; + my ($i); + + for $i (0 .. $duration - 1) { + my ($cyear, $cweek, $cwday, $index_day, $hour_ref, $eventsdata, @hour, $err); + + ($cyear, $cweek, $cwday) = get_year_week_wday_by_year_week_wday_plus_days ($year, $week, $wday, $i); + + $index_day = day_index ($rt, $cyear, $cweek, $cwday); + + + $eventsdata = read_events ($rt, $cyear, $cweek); + $hour_ref = $eventsdata->[$index_day]->{$hour}; + if (ref $hour_ref eq 'ARRAY') { + my ($i); + @hour = @$hour_ref; + for $i (0 .. $#hour) { + if ($hour[$i]->{'id'} == $id) { + splice (@hour, $i, 1); + goto re_found; + } + } + } + return "internal error: the event isn't in the database (user ${main::DATA_ID}, year $cyear, week $cweek, wday $cwday, id $id, rt $rt)"; + +re_found: + if (@hour) { + $eventsdata->[$index_day]->{$hour+0} = \@hour; + } else { + # no events left for this hour, so remove whole hour + delete $eventsdata->[$index_day]->{$hour}; + } + $err = write_events ($eventsdata, $rt, $cyear, $cweek); + if ($err) { + return $err; + } + } + return ''; +} + +sub add_events { + my ($rt, $year, $week, $wday, $duration, $hour, $id, $min, $lengthmin, $title, $data, $alval, $altype, $alchk, $strike) = @_; + my ($i, $err); + + if (!$alval) + { + $alval = 0; + } + if (!$altype) + { + $altype = 0; + } + if (!$alchk) + { + $alchk = ""; + } + if (!$strike) + { + $strike = 0; + } + # Figure out the new $id + if (! $id) { + my ($gendata); + + # Read, update, and write new highest event id into general database + $gendata = read_general (); + if (! ref $gendata eq 'HASH') { + $err = $gendata; + return $err; + } + $id = $gendata->{'highid'} + 1; + $gendata->{'highid'} = $id; + $err = write_general ($gendata); + if ($err) { + return $err; + } + } + + + for $i (0 .. $duration - 1) { + my ($cyear, $cweek, $cwday, $index_day, $eventsdata); + + + ($cyear, $cweek, $cwday) = get_year_week_wday_by_year_week_wday_plus_days ($year, $week, $wday, $i); + + + $index_day = day_index ($rt, $cyear, $cweek, $cwday); + + # Read events, add, write back + $eventsdata = read_events ($rt, $cyear, $cweek); + push @{$eventsdata->[$index_day]->{$hour+0}}, + { + 'id' => $id + 0, + 'min' => $min + 0, + 'lengthmin' => $lengthmin + 0, + 'title' => $title, + 'data' => $data, + 'rt' => $rt, + 'startyear' => $year + 0, + 'startweek' => $week + 0, + 'startwday' => $wday + 0, + 'duration' => $duration + 0, + 'alval' => $alval + 0, + 'altype' => $altype + 0, + 'alchk' => $alchk, + 'strike' => $strike, + }; + + $err = write_events ($eventsdata, $rt, $cyear, $cweek); + if ($err) { + return $err; + } + + } + return ''; +} + +sub add_event_commit () { + my ($id, $year, $month, $day, $week, $wday, $wday_name, $rt); + my ($hour1, $min1, $hour2, $min2, $lengthmin, $title, $data, $endhour, $endmin); + my ($err, $i); + my ($oldrt, $oldhour); + my ($oldhour_ref, @hour, $oldduration, $duration); + my ($startyear, $startweek, $startwday, $startmonth, $startday); + my ($alval, $altype, $alchk, $strike); + + $oldrt = $::query->{'oldrt'}; + $oldhour = $::query->{'oldhour'}; + $oldduration = $::query->{'oldduration'}; + + $id = $::query->{'id'} || ''; + $year = $::query->{'year'}; + $week = $::query->{'week'}; + $wday = $::query->{'wday'}; + $startyear = $::query->{'startyear'}; + $startweek = $::query->{'startweek'}; + $startwday = $::query->{'startwday'}; + $duration = $::query->{'duration'}; + + $hour1 = $::query->{'hour1'}; + $min1 = $::query->{'min1'}; + $hour2 = $::query->{'hour2'}; + $min2 = $::query->{'min2'}; + $rt = $::query->{'rt'}; + $title = strip_space $::query->{'title'}; + $data = strip_space $::query->{'data'} || ''; + + $alval = $::query->{'alval'} || 0; + $altype = $::query->{'altype'} || 0; + $alchk = $::query->{'alchk'} || ""; + $strike = $::query->{'strike'} || 0; + + ($startmonth, $startday) = get_month_day_by_wday_year_week ($startwday, $startyear, $startweek); + + if ($hour1 == -1) + { + $min1 = -1; + $hour2 = -1; + $min2 = -1; + $lengthmin = -1; + } + else + { + $lengthmin = $hour2*60 + $min2 - $hour1*60 - $min1; + } + + if (! defined $title or ! $title) { + return "The title of the event must be entered."; + } elsif (! &ParseDate ("$startmonth/$startday/$startyear")) { + return "Date $startday.$startmonth.$startyear is invalid." + } elsif ($duration < 1 or $duration > $::MAX_DURATION) { + return "You chose impossible duration \"$duration\"." + } elsif ($rt eq 'w' and ! fit_in_week ($startyear, $startweek, $startwday, $duration)) { + return "Weekly repeating event must fit entirely in one week." + } elsif ($rt eq 'm' and ! fit_in_month ($startyear, $startweek, $startwday, $duration)) { + return "Monthly repeating event must fit entirely in one month (the month you placed it in specifically)." + } elsif ($rt ne 'n' and $rt ne 'w' and $rt ne 'm') { + return "You chose impossible repeat type \"$rt\"." + } elsif (has_html $title or has_html $data) { + return "Text contains one more more of the illegal characters <, > and &." + } elsif ($lengthmin <= 0 && $hour1 != -1) { + return "The start of the event ($hour1:$min1) must be before its end ($hour2:$min2)." + } + + $title = newline_to_html $title; + $data = newline_to_html $data; + + # + # Remove the old event. The old one only exists if $id is set so this is + # an edit command. + # + + if ($id) { + $err = remove_events ($oldrt, $startyear, $startweek, $startwday, $oldduration, $oldhour, $id); + if ($err) { + return $err; + } + } + + # + # Figure out the new $id + # + if (! $id) { + ($id, $err) = next_id (); + if ($err) { + return $err; + } + } + + # + # Add event for each day + # + + $err = add_events ($rt, $startyear, $startweek, $startwday, $duration, $hour1, $id, + $min1, $lengthmin, $title, $data, $alval, $altype, $alchk, $strike); + if ($err) { + return $err; + } + # + # When we get here, add has been succesful. + # + # Return the id and some other items to redraw(), since he doesn't know + # them otherwise. + # + return { 'id' => $id, 'lengthmin' => $lengthmin, 'data' => $data, 'title' => $title }; +} + +sub remove_event_commit ($$$$$$$$$$$$$$) { + my ($year, $week, $wday, $id, $hour, $rt, $min, $lengthmin, $title, $data, $startyear, $startweek, $startwday, $duration) = @_; + my ($err); + + if (! defined $id or ! defined $week or ! defined $wday or ! defined $rt or ! defined $duration) { + return "some data of the event to be removed not given"; + } + + # + # Copy data into clipboard + # + clipboard_set ($::REMOTE_USER, + { 'hour' => $hour, 'min' => $min, 'lengthmin' => $lengthmin, + 'title' => $title, 'data' => $data, 'rt' => $rt, + 'startyear' => $startyear, 'startweek' => $startweek, 'startwday' => $startwday, 'duration' => $duration } ); + + # + # Remove the event from the database + # + $err = remove_events ($rt, $startyear, $startweek, $startwday, $duration, $hour, $id); + if ($err) { + return $err; + } + + return ''; +} + +sub delete_event_commit ($$$$$$$$$$$$$$) { + my ($year, $week, $wday, $id, $hour, $rt, $min, $lengthmin, $title, $data, $startyear, $startweek, $startwday, $duration) = @_; + my ($err); + + if (! defined $id or ! defined $week or ! defined $wday or ! defined $rt or ! defined $duration) { + return "some data of the event to be removed not given"; + } + + # + # Remove the event from the database + # + $err = remove_events ($rt, $startyear, $startweek, $startwday, $duration, $hour, $id); + if ($err) { + return $err; + } + + return ''; +} + +sub switch_strike_event_commit { + my ($year, $week, $wday, $id, $hour, $rt, $min, $lengthmin, $title, $data, $startyear, $startweek, $startwday, $duration, $alval, $altype, $alchk, $strike) = @_; + my ($err); + + if (! defined $id or ! defined $week or ! defined $wday or ! defined $rt or ! defined $duration) { + return "some data of the event to be striked not given"; + } + + # + # Strike/unstrike the event in the database + # + + $strike = 1 - $strike; + $err = remove_events ($rt, $startyear, $startweek, $startwday, $duration, $hour, $id); + if ($err) { return $err; } + $err = add_events ($rt, $startyear, $startweek, $startwday, $duration, $hour, $id, $min, $lengthmin, $title, $data, $alval, $altype, $alchk, $strike); + if ($err) { return $err; } + return ''; +} + +sub copy_event () { + my ($hour, $min, $lengthmin, $title, $data, $rt, $startyear, $startweek, $startwday, $duration); + + $hour = $::query->{'hour'}; + $min = $::query->{'min'}; + $lengthmin = $::query->{'lengthmin'}; + $title = $::query->{'title'}; + $data = $::query->{'data'}; + $rt = $::query->{'rt'}; + $startyear = $::query->{'startyear'}; + $startweek = $::query->{'startweek'}; + $startwday = $::query->{'startwday'}; + $duration = $::query->{'duration'}; + + return if ! check_write_access (); + + clipboard_set ($::REMOTE_USER, + { 'hour' => $hour, 'min' => $min, 'lengthmin' => $lengthmin, + 'title' => $title, 'data' => $data, 'rt' => $rt, + 'startyear' => $startyear, 'startweek' => $startweek, 'startwday' => $startwday, 'duration' => $duration } ); + + # Show_even() re-reads the variables from %$::query + show_event (); +} + +sub paste_event_commit ($$$) { + my ($year, $week, $wday) = @_; + my ($event_ref, $err, $id); + + + # Read the event data from clipboard + $event_ref = clipboard_get ($::REMOTE_USER); + if (! $event_ref) { + return "There is no event in clipboard for your computer $::REMOTE_USER."; + } + + # + # Find new id + # + ($id, $err) = next_id (); + if ($err) { + return $err; + } + + # + # Update some fields + # + + $event_ref->{'startyear'} = $year; + $event_ref->{'startweek'} = $week; + $event_ref->{'startwday'} = $wday; + $event_ref->{'id'} = $id; + + # + # Make some sanity checks + # + + my ($startmonth, $startday, $startyear, $startweek, $startwday, $duration, $rt); + ($startmonth, $startday) = get_month_day_by_wday_year_week ($wday, $year, $week); + $startyear = $year; $startweek = $week; $startwday = $wday; + $duration = $event_ref->{'duration'}; + $rt = $event_ref->{'rt'}; + + if (! &ParseDate ("$startmonth/$startday/$startyear")) { + return "Date $startday.$startmonth.$startyear is invalid." + } elsif ($rt eq 'w' and ! fit_in_week ($startyear, $startweek, $startwday, $duration)) { + return "Weekly repeating event must fit entirely in one week." + } elsif ($rt eq 'm' and ! fit_in_month ($startyear, $startweek, $startwday, $duration)) { + return "Monthly repeating event must fit entirely in one month (the month you placed it in, specifically)." + } + + # + # Add event for each day + # + + + $err = add_events ( + $event_ref->{'rt'}, + $event_ref->{'startyear'}, + $event_ref->{'startweek'}, + $event_ref->{'startwday'}, + $event_ref->{'duration'}, + $event_ref->{'hour'}, + $event_ref->{'id'}, + $event_ref->{'min'}, + $event_ref->{'lengthmin'}, + $event_ref->{'title'}, + $event_ref->{'data'} ); + if ($err) { + return $err; + } + + # When we get here, add has been succesful + return $event_ref; +} + +sub xxx_event_error () { + my ($error1, $error2); + $error1 = $::query->{'error1'}; + $error2 = $::query->{'error2'}; + + print Socket $::H_BODY; + print Socket "
\n"; + print Socket "\n"; + print Socket ""; + print Socket "
\n"; + print Socket "

$error1

\n"; + print Socket "

$error2\n

\n"; + print Socket "\n"; + return; +} + +sub show_event_list () { + my ($year, $week, $alldata_ref, $lastweek); + my ($cweek, $cyear, $cmonth, $cday, $end_cmonth, $end_cday); + my ($now_ref, $now_year, $now_week, $now_wday, $week_ref, $wday, $hour); + + $year = $::query->{'year'}; + $week = $::query->{'week'}; + + $alldata_ref = build_week_list ($year, $week); + if (ref $alldata_ref ne 'ARRAY') { + print Socket "

Listing events failed: $alldata_ref

\n"; + return; + } + + $now_ref = get_now (); + ($now_year, $now_week, $now_wday) = ($now_ref->{'year'}, $now_ref->{'week'}, $now_ref->{'wday'}); + + print Socket $::H_BODY; + + print Socket "
\n
\n

\n"; + print Socket "\n"; + my $str = sprintf(__("Tâches depuis la semaine %d de l'année %d"), $week, $year); + print Socket ""; + printf Socket "%s %s - %s\n", + __($::weekdays_short[$wday]), + format_time_padded ($hour1, $min1), + format_time_padded ($hour2, $min2); + print Socket "
$str\n"; + print Socket "
\n"; + + my $st = strcal(); + $lastweek = -1; + for $week_ref (@$alldata_ref) { + for $wday (0..6) { + for $hour (sort {$a <=> $b} keys %{$week_ref->[0]->[$wday]}) { + my ($event_ref); + $cyear = $week_ref->[1]; + $cweek = $week_ref->[2]; + + # Change of week + if ($cweek != $lastweek) { + my ($b_month, $b_day, $e_month, $e_day, $color); + $lastweek = $cweek; + ($b_month, $b_day) = get_month_day_by_firstday_year_week ($cyear, $cweek); + ($e_month, $e_day) = get_month_day_by_wday_year_week (6, $cyear, $cweek); + if ($cyear == $now_year and $cweek == $now_week) { + $color = $::NOW_TDCOLOR; + } else { + $color = $::TDCOLOR; + } + print Socket "
".sprintf(__("Semaine %s"), $cweek).", ", pd_year_month_day ($cyear, $b_month, $b_day), " - ", pd_year_month_day ($cyear, $e_month, $e_day), "\n"; + } + + for $event_ref (@{$week_ref->[0]->[$wday]->{$hour}}) { + my (%e, $hour1, $min1, $hour2, $min2, $lengthmin); + my ($tmonth, $tday, $twday, $id, $min, $title, $data, $rt, $day, $color, $rcolor); + my ($startyear, $startweek, $startwday, $duration); + %e = %$event_ref; + $hour1 = $hour; + $id = $e{'id'}; + $min1 = $e{'min'}; + $lengthmin = $e{'lengthmin'}; + $title = $e{'title'}; + $data = $e{'data'}; + $rt = $e{'rt'}; + $startyear = $event_ref->{'startyear'}; + $startweek = $event_ref->{'startweek'}; + $startwday = $event_ref->{'startwday'}; + $duration = $event_ref->{'duration'}; + + $hour2 = ($hour1 + int (($min1+$lengthmin) / 60)) % 24; + $min2 = ($min1 + $lengthmin) % 60; + ($cmonth, $cday) = get_month_day_by_wday_year_week ($wday, $cyear, $cweek); + if ($cyear == $now_year and $cweek == $now_week and $wday == $now_wday) { + $color = " BGCOLOR=\"$::NOW_TDCOLOR\""; + } else { + $color = " BGCOLOR=\"$::BG_TDCOLOR\""; + } + + if ($rt eq 'w') { + $rcolor = "COLOR=\"$::REPEAT_WEEK_TEXTCOLOR\""; + } elsif ($rt eq 'm') { + $rcolor = "COLOR=\"$::REPEAT_MONTH_TEXTCOLOR\""; + } else { + $rcolor = ''; + } + print Socket "
", $e{'title'}, "", $data || " ", "\n"; + } + } + } + } + print Socket "

\n"; + + return; +} + +sub show_other_views (;$$) { + my ($year, $week) = @_; + my ($month, $moffset); + + $year = $year || $::query->{'year'}; + $week = $week || $::query->{'week'}; + + print Socket $::H_BODY; + + print Socket "

\n\n"; + print Socket "
".__("Autres vues").""; + print Socket "\n
\n"; + print Socket "\n"; + print Socket "\n"; + print Socket "\n"; + print Socket "
    \n"; + my $str = sprintf(__("Agendas %s"), $::ORGANIZATION); + my $st = strcal(); + print Socket "
  • $str\n"; + $str = sprintf(__("Tâches depuis la semaine %d de l'année %d"), $week, $year); + print Socket "
  • $str"; + print Socket "
    \n"; + $str = sprintf(__("Purger l'agenda jusqu'à la semaine %d de l'année %d"), , $week, $year); + if (!$st && $::USER !~ /^\wo_/ && -e "/dev/pilot") + { + print Socket "
  • $str"; + } + print Socket "
\n"; + + print Socket "
\n"; + my ($cyear, $cweek, $cmonth, $cday, $now_year, $now_week, $n); + $n = get_now (); + $now_year = $n->{'year'}; $now_week = $n->{'week'}; + ($cyear, $cweek) = get_year_week_by_firstday_year_week_minus_days ($year, $week, + ($::MANY_WEEKS_VERT * $::MANY_WEEKS_HOR + int ($::MANY_WEEKS_HOR / 2)) * 7); + for $moffset (-$::MANY_WEEKS_VERT .. $::MANY_WEEKS_VERT) { + my ($m); + print Socket "\n"; + for $m (0 .. $::MANY_WEEKS_HOR-1) { + ($cmonth, $cday) = get_month_day_by_firstday_year_week ($cyear, $cweek); + if ($cweek == $now_week and $cyear == $now_year) { + printf Socket "\n", $cweek, pd_month_day_padded ($cmonth, $cday); + } else { + printf Socket "\n", $cweek, pd_month_day_padded ($cmonth, $cday); + } + $cweek++; + if ($cweek > 50) { + $cweek--; + ($cyear, $cweek) = get_next_year_week ($cyear, $cweek); + } + } + print Socket "\n"; + } + print Socket "
%02d : %s%02d : %s
\n"; + + print Socket "
\n"; + print Socket "
\n\n"; + return; +} + +sub redraw_print_frameset ($$$) { + my ($tweek, $tday, $tevent) = @_; + my ($top_y, $left_x); + + if ($::SCREEN_RESOLUTION eq '800x600') { + $top_y = 200; + $left_x = 250; + } else { + $top_y = 250; + $left_x = 320 + } + + my $title = sprintf(__("Agenda %s"), $::LONG_NAME); + my $st = ""; + if ($::query->{'cal'}) + { + $st .= "&cal=".join("&cal=", split(/\n/, $::query->{'cal'})); + } + + print Socket <$title + +frameborder="0" marginheight="0" marginwidth="3" name="fhead" scrolling="no" target="fday" noresize> + + + + + + +<body> +</body> + + +END + return; +} + +sub check_write_access () { + if ($::READ_ONLY eq 'true') { + print Socket $::H_BODY; + print Socket "
\n"; + print Socket "\n"; + print Socket "
 

This user has no write permission.

 

\n"; + print Socket "\n"; + return 0; + } + return 1; +} + +sub redraw () { + my ($view, $id, $year, $week, $wday, $wday_name); + my ($hour, $min, $lengthmin, $title, $data, $rt); + my ($tweek, $tday, $tevent); + my ($startyear, $startweek, $startwday, $duration); + my ($alval, $altype, $alchk, $strike); + + $view = $::query->{'view'}; + $id = $::query->{'id'}; + $year = $::query->{'year'}; + $week = $::query->{'week'}; + $wday = $::query->{'wday'} || 0; + $wday_name = $::weekdays[$wday]; + $hour = $::query->{'hour'}; + $min = $::query->{'min'}; + $lengthmin = $::query->{'lengthmin'}; + $title = $::query->{'title'}; + $data = $::query->{'data'}; + $rt = $::query->{'rt'} || 'n'; + $startyear = $::query->{'startyear'}; + $startweek = $::query->{'startweek'}; + $startwday = $::query->{'startwday'}; + $duration = $::query->{'duration'}; + $alval = $::query->{'alval'}; + $altype = $::query->{'altype'}; + $alchk = $::query->{'alchk'} || ""; + $strike = $::query->{'strike'} || 0; + + if (! defined $view or $view eq '' or $view eq 'default') { + $tweek = "$::MY_URL/?t=week"; + $tday = "$::MY_URL/?t=day"; + $tevent = "$::MY_URL/?t=event"; + redraw_print_frameset ($tweek, $tday, $tevent); + } elsif ($view eq 'week') { + $tweek = "$::MY_URL/?t=week&year=$year&week=$week"; + $tday = "$::MY_URL/?t=day"; + $tevent = "$::MY_URL/?t=event"; + redraw_print_frameset ($tweek, $tday, $tevent); + } elsif ($view eq 'event') { + $tweek = "$::MY_URL/?t=week&year=$year&week=$week"; + $tday = "$::MY_URL/?t=day&year=$year&week=$week&wday=$wday"; + $tevent = "$::MY_URL/?t=event&id=$id&year=$year&week=$week&wday=$wday&hour=$hour&min=$min&lengthmin=$lengthmin&title=" . url_encode ($title) . "&data=" . url_encode ($data) . "&rt=$rt&startyear=$startyear&startweek=$startweek&startwday=$startwday&duration=$duration"; + redraw_print_frameset ($tweek, $tday, $tevent); + } elsif ($view eq 'after_remove_event') { + my ($err); + return if ! check_write_access (); + $err = remove_event_commit ($year, $week, $wday, $id, $hour, $rt, $min, $lengthmin, $title, $data, $startyear, $startweek, $startwday, $duration); + $tweek = "$::MY_URL/?t=week&year=$year&week=$week"; + $tday = "$::MY_URL/?t=day&year=$year&week=$week&wday=$wday&rt=$rt"; + if ($err) { + $tevent = "$::MY_URL/?t=xxx_event_error&error1=" . url_encode ("Error removing event") . "&error2=" . (url_encode $err); + } else { + $tevent = "$::MY_URL/?t=event"; + } + redraw_print_frameset ($tweek, $tday, $tevent); + } elsif ($view eq 'after_delete_event') { + my ($err); + return if ! check_write_access (); + $err = delete_event_commit ($year, $week, $wday, $id, $hour, $rt, $min, $lengthmin, $title, $data, $startyear, $startweek, $startwday, $duration); + $tweek = "$::MY_URL/?t=week&year=$year&week=$week"; + $tday = "$::MY_URL/?t=day&year=$year&week=$week&wday=$wday&rt=$rt"; + if ($err) { + $tevent = "$::MY_URL/?t=xxx_event_error&error1=" . url_encode ("Error removing event") . "&error2=" . (url_encode $err); + } else { + $tevent = "$::MY_URL/?t=event"; + } + redraw_print_frameset ($tweek, $tday, $tevent); + } elsif ($view eq 'paste_event') { + my ($err); + return if ! check_write_access (); + $err = paste_event_commit ($year, $week, $wday); + $tweek = "$::MY_URL/?t=week&year=$year&week=$week"; + $tday = "$::MY_URL/?t=day&year=$year&week=$week&wday=$wday&rt=$rt"; + if (ref $err ne 'HASH') { + $tevent = "$::MY_URL/?t=xxx_event_error&error1=" . url_encode ("Error pasting event from clipboard") . "&error2=" . (url_encode $err); + } else { + $tevent = "$::MY_URL/?t=event&id=" . $err->{'id'} . "&year=$year&week=$week&wday=$wday&hour=" . $err->{'hour'} . "&min=" . $err->{'min'} . "&lengthmin=" . $err->{'lengthmin'} . "&title=" . url_encode ($err->{'title'}) . "&data=" . url_encode ($err->{'data'}) . "&rt=" . $err->{'rt'} . "&startyear=" . $err->{'startyear'} . "&startweek=" . $err->{'startweek'} . "&startwday=" . $err->{'startwday'} . "&duration=" . $err->{'duration'}; + } + redraw_print_frameset ($tweek, $tday, $tevent); + } elsif ($view eq 'add_event') { + my ($err); + return if ! check_write_access (); + $err = add_event_commit (); + $tweek = "$::MY_URL/?t=week&year=$year&week=$week"; + $tday = "$::MY_URL/?t=day&year=$year&week=$week&wday=$wday&rt=$rt"; + if (ref $err ne 'HASH') { + $tevent = "$::MY_URL/?t=xxx_event_error&error1=" . url_encode ("Error adding an event") . "&error2=" . (url_encode $err); + } else { + $tevent = "$::MY_URL/?t=event&id=" . $err->{'id'} . "&year=$year&week=$week&wday=$wday&hour=" . $::query->{'hour1'} . "&min=" . $::query->{'min1'} . "&lengthmin=" . $err->{'lengthmin'} . "&title=" . url_encode ($err->{'title'}) . "&data=" . url_encode ($err->{'data'}) . "&rt=$rt&startyear=$startyear&startweek=$startweek&startwday=$startwday&duration=$duration&alval=$alval&altype=$altype&alchk=$alchk&strike=$strike"; + } + redraw_print_frameset ($tweek, $tday, $tevent); + } elsif ($view eq 'after_strike_event') { + my ($err); + return if ! check_write_access (); + $err = switch_strike_event_commit ($year, $week, $wday, $id, $hour, $rt, $min, $lengthmin, $title, $data, $startyear, $startweek, $startwday, $duration, $alval, $altype, $alchk, $strike); + $tweek = "$::MY_URL/?t=week&year=$year&week=$week"; + $tday = "$::MY_URL/?t=day&year=$year&week=$week&wday=$wday&rt=$rt"; + if ($err) { + $tevent = "$::MY_URL/?t=xxx_event_error&error1=" . url_encode ("Error striking event") . "&error2=" . (url_encode $err); + } else { + $strike = 1 - $strike; + $tevent = "$::MY_URL/?t=event&id=$id&year=$year&week=$week&wday=$wday&hour=$hour&min=$min&lengthmin=$lengthmin&title=" . url_encode ($title) . "&data=" . url_encode ($data) . "&rt=$rt&startyear=$startyear&startweek=$startweek&startwday=$startwday&duration=$duration&alval=$alval&altype=$altype&alchk=$alchk&strike=$strike"; + } + redraw_print_frameset ($tweek, $tday, $tevent); + } else { + print Socket "

Redraw: Unknown view \"$view\".\n"; + } + return; +} + +sub create_users () { + my (@users, $user); + + if (! chdir ($::DIRECTORY)) { + return "Can't change into directory $::DIRECTORY: $!

Make sure it exists, and its permissions are as explained in file INSTALL of the Wcal distribution."; + } + + @users = sort (keys (%$::USER_CONFS)); + for $user (@users) { + my ($desc); + $desc = $::USER_CONFS->{$user}->{'long name'}; + if (-d $user and -M $user > -M $::CONF_FILE) { + system "/bin/rm -rf $user"; + } + if (! -d $user) { + my ($access_file, $access_file_name); + if (! mkdir ($user, 0755)) { + return "Internal error: can't create user '$user': $!"; + } + if (! link ('index.cgi', $user . '/index.cgi')) { + return "Internal error: can't create hard link 'index.cgi -> $user/index.cgi': $!"; + } + $access_file_name = $::USER_CONFS->{$user}->{'access file name'}; + $access_file = $::USER_CONFS->{$user}->{'access file'}; + if ($access_file and $access_file_name) { + if (! open (AF, ">$user/$access_file_name")) { + return "Internal error: can't create access file $user/$access_file_name: $!"; + } + print AF "# This file is automatically generated from $::CONF_FILE - do not edit\n"; + print AF $access_file; + close AF; + } + } + } + return ''; +} + +# Removes all cache files. +sub purge_cache_all () { + my (@files); + if (! opendir (DIR, "$::DB_DIR/cache")) { + return; + } + @files = grep { /^w-[^-]+-\d\d\d\d\d\d\.html$/ } readdir (DIR); + closedir DIR; + for (@files) { + unlink "$::DB_DIR/cache/$_" or warn "can't remove $::DB_DIR/cache/$_: $!\n"; + } +} + +# Removes cached week views and Date::Manip wrapper caches. +# Supposed to be called at first request after midnight. +sub purge_cache_newday () { + my (@files); + my ($now_ref, $foo, $week1, $week2); + + # + # Cached dates. + # Other date caches stay valid accross date change. + # + + undef $::now_cache; + + # + # Cached week for this week, or all weeks if the week has changed + # + $now_ref = get_now (); + $week1 = sprintf ("%02d", $now_ref->{'week'}); + ($foo, $week2) = get_prev_year_week ($now_ref->{'year'}, $week1); + $week2 = sprintf ("%02d", $week2); + + if (! opendir (DIR, "$::DB_DIR/cache")) { + return; + } + if ($week1 == $week2) { + @files = grep { /^w-[^-]+-\d\d\d\d$week1\.html$/ } readdir (DIR); + } else { + @files = grep { /^w-[^-]+-\d\d\d\d\d\d\.html$/ } readdir (DIR); + } + closedir DIR; + for (@files) { + unlink "$::DB_DIR/cache/$_" or warn "can't remove $::DB_DIR/cache/$_: $!\n"; + } + + return; +} + +# Create socket and make it listen +sub init_socket () { + if (! socket (SSocket, PF_INET, SOCK_STREAM, getprotobyname('tcp')) ) { + print STDERR "$0: can't create socket: $!"; + exit 1; + } + + if (! setsockopt(SSocket, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) ) { + print STDERR "$0: can't set socketopt SO_REUSEADDR: $!"; + exit 1; + } + + if (! bind(SSocket, sockaddr_in($::PORT, INADDR_ANY)) ) { + print STDERR "$0: can't bind socket to INADDR_ANY: $!"; + exit 1; + } + + if (! listen(SSocket, SOMAXCONN) ) { + print STDERR "$0: can't make socket listen: $!"; + exit 1; + } +} + +sub parse_config_line ($) { + my ($line) = @_; + my ($key, $value); + + if ($line =~ /^#/ or $line =~ /^\s*$/) { + return ''; + } + if ($line =~ /=/) { + ($key, $value) = split (/=/, $_, 2); + } else { + ($key, $value) = ($_, ''); + } + $key =~ s/^\s*(.*?)\s*$/$1/; + $key = lc $key; + $key =~ tr/ \t/ /s; + if ($key !~ /^[a-z0-9_ ]+$/) { + return "Config file line $. key '$key' has invalid characters"; + } + $value =~ s/^\s*(.*?)\s*$/$1/; + return {'key' => $key, 'value' => $value}; +} + +sub import_settings ($) { + my ($s_ref) = @_; + $::PASSWORD = $s_ref->{'password'}; + $::PORT = $s_ref->{'port'}; + $::BASE_URL = $s_ref->{'base url'}; + $::IMG_URL = $::BASE_URL . '/images'; + $::ORGANIZATION = $s_ref->{'organization'}; + $::LONG_NAME = $s_ref->{'long name'}; + $::READ_ONLY = $s_ref->{'read only'}; + $::DATA_ID = $s_ref->{'data id'}; + $::FIRST_DAY = $s_ref->{'first day'}; + $::DATE_FORMAT = $s_ref->{'date format'}; + $::CLOCK = $s_ref->{'clock'}; + $::SCREEN_RESOLUTION = $s_ref->{'screen resolution'}; + + $::MY_URL = $::BASE_URL . "/" . (defined $::USER ? $::USER.'/index.cgi' : 'index.cgi'); + $::MY_URL_XUSER = $::BASE_URL . "/" . (defined $::USER ? 'X_USER_X/index.cgi' : 'index.cgi'); + $::REMOTE_LANG = $s_ref->{'remote lang'} || "en"; + $::REMOTE_MAIL = $s_ref->{'remote mail'} || "root"; + $::REMOTE_CAL = $s_ref->{'remote cal'} || "all"; + + $::H_BODY = "\n"; + return; +} + +# Reads global config data from /etc/wcal.conf +sub read_config () { + my ($key, $value, $line, $line_ref, %global_conf, %user_confs, $cuser, $conf_ref); + my (@current_access_file, $reading_access_file); + my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks); + my ($valid_line); + + if (! open (CONF, "<$::CONF_FILE")) { + my $str1 = "can't open $::CONF_FILE for reading: $!"; + return \$str1; + } + + # Conf file must not be read/writeable by 'other' + ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat (CONF); + if ($mode & 6) { + close CONF; + my $str2 = "Configuration file $::CONF_FILE must not have read or write permission for 'other' (do 'chmod o= $::CONF_FILE)'.\n"; + return \$str2; + } + + # Empty the settings from the last run + %global_conf = (); %user_confs = (); + $conf_ref = \%global_conf; + $cuser = ''; + $reading_access_file = 0; + + # Set some defaults + $global_conf{'host'} = 'localhost'; + $global_conf{'port'} = '13134'; + $global_conf{'base url'} = '/wcal'; + $global_conf{'access file name'} = '.htaccess'; + $global_conf{'access file'} = ''; + $global_conf{'organization'} = 'My Organization'; + $global_conf{'read only'} = 'false'; + $global_conf{'first day'} = 'monday'; + $global_conf{'date format'} = 1; + $global_conf{'clock'} = '24-hour'; + $global_conf{'screen resolution'} = '1024x768'; + $global_conf{'top left corner'} = ' '; + $global_conf{'remote lang'} = 'en'; + $global_conf{'remote mail'} = 'root'; + $global_conf{'remote cal'} = 'all'; + + while () { + chomp; + $line = $_; + + # If we are reading access file, do special processing + if ($reading_access_file) { + $line =~ s/^\s*(.*?)\s*$/$1/; + if ($line =~ /^end access file$/i) { + $conf_ref->{'access file'} = join ("\n", @current_access_file) . "\n"; + $reading_access_file = 0; + } else { + push @current_access_file, $line; + } + next; + } + + # Else do the normal processing + + $line_ref = parse_config_line ($line); + next if ! $line_ref; # skip comments and empty lines + if (! ref $line_ref) { # if return value not empty, and not a reference, it's an error + close CONF; + return \$line_ref; + } + $key = $line_ref->{'key'}; + $value = $line_ref->{'value'}; + + # 'user' field starts user definition + if ($key eq 'user') { + if (! defined $global_conf{'password'}) { + close CONF; + my $str2 = "Configuration file $::CONF_FILE line $. starts user definition, but the password field hasn't yet occured (password must be given in global section before any user definitions)"; + return \$str2; + } + $cuser = $value; + $conf_ref = {}; + next; + } + + # 'end user' field ends user definition + if ($key eq 'end user') { + my ($gkey); + # Copy each global conf field into user definition if it isn't given in user definition + for $gkey (keys %global_conf) { + if (! defined $conf_ref->{$gkey}) { + $conf_ref->{$gkey} = $global_conf{$gkey}; + } + } + # Long name = user, if not explicitely given + if (! defined $conf_ref->{'long name'}) { + $conf_ref->{'long name'} = $cuser; + } + # Data id = user, if not explicitely given + if (! defined $conf_ref->{'data id'}) { + $conf_ref->{'data id'} = $cuser; + } + # Empty 'top left corner' replaced with  , to make browsers happier. + if ($conf_ref->{'top left corner'} eq '') { + $conf_ref->{'top left corner'} = ' '; + } + $user_confs{$cuser} = $conf_ref; + $cuser = ''; + $conf_ref = \%global_conf; + next; + } + + # 'access file' starts (default / user) access file definition + if ($key eq 'access file') { + if ($value) { + close CONF; + my $str3 = "Configuration file $::CONF_FILE line $. starts access file definition, but has value field"; + return \$str3; + } + @current_access_file = (); + $reading_access_file = 1; + next; + } + + $valid_line = 0; + + # Some fields can only occur in global section + if ($cuser and ($key eq 'port' or $key eq 'address')) { + close CONF; + my $str4 = "Configuration file $::CONF_FILE line $. has in user $cuser definition parameter '$key' that can only occur in global section"; + return \$str4; + } + + # And some only in user definition + if (! $cuser and ($key eq 'long name' or $key eq 'read only')) { + close CONF; + my $str5 = "Configuration file $::CONF_FILE line $. has in global section parameter '$key' that can only occur in user definition"; + return \$str5; + } + + # READ ONLY field can have only values true or false + if ($key eq 'read only') { + $value = lc $value; + if ($value ne 'true' and $value ne 'false') { + close CONF; + my $str6 = "Configuration file $::CONF_FILE line $. has invalid value for option 'read only'. Allowed values are true and false (default false)."; + return \$str6; + } + $valid_line = 1; + } + + # FIRST DAY can be only monday or sunday + elsif ($key eq 'first day') { + if ($value ne 'monday' and $value ne 'sunday') { + close CONF; + my $str7 = "Configuration file $::CONF_FILE line $. has invalid value for option 'first day'. Allowed values are monday and sunday (default monday)."; + return \$str7; + } + $value = lc $value; + $valid_line = 1; + } + + # DATE FORMAT must be between 1 and 5 + elsif ($key eq 'date format') { + if ($value < 1 or $value > 6) { + close CONF; + my $str8 = "Configuration file $::CONF_FILE line $. has invalid value for option 'date format'. Allowed values are 1 to 6 (default 1)."; + return \$str8; + } + $valid_line = 1; + } + + # PORT must be a integer number + elsif ($key eq 'port') { + if ($value !~ /^\d+$/ or $value < 1 or $value > 65535) { + close CONF; + my $str9 = "Configuration file $::CONF_FILE line $. has invalid value for option 'port'. Allowed values are integer numbers between 1 and 65535 (default 13134)."; + return \$str9; + } + $valid_line = 1; + } + + # CLOCK must be 24-hour or 12-hour + elsif ($key eq 'clock') { + $value = lc $value; + if ($value ne '24-hour' and $value ne '12-hour') { + close CONF; + my $str10 = "Configuration file $::CONF_FILE line $. has invalid value for option 'clock'. Allowed values are '24-hour' and '12-hour' (default 24-hour)."; + return \$str10; + } + $valid_line = 1; + } + + # SCREEN RESOLUTION must be 800x600 or 1024x768 + elsif ($key eq 'screen resolution') { + $value = lc $value; + if ($value ne '800x600' and $value ne '1024x768') { + close CONF; + my $str11 = "Configuration file $::CONF_FILE line $. has invalid value for option 'screen resolution'. Allowed values are '800x600' and '1024x768' (default 1024x768)."; + return \$str11; + } + $valid_line = 1; + } + + # DATABASE DIRECTORY is deprecated + elsif ($key eq 'database directory') { + print STDERR "Configuration variable 'database directory' is deprecated and won't be used."; + print STDERR "If you have database in a place other than $::DB_DIR, move it there now."; + $valid_line = 1; + } + + # OTHERS - no particular syntax required, but must be a valid key + elsif ($key eq 'base url' or $key eq 'password' or + $key eq 'organization' or $key eq 'access file name' or $key eq 'data id' or + $key eq 'long name' or $key eq 'address' or $key eq 'top left corner' or + $key eq 'lang' or $key eq 'remote lang' or $key eq 'remote mail' or $key eq 'remote cal' or $key eq 'install dir') { + $valid_line = 1; + } + + if (! $valid_line) { + close CONF; + my $str12 = "Configuration file $::CONF_FILE line $. has unknown option '$key'."; + return \$str12; + } + + $conf_ref->{$key} = $value; + } + close CONF; + if ($reading_access_file or $cuser) { + my $str13 = "End of configuration file $::CONF_FILE while reading access file or user definition"; + return \$str13; + } + + $::GLOBAL_CONF = \%global_conf; + $::USER_CONFS = \%user_confs; + + import_settings ($::GLOBAL_CONF); + + purge_cache_all (); + + return 1; +} + +# Read CGI environment and conf option from Socket (sent by the cgi-proxy) +sub read_environment () { + my ($key, $value, $user); + + while () { + chomp; + last if /^$/; + my ($key, $value); + + ($key, $value) = split (/=/, $_, 2); + $key =~ tr/\x00-\x1f\x80-\x9f//d; # filter out control characters, + $value =~ tr/\x00-\x1f\x80-\x9f//d; # including infamous NUL + if ($key eq 'QUERY_STRING') { + $::query = decode_url_encoded_data \$value; + } elsif ($key eq 'REMOTE_USER') { + $::REMOTE_USER = $value; + $ENV{REMOTE_USER} = $::REMOTE_USER; + } elsif ($key eq 'REMOTE_PASS') { + $::REMOTE_PASS = $value; + $ENV{REMOTE_PASS} = $::REMOTE_PASS; + } elsif ($key eq 'REMOTE_MAIL') { + $::REMOTE_MAIL = $value; + } elsif ($key eq 'REMOTE_CAL') { + $::REMOTE_CAL = $value; + } elsif ($key eq 'REMOTE_LANG') { + $::REMOTE_LANG = $value; + } elsif ($key =~ /^__/) { + $key = lc $key; + if ($key eq '__password') { +# print Socket "

got: '$value', correct is '$::PASSWORD'\n"; + if ($value ne $::PASSWORD) { + print Socket "

Invalid password\n"; + return 0; + } + } elsif ($key eq '__user') { + $::USER = $value; + } elsif ($key eq '__gid') { + $::GID = $value; + } else { + print Socket "

Unknown configuration option '$key'\n"; + return 0; + } + } + } +# print Socket "

$::MY_URL\n"; + + # Make sure we received all the mandatory options + if (! $::USER) { + print Socket "

User not sent by cgi-proxy\n"; + return 0; + } + return 1; +} + +sub siginthandler { + exit (1); +} + +sub sighuphandler { + my ($res); + + return; # do nothing, this is just a kludge that doesn't work + + $::GID = 60; + $res = read_config (); + if (ref $res) { + print STDERR "

Re-reading configureation failed: ", $$res, " - configuration not changed.\n"; + } else { + $res = create_users (); + if ($res) { + print STDERR "

Error creating new users: $res\n"; + } + } + + return; +} + +sub check_and_set_first_day () { + my ($gen); + + $gen = read_general (); + if (! ref $gen) { + print "error: $gen\n"; + exit (1); + } + if (defined $gen->{'first day'} and $gen->{'first day'} ne $::FIRST_DAY) { + print <{'first day'}) { + my ($err); + + $gen->{'first day'} = $::FIRST_DAY; + $err = write_general ($gen); + if ($err) { + print "error: $err\n"; + exit (1); + } + } + return; +} + +sub show_links () { + print Socket $::H_BODY; + my @cals = split(/\n/, $::query->{'cal'}); + my $st = ""; + if ($::query->{'year'} && $::query->{'week'}) + { + $st = '&year='.$::query->{'year'}.'&week='.$::query->{'week'}; + } + print Socket '

'.$::LONG_NAME."

\n"; + print Socket "\n"; + my $cal; + my $i = 1; + foreach $cal (@cals) + { + print Socket '
'.%{$::USER_CONFS->{$cal}}->{'long name'}."\n"; + } + print Socket "
"; +} + +sub clear_events { + my $week = shift; + my $year = shift; + + if ($week) + { + my $file; + opendir(DIR, $::DB_DIR); + while ($file = readdir(DIR)) + { + if ($file =~ /^w\-$::USER\-(\d\d\d\d)(\d\d)\.db$/) + { + if (($1 < $year) || (($1 == $year) && ($2 <= $week))) + { + unlink($::DB_DIR."/".$file); + } + + } + } + closedir(DIR); + opendir(DIR, $::DB_DIR."/cache"); + while ($file = readdir(DIR)) + { + if ($file =~ /^w\-$::USER\-(\d\d\d\d)(\d\d)\.html$/) + { + if (($1 < $year) || (($1 == $year) && ($2 <= $week))) + { + unlink($::DB_DIR."/cache/".$file); + } + + } + } + closedir(DIR); + + } + else + { + system('\rm '.$::DB_DIR.'/*'.$::USER.'*'); + system('\rm '.$::DB_DIR.'/cache/*'.$::USER.'*'); + } +} + +sub all_events () { + opendir(DIR, $::DB_DIR); + my @events; + my $event; + my $evw; + my $i; + my $key; + my $val; + my $file; + while ($file = readdir(DIR)) + { + if ($file =~ /^w\-$::USER\-(\d\d\d\d)(\d\d)\.db$/) + { + $evw = read_events('n', $1, $2); + for ($i=0; $i<7; $i++) + { + while (($key, $val) = each(%{@{$evw}[$i]})) + { + foreach $event (@{$val}) + { + $event->{hour} = $key; + push @events, $event; + } + } + } + } + } + + return \@events; +} + +sub show_purge () { + my $str; + my $week = $::query->{'week'}; + my $year = $::query->{'year'}; + + print Socket $::H_BODY; + print Socket "\n"; + print Socket "\n"; + print Socket "
"; + print Socket __("Transfert Palm Pilot"); + print Socket "
\n"; + if ($::query->{'purge'}) + { + clear_events($week, $year); + $str = sprintf(__("Agenda %s purgé jusqu'à la semaine %d de l'année %d"), ''.$::LONG_NAME.'', $week, $year); + print Socket "

$str

\n"; + } + elsif ($::query->{'cancel'}) + { + $str = sprintf(__("Purge de l'agenda %s annulée"), ''.$::LONG_NAME.''); + print Socket "

$str

\n"; + } + else + { + print Socket "\n"; + print Socket "\n"; + print Socket "\n"; + print Socket "\n"; + print Socket "{'week'}>\n"; + print Socket "{'year'}>\n"; + my $submit_value = __("Oui"); + print Socket "

".sprintf(__("Voulez-vous réellement purger l'agenda %s jusqu'à la semaine %d de l'année %d ?"), $::LONG_NAME, $::query->{'week'}, $::query->{'year'})."\n"; + print Socket "

\n"; + $submit_value = __("Non"); + print Socket "\n"; + print Socket "
\n"; + } + print Socket "
\n"; +} + +sub checkAlarm +{ + my $event = shift; + my $cal = shift; + my $hour = shift; + my $day = shift; + my $et = shift; + my $week = shift; + my $year = shift; + + my @types = ("", "minutes", "hours" , "days"); + # compute start date + my $now=&ParseDate("today"); + my $begin; + my $end; + my $alarm; + my $strdate; + my @format; + my @tab; + my $chk = 1; + my $min = $event->{min}; + my $notime; + if ($hour == -1) + { + $hour = 0; + $min = 0; + $notime = 1; + } + if ($et eq "w") + { + $begin = &DateCalc(&ParseDate(sprintf("%04d-w%02d-%d", $event->{startyear}, $event->{startweek}, $event->{startwday}+1)), sprintf("+%dhours +%dminutes", $hour, $min)); + $alarm = &DateCalc($begin, sprintf("-%d%s", $event->{alval}, $types[$event->{altype}])); + if ($alarm gt $now) + { + return 0; + } + } + elsif ($et eq "rw") + { + $alarm = &DateCalc($now, sprintf("+%d%s", $event->{alval}, $types[$event->{altype}])); + @format = ("%w"); + my $wday = &UnixDate($alarm, @format); + $wday = 1 + $event->{startwday} - $wday; + if ($wday > 0) + { + return 0; + } + elsif ($wday < 0) + { + $begin = &DateCalc($alarm, $wday."days"); + } + else + { + $begin = $alarm; + } + $begin = substr($begin,0,8).sprintf("%02d:%02d:00", $hour, $min); + if ($begin gt $alarm || ($event->{alchk} && $begin le $event->{alchk})) + { + return 0; + } + + $chk = $begin; + } + elsif ($et eq "rm") + { + $alarm = &DateCalc($now, sprintf("+%d%s", $event->{alval}, $types[$event->{altype}])); + @format = ("%d"); + my $mday = &UnixDate($alarm, @format); + $mday = $day - $mday; + if ($mday > 0) + { + return 0; + } + elsif ($mday < 0) + { + $begin = &DateCalc($alarm, $mday."days"); + } + else + { + $begin = $alarm; + } + $begin = substr($begin,0,8).sprintf("%02d:%02d:00", $hour, $min); + if ($begin gt $alarm || ($event->{alchk} && $begin le $event->{alchk})) + { + return 0; + } + + $chk = $begin; + } + + my $email = "root"; + my $head; + my $body; + $email = $::REMOTE_MAIL; + $head = sprintf(__("Agenda %s"), %{$::USER_CONFS->{$cal}}->{'long name'}); + + if ($notime) + { + @format = ("%d","%m","%y"); + @tab = &UnixDate($begin, @format); + $strdate = sprintf("%02d/%02d/%02d", @tab); + } + else + { + @format = ("%d","%m","%y","%H","%M"); + @tab = &UnixDate($begin, @format); + $strdate = sprintf("%02d/%02d/%02d %02d:%02d", @tab); + $end = &DateCalc($begin, sprintf("+%dminutes", $event->{lengthmin})); + @format = ("%H","%M"); + @tab = &UnixDate($end, @format); + $strdate .= sprintf(" - %02d:%02d", @tab); + } + my $data = $event->{data}; + $data =~ s/
/\n\t /mg; + + $body = $head."\n"; + + $head .= " : ".$event->{title}."\n"; + $body .= "************************************************************\n"; + $body .= __("Date")."\t: ".$strdate."\n"; + $body .= __("Titre")."\t: ".$event->{title}."\n"; + if ($data) + { + $body .= __("Note")."\t: ".$data."\n"; + } + $body .= "************************************************************\n"; + system('mail -s "'.$head.'" '.$email." <); + $/ = $old_slash; + close F; + shift @$refs; + $day = 0; + foreach $devents (@$refs) + { + while (($hour,$events) = each (%$devents)) + { + foreach $event (@$events) + { + if ($event->{alval} && !$event->{strike} && (!$event->{alchk} || $event->{alchk} ne "1")) + { + if ($chk = checkAlarm($event, $cal, $hour, $day, $et, $week, $year)) + { + $::USER = $cal; + $::DATA_ID = $cal; + print Socket "$event->{'title'} ($event->{'id'}) checked\n"; + $err = remove_events( + $event->{'rt'}, + $event->{'startyear'}, + $event->{'startweek'}, + $event->{'startwday'}, + $event->{'duration'}, + $hour, + $event->{'id'}); + if (!$err) + { + $err = add_events( + $event->{'rt'}, + $event->{'startyear'}, + $event->{'startweek'}, + $event->{'startwday'}, + $event->{'duration'}, + $hour, + $event->{'id'}, + $event->{'min'}, + $event->{'lengthmin'}, + $event->{'title'}, + $event->{'data'}, + $event->{'alval'}, + $event->{'altype'}, + $chk); + } + } + } + } + } + $day++; + } + } + } + } + closedir(DIR); +} + +sub list_users () { + my (@users, $user); + + my $str = sprintf(__("Agendas %s"), $::ORGANIZATION); + print Socket "$str\n"; + print Socket $::H_BODY; + + if (! chdir ($::DIRECTORY)) { + print Socket "

Can't change into directory $::DIRECTORY: $!"; + print Socket "

Make sure it exists, and its permissions are as explained in file INSTALL of the Wcal distribution."; + exit (1); + } + + print Socket "

$str

"; + print Socket sprintf(__("Cliquez sur un calendrier ou sélectionnez les calendriers pour la %s"), ''.__("Fusion").""); + print Socket '
'."\n"; + print Socket "

    \n"; + @users = sort (keys (%$::USER_CONFS)); + for $user (@users) { + my ($desc); + $desc = $::USER_CONFS->{$user}->{'long name'}; + print Socket ' '; + print Socket "$desc
    \n"; + } + print Socket "
\n"; + print Socket '
'."\n"; + print Socket "\n"; + return; +} + +sub main () { + my ($config_result, $last_run, $last_conf, $res); + + $SIG{HUP} = $SIG{PIPE} = 'IGNORE'; + $SIG{ALRM} = \&purge_cache_all; + $ENV{'PATH'} = '/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin'; # To pass taint checks + + # + # Make sure the $::DIRECTORY variable is set + # + + if ($::DIRECTORY =~ /^_X_DIRECTOR/) { + print "You haven't set the variable $::DIRECTORY at top of wcald.\n"; + print "It is automatically set by Makefile. If you're installing manually,\n"; + print "set the variable the name directory you installed wcald to.\n"; + exit 1; + } + + # + # Refuse to run as root + # + if ($> == 0) { + die "Won't run as root.\n"; + } + + # + # Check some file permissions + # + if (! -d $::DB_DIR or ! -w $::DB_DIR or (sprintf "%04o", ((stat ($::DB_DIR))[2]) & 07777) ne '0700') { + die "$::DB_DIR must exist, be writable and have mode 0700.\n"; + } + if (! -d "$::DB_DIR/cache") { + mkdir ("$::DB_DIR/cache", 0700) or die "mkdir (\"$::DB_DIR/cache\", 0700): $!\n"; + } + if (! -d $::DIRECTORY or ! -w $::DIRECTORY or (sprintf "%04o", ((stat ($::DIRECTORY))[2]) & 07777) ne '0750') { + die "$::DIRECTORY must exist, be writable and have mode 0750.\n"; + } + + # + # Read in wcal.msg + # + open(MSG, $::MSG_FILE) or die "Can't open $::MSG_FILE for reading: $!"; + my $old_slash = $/; + undef $/; + $::msgs = eval (); + $/ = $old_slash; + close(MSG); + + # + # Read configuration and create users + # + $config_result = read_config (); + if (ref $config_result) { + die "$0: error: ", $$config_result, "\n"; + } + $res = create_users (); + if ($res) { + die "$0: error creating users: $res\n"; + } + + # + # Make sure current database (if one exists) uses same 'first day' + # parameter as we're currently using. + # + check_and_set_first_day (); + + # + # Set the date constants + # + if ($::FIRST_DAY eq 'monday') { + @::weekdays = qw (Lundi Mardi Mercredi Jeudi Vendredi Samedi Dimanche); + @::weekdays_short = qw (lun mar mer jeu ven sam dim); + @::weekdays2 = qw (Monday Tuesday Wednesday Thursday Friday Saturday Sunday); + &Date_Init ('FirstDay=1'); + } else { + @::weekdays = qw (Dimanche Lundi Mardi Mercredi Jeudi Vendredi Samedi); + @::weekdays_short = qw (dim lun mar mer jeu ven sam); + @::weekdays2 = qw (Sunday Monday Tuesday Wednesday Thursday Friday Saturday); + &Date_Init ('FirstDay=7'); + } + @::months = qw (Erreur Janvier Février Mars Avril Mai Juin Juillet Août Septembre Octobre Novembre Décembre); + + init_socket (); + + $last_run = (localtime(time))[7]; + $last_conf = 0; + + while (1) { + my ($query_type, $pid, $paddr, $cgi, %user, $now_run, $now_conf); + + $SIG{'INT'} = $SIG{'TERM'} = \&siginthandler; + $SIG{'HUP'} = \&sighuphandler; + $paddr = accept (Socket, SSocket); + $SIG{'INT'} = $SIG{'TERM'} = $SIG{'HUP'} = 'IGNORE'; + + # + # Purge cache when day changes + # + $now_run = (localtime(time))[7]; + if ($now_run != $last_run) { + $last_run = $now_run; + purge_cache_newday (); + } + + # + # Start outputing HTML + # + print Socket $::HTTP_HEADER; + print Socket "\n"; + + # + # Read CGI environment and the config options that cgi-proxy passes to us + # Includes QUERY_STRING (decoded into $::query), + # __password (checked) and __user (stored in $::USER) + # + if (! read_environment ()) { + next; + } + + # + # Re-read config if it has changed + # + $now_conf = (stat($::CONF_FILE))[9]; + if ($now_conf != $last_conf) { + $last_conf = $now_conf; + $config_result = read_config (); + if (ref $config_result) { + while () { chomp; last if /^$/; }; # Read environment to make cgi-proxy happy + print Socket $::H_BODY; + print Socket "

Configuration has changed.

"; + print Socket "

Re-reading configureation failed: ", $$config_result, " - configuration not changed.\n"; + print Socket "

Click me to go to the main view\n"; + print Socket "\n"; + next; + } else { + $res = create_users (); + if ($res) { + print Socket $::H_BODY; + print Socket "

Configuration has changed.

"; + print Socket "

Error creating new users: $res\n"; + print Socket "

Click me to go to the main view\n"; + print Socket "\n"; + next; + } + # fall through: just serve the request + } + } + + # show a list of users + if (!$::query->{'cal'} && $::USER eq $::PATH_BASENAME) { + list_users (); + next; + } + + # + # Dispatch the operation + # + + # Make sure the users exists + if ($::USER_CONFS->{$::USER}) { + import_settings ($::USER_CONFS->{$::USER}); + $::DATA_IDS = 0; + } + else # fusion + { + $::READ_ONLY = 'true'; + $::DATA_ID = 0; + $::DATA_IDS = $::query->{'cal'}; + $::LONG_NAME = __('Fusion'); + $::MY_URL = $::BASE_URL.'/fusion.cgi'; + $::MY_URL_XUSER = $::BASE_URL.'/fusion.cgi'; + } + + $::query->{'u'} = $::USER; + $query_type = $::query->{'t'} || ''; + if ($query_type eq 'redraw' or $query_type eq '') { + redraw (); + } elsif ($query_type eq 'week') { + show_week (); + } elsif ($query_type eq 'day') { + show_day (); + } elsif ($query_type eq 'event') { + show_event (); + } elsif ($query_type eq 'copy_event') { + copy_event (); + } elsif ($query_type eq 'add_edit') { + add_edit_event_ask () if check_write_access (); + } elsif ($query_type eq 'add_edit_confirm') { + add_edit_event_confirm () if check_write_access (); + } elsif ($query_type eq 'remove') { + remove_event_confirm () if check_write_access (); + } elsif ($query_type eq 'xxx_event_error') { + xxx_event_error (); + } elsif ($query_type eq 'event_list') { + show_event_list (); + } elsif ($query_type eq 'views') { + show_other_views (); + } elsif ($query_type eq 'links') { + show_links (); + } elsif ($query_type eq 'purge') { + show_purge (); + } elsif ($query_type eq 'checkalarm') { + check_alarm (); + } else { + print Socket "

Unknown command '$query_type'.

\n"; + } + undef $::query; + + } continue { + print Socket "\n"; + close Socket; + } + + # not reached +} + +main(); diff --git a/webuser b/webuser new file mode 100755 index 0000000..83f6c06 --- /dev/null +++ b/webuser @@ -0,0 +1,66 @@ +#!/bin/bash + +set -e + +ACTION=$1 +USER=$2 +WEBSITE=$3 + +USERROOT=/home +WEBROOT=/var/www +LOGROOT=/var/log/apache +WEBALIZER_OPTIONS="-q" + +function webdir() { echo $1/VIRTUAL/$2; } +function userwebdir() { echo $1/$2/web_$3; } +#function logdir() { echo ${LOGROOT}/${WEBSITE}; } +#function logfiles() { $(ls -r $LOGDIR/*-access*.gz) $(ls -r $LOGDIR/access*.??.gz) $(ls -r $LOGDIR/access*.?.gz) $(ls -r $LOGDIR/access*.?); } +function logfiles() { ls $1/$2/????.??.??; } +function host() { cat $1/VIRTUAL/$2/hostname || exit 1; } +function domain() { cat $1/VIRTUAL/$2/domainname || exit 1; } +function thishost() { cat /etc/mailhost; } + +# The above can be overridden in /etc/local/www + +. /etc/local/www || exit 1 + +function apache_virtualhost() { + echo "\ + + ServerName $WEBSITE + ServerAdmin $USER@$(thishost) + DocumentRoot $(userwebdir ${USERROOT} ${USERID} ${WEBSITE}) + user $USER + group $USER + +" > /etc/apache/virtual/$WEBSITE +} + +test "x$WEBSITE" = "x" && $ACTION="" + +case "$ACTION" in + add) + echo "Adding web user $USER: " + adduser $USER || exit 1 + mkdir $(userwebdir ${USERROOT} ${USERID} ${WEBSITE}) + user-init $USER + $(apache_virtualhost) + ;; + attach) + echo "Attaching website to existing user $USER: " + mkdir $(userwebdir ${USERROOT} ${USERID} ${WEBSITE}) + user-init $USER + $(apache_virtualhost) + ;; + *) + echo "Usage:" + echo "Adding a new user:" + echo " $(basename $0) add " + echo + echo "Attaching a website to an existing user:" + echo " $(basename $0) attach " + exit 1 + ;; +esac + +exit 0 diff --git a/webuser-refresh-all b/webuser-refresh-all new file mode 100755 index 0000000..9cfad4e --- /dev/null +++ b/webuser-refresh-all @@ -0,0 +1,7 @@ +#!/bin/sh + +for WEBSITE in $(ls /web) +do + test $WEBSITE != "lost+found" \ + && /usr/local/sbin/webuser refresh $WEBSITE +done -- cgit v1.2.3