Index: bian/.gitignore
===================================================================
--- debian/.gitignore	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
+++ 	(revision )
@@ -1,1 +1,0 @@
-*~
Index: bian/changelog
===================================================================
--- debian/changelog	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
+++ 	(revision )
@@ -1,17 +1,0 @@
-gutenbach-remctl (1.02) unstable; urgency=low
-
-  * Depend on version 2.0 or greater of gutenbach
-
- -- Jessica Hamrick <jhamrick@mit.edu>  Thu, 13 May 2010 17:42:53 -0400
-
-gutenbach-remctl (1.01) unstable; urgency=low
-
-  * Just say the new volume, not who changed it.
-
- -- Jessica Hamrick <jhamrick@mit.edu>  Mon, 14 Dec 2009 22:04:43 -0500
-
-gutenbach-remctl (1.0) unstable; urgency=low
-
-  * Initial release.
-
- -- Jessica Hamrick <jhamrick@mit.edu>  Mon, 14 Dec 2009 22:03:44 -0500
Index: bian/compat
===================================================================
--- debian/compat	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
+++ 	(revision )
@@ -1,1 +1,0 @@
-5
Index: bian/control
===================================================================
--- debian/control	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
+++ 	(revision )
@@ -1,13 +1,0 @@
-Source: gutenbach-remctl
-Section: sound
-Priority: extra
-Maintainer: Jessica Hamrick <jhamrick@mit.edu>
-Build-Depends: cdbs (>= 0.4.23-1.1), debhelper (>= 5)
-Standards-Version: 3.8.0
-
-Package: gutenbach-remctl
-Architecture: all
-Pre-Depends: debconf
-Depends: ${shlibs:Depends}, ${misc:Depends}, gutenbach (>= 2.0), alsa-utils, cd-discid, remctl-server
-Description: Allows a remote user to control the volume, view
- the current status of the queue, or play tracks from a cd.
Index: bian/copyright
===================================================================
--- debian/copyright	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
+++ 	(revision )
@@ -1,1 +1,0 @@
-Copyright 2009 Jessica Hamrick
Index: bian/gutenbach-remctl.dirs
===================================================================
--- debian/gutenbach-remctl.dirs	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
+++ 	(revision )
@@ -1,3 +1,0 @@
-usr/lib/gutenbach
-usr/lib/gutenbach/remctl
-etc/remctl/conf.d
Index: bian/gutenbach-remctl.init
===================================================================
--- debian/gutenbach-remctl.init	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
+++ 	(revision )
@@ -1,114 +1,0 @@
-#!/bin/bash
-### BEGIN INIT INFO
-# Provides:          gutenbach-remctl
-# Required-Start:    $remote_fs $syslog
-# Required-Stop:     $remote_fs $syslog
-# Default-Start:     2 3 4 5
-# Default-Stop:      0 1 6
-# Short-Description: Starts remctld.
-# Description:       Starts remctld.
-### END INIT INFO
-
-case "$1" in
-    start)
-	if [ -e /var/run/gutenbach/remctld.pid ]; then
-	    pid=$(cat /var/run/gutenbach/remctld.pid)
-	    if [ -n "$pid" ]; then
-		kill -0 "$pid"
-		if [ "$?" == 0 ]; then
-		    echo "Ending remctld..." >&2
-		    kill "$pid" || true
-		fi
-	    fi
-	fi
-
-	pid=$(sudo lsof -i :4373 | grep remctld | sed s/remctld\ // | sed s/\ .*//)
-	if [ -n "$pid" ]; then
-	    kill -0 "$pid"
-	    if [ "$?" == 0 ]; then
-		echo "Ending remctld socket binding..." >&2
-		kill "$pid" || true
-	    fi
-	fi
-
-	echo "Starting remctld..." >&2
-	remctld -m -P /var/run/gutenbach/remctld.pid &
-	;;
-
-    stop)
-	if [ -e /var/run/gutenbach/remctld.pid ]; then
-	    pid=$(cat /var/run/gutenbach/remctld.pid)
-	    if [ -n "$pid" ]; then
-		kill -0 "$pid"
-		if [ "$?" == 0 ]; then
-		    echo "Ending remctld..." >&2
-		    kill "$pid" || true
-		fi
-	    fi
-	fi
-
-	pid=$(sudo lsof -i :4373 | grep remctld | sed s/remctld\ // | sed s/\ .*//)
-	if [ -n "$pid" ]; then
-	    kill -0 "$pid"
-	    if [ "$?" == 0 ]; then
-		echo "Ending remctld socket binding..." >&2
-		kill "$pid" || true
-	    fi
-	fi
-	;;
-
-    restart)
-	if [ -e /var/run/gutenbach/remctld.pid ]; then
-	    pid=$(cat /var/run/gutenbach/remctld.pid)
-	    if [ -n "$pid" ]; then
-		kill -0 "$pid"
-		if [ "$?" == 0 ]; then
-		    echo "Ending remctld..." >&2
-		    kill "$pid" || true
-		fi
-	    fi
-	fi
-
-	pid=$(sudo lsof -i :4373 | grep remctld | sed s/remctld\ // | sed s/\ .*//)
-	if [ -n "$pid" ]; then
-	    kill -0 "$pid"
-	    if [ "$?" == 0 ]; then
-		echo "Ending remctld socket binding..." >&2
-		kill "$pid" || true
-	    fi
-	fi
-
-	echo "Starting remctld..." >&2
-	remctld -m -P /var/run/gutenbach/remctld.pid &
-	;;
-
-    force-reload)
-	if [ -e /var/run/gutenbach/remctld.pid ]; then
-	    pid=$(cat /var/run/gutenbach/remctld.pid)
-	    if [ -n "$pid" ]; then
-		kill -0 "$pid"
-		if [ "$?" == 0 ]; then
-		    echo "Ending remctld..." >&2
-		    kill "$pid" || true
-		fi
-	    fi
-	fi
-
-	pid=$(sudo lsof -i :4373 | grep remctld | sed s/remctld\ // | sed s/\ .*//)
-	if [ -n "$pid" ]; then
-	    kill -0 "$pid"
-	    if [ "$?" == 0 ]; then
-		echo "Ending remctld socket binding..." >&2
-		kill "$pid" || true
-	    fi
-	fi
-
-	echo "Starting remctld..." >&2
-	remctld -m -P /var/run/gutenbach/remctld.pid &
-	;;
-
-    *)
-	echo "Usage: $0 {start|stop|restart}"
-	exit 1
-	;;
-esac
Index: bian/gutenbach-remctl.install
===================================================================
--- debian/gutenbach-remctl.install	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
+++ 	(revision )
@@ -1,2 +1,0 @@
-debian/lib/for-gutenbach/* /usr/lib/gutenbach/remctl/
-debian/lib/for-remctl/* /etc/remctl/conf.d/
Index: bian/gutenbach-remctl.postinst
===================================================================
--- debian/gutenbach-remctl.postinst	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
+++ 	(revision )
@@ -1,52 +1,0 @@
-#!/bin/sh
-# postinst script for gutenbach-remctl
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-#        * <postinst> `configure' <most-recently-configured-version>
-#        * <old-postinst> `abort-upgrade' <new version>
-#        * <conflictor's-postinst> `abort-remove' `in-favour' <package>
-#          <new-version>
-#        * <postinst> `abort-remove'
-#        * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
-#          <failed-install-package> <version> `removing'
-#          <conflicting-package> <version>
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-case "$1" in
-    configure|reconfigure)
-	chmod +x /usr/lib/gutenbach/remctl/cd-cddb
-	chmod +x /usr/lib/gutenbach/remctl/cd-cddb-get
-	chmod +x /usr/lib/gutenbach/remctl/cd-play
-	chmod +x /usr/lib/gutenbach/remctl/status-clear
-	chmod +x /usr/lib/gutenbach/remctl/status-get
-	chmod +x /usr/lib/gutenbach/remctl/volume-down
-	chmod +x /usr/lib/gutenbach/remctl/volume-get
-	chmod +x /usr/lib/gutenbach/remctl/volume-help
-	chmod +x /usr/lib/gutenbach/remctl/volume-is-muted
-	chmod +x /usr/lib/gutenbach/remctl/volume-mute
-	chmod +x /usr/lib/gutenbach/remctl/volume-set
-	chmod +x /usr/lib/gutenbach/remctl/volume-show
-	chmod +x /usr/lib/gutenbach/remctl/volume-up
-	chmod +x /usr/lib/gutenbach/remctl/volume-zephyr
-    ;;
-
-    abort-upgrade|abort-remove|abort-deconfigure)
-    ;;
-
-    *)
-        echo "postinst called with unknown argument \`$1'" >&2
-        exit 1
-    ;;
-esac
-
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts.
-
-#DEBHELPER#
-
-exit 0
Index: bian/gutenbach-remctl.postrm
===================================================================
--- debian/gutenbach-remctl.postrm	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
+++ 	(revision )
@@ -1,49 +1,0 @@
-#!/bin/sh
-# postrm script for gutenbach-remctl
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-#        * <postrm> `remove'
-#        * <postrm> `purge'
-#        * <old-postrm> `upgrade' <new-version>
-#        * <new-postrm> `failed-upgrade' <old-version>
-#        * <new-postrm> `abort-install'
-#        * <new-postrm> `abort-install' <old-version>
-#        * <new-postrm> `abort-upgrade' <old-version>
-#        * <disappearer's-postrm> `disappear' <overwriter>
-#          <overwriter-version>
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-
-case "$1" in
-    purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
-	if [ "$1" = "purge" ]; then
-	    if [ -e /usr/lib/gutenbach/remctl ]; then
-		echo "Removing /usr/lib/gutenbach/remctl/ ..." >&2
-		rm -r  /usr/lib/gutenbach/remctl
-	    fi
-
-	    if [ -e /etc/remctl/conf.d ]; then
-		echo "Removing /etc/remctl/conf.d ..." >&2
-		rm /etc/remctl/conf.d
-		mkdir /etc/remctl/conf.d
-	    fi
-     	fi
-    ;;
-
-    *)
-        echo "postrm called with unknown argument \`$1'" >&2
-        exit 1
-    ;;
-esac
-
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts.
-
-#DEBHELPER#
-
-exit 0
Index: bian/rules
===================================================================
--- debian/rules	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
+++ 	(revision )
@@ -1,3 +1,0 @@
-#!/usr/bin/make -f
-
-include /usr/share/cdbs/1/rules/debhelper.mk
Index: tenbach-remctl/README
===================================================================
--- gutenbach-remctl/README	(revision cc0d7053781e98c7ca1c2ab048d9941ddb3866c0)
+++ 	(revision )
@@ -1,10 +1,0 @@
-gutenbach-remctl is a package which provides a remctl configuration,
-enabling users to remotely adjust settings such as volume.
-
-Questions and comments should be directed to gutenbach@mit.edu
-
-TODO:
-- need to add logging to see what's going wrong if a command fails
-- figure out how to determine the correct channel/mixer
-- better yet, figure out how to use pulse, and don't have to worry
-  about what the correct channel or mixer is at all
Index: tenbach-remctl/cd
===================================================================
--- gutenbach-remctl/cd	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ 	(revision )
@@ -1,3 +1,0 @@
-cd cddb     /usr/lib/gutenbach/remctl/cd-cddb     ANYUSER
-cd cddb-get /usr/lib/gutenbach/remctl/cd-cddb-get ANYUSER
-cd play     /usr/lib/gutenbach/remctl/cd-play     ANYUSER
Index: tenbach-remctl/cd-cddb
===================================================================
--- gutenbach-remctl/cd-cddb	(revision 6bb3ffb249dc254896b2f89be72367ae76fad5e9)
+++ 	(revision )
@@ -1,20 +1,0 @@
-#!/bin/sh
-PATH="$(dirname $0):$PATH"
-
-die () {
-    echo "$@";
-    exit 1;
-}
-
-CACHE=$(cd-cddb-get) || die "Unable to fetch CDDB information."
-
-. "$CACHE"
-
-echo "Artist: $DARTIST";
-echo "Album: $DALBUM";
-echo "Tracks: $TRACKS";
-
-for i in $(seq 1 $TRACKS); do
-    echo -n "Track $i: "
-    eval 'echo $TRACK'$i
-done
Index: tenbach-remctl/cd-cddb-get
===================================================================
--- gutenbach-remctl/cd-cddb-get	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ 	(revision )
@@ -1,37 +1,0 @@
-#!/bin/sh
-PATH="$(dirname $0):$PATH"
-
-CDDBURL="http://freedb.freedb.org/~cddb/cddb.cgi"
-MYUSER="gutenbach"
-MYHOST=`hostname`".mit.edu"
-
-DISCID=$(cd-discid /dev/cdrom 2>/dev/null) || exit 1
-
-set -- $DISCID
-
-ID="$1"
-TRACKS="$2"
-
-mkdir -p /var/cache/gutenbach
-
-CACHEFILE="/var/cache/gutenbach/$ID"
-
-if [ ! -e "$CACHEFILE" ]; then
-
-    CD=$(cddb-tool query "$CDDBURL" 3 "$MYUSER" "$MYHOST" $DISCID) \
-	|| exit 2;
-
-    file=$(tempfile)
-
-    cddb-tool read "$CDDBURL" 3 "$MYUSER" "$MYHOST" \
-	$(echo "$CD" | head -1 | awk '{print $2 " " $3}') > "$file"  \
-	|| exit 3;
-
-    echo "TRACKS=$TRACKS"   >  "$CACHEFILE"
-    cddb-tool parse "$file" >> "$CACHEFILE"
-
-    rm "$file"
-fi
-
-echo "$CACHEFILE"
-exit 0;
Index: tenbach-remctl/cd-play
===================================================================
--- gutenbach-remctl/cd-play	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ 	(revision )
@@ -1,37 +1,0 @@
-#!/bin/sh
-PATH="$(dirname $0):$PATH"
-
-CDDB=
-
-CACHE=$(cd-cddb-get)
-
-printername=$(/usr/lib/gutenbach/gutenbach-get-config queue)
-
-if [ "$?" = 0 ]; then
-    CDDB=1
-fi
-
-. "$CACHE"
-
-play_one () {
-    T=$1
-    if [ -n "$CDDB" ]; then
-	DESC=" - "$(eval 'echo $TRACK'$T)
-    fi
-
-    echo "cdda://$(($T - 1))" | mit-lpr -P"$queue"@localhost -J "Track $T$DESC";
-    echo "Queued Track $T$DESC";
-}
-
-TOPLAY="$2"
-
-if [ -z "$TOPLAY" -o "$TOPLAY" = "all" ]; then
-    for i in $(seq 1 "$TRACKS"); do
-	play_one $i
-    done
-else
-    shift;
-    for i in "$@"; do
-	play_one $i
-    done
-fi
Index: tenbach-remctl/status
===================================================================
--- gutenbach-remctl/status	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ 	(revision )
@@ -1,2 +1,0 @@
-status get   /usr/lib/gutenbach/remctl/status-get   ANYUSER
-status clear /usr/lib/gutenbach/remctl/status-clear ANYUSER
Index: tenbach-remctl/status-clear
===================================================================
--- gutenbach-remctl/status-clear	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ 	(revision )
@@ -1,3 +1,0 @@
-#!/bin/sh
-PATH="$(dirname $0):$PATH"
-> /var/run/gutenbach/status
Index: tenbach-remctl/status-get
===================================================================
--- gutenbach-remctl/status-get	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ 	(revision )
@@ -1,3 +1,0 @@
-#!/bin/sh
-PATH="$(dirname $0):$PATH"
-cat /var/run/gutenbach/status
Index: tenbach-remctl/voldaemon.c
===================================================================
--- gutenbach-remctl/voldaemon.c	(revision 9c6635060a1adeb1457077d8413e99210ccd878d)
+++ 	(revision )
@@ -1,319 +1,0 @@
-/*
- * $Id: voldaemon.c,v 1.1 2008-09-27 08:49:10 root Exp $
- * $Source: /tmp/tmp.UFBNno9997/RCS/voldaemon.c,v $
- */
-
-#include <stdio.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <sys/ioctl.h>
-#include <sys/time.h>
-#include <errno.h>
-#include <assert.h>
-#include <netinet/in.h>
-
-
-#define LISTEN_PORT 8930
-#define MULTI_PORT 8931
-#define MULTI_ADDR "224.0.1.20"
-
-/* Block queries from off this subnet net 18.*/
-#define OUR_SUBNET      187
-
-typedef struct volmessage{
-  char A;
-  char request;
-  char value;
-  unsigned char ip_3;
-  unsigned char ip_2;
-  unsigned char ip_1;
-  unsigned char ip_0;
-  char ZE;
-} volmessage;
-
-#define MESSAGE_SIZE (sizeof(volmessage))
-
-/* The client makes requests using the following packets.  Responses
-   to queries and status reports from the server follow the same
-   pattern
-
-  Request/Response
-
-  'AVxiiiiZ'     Volume
-  'AMxiiiiZ'     Mute
-  'AQQiiiiZ'     Query 
-
-  Response Only
-  'AVeiiiiE'     Volume Set Error 
-  'AMeiiiiE'     Mute Set Error 
-  'AEeiiiiE'     Unknown error 
-
-   e=1  Permission denied (invalid IP)
-   e=2  Invalid request (Bad Packet)
-   
-*/
-
-void message(char *m){
-  write(STDERR_FILENO,m,strlen(m));
-}
-
-void abortm(char *m){
-  message(m);
-  message("Errno = ");
-  message(strerror(errno));
-  message("\n");
-  exit(1);
-}
-
-struct in_addr multiaddr={0};
-
-int multicast(volmessage *m){
-  static int sockfd=-1;
-  static struct sockaddr_in dest;
-  int destlen=sizeof(dest);
-
-  if(sockfd==-1)
-    if((sockfd=socket(AF_INET,SOCK_DGRAM,0))<0)
-      abortm("Could not open multicast socket\n");
-
-  if(multiaddr.s_addr==0)
-    multiaddr.s_addr = inet_addr(MULTI_ADDR);
-      /*inet_aton(MULTI_ADDR,&multiaddr);*/
-
-  memset(&dest,0,sizeof(dest));
-  dest.sin_family=AF_INET;
-  dest.sin_addr.s_addr=multiaddr.s_addr;
-  dest.sin_port=htons(MULTI_PORT);
-
-  if(sendto(sockfd,m,MESSAGE_SIZE,0,(struct sockaddr *)&dest,destlen)<0){
-    message("Multicast send error\n");
-    return(1);
-  }
-  return(0);
-}
-
-int sockfd=-1,volfd;
-static struct sockaddr_in me,you;
-static int youlen=sizeof(you);
-struct in_addr multiaddr;
-
-int opensock(){
-  if((sockfd=socket(AF_INET,SOCK_DGRAM,0))<0)
-    abortm("Could not open listen socket\n");
-
-  memset(&me,0,sizeof(me));
-  me.sin_family=AF_INET;
-  me.sin_addr.s_addr=htonl(INADDR_ANY);
-  me.sin_port=htons(LISTEN_PORT);
-
-  if(bind(sockfd,(struct sockaddr *)&me,sizeof(me))<0)
-    abortm("Could not bind to listen port\n");
-
-  listen(sockfd,5);
-  return(sockfd);
-}
-
-/*  -1 blocks
-    0  nonblocking
-    +n timeout 
-*/
-
-int
-fetch (int usec, volmessage *m) {
-  fd_set fdset;
-  int retval;
-  struct timeval tv;
-  if(sockfd==-1)
-    opensock();
-
-  FD_ZERO(&fdset);
-  FD_SET(sockfd, &fdset);
-
-  tv.tv_sec = 0;
-  tv.tv_usec = usec;
-  retval = select(sockfd+1,
-		  &fdset, NULL, NULL,
-		  ((usec == -1) ? NULL : &tv));
-  if (retval == 1) {
-    /* Select got us a single descriptor to play with, as we
-     * expected. Pull a message from it. */
-    int youlen = sizeof(you);
-    retval = recvfrom(sockfd,
-		      m, MESSAGE_SIZE,
-		      0,
-		      (struct sockaddr *)&you, &youlen);
-    if(retval < MESSAGE_SIZE || m->A != 'A' || m->ZE != 'Z') {
-      message("Packet receive error\n");
-      return(0);
-    } else {
-      return(1);
-    }
-  } else {
-    if (retval == 0) {
-      /* This means the timeout finished. */
-    } else if (retval == -1) {
-      fprintf(stderr, "Select error. errno = %d (%s).\n",
-	      errno, strerror(errno));
-    } else {
-      fprintf(stderr, "Select returned unexpected value %d.\n", retval);
-    }
-    /* Whatever error we get, we just return 0. */
-    return(0);
-  }
-}
-
-
-int
-reply (volmessage *m) {
-  int retval;
-  assert(sockfd != -1);
-  
-  retval = sendto(sockfd,
-		  m, MESSAGE_SIZE,
-		  0,
-		  (struct sockaddr *)&you, youlen);
-  if(retval < 0){
-    message("Reply error\n");
-    return(1);
-  }
-  return(0);
-}
-  
-int vol_iterate(int newval){
-  static int val=0;
-  char command[100];
-  
-
-  if (val==newval)
-    return(0);
-
-  if (val<newval) {
-    val+=8;
-    if(val>newval)
-      val=newval;
-  } else {
-    val-=8;
-    if(val<newval)
-      val=newval;
-  }
-  sprintf(command,"/usr/bin/aumix -v %d",val);
-  fprintf(stderr,"Setting volume to %d\n",val);
-  system(command);
-  return(1);
-}
-
-int main(){
-  unsigned char volume=32,mute=0,ip_3=0,ip_2=0,ip_1=0,ip_0=0;
-  int messagestatus=1,volstatus=1;
-  volmessage m;
-
-  system("/usr/bin/aumix -v 0");
-  system("/usr/bin/aumix -i 100");
-  system("/usr/bin/aumix -l 100");
-
-  message("\nSIPB volume daemon running\n");
-
-  /* We want to process messages quickly, then get to the business of
-     controlling ther hardware when no messages are in the queue. 
-     The select on the message fetch also does our timing for volume
-     iteration */
-
-  while(1){
-    if(messagestatus==1)
-      /* Messages likely pending.  Process quickly */
-      messagestatus=fetch(0,&m);
-    else
-      if(volstatus==1){
-	/* No messages, but volume to be updated */
-	messagestatus=fetch(50,&m);
-	if(messagestatus==0)volstatus=vol_iterate(mute?0:volume);
-      }else{
-	/* Steady state.  Wait (almost) forever until a message */
-	m.ip_0=ip_0;
-	m.ip_1=ip_1;
-	m.ip_2=ip_2;
-	m.ip_3=ip_3;
-	m.A='A';
-	m.request='V';
-	m.value=volume;
-	m.ZE='Z';
-    	multicast(&m);
-	m.A='A';
-	m.request='M';
-	m.value=mute;
-	m.ZE='Z';
-    	multicast(&m);
-	messagestatus=fetch(400000,&m);
-      }
-
-    if(messagestatus){
-      int error=0;
-      m.ip_3=(ntohl(you.sin_addr.s_addr)>>24)&0xff;
-      m.ip_2=(ntohl(you.sin_addr.s_addr)>>16)&0xff;
-      m.ip_1=(ntohl(you.sin_addr.s_addr)>>8)&0xff;
-      m.ip_0=ntohl(you.sin_addr.s_addr)&0xff;
-
-      if(m.ip_3!=18 || m.ip_2!=OUR_SUBNET){
-	error=1;
-      }
-
-      switch(m.request){
-      case 'V':
-	if(error){
-	  m.value=error;
-	  m.ZE='E';
-	  reply(&m);
-	  message("Request from off subnet rejected\n");
-	}else{
-	  ip_0=m.ip_0;
-	  ip_1=m.ip_1;
-	  ip_2=m.ip_2;
-	  ip_3=m.ip_3;
-	  volume=m.value;
-	  reply(&m);
-	  multicast(&m);
-	  volstatus=1;
-	}
-	break;
-      case 'M':
-	if(error){
-	  m.value=error;
-	  m.ZE='E';
-	  reply(&m);	  
-	  message("Request from off subnet rejected\n");
-	}else{
-	  ip_0=m.ip_0;
-	  ip_1=m.ip_1;
-	  ip_2=m.ip_2;
-	  ip_3=m.ip_3;
-	  mute=(m.value?1:0);
-	  m.value=mute;
-	  reply(&m);
-	  multicast(&m);
-	  volstatus=1;
-	}
-	break;
-      case 'Q':
-	m.ip_0=ip_0;
-	m.ip_1=ip_1;
-	m.ip_2=ip_2;
-	m.ip_3=ip_3;
-	m.request='V';
-	m.value=volume;
-	reply(&m);
-	m.request='M';
-	m.value=mute;
-	reply(&m);
-	break;
-      default:
-	reply((volmessage *)"AE20000E");
-	break;
-      }
-    }
-  }
-}
Index: tenbach-remctl/volume
===================================================================
--- gutenbach-remctl/volume	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ 	(revision )
@@ -1,25 +1,0 @@
-volume set  /usr/lib/gutenbach/remctl/volume-set	 ANYUSER 
-volume get  /usr/lib/gutenbach/remctl/volume-get    ANYUSER
-volume show /usr/lib/gutenbach/remctl/volume-show   ANYUSER
-volume mute /usr/lib/gutenbach/remctl/volume-mute   ANYUSER 
-volume up   /usr/lib/gutenbach/remctl/volume-up     ANYUSER 
-volume down /usr/lib/gutenbach/remctl/volume-down   ANYUSER 
-volume is-muted /usr/lib/gutenbach/remctl/volume-is-muted   ANYUSER 
-
-v set  /usr/lib/gutenbach/remctl/volume-set    ANYUSER 
-v get  /usr/lib/gutenbach/remctl/volume-get    ANYUSER
-v show /usr/lib/gutenbach/remctl/volume-show   ANYUSER
-v mute /usr/lib/gutenbach/remctl/volume-mute   ANYUSER 
-v up   /usr/lib/gutenbach/remctl/volume-up     ANYUSER 
-v down /usr/lib/gutenbach/remctl/volume-down   ANYUSER 
-v is-muted /usr/lib/gutenbach/remctl/volume-is-muted   ANYUSER 
-
-v u /usr/lib/gutenbach/remctl/volume-up   ANYUSER
-v d /usr/lib/gutenbach/remctl/volume-down ANYUSER
-
-volume u /usr/lib/gutenbach/remctl/volume-up   ANYUSER
-volume d /usr/lib/gutenbach/remctl/volume-down ANYUSER
-
-volume help /usr/lib/gutenbach/remctl/volume-help ANYUSER
-v      help /usr/lib/gutenbach/remctl/volume-help ANYUSER
-help   ALL  /usr/lib/gutenbach/remctl/volume-help ANYUSER
Index: tenbach-remctl/volume-down
===================================================================
--- gutenbach-remctl/volume-down	(revision 6bb3ffb249dc254896b2f89be72367ae76fad5e9)
+++ 	(revision )
@@ -1,3 +1,0 @@
-#!/bin/sh
-PATH="$(dirname $0):$PATH"
-volume-set . $(( $(volume-get .) - 1 ))
Index: tenbach-remctl/volume-get
===================================================================
--- gutenbach-remctl/volume-get	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ 	(revision )
@@ -1,8 +1,0 @@
-#!/bin/sh
-PATH="$(dirname $0):$PATH"
-
-mixer=$(/usr/lib/gutenbach/gutenbach-get-config mixer)
-channel=$(/usr/lib/gutenbach/gutenbach-get-config channel)
-
-amixer get $mixer | grep "$channel" \
- | perl -lne 'print $1 if (/: Playback ([0-9]+)/)'
Index: tenbach-remctl/volume-help
===================================================================
--- gutenbach-remctl/volume-help	(revision 6bb3ffb249dc254896b2f89be72367ae76fad5e9)
+++ 	(revision )
@@ -1,15 +1,0 @@
-#!/bin/bash
-PATH="$(dirname $0):$PATH"
-cat <<EOF
-Usage: remctl zsr.mit.edu <command> <subcommand> [..args..]
-Commands:
-  volume up        - increment volume
-  volume down      - decrement volume
-  volume get       - get volume
-  volume set [vol] - set volume, in range 0..31
-  volume mute      - zero volume
-  volume help,
-   help <anything> - print this help
-
-'v', 'u', 'd' abbreviate 'volume', 'up', 'down' respectively.
-EOF
Index: tenbach-remctl/volume-is-muted
===================================================================
--- gutenbach-remctl/volume-is-muted	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ 	(revision )
@@ -1,17 +1,0 @@
-#!/bin/sh
-PATH="$(dirname $0):$PATH"
-
-mixer=$(/usr/lib/gutenbach/gutenbach-get-config mixer)
-channel=$(/usr/lib/gutenbach/gutenbach-get-config channel)
-
-volstat=$(amixer get $mixer | grep "$channel" \
- | perl -lne 'print $1 if (/: Playback .*\[(on|off)\]$/)'
-)
-
-case "$volstat" in 
-    on)  result=unmuted;;
-    off) result=muted;;
-    *)   result=unknown;;
-esac
-
-echo $result
Index: tenbach-remctl/volume-mute
===================================================================
--- gutenbach-remctl/volume-mute	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ 	(revision )
@@ -1,10 +1,0 @@
-#!/bin/sh
-PATH="$(dirname $0):$PATH"
-
-mixer=$(/usr/lib/gutenbach/gutenbach-get-config mixer)
-channel=$(/usr/lib/gutenbach/gutenbach-get-config channel)
-
-amixer set $mixer toggle > /dev/null
-volume-is-muted
-
-volume-zephyr
Index: tenbach-remctl/volume-set
===================================================================
--- gutenbach-remctl/volume-set	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ 	(revision )
@@ -1,10 +1,0 @@
-#!/bin/sh
-PATH="$(dirname $0):$PATH"
-
-mixer=$(/usr/lib/gutenbach/gutenbach-get-config mixer)
-channel=$(/usr/lib/gutenbach/gutenbach-get-config channel)
-
-amixer set $mixer "$2" | grep "$channel" \
- | perl -lne 'print $1 if (/: Playback ([0-9]+)/)'
-
-volume-zephyr
Index: tenbach-remctl/volume-show
===================================================================
--- gutenbach-remctl/volume-show	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ 	(revision )
@@ -1,7 +1,0 @@
-#!/bin/sh
-PATH="$(dirname $0):$PATH"
-
-mixer=$(/usr/lib/gutenbach/gutenbach-get-config mixer)
-channel=$(/usr/lib/gutenbach/gutenbach-get-config channel)
-
-amixer get $mixer
Index: tenbach-remctl/volume-up
===================================================================
--- gutenbach-remctl/volume-up	(revision 6bb3ffb249dc254896b2f89be72367ae76fad5e9)
+++ 	(revision )
@@ -1,3 +1,0 @@
-#!/bin/sh
-PATH="$(dirname $0):$PATH"
-volume-set . $(( $(volume-get .) + 1 ))
Index: tenbach-remctl/volume-zephyr
===================================================================
--- gutenbach-remctl/volume-zephyr	(revision 4c91fc6e62ec5d8ebeb5fdf1c2ac40118489f184)
+++ 	(revision )
@@ -1,28 +1,0 @@
-#!/bin/sh
-PATH="$(dirname $0):$PATH"
-class=$(/usr/lib/gutenbach/gutenbach-get-config zephyr-class)
-host=$(/usr/lib/gutenbach/gutenbach-get-config host)
-queue=$(/usr/lib/gutenbach/gutenbach-get-config queue)
-instance="$queue-volume@$host"
-
-mixer=$(/usr/lib/gutenbach/gutenbach-get-config mixer)
-channel=$(/usr/lib/gutenbach/gutenbach-get-config channel)
-
-echo "Class: $class" >> /tmp/gutenbach-remctl.log
-echo "Host: $host" >> /tmp/gutenbach-remctl.log
-echo "Queue: $queue" >> /tmp/gutenbach-remctl.log
-echo "Instance: $instance" >> /tmp/gutenbach-remctl.log
-echo "Mixer: $mixer" >> /tmp/gutenbach-remctl.log
-echo "Channel: $channel" >> /tmp/gutenbach-remctl.log
-
-muted=$(volume-is-muted)
-mute_str=" [$muted]"
-if [ "$muted" = "unmuted" ]; then
-    mute_str=""
-fi
-
-start-stop-daemon --stop --oknodo --pidfile /usr/lib/gutenbach/gutenbach-zephyr >/dev/null 2>&1
-start-stop-daemon --start --pidfile /usr/lib/gutenbach/gutenbach-zephyr --make-pidfile --background --exec /bin/sh -- -c "sleep 10 && zwrite -c $class -i $instance -d -n -m 'volume changed to $(volume-get)$mute_str' > /dev/null" >/dev/null 2>&1
-
-echo "sleep 10 && zwrite -c $class -i $instance -d -n -m 'volume changed to $(volume-get)$mute_str' > /dev/null" >> /tmp/gutenbach-remctl.log
-echo "" >> /tmp/gutenbach-remctl.log
Index: gutenbach-web/.gitignore
===================================================================
--- gutenbach-web/.gitignore	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/.gitignore	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,7 @@
+*.pyc
+dispatch.fcgi
+tg2env
+*.pyo
+*~
+.python-eggs/*
+devdata.db
Index: gutenbach-web/.htaccess
===================================================================
--- gutenbach-web/.htaccess	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/.htaccess	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,14 @@
+AddDefaultCharset utf-8
+
+Options +FollowSymLinks +ExecCGI
+
+# It's very useful to run dispatch.fcgi as a regular CGI module when
+# doing development work, as useful error messages are then dumped
+# to error.log
+#AddHandler cgi-script .fcgi
+#AddHandler fastcgi-script .fcgi
+
+RewriteEngine On
+RewriteRule ^(dispatch\.fcgi/.*)$ - [L]
+RewriteRule ^(static/.*)$ - [L]
+RewriteRule ^(.*)$ dispatch\.fcgi/$1 [L]
Index: gutenbach-web/MANIFEST.in
===================================================================
--- gutenbach-web/MANIFEST.in	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/MANIFEST.in	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,4 @@
+recursive-include sipbmp3web/public *
+include sipbmp3web/public/favicon.ico
+recursive-include sipbmp3web/i18n *
+recursive-include sipbmp3web/templates *
Index: gutenbach-web/PKG-INFO
===================================================================
--- gutenbach-web/PKG-INFO	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/PKG-INFO	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: sipbmp3-web
+Version: 0.1dev
+Summary: UNKNOWN
+Home-page: UNKNOWN
+Author: UNKNOWN
+Author-email: UNKNOWN
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
Index: gutenbach-web/README.txt
===================================================================
--- gutenbach-web/README.txt	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/README.txt	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,14 @@
+This directory exists so that Subversion-based projects can share a single
+copy of the ``ez_setup`` bootstrap module for ``setuptools``, and have it
+automatically updated in their projects when ``setuptools`` is updated.
+
+For your convenience, you may use the following svn:externals definition::
+
+    ez_setup svn://svn.eby-sarna.com/svnroot/ez_setup
+
+You can set this by executing this command in your project directory::
+
+    svn propedit svn:externals .
+
+And then adding the line shown above to the file that comes up for editing.
+Then, whenever you update your project, ``ez_setup`` will be updated as well.
Index: gutenbach-web/SOURCES.txt
===================================================================
--- gutenbach-web/SOURCES.txt	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/SOURCES.txt	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,65 @@
+MANIFEST.in
+README.txt
+setup.cfg
+setup.py
+sipbmp3_web.egg-info/PKG-INFO
+sipbmp3_web.egg-info/SOURCES.txt
+sipbmp3_web.egg-info/dependency_links.txt
+sipbmp3_web.egg-info/entry_points.txt
+sipbmp3_web.egg-info/paster_plugins.txt
+sipbmp3_web.egg-info/requires.txt
+sipbmp3_web.egg-info/top_level.txt
+sipbmp3web/__init__.py
+sipbmp3web/websetup.py
+sipbmp3web/config/__init__.py
+sipbmp3web/config/app_cfg.py
+sipbmp3web/config/environment.py
+sipbmp3web/config/middleware.py
+sipbmp3web/controllers/__init__.py
+sipbmp3web/controllers/error.py
+sipbmp3web/controllers/root.py
+sipbmp3web/controllers/secure.py
+sipbmp3web/controllers/template.py
+sipbmp3web/i18n/ru/LC_MESSAGES/sipbmp3web.po
+sipbmp3web/lib/__init__.py
+sipbmp3web/lib/app_globals.py
+sipbmp3web/lib/base.py
+sipbmp3web/lib/helpers.py
+sipbmp3web/model/__init__.py
+sipbmp3web/model/auth.py
+sipbmp3web/public/favicon.ico
+sipbmp3web/public/css/style.css
+sipbmp3web/public/images/contentbg.png
+sipbmp3web/public/images/error.png
+sipbmp3web/public/images/header_inner2.png
+sipbmp3web/public/images/headerbg.png
+sipbmp3web/public/images/info.png
+sipbmp3web/public/images/inputbg.png
+sipbmp3web/public/images/loginbg.png
+sipbmp3web/public/images/loginbottombg.png
+sipbmp3web/public/images/loginheader-left.png
+sipbmp3web/public/images/loginheader-right.png
+sipbmp3web/public/images/menu-item-actibg-first.png
+sipbmp3web/public/images/menu-item-actibg.png
+sipbmp3web/public/images/menu-item-border.png
+sipbmp3web/public/images/menubg.png
+sipbmp3web/public/images/ok.png
+sipbmp3web/public/images/pagebg.png
+sipbmp3web/public/images/star.png
+sipbmp3web/public/images/strype2.png
+sipbmp3web/public/images/under_the_hood_blue.png
+sipbmp3web/templates/__init__.py
+sipbmp3web/templates/about.html
+sipbmp3web/templates/authentication.html
+sipbmp3web/templates/debug.html
+sipbmp3web/templates/error.html
+sipbmp3web/templates/footer.html
+sipbmp3web/templates/header.html
+sipbmp3web/templates/index.html
+sipbmp3web/templates/login.html
+sipbmp3web/templates/master.html
+sipbmp3web/templates/sidebars.html
+sipbmp3web/tests/__init__.py
+sipbmp3web/tests/test_models.py
+sipbmp3web/tests/functional/__init__.py
+sipbmp3web/tests/functional/test_root.py
Index: gutenbach-web/about.html
===================================================================
--- gutenbach-web/about.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/about.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,132 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
+                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <xi:include href="master.html" />
+
+<head>
+  <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+  <title>Learning TurboGears 2.0: Quick guide to the Quickstart pages.</title>
+</head>
+
+<body>
+    ${sidebar_top()}
+    ${sidebar_bottom()}
+  <div id="getting_started">
+    <h2>Architectural basics of a quickstart TG2 site.</h2>
+    <p>The TG2 quickstart command produces this basic TG site.  Here's how it works.</p>
+    <ol id="getting_started_steps">
+      <li class="getting_started">
+        <h3>Code my data model</h3>
+        <p> When you want a model for storing favorite links or wiki content, 
+            the <strong>/model</strong> folder in your site is ready to go.</p>
+        <p> You can build a dynamic site without any data model at all. There 
+            still be a default data-model template for you if you didn't enable 
+            identity in quickstart. If you enable identity, you'll got 
+            identity data-model made for you.</p>
+      </li>
+      <li class="getting_started">
+        <h3>Design my URL structure</h3>
+        <p> The "<span class="code">root.py</span>" file under the 
+            <strong>/controllers</strong> folder has your URLs.  When you 
+            called this url (<span class="code"><a href="${tg.url('/about')}">about</a></span>), 
+            the command went through the RootController class to the 
+            <span class="code">about</span><span class="code">()</span> method.</p>
+        <p> Those Python methods are responsible to create the dictionary of
+             variables that will be used in your web views (template).</p>
+      </li>
+      <li class="getting_started">
+        <h3>Reuse the web page elements</h3>
+        <p> A web page viewed by user could be constructed by single or 
+            several reusable templates under <strong>/templates</strong>. 
+            Take 'about' page for example, each reusable templates generating 
+            a part of the page. We'll cover them in the order of where they are 
+            found, listed near the top of the about.html template</p>
+        <p> <strong><span class="code">header.html</span></strong> - The 
+            "header.html" template contains the HTML code to display the 
+            'header': The blue gradient, TG2 logo, and some site text at the 
+            top of every page it is included on. When the "about.html" template 
+            is called, it includes this "header.html" template (and the others) 
+            with a <span class="code">&lt;xi:include /&gt;</span> tag, part of 
+            the Genshi templating system. The "header.html" template is not a 
+            completely static HTML -- it also dynamically displays the current 
+            page name with a Genshi template method called "replace" with the 
+            code: <span class="code">&lt;span py:replace="page"/&gt;</span>. 
+            It means replace this <span class="code">&lt;span /&gt;</span> 
+            region with the contents found in the variable 'page' that has 
+            been sent in the dictionary to this "about.html" template, and is 
+            available through that namespace for use by this "header.html" 
+            template.  That's how it changes in the header depending on what 
+            page you are visiting.
+        </p>
+        <p> <strong><span class="code">sidebars.html</span></strong> - The
+             sidebars (navigation areas on the right side of the page) are 
+             generated as two separate <span class="code">py:def</span> blocks 
+             in the "sidebars.html" template.  The <span class="code">py:def</span> 
+             construct is best thought of as a "macro" code... a simple way to 
+             separate and reuse common code snippets.  All it takes to include 
+             these on the "about.html" page template is to write  
+             <span class="code">
+<br/><br/>
+    $${sidebar_top()}
+<br/>
+    $${sidebar_bottom()}
+<br/><br/>       
+        </span> in the page where they are wanted.  CSS styling (in 
+        "/public/css/style.css") floats them off to the right side.  You can 
+        remove a sidebar or add more of them, and the CSS will place them one 
+        atop the other.</p>
+        <p>This is, of course, also exactly how the header and footer 
+            templates are also displayed in their proper places, but we'll 
+            cover that in the "master.html" template below.</p>
+        <p>Oh, and in sidebar_top we've added a dynamic menu that shows the 
+            link to this page at the top when you're at the "index" page, and 
+            shows a link to the Home (index) page when you're here.  Study the 
+            "sidebars.html" template to see how we used 
+            <span class="code">py:choose</span> for that.</p>
+        <p> <strong><span class="code">footer.html</span></strong> - The 
+            "footer.html" block is simple, but also utilizes a special 
+            "replace" method to set the current YEAR in the footer copyright 
+            message. The code is: 
+            <span class="code">&lt;span py:replace="now.strftime('%Y')"&gt;
+                </span> and it uses the variable "now" that was passed 
+            in with the dictionary of variables.  But because "now" is a 
+            datetime object, we can use the Python 
+            <span class="code">"strftime()"</span> method with the "replace" 
+            call to say "Just Display The Year Here".  Simple, elegant; we 
+            format the date display in the template (the View in the 
+            Model/View/Controller architecture) rather than formatting it in 
+            the Controller method and sending it to the template as a string 
+            variable.</p>
+        <p> <strong><span class="code">master.html</span></strong> - The 
+            "master.html" template is called last, by design.  The "master.html" 
+            template controls the overall design of the page we're looking at, 
+            calling first the "header" py:def macro, then the putting everything 
+            from this "about.html" template into the "main_content" div, and 
+            then calling the "footer" macro at the end.  Thus the "master.html" 
+            template provides the overall architecture for each page in this 
+            site.</p>
+        <p>But why then shouldn't we call it first?  Isn't it the most 
+            important?  Perhaps, but that's precisely why we call it LAST. 
+            The "master.html" template needs to know where to find everything 
+            else, everything that it will use in py:def macros to build the
+             page.  So that means we call the other templates first, and then 
+             call "master.html". </p>
+        <p>There's more to the "master.html" template... study it to see how 
+           the &lt;title&gt; tags and static JS and CSS files are brought into 
+           the page.  Templating with Genshi is a powerful tool and we've only 
+           scratched the surface.  There are also a few little CSS tricks 
+           hidden in these pages, like the use of a "clearingdiv" to make 
+           sure that your footer stays below the sidebars and always looks 
+           right.  That's not TG2 at work, just CSS.  You'll need all your 
+           skills to build a fine web app, but TG2 will make the hard parts 
+           easier so that you can concentrate more on good design and content 
+           rather than struggling with mechanics.</p>
+      </li>
+    </ol>
+    <p>Good luck with TurboGears 2!</p>
+  </div>
+</body>
+</html>
Index: gutenbach-web/app_cfg.py
===================================================================
--- gutenbach-web/app_cfg.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/app_cfg.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,44 @@
+from tg.configuration import AppConfig, Bunch
+import sipbmp3web
+from sipbmp3web import model
+from sipbmp3web.lib import app_globals, helpers
+
+base_config = AppConfig()
+base_config.renderers = []
+
+base_config.package = sipbmp3web
+
+#Set the default renderer
+base_config.default_renderer = 'genshi'
+base_config.renderers.append('genshi') 
+# if you want raw speed and have installed chameleon.genshi
+# you should try to use this renderer instead.
+# warning: for the moment chameleon does not handle i18n translations
+#base_config.renderers.append('chameleon_genshi') 
+
+#Configure the base SQLALchemy Setup
+base_config.use_sqlalchemy = True
+base_config.model = sipbmp3web.model
+base_config.DBSession = sipbmp3web.model.DBSession
+
+# Configure the authentication backend
+base_config.auth_backend = 'sqlalchemy'
+base_config.sa_auth.dbsession = model.DBSession
+# what is the class you want to use to search for users in the database
+base_config.sa_auth.user_class = model.User
+# what is the class you want to use to search for groups in the database
+base_config.sa_auth.group_class = model.Group
+# what is the class you want to use to search for permissions in the database
+base_config.sa_auth.permission_class = model.Permission
+
+# override this if you would like to provide a different who plugin for 
+# managing login and logout of your application
+base_config.sa_auth.form_plugin = None
+
+# You may optionally define a page where you want users to be redirected to
+# on login:
+base_config.sa_auth.post_login_url = '/post_login'
+
+# You may optionally define a page where you want users to be redirected to
+# on logout:
+base_config.sa_auth.post_logout_url = '/post_logout'
Index: gutenbach-web/app_globals.py
===================================================================
--- gutenbach-web/app_globals.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/app_globals.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,13 @@
+"""The application's Globals object"""
+
+class Globals(object):
+    """Globals acts as a container for objects available throughout the
+    life of the application
+    """
+
+    def __init__(self):
+        """One instance of Globals is created during application
+        initialization and is available during requests via the 'g'
+        variable
+        """
+        pass
Index: gutenbach-web/auth.py
===================================================================
--- gutenbach-web/auth.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/auth.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,161 @@
+import sys
+try:
+    from hashlib import sha1
+except ImportError:
+    sys.exit('ImportError: no module named hashlib\nIf you are on python2.4 this library is not part of python. Please install it. Example: easy_install hashlib')
+import os
+from datetime import datetime
+
+from sqlalchemy import Table, ForeignKey, Column
+from sqlalchemy.types import String, Unicode, UnicodeText, Integer, DateTime, \
+                             Boolean, Float
+from sqlalchemy.orm import relation, backref, synonym
+
+from sipbmp3web.model import DeclarativeBase, metadata, DBSession
+
+
+# This is the association table for the many-to-many relationship between
+# groups and permissions.
+group_permission_table = Table('tg_group_permission', metadata,
+    Column('group_id', Integer, ForeignKey('tg_group.group_id',
+        onupdate="CASCADE", ondelete="CASCADE")),
+    Column('permission_id', Integer, ForeignKey('tg_permission.permission_id',
+        onupdate="CASCADE", ondelete="CASCADE"))
+)
+
+# This is the association table for the many-to-many relationship between
+# groups and members - this is, the memberships.
+user_group_table = Table('tg_user_group', metadata,
+    Column('user_id', Integer, ForeignKey('tg_user.user_id',
+        onupdate="CASCADE", ondelete="CASCADE")),
+    Column('group_id', Integer, ForeignKey('tg_group.group_id',
+        onupdate="CASCADE", ondelete="CASCADE"))
+)
+
+# auth model
+
+class Group(DeclarativeBase):
+    """An ultra-simple group definition.
+    """
+    __tablename__ = 'tg_group'
+
+    group_id = Column(Integer, autoincrement=True, primary_key=True)
+    group_name = Column(Unicode(16), unique=True, nullable=False)
+    display_name = Column(Unicode(255))
+    created = Column(DateTime, default=datetime.now)
+    users = relation('User', secondary=user_group_table, backref='groups')
+
+    def __repr__(self):
+        return '<Group: name=%s>' % self.group_name
+
+    def __unicode__(self):
+        return self.group_name
+
+#
+# The 'info' argument we're passing to the email_address and password columns
+# contain metadata that Rum (http://python-rum.org/) can use generate an
+# admin interface for your models.
+#
+class User(DeclarativeBase):
+    """Reasonably basic User definition. Probably would want additional
+    attributes.
+    """
+    __tablename__ = 'tg_user'
+
+    user_id = Column(Integer, autoincrement=True, primary_key=True)
+    user_name = Column(Unicode(16), unique=True, nullable=False)
+    email_address = Column(Unicode(255), unique=True, nullable=False,
+                           info={'rum': {'field':'Email'}})
+    display_name = Column(Unicode(255))
+    _password = Column('password', Unicode(80),
+                       info={'rum': {'field':'Password'}})
+    created = Column(DateTime, default=datetime.now)
+
+    def __repr__(self):
+        return '<User: email="%s", display name="%s">' % (
+                self.email_address, self.display_name)
+
+    def __unicode__(self):
+        return self.display_name or self.user_name
+
+    @property
+    def permissions(self):
+        perms = set()
+        for g in self.groups:
+            perms = perms | set(g.permissions)
+        return perms
+
+    @classmethod
+    def by_email_address(cls, email):
+        """A class method that can be used to search users
+        based on their email addresses since it is unique.
+        """
+        return DBSession.query(cls).filter(cls.email_address==email).first()
+
+    @classmethod
+    def by_user_name(cls, username):
+        """A class method that permits to search users
+        based on their user_name attribute.
+        """
+        return DBSession.query(cls).filter(cls.user_name==username).first()
+
+
+    def _set_password(self, password):
+        """Hash password on the fly."""
+        hashed_password = password
+
+        if isinstance(password, unicode):
+            password_8bit = password.encode('UTF-8')
+        else:
+            password_8bit = password
+        
+        salt = sha1()
+        salt.update(os.urandom(60))
+        hash = sha1()
+        hash.update(password_8bit + salt.hexdigest())
+        hashed_password = salt.hexdigest() + hash.hexdigest()
+
+        # make sure the hased password is an UTF-8 object at the end of the
+        # process because SQLAlchemy _wants_ a unicode object for Unicode columns
+        if not isinstance(hashed_password, unicode):
+            hashed_password = hashed_password.decode('UTF-8')
+
+        self._password = hashed_password
+
+    def _get_password(self):
+        """returns password
+        """
+        return self._password
+
+    password = synonym('_password', descriptor=property(_get_password,
+                                                        _set_password))
+
+    def validate_password(self, password):
+        """Check the password against existing credentials.
+
+        :param password: the password that was provided by the user to
+            try and authenticate. This is the clear text version that we will
+            need to match against the hashed one in the database.
+        :type password: unicode object.
+        :return: Whether the password is valid.
+        :rtype: bool
+        
+        """
+        hashed_pass = sha1()
+        hashed_pass.update(password + self.password[:40])
+        return self.password[40:] == hashed_pass.hexdigest()
+
+
+class Permission(DeclarativeBase):
+    """A relationship that determines what each Group can do
+    """
+    __tablename__ = 'tg_permission'
+
+    permission_id = Column(Integer, autoincrement=True, primary_key=True)
+    permission_name = Column(Unicode(16), unique=True, nullable=False)
+    description = Column(Unicode(255))
+    groups = relation(Group, secondary=group_permission_table,
+                      backref='permissions')
+
+    def __unicode__(self):
+        return self.permission_name
Index: gutenbach-web/authentication.html
===================================================================
--- gutenbach-web/authentication.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/authentication.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,67 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
+                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <xi:include href="master.html" />
+
+<head>
+  <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+  <title>Learning TurboGears 2.0: Quick guide to authentication.</title>
+</head>
+
+<body>
+    ${sidebar_top()}
+    ${sidebar_bottom()}
+  <div id="getting_started">
+    <h2>Authentication &amp; Authorization in a TG2 site.</h2>
+    <p>If you have access to this page, this means you have enabled authentication and authorization
+    in the quickstart to create your project.</p>
+    <p>
+    The paster command will have created a few specific controllers for you. But before you
+    go to play with those controllers you'll need to make sure your application has been
+    properly bootstapped.
+    This is dead easy, here is how to do this:
+    </p>
+
+    <span class="code">
+    paster setup-app development.ini
+    </span>
+
+    <p>
+    inside your application's folder and you'll get a database setup (using the preferences you have
+    set in your development.ini file). This database will also have been prepopulated with some
+    default logins/passwords so that you can test the secured controllers and methods.
+    </p>
+    <p>
+    To change the comportement of this setup-app command you just need to edit the <span class="code">websetup.py</span> file.
+    </p>
+    <p>
+    Now try to visiting the <a href="${tg.url('/manage_permission_only')}">manage_permission_only</a> URL. You will be challenged with a login/password form.
+    </p>
+    <p>
+    Only managers are authorized to visit this method. You will need to log-in using:
+        <p>
+        <span class="code">
+        login: manager
+        </span>
+        </p>
+        <p>
+        <span class="code">
+        password: managepass
+        </span>
+        </p>
+    </p>
+    <p>
+    Another protected resource is <a href="${tg.url('/editor_user_only')}">editor_user_only</a>. This one is protected by a different set of permissions.
+    You will need to be <span class="code">editor</span> with a password of <span class="code">editpass</span> to be able to access it.
+    </p>
+    <p>
+    The last kind of protected resource in this quickstarted app is a full so called <a href="${tg.url('/secc')}">secure controller</a>. This controller is protected globally.
+    Instead of having a @require decorator on each method, we have set an allow_only attribute at the class level. All the methods in this controller will
+    require the same level of access. You need to be manager to access <a href="${tg.url('/secc')}">secc</a> or <a href="${tg.url('/secc/some_where')}">secc/some_where</a>.
+    </p>
+  </div>
+</body>
+</html>
Index: gutenbach-web/base.py
===================================================================
--- gutenbach-web/base.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/base.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,36 @@
+"""The base Controller API
+
+Provides the BaseController class for subclassing.
+"""
+from tg import TGController, tmpl_context
+from tg.render import render
+from tg import request
+
+import sipbmp3web.model as model
+
+from pylons.i18n import _, ungettext, N_
+from tw.api import WidgetBunch
+
+class Controller(object):
+    """Base class for a web application's controller.
+
+    Currently, this provides positional parameters functionality
+    via a standard default method.
+    """
+
+class BaseController(TGController):
+    """Base class for the root of a web application.
+
+    Your web application should have one of these. The root of
+    your application is used to compute URLs used by your app.
+    """
+
+    def __call__(self, environ, start_response):
+        """Invoke the Controller"""
+        # TGController.__call__ dispatches to the Controller method
+        # the request is routed to. This routing information is
+        # available in environ['pylons.routes_dict']
+        
+        request.identity = request.environ.get('repoze.who.identity')
+        tmpl_context.identity = request.identity
+        return TGController.__call__(self, environ, start_response)
Index: gutenbach-web/controller.template
===================================================================
--- gutenbach-web/controller.template	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/controller.template	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,22 @@
+"""Main Controller"""
+# standard library imports
+
+# turbogears imports
+from tg import expose
+#from tg import redirect, validate, flash
+
+# third party imports
+#from pylons.i18n import ugettext as _
+#from repoze.what import predicates
+
+# project specific imports
+from sipbmp3web.lib.base import BaseController
+#from sipbmp3web.model import DBSession, metadata
+
+class SampleController(BaseController):
+    #Uncomment this line if your controller requires an authenticated user
+    #allow_only = authorize.not_anonymous()
+
+    @expose('sipbmp3web.templates.index')
+    def index(self):
+        return dict(page='index')
Index: gutenbach-web/debug.html
===================================================================
--- gutenbach-web/debug.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/debug.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,20 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
+                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<xi:include href="master.html" />
+
+<head>
+  <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+  <title>Sample Template, for looking at template locals</title>
+</head>
+
+<body>
+    <h1>All objects from locals():</h1>
+
+    <div py:for="item in sorted(locals()['data'].keys())">
+      ${item}: ${repr(locals()['data'][item])}</div>
+</body>
+</html>
Index: gutenbach-web/dependency_links.txt
===================================================================
--- gutenbach-web/dependency_links.txt	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/dependency_links.txt	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,1 @@
+
Index: gutenbach-web/deployment.ini
===================================================================
--- gutenbach-web/deployment.ini	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/deployment.ini	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,95 @@
+#
+# sipbmp3-web - TurboGears configuration
+#
+# The %(here)s variable will be replaced with the parent directory of this file
+#
+[DEFAULT]
+# WARGING == If debug is not set to false, you'll get the interactive
+# debugger on production, which is a huge security hole. 
+
+debug = false
+email_to = you@yourdomain.com
+smtp_server = localhost
+error_email_from = paste@localhost
+
+[server:main]
+use = egg:Paste#http
+host = 0.0.0.0
+port = 8080
+
+[app:main]
+use = egg:sipbmp3-web
+full_stack = true
+cache_dir = %(here)s/data
+beaker.session.key = sipbmp3web
+beaker.session.secret = ${app_instance_secret}
+app_instance_uuid = ${app_instance_uuid}
+
+# If you'd like to fine-tune the individual locations of the cache data dirs
+# for the Cache data, or the Session saves, un-comment the desired settings
+# here:
+#beaker.cache.data_dir = %(here)s/data/cache
+#beaker.session.data_dir = %(here)s/data/sessions
+# Specify the database for SQLAlchemy to use via
+# turbogears.database
+# %(here) may include a ':' character on Windows environments; this can
+# invalidate the URI when specifying a SQLite db via path name
+sqlalchemy.url = sqlite:///%(here)s/somedb.db
+sqlalchemy.echo = False
+
+# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
+# Debug mode will enable the interactive debugging tool, allowing ANYONE to
+# execute malicious code after an exception is raised.
+#set debug = false
+
+# Logging configuration
+# Add additional loggers, handlers, formatters here
+# Uses python's logging config file format
+# http://docs.python.org/lib/logging-config-fileformat.html
+
+[loggers]
+keys = root, sipbmp3web, sqlalchemy, auth
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+# If you create additional loggers, add them as a key to [loggers]
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_sipbmp3web]
+level = INFO
+handlers =
+qualname = sipbmp3web
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+# "level = INFO" logs SQL queries.
+# "level = DEBUG" logs SQL queries and results.
+# "level = WARN" logs neither.  (Recommended for production systems.)
+
+
+# A logger for authentication, identification and authorization -- this is
+# repoze.who and repoze.what:
+[logger_auth]
+level = WARN
+handlers = 
+qualname = auth
+
+# If you create additional handlers, add them as a key to [handlers]
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+# If you create additional formatters, add them as a key to [formatters]
+[formatter_generic]
+format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
Index: gutenbach-web/development.ini
===================================================================
--- gutenbach-web/development.ini	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/development.ini	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,107 @@
+#
+# sipbmp3-web - Pylons development environment configuration
+#
+# The %(here)s variable will be replaced with the parent directory of this file
+#
+# This file is for deployment specific config options -- other configuration
+# that is always required for the app is done in the config directory, 
+# and generally should not be modified by end users. 
+
+[DEFAULT]
+debug = true
+# Uncomment and replace with the address which should receive any error reports
+#email_to = you@yourdomain.com
+smtp_server = localhost
+error_email_from = paste@localhost
+
+[server:main]
+use = egg:Paste#http
+host = 127.0.0.1 
+port = 8080
+
+[app:main]
+use = egg:sipbmp3-web
+full_stack = true
+#lang = ru
+cache_dir = %(here)s/data
+beaker.session.key = sipbmp3web
+beaker.session.secret = somesecret
+
+# If you'd like to fine-tune the individual locations of the cache data dirs
+# for the Cache data, or the Session saves, un-comment the desired settings
+# here:
+#beaker.cache.data_dir = %(here)s/data/cache
+#beaker.session.data_dir = %(here)s/data/sessions
+
+# pick the form for your database
+# %(here) may include a ':' character on Windows environments; this can
+# invalidate the URI when specifying a SQLite db via path name
+# sqlalchemy.url=postgres://username:password@hostname:port/databasename 
+# sqlalchemy.url=mysql://username:password@hostname:port/databasename
+
+
+# If you have sqlite, here's a simple default to get you started
+# in development
+
+sqlalchemy.url = sqlite:///%(here)s/devdata.db
+#echo shouldn't be used together with the logging module.
+sqlalchemy.echo = false
+sqlalchemy.echo_pool = false
+sqlalchemy.pool_recycle = 3600
+
+# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
+# Debug mode will enable the interactive debugging tool, allowing ANYONE to
+# execute malicious code after an exception is raised.
+#set debug = false
+
+# Logging configuration
+# Add additional loggers, handlers, formatters here
+# Uses python's logging config file format
+# http://docs.python.org/lib/logging-config-fileformat.html
+
+[loggers]
+keys = root, sipbmp3web, sqlalchemy, auth
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+# If you create additional loggers, add them as a key to [loggers]
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_sipbmp3web]
+level = DEBUG
+handlers =
+qualname = sipbmp3web
+
+[logger_sqlalchemy]
+level = INFO
+handlers =
+qualname = sqlalchemy.engine
+# "level = INFO" logs SQL queries.
+# "level = DEBUG" logs SQL queries and results.
+# "level = WARN" logs neither.  (Recommended for production systems.)
+
+
+# A logger for authentication, identification and authorization -- this is
+# repoze.who and repoze.what:
+[logger_auth]
+level = WARN
+handlers = 
+qualname = auth
+
+# If you create additional handlers, add them as a key to [handlers]
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+# If you create additional formatters, add them as a key to [formatters]
+[formatter_generic]
+format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
Index: gutenbach-web/dispatch.fcgi.in
===================================================================
--- gutenbach-web/dispatch.fcgi.in	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/dispatch.fcgi.in	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+# If you're using virtualenv, make sure you edit the shebang to use
+# that binary.
+
+# This is a sample dispatch.fcgi file. You will need to copy it to
+# a web accessible directory, and change some of the configuration
+# options.
+
+# This is an ugly hack for some Fedora specific beahvior
+__requires__ = ''
+
+import sys, os
+
+# Where is the actual application turbogears code located?
+turbogears = "/path/to/turbogears"
+
+sys.path.insert(0, turbogears)
+
+# Load the WSGI application from the config file
+from paste.deploy import loadapp
+wsgi_app = loadapp('config:' + turbogears + '/development.ini')
+
+# Deploy it using FastCGI
+if __name__ == '__main__':
+    from flup.server.fcgi import WSGIServer
+    WSGIServer(wsgi_app).run()
+
+
Index: gutenbach-web/entry_points.txt
===================================================================
--- gutenbach-web/entry_points.txt	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/entry_points.txt	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,7 @@
+
+    [paste.app_factory]
+    main = sipbmp3web.config.middleware:make_app
+
+    [paste.app_install]
+    main = pylons.util:PylonsInstaller
+    
Index: gutenbach-web/environment.py
===================================================================
--- gutenbach-web/environment.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/environment.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,4 @@
+from sipbmp3web.config.app_cfg import base_config
+
+#Use base_config to setup the environment loader function
+load_environment = base_config.make_load_environment()
Index: gutenbach-web/error.html
===================================================================
--- gutenbach-web/error.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/error.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,19 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <xi:include href="master.html" />
+
+<head>
+  <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+  <title>A ${code} Error has Occurred </title>
+</head>
+
+<body>
+<h1>Error ${code}</h1>
+
+<div>${XML(message)}</div>
+</body>
+</html>
Index: gutenbach-web/error.py
===================================================================
--- gutenbach-web/error.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/error.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,29 @@
+import os.path
+
+import paste.fileapp
+import tg
+from pylons.controllers.util import forward
+from pylons.middleware import error_document_template, media_path
+
+from sipbmp3web.lib.base import BaseController
+
+class ErrorController(object):
+    """Generates error documents as and when they are required.
+
+    The ErrorDocuments middleware forwards to ErrorController when error
+    related status codes are returned from the application.
+
+    This behaviour can be altered by changing the parameters to the
+    ErrorDocuments middleware in your config/middleware.py file.
+    """
+
+    @tg.expose('sipbmp3web.templates.error')
+    def document(self, *args, **kwargs):
+        """Render the error document"""
+        resp = tg.request.environ.get('pylons.original_response')
+        default_message = ("<p>We're sorry but we weren't able to process "
+        " this request.</p>")
+        values = dict(prefix=tg.request.environ.get('SCRIPT_NAME', ''),
+                 code=tg.request.params.get('code', resp.status_int),
+                 message=tg.request.params.get('message', default_message))
+        return values
Index: gutenbach-web/footer.html
===================================================================
--- gutenbach-web/footer.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/footer.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,17 @@
+<html xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude"
+      py:strip="">
+<py:def function="footer">
+<div id="footer"> 
+  <div class="flogo">
+    <img src="${tg.url('/images/under_the_hood_blue.png')}" alt="TurboGears" />
+    <p><a href="http://www.turbogears.org/2.0/">Powered by TurboGears 2</a></p>
+  </div>
+  <div class="foottext">
+    <p>TurboGears is a open source front-to-back web development
+      framework written in Python. Copyright (c) 2005-2008 </p>
+  </div>
+  <div class="clearingdiv"></div>
+</div>
+</py:def>    
+</html>
Index: gutenbach-web/header.html
===================================================================
--- gutenbach-web/header.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/header.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,12 @@
+<html xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude"
+      py:strip="">
+<py:def function="header">
+  <div id="header">
+  	<h1>
+  		Welcome to TurboGears 2
+		<span class="subtitle">The Python web metaframework</span>
+	</h1>
+  </div>
+</py:def>
+</html>
Index: gutenbach-web/helpers.py
===================================================================
--- gutenbach-web/helpers.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/helpers.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,1 @@
+from webhelpers import date, feedgenerator, html, number, misc, text 
Index: gutenbach-web/index.html
===================================================================
--- gutenbach-web/index.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/index.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,40 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
+                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <xi:include href="master.html" />
+
+<head>
+  <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+  <title>Welcome to TurboGears 2.0, standing on the 
+  shoulders of giants, since 2007</title>
+</head>
+
+<body>
+    ${sidebar_top()}
+  <div id="getting_started">
+    <h2>Presentation</h2>
+    <p>TurboGears 2 is rapid web application development toolkit designed to make your life easier.</p>
+    <ol id="getting_started_steps">
+      <li class="getting_started">
+        <h3>Code your data model</h3>
+        <p> Design your data model, Create the database, and Add some bootstrap data.</p>
+      </li>
+      <li class="getting_started">
+        <h3>Design your URL architecture</h3>
+        <p> Decide your URLs, Program your controller methods, Design your 
+            templates, and place some static files (CSS and/or JavaScript). </p>
+      </li>
+      <li class="getting_started">
+        <h3>Distribute your app</h3>
+        <p> Test your source, Generate project documents, Build a distribution.</p>
+      </li>
+    </ol>
+  </div>
+  <div class="clearingdiv" />
+  <div class="notice"> Thank you for choosing TurboGears. 
+  </div>
+</body>
+</html>
Index: gutenbach-web/login.html
===================================================================
--- gutenbach-web/login.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/login.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,24 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
+                    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+    xmlns:py="http://genshi.edgewall.org/"
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<xi:include href="master.html" />
+
+<head>
+<meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+<title>Login Form</title>
+</head>
+
+<body>
+<div id="loginform">
+<form action="${tg.url('/login_handler', came_from = came_from.encode('utf-8'), __logins = login_counter.encode('utf-8'))}" method="POST" class="loginfields">
+    <h2><span>Login</span></h2>
+    <label for="login">Username:</label><input type="text" id="login" name="login" class="text"></input><br/>
+    <label for="password">Password:</label><input type="password" id="password" name="password" class="text"></input>
+    <input type="submit" id="submit" value="Login" />
+</form>
+</div>
+</body>
+</html>
Index: gutenbach-web/master.html
===================================================================
--- gutenbach-web/master.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/master.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,44 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude"
+      py:strip="">
+    <xi:include href="header.html" />
+    <xi:include href="sidebars.html" />
+    <xi:include href="footer.html" />
+<head py:match="head" py:attrs="select('@*')">
+    <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+    <title py:replace="''">Your title goes here</title>
+    <meta py:replace="select('*')"/>
+    <link rel="stylesheet" type="text/css" media="screen" href="${tg.url('/css/style.css')}" />
+</head>
+
+<body py:match="body" py:attrs="select('@*')">
+  ${header()}
+  <ul id="mainmenu">
+    <li class="first"><a href="${tg.url('/')}" class="${('', 'active')[defined('page') and page==page=='index']}">Welcome</a></li>
+        <li><a href="${tg.url('/about')}" class="${('', 'active')[defined('page') and page==page=='about']}">About</a></li>
+        <li py:if="tg.auth_stack_enabled"><a href="${tg.url('/auth')}" class="${('', 'active')[defined('page') and page==page=='auth']}">Authentication</a></li>
+        <li><a href="http://groups.google.com/group/turbogears">Contact</a></li>
+    <span py:if="tg.auth_stack_enabled" py:strip="True">
+        <li py:if="not request.identity" id="login" class="loginlogout"><a href="${tg.url('/login')}">Login</a></li>
+        <li py:if="request.identity" id="login" class="loginlogout"><a href="${tg.url('/logout_handler')}">Logout</a></li>
+        <li py:if="request.identity" id="admin" class="loginlogout"><a href="${tg.url('/admin')}">Admin</a></li>
+    </span>
+  </ul>
+  <div id="content">
+    <py:if test="defined('page')">
+    <div class="currentpage">
+     Now Viewing: <span py:replace="page"/>
+     </div>
+    </py:if>
+    <py:with vars="flash=tg.flash_obj.render('flash', use_js=False)">
+        <div py:if="flash" py:content="XML(flash)" />
+    </py:with>
+    <div py:replace="select('*|text()')"/>
+    <!-- End of main_content -->
+    ${footer()}
+  </div>
+</body>
+</html>
Index: gutenbach-web/middleware.py
===================================================================
--- gutenbach-web/middleware.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/middleware.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,14 @@
+"""TurboGears middleware initialization"""
+from sipbmp3web.config.app_cfg import base_config
+from sipbmp3web.config.environment import load_environment
+
+#Use base_config to setup the necessary WSGI App factory. 
+#make_base_app will wrap the TG2 app with all the middleware it needs. 
+make_base_app = base_config.setup_tg_wsgi_app(load_environment)
+
+def make_app(global_conf, full_stack=True, **app_conf):
+    app = make_base_app(global_conf, full_stack=True, **app_conf)
+    #Wrap your base turbogears app with custom middleware
+    return app
+
+
Index: gutenbach-web/model.template
===================================================================
--- gutenbach-web/model.template	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/model.template	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,14 @@
+from sqlalchemy import *
+from sqlalchemy.orm import mapper, relation
+from sqlalchemy import Table, ForeignKey, Column
+from sqlalchemy.types import Integer, Unicode
+#from sqlalchemy.orm import relation, backref
+
+from sipbmp3web.model import DeclarativeBase, metadata, DBSession
+
+class SampleModel(DeclarativeBase):
+    __tablename__ = 'sample_model'
+
+    id = Column(Integer, primary_key=True)
+    data = Column(Unicode(255), nullable=False)
+
Index: gutenbach-web/paster_plugins.txt
===================================================================
--- gutenbach-web/paster_plugins.txt	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/paster_plugins.txt	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,4 @@
+PasteScript
+Pylons
+TurboGears2
+tg.devtools
Index: gutenbach-web/requires.txt
===================================================================
--- gutenbach-web/requires.txt	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/requires.txt	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,4 @@
+TurboGears2
+ToscaWidgets >= 0.9.1
+zope.sqlalchemy
+repoze.what-quickstart
Index: gutenbach-web/root.py
===================================================================
--- gutenbach-web/root.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/root.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,59 @@
+"""Main Controller"""
+from sipbmp3web.lib.base import BaseController
+from tg import expose, flash, require, url, request, redirect
+from pylons.i18n import ugettext as _
+#from tg import redirect, validate
+from sipbmp3web.model import DBSession, metadata
+from sipbmp3web.controllers.error import ErrorController
+from sipbmp3web import model
+from catwalk.tg2 import Catwalk
+from repoze.what import predicates
+from sipbmp3web.controllers.secure import SecureController
+
+class RootController(BaseController):
+    admin = Catwalk(model, DBSession)
+    error = ErrorController()
+
+    @expose('sipbmp3web.templates.index')
+    def index(self):
+        return dict(page='index')
+
+    @expose('sipbmp3web.templates.about')
+    def about(self):
+        return dict(page='about')
+
+    @expose('sipbmp3web.templates.authentication')
+    def auth(self):
+        return dict(page='auth')
+
+    @expose('sipbmp3web.templates.index')
+    @require(predicates.has_permission('manage', msg=_('Only for managers')))
+    def manage_permission_only(self, **kw):
+        return dict(page='managers stuff')
+
+    @expose('sipbmp3web.templates.index')
+    @require(predicates.is_user('editor', msg=_('Only for the editor')))
+    def editor_user_only(self, **kw):
+        return dict(page='editor stuff')
+
+    @expose('sipbmp3web.templates.login')
+    def login(self, came_from=url('/')):
+        login_counter = request.environ['repoze.who.logins']
+        if login_counter > 0:
+            flash(_('Wrong credentials'), 'warning')
+        return dict(page='login', login_counter=str(login_counter),
+                    came_from=came_from)
+
+    @expose()
+    def post_login(self, came_from=url('/')):
+        if not request.identity:
+            login_counter = request.environ['repoze.who.logins'] + 1
+            redirect(url('/login', came_from=came_from, __logins=login_counter))
+        userid = request.identity['repoze.who.userid']
+        flash(_('Welcome back, %s!') % userid)
+        redirect(came_from)
+
+    @expose()
+    def post_logout(self, came_from=url('/')):
+        flash(_('We hope to see you soon!'))
+        redirect(came_from)
Index: gutenbach-web/secure.py
===================================================================
--- gutenbach-web/secure.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/secure.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,29 @@
+"""Test Secure Controller"""
+from sipbmp3web.lib.base import BaseController
+from tg import expose, flash
+from pylons.i18n import ugettext as _
+#from tg import redirect, validate
+#from sipbmp3web.model import DBSession, metadata
+#from dbsprockets.dbmechanic.frameworks.tg2 import DBMechanic
+#from dbsprockets.saprovider import SAProvider
+from repoze.what.predicates import has_permission
+
+
+class SecureController(BaseController):
+    """Sample controller-wide authorization"""
+
+    # The predicate that must be met for all the actions in this controller:
+    allow_only = has_permission('manage',
+                                msg=_('Only for people with the "manage" permission'))
+
+    @expose('sipbmp3web.templates.index')
+    def index(self):
+        flash(_("Secure Controller here"))
+        return dict(page='index')
+
+    @expose('sipbmp3web.templates.index')
+    def some_where(self):
+        """should be protected because of the require attr
+        at the controller level.
+        """
+        return dict(page='some_where')
Index: gutenbach-web/setup.cfg
===================================================================
--- gutenbach-web/setup.cfg	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/setup.cfg	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,31 @@
+[egg_info]
+tag_build = dev
+tag_svn_revision = true
+
+[easy_install]
+find_links = http://www.pylonshq.com/download/
+
+[nosetests]
+with-pylons=test.ini
+
+# Babel configuration
+[compile_catalog]
+domain = sipbmp3web
+directory = sipbmp3web/i18n
+statistics = true
+
+[extract_messages]
+add_comments = TRANSLATORS:
+output_file = sipbmp3web/i18n/sipbmp3web.pot
+width = 80
+
+[init_catalog]
+domain = sipbmp3web
+input_file = sipbmp3web/i18n/sipbmp3web.pot
+output_dir = sipbmp3web/i18n
+
+[update_catalog]
+domain = sipbmp3web
+input_file = sipbmp3web/i18n/sipbmp3web.pot
+output_dir = sipbmp3web/i18n
+previous = true
Index: gutenbach-web/setup.py
===================================================================
--- gutenbach-web/setup.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/setup.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,44 @@
+try:
+    from setuptools import setup, find_packages
+except ImportError:
+    from ez_setup import use_setuptools
+    use_setuptools()
+    from setuptools import setup, find_packages
+
+setup(
+    name='sipbmp3-web',
+    version='0.1',
+    description='',
+    author='',
+    author_email='',
+    #url='',
+    install_requires=[
+        "TurboGears2",
+        "ToscaWidgets >= 0.9.1",
+        "zope.sqlalchemy",
+        
+        "repoze.what-quickstart",
+                ],
+    setup_requires=["PasteScript>=1.6.3"],
+    paster_plugins=['PasteScript', 'Pylons', 'TurboGears2', 'tg.devtools'],
+    packages=find_packages(exclude=['ez_setup']),
+    include_package_data=True,
+    test_suite='nose.collector',
+    tests_require=['WebTest', 'BeautifulSoup'],
+    package_data={'sipbmp3web': ['i18n/*/LC_MESSAGES/*.mo',
+                                 'templates/*/*',
+                                 'public/*/*']},
+    message_extractors = {'sipbmp3web': [
+            ('**.py', 'python', None),
+            ('templates/**.mako', 'mako', None),
+            ('templates/**.html', 'genshi', None),
+            ('public/**', 'ignore', None)]},
+
+    entry_points="""
+    [paste.app_factory]
+    main = sipbmp3web.config.middleware:make_app
+
+    [paste.app_install]
+    main = pylons.util:PylonsInstaller
+    """,
+)
Index: gutenbach-web/sidebars.html
===================================================================
--- gutenbach-web/sidebars.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/sidebars.html	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,35 @@
+<html xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude"
+      py:strip="">
+
+<py:def function="sidebar_top">
+  <div id="sb_top" class="sidebar">
+      <h2>Get Started with TG2</h2>
+      <ul class="links">
+        <li py:choose="">
+          <span py:when="page=='index'"><a href="${tg.url('/about')}">About this page</a> A quick guide to this TG2 site </span>
+          <span py:otherwise=""><a href="${tg.url('/')}">Home</a> Back to your Quickstart Home page </span>
+        </li>
+        <li><a href="http://www.turbogears.org/2.0/docs/">TG2 Documents</a> - Read everything in the Getting Started section</li>
+        <li><a href="http://docs.turbogears.org/1.0">TG1 docs</a> (still useful, although a lot has changed for TG2) </li>
+        <li><a href="http://groups.google.com/group/turbogears"> Join the TG Mail List</a> for general TG use/topics  </li>
+      </ul>
+  </div>
+</py:def>    
+
+<py:def function="sidebar_bottom">
+  <div id="sb_bottom" class="sidebar">
+      <h2>Developing TG2</h2>
+      <ul class="links">
+        <li><a href="http://docs.turbogears.org/2.0/RoughDocs/">More TG2 Documents</a> in progress</li>
+        <li><a href="http://trac.turbogears.org/query?status=new&amp;status=assigned&amp;status=reopened&amp;group=type&amp;milestone=2.0&amp;order=priority">TG2 Trac tickets</a> What's happening now in TG2 development</li>
+        <li><a href="http://trac.turbogears.org/timeline">TG Dev timeline</a> (recent ticket updates, svn checkins, wiki changes)</li>
+        <li><a href="http://svn.turbogears.org/trunk">TG2 SVN repository</a> For checking out a copy</li>
+        <li><a href="http://turbogears.org/2.0/docs/main/Contributing.html#installing-the-development-version-of-turbogears-2-from-source">Follow these instructions</a> For installing your copy</li>
+        <li><a href="http://trac.turbogears.org/browser/trunk">TG2 Trac's svn view</a> In case you need a quick look</li>
+        <li><a href="http://groups.google.com/group/turbogears-trunk"> Join the TG-Trunk Mail List</a> for TG2 discuss/dev </li>
+      </ul>
+  </div>
+</py:def>    
+
+</html>
Index: gutenbach-web/sipbmp3web.po
===================================================================
--- gutenbach-web/sipbmp3web.po	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/sipbmp3web.po	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,24 @@
+# Russian translations for ${package}.
+# Copyright (C) 2008 ORGANIZATION
+# This file is distributed under the same license as the ${package} project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ${package} 0.0.0\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2008-01-13 14:00+0200\n"
+"PO-Revision-Date: 2008-01-13 14:00+0200\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: ru <LL@li.org>\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.1\n"
+
+#: ${package}/controllers/root.py:13
+msgid "Your application is now running"
+msgstr "Ваши приложение успешно запущено"
+
Index: gutenbach-web/style.css
===================================================================
--- gutenbach-web/style.css	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/style.css	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,307 @@
+html {
+	background: #555555 url('../images/pagebg.png') top left repeat-x;
+	text-align: center;
+	margin: 0;
+	padding: 0;
+}
+
+body {
+	text-align: left;
+	width: 960px;
+	margin: 0 auto;
+	padding: 0;
+	font-size: 12px;
+	font-family:"Lucida Grande","Lucida Sans Unicode",geneva,verdana,sans-serif;
+}
+
+a {
+	color: #286571;
+}
+
+#header {
+	height: 132px;
+	margin: 10px 10px 0 10px;
+	background: url('../images/headerbg.png') top left no-repeat;
+}
+
+#header h1 {
+	padding: 0;
+	margin: 0;
+	padding-top: 30px;
+	padding-left: 180px;
+	color: #fff;
+	position: relative;
+	font-size: 36px;
+}
+
+#header h1 .subtitle {
+	font-size: 60%;
+	position: absolute;
+	left: 240px;
+	top: 70px;
+}
+
+ul#mainmenu {
+	margin: 0;
+	padding: 0 10px;
+	background: url('../images/menubg.png') top left no-repeat;
+	height: 38px;
+}
+
+ul#mainmenu li {
+	list-style-type: none;
+	margin: 0;
+	padding: 0;
+	position: relative;
+	display: inline;
+	float: left;
+}
+
+ul#mainmenu li a {
+	color: #fff;
+	float: left;
+	height: 31px;
+	display: block;
+	line-height: 30px;
+	vertical-align: middle;
+	padding: 0 10px;
+	font-size: 12px;
+	text-decoration: none;
+	background: url('../images/menu-item-border.png') left top no-repeat;
+}
+
+ul#mainmenu li a:hover, ul#mainmenu li a.active {
+	background: url('../images/menu-item-actibg.png') left top no-repeat;
+}
+
+ul#mainmenu li.first a {
+	background: none;
+}
+
+ul#mainmenu li.first a:hover, ul#mainmenu li.first a.active {
+	background: url('../images/menu-item-actibg-first.png') left top no-repeat;
+}
+
+ul#mainmenu li.loginlogout
+{
+    float: right;
+    right: 10px;
+}
+
+ul#mainmenu li.loginlogout a:hover
+{
+    background: url('../images/menu-item-border.png') left top no-repeat;
+}
+
+#content {
+	background: #fff url('../images/contentbg.png') left bottom no-repeat;
+	margin : 0 10px 10px 10px;
+	padding: 0 10px;
+}
+
+#content .currentpage {
+	margin-top: 0;
+}
+
+/* Login form*/
+
+#loginform
+{
+    text-align: center;
+}
+
+form.loginfields
+{
+    text-align: left;
+    width: 270px;
+    background: url('../images/loginbg.png') top left no-repeat;
+    padding: 0;
+    margin: 0 auto;
+    border: 0;
+}
+
+form.loginfields h2
+{
+    font-size: 16px;
+    float: left;
+    padding: 0;
+    margin: 5px;
+    margin-top: 0;
+    background: url('../images/loginheader-left.png') top left no-repeat;
+}
+
+* html form.loginfields h2
+{
+    margin-left: 3px;
+    width: 80px;
+    text-align: center;
+}
+
+form.loginfields h2 span
+{
+    background: url('../images/loginheader-right.png') right top no-repeat;
+    height: 30px;
+    line-height: 30px;
+    display: block;
+    font-size: 100%;
+    font-weight: normal;
+    color: #fff;
+    padding: 0 10px;
+}
+
+*+html form.loginfields h2 span
+{
+    padding: 0 20px;
+}
+
+form.loginfields label
+{
+    clear: left;
+    float: left;
+    margin-top: 5px;
+    margin-left: 10px;
+    width: 65px;
+    line-height: 31px;
+    vertical-align: middle;
+}
+
+form.loginfields input.text
+{
+    float: left;
+    margin-left: 10px;
+    margin-top: 5px;
+    width: 165px;
+    height: 21px;
+    padding: 5px;
+    border: 0;
+    background: url('../images/inputbg.png') top left no-repeat;
+    color: #fff;
+}
+
+form.loginfields input#submit
+{
+    background: url('../images/loginbottombg.png') bottom left no-repeat;
+    width: 270px;
+    height: 61px;
+    border: 0;
+    margin-top: 10px;
+    color: #fff;
+    padding: 10px 140px 20px 10px;
+}
+
+* html form.loginfields input#submit
+{
+    clear: both;
+    margin-left: -10px;
+}
+
+/* Sidebar */
+.sidebar {
+  border: 1px solid #cce;
+  background-color: #eee;
+  margin: 0.5em;
+  margin-left: 1.5em;
+  padding: 1em;
+  float: right;
+  width: 200px;
+  font-size: 88%;
+  background-color: #fffe1;
+  background-repeat:repeat-x;
+}
+
+.sidebar h2 {
+  margin-top: 0;
+  color: black;
+}
+
+.sidebar ul {
+  margin-left: 1.5em;
+  padding-left: 0;
+}
+
+
+
+#sb_top {
+  clear: right;
+}
+
+#sb_bottom {
+  clear: right;
+}
+
+#getting_started {
+  margin-left: 20px;
+}
+
+#getting_started_steps a {
+  text-decoration: none;
+}
+
+#getting_started_steps a:hover {
+  text-decoration: underline;
+}
+
+#getting_started_steps li {
+  margin-bottom: 0.5em;
+}
+
+/* Other and footer */
+#footer {
+	background: #fff;
+	padding: 10px;
+	color:#888888;
+	font-size:90%;
+}
+.flogo {
+	float:left;
+	margin-bottom:0px;
+	padding-left:20px;
+	padding-right:20px;
+	padding-top:0px;
+}
+.foottext p {
+	margin: 0; padding: 0;
+}
+.code {
+font-family:monospace;
+font-size:127%;
+}
+span.code {
+background:#EEEEEE none repeat scroll 0% 0%;
+font-weight:bold;
+}
+#flash {
+font-size:120%;
+font-weight:bolder;
+margin:0pt auto 0.5em;
+width:680px;
+}
+#flash div {
+padding:5px 15px 15px 55px;
+}
+#flash .ok {
+background:#EEEEFF url(../images/ok.png) no-repeat scroll left center;
+border:1px solid #CCCCEE;
+}
+#flash .warning {
+background:#FF9999 url(../images/error.png) no-repeat scroll left center;
+border:1px solid #FF3333;
+}
+#flash .alert {
+background:#FFFF99 url(../images/info.png) no-repeat scroll left center;
+border:1px solid #FFFF00;
+}
+.notice {
+background:#EEEEFF url(../images/info.png) no-repeat scroll left center;
+border:1px solid #CCCCEE;
+margin:0.5em auto;
+padding:15px 10px 15px 55px;
+width:680px;
+}
+.fielderror {
+color:red;
+font-weight:bold;
+}
+div.clearingdiv {
+clear:both;
+}
Index: gutenbach-web/template.py
===================================================================
--- gutenbach-web/template.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/template.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,27 @@
+from sipbmp3web.lib.base import *
+
+class TemplateController(BaseController):
+
+    def view(self, url):
+        """By default, the final controller tried to fulfill the request
+        when no other routes match. It may be used to display a template
+        when all else fails, e.g.::
+
+            def view(self, url):
+                return render('/%s' % url)
+
+        Or if you're using Mako and want to explicitly send a 404 (Not
+        Found) response code when the requested template doesn't exist::
+
+            import mako.exceptions
+
+            def view(self, url):
+                try:
+                    return render('/%s' % url)
+                except mako.exceptions.TopLevelLookupException:
+                    abort(404)
+
+        By default this controller aborts the request with a 404 (Not
+        Found)
+        """
+        abort(404)
Index: gutenbach-web/test.ini
===================================================================
--- gutenbach-web/test.ini	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/test.ini	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,22 @@
+#
+# sipbmp3-web - TurboGears 2 testing environment configuration
+#
+# The %(here)s variable will be replaced with the parent directory of this file
+#
+[DEFAULT]
+debug = true
+# Uncomment and replace with the address which should receive any error reports
+# email_to = you@yourdomain.com
+smtp_server = localhost
+error_email_from = paste@localhost
+
+[server:main]
+use = egg:Paste#http
+host = 0.0.0.0
+port = 5000
+
+[app:main]
+sqlalchemy.url = sqlite:///:memory:
+use = config:development.ini
+
+# Add additional test specific configuration options as necessary.
Index: gutenbach-web/test_models.py
===================================================================
--- gutenbach-web/test_models.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/test_models.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+"""Test suite for the TG app's models"""
+
+from tg.testutil import DBTest
+from nose.tools import eq_
+
+from sipbmp3web import model
+
+
+class TestModel(DBTest):
+    """The base class for testing models in you TG project."""
+    model = model
+
+class TestUser(TestModel):
+    """Test case for the User model."""
+    
+    def setUp(self):
+        super(TestUser, self).setUp()
+        self.member = model.User()
+        self.member.user_name = u"ignucius"
+        self.member.email_address = u"ignucius@example.org"
+        
+    def test_member_creation_username(self):
+        """The member constructor must set the user name right"""
+        eq_(self.member.user_name, u"ignucius")
+        
+    def test_member_creation_email(self):
+        """The member constructor must set the email right"""
+        eq_(self.member.email_address, u"ignucius@example.org")
+    
+    def test_no_permissions_by_default(self):
+        """User objects should have no permission by default."""
+        eq_(len(self.member.permissions), 0)
+        
+    def test_getting_by_email(self):
+        """Users should be fetcheable by their email addresses"""
+        model.DBSession.add(self.member)
+        him = model.User.by_email_address(u"ignucius@example.org")
+        eq_(him, self.member)
Index: gutenbach-web/test_root.py
===================================================================
--- gutenbach-web/test_root.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/test_root.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+
+from sipbmp3web.tests import TestController
+from nose.tools import assert_true
+
+# This is an example of how you can write functional tests for your controller.
+# As opposed to a pure unit-test which test a small unit of functionallity,
+# these functional tests exercise the whole app and it's WSGI stack.
+# Please read http://pythonpaste.org/webtest/ for more information
+
+class TestRootController(TestController):
+    def test_index(self):
+        response = self.app.get('/')
+        msg = 'TurboGears 2 is rapid web application development toolkit '\
+              'designed to make your life easier.'
+        # You can look for specific strings:
+        assert_true(msg in response)
+        # You can also access a BeautifulSoup'ed version
+        # first run $ easy_install BeautifulSoup and then run this test 
+        links = response.html.findAll('a')
+        assert_true(links, "Mummy, there are no links here!")
Index: gutenbach-web/top_level.txt
===================================================================
--- gutenbach-web/top_level.txt	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/top_level.txt	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,1 @@
+sipbmp3web
Index: gutenbach-web/virtualenv-create
===================================================================
--- gutenbach-web/virtualenv-create	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/virtualenv-create	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,20 @@
+#!/bin/bash
+easy_install -i http://www.turbogears.org/2.0/downloads/current/index tg.devtools
+easy_install tg.devtools
+easy_install tg.devtools
+easy_install flup
+
+# Install remctl
+wget http://archives.eyrie.org/software/kerberos/remctl-2.13.tar.gz
+tar -xf remctl-2.13.tar.gz
+rm remctl-2.13.tar.gz
+cd remctl-2.13
+./configure --enable-python
+make
+cd python
+python setup.py build
+python setup.py install
+cd ../..
+rm -rf remctl-2.13
+
+paster --help
Index: gutenbach-web/virtualenv-init.sh
===================================================================
--- gutenbach-web/virtualenv-init.sh	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/virtualenv-init.sh	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,9 @@
+#!/bin/bash
+CURRENT=virtualenv-1.3.2
+wget "http://pypi.python.org/packages/source/v/virtualenv/$CURRENT.tar.gz"
+tar xzf "$CURRENT.tar.gz"
+python "$CURRENT/virtualenv.py" --no-site-packages tg2env
+rm "$CURRENT.tar.gz"
+rm -rf "$CURRENT"
+source tg2env/bin/activate
+source virtualenv-create
Index: gutenbach-web/websetup.py
===================================================================
--- gutenbach-web/websetup.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
+++ gutenbach-web/websetup.py	(revision 973dd91e6f429c40bd3f2b6a1c1927d9a69c322c)
@@ -0,0 +1,52 @@
+"""Setup the sipbmp3-web application"""
+import logging
+
+import transaction
+from tg import config
+
+from sipbmp3web.config.environment import load_environment
+
+log = logging.getLogger(__name__)
+
+def setup_app(command, conf, vars):
+    """Place any commands to setup sipbmp3web here"""
+    load_environment(conf.global_conf, conf.local_conf)
+    # Load the models
+    from sipbmp3web import model
+    print "Creating tables"
+    model.metadata.create_all(bind=config['pylons.app_globals'].sa_engine)
+
+    u = model.User()
+    u.user_name = u'manager'
+    u.display_name = u'Example manager'
+    u.email_address = u'manager@somedomain.com'
+    u.password = u'managepass'
+
+    model.DBSession.add(u)
+
+    g = model.Group()
+    g.group_name = u'managers'
+    g.display_name = u'Managers Group'
+
+    g.users.append(u)
+
+    model.DBSession.add(g)
+
+    p = model.Permission()
+    p.permission_name = u'manage'
+    p.description = u'This permission give an administrative right to the bearer'
+    p.groups.append(g)
+
+    model.DBSession.add(p)
+
+    u1 = model.User()
+    u1.user_name = u'editor'
+    u1.display_name = u'Example editor'
+    u1.email_address = u'editor@somedomain.com'
+    u1.password = u'editpass'
+
+    model.DBSession.add(u1)
+    model.DBSession.flush()
+
+    transaction.commit()
+    print "Successfully setup"
