#!/bin/bash

##
# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# DRI: Wilfredo Sanchez, wsanchez@apple.com
##

set -e
set -u

wd="$(cd "$(dirname "$0")" && pwd)";

##
# Command line
##

      verbose="";
   setup_only="false";
  force_setup="false";
disable_setup="false";
   print_path="false";
      install="";
    daemonize="-X";
         kill="false";
      restart="false";
  plugin_name="caldav";
 service_type="Combined";
     read_key="";
      profile="";

usage ()
{
  program="$(basename "$0")";

  if [ "${1--}" != "-" ]; then echo "$@"; echo; fi;

  echo "Usage: ${program} [-hvsfpndkr] [-K key] [-iI dst] [-t type] [-S statsfile]";
  echo "Options:";
  echo "	-h  Print this help and exit";
  echo "	-v  Be verbose";
  echo "	-s  Run setup only; don't run server";
  echo "	-f  Force setup to run";
  echo "	-p  Print PYTHONPATH value for server and exit";
  echo "	-n  Do not run setup";
  echo "        -d  Run caldavd as a daemon";
  echo "        -k  Stop caldavd";
  echo "        -r  Restart caldavd";
  echo "        -K  Print value of configuration key and exit";
  echo "        -i  Perform a system install into dst; implies -s";
  echo "        -I  Perform a home install into dst; implies -s";
  echo "        -t  Select the server process type (Master, Slave or Combined)";
  echo "        -S  Write a pstats object to the given file when the server is stopped.";

  if [ "${1-}" == "-" ]; then return 0; fi;
  exit 64;
}

while getopts 'hvsfnpkrdP:i:I:t:K:S:' option; do
  case "$option" in
    '?') usage; ;;
    'h') usage -; exit 0; ;;
    'v')       verbose="-v"; ;;
    's')    setup_only="true"; ;;
    'f')   force_setup="true"; ;;
    'n') disable_setup="true"; ;;
    'p')    print_path="true"; ;;
    'k')          kill="true"; ;;
    'r')       restart="true"; ;;
    'd')     daemonize=""; ;;
    'P')   plugin_name="${OPTARG}"; ;;
    'i')    setup_only="true"; install="${OPTARG}"; install_flag="--root="; ;;
    'I')    setup_only="true"; install="${wd}/build/dst"; install_flag="--root="; install_home="${OPTARG}"; ;;
    't')  service_type="${OPTARG}"; ;;
    'K')      read_key="${OPTARG}"; ;;
    'S')       profile="-p ${OPTARG}"; ;;
  esac;
done;
shift $((${OPTIND} - 1));

if [ $# != 0 ]; then usage "Unrecognized arguments:" "$@"; fi;

##
# Set up paths
##

py_version ()
{
  local python="$1"; shift;
  echo "$("${python}" -c "from distutils.sysconfig import get_python_version; print get_python_version()")";
}

try_python ()
{
  local python="$1"; shift;

  if [ -z "${python}" ]; then return 1; fi;

  if ! type "${python}" > /dev/null 2>&1; then return 1; fi;
  local py_version="$(py_version "${python}")";
  if [ "${py_version/./}" -lt "24" ]; then return 1; fi;

  return 0;
}

for v in "" "2.5" "2.4"; do
  for p in								\
    "${PYTHON:=}"							\
    "python${v}"							\
    "/usr/local/bin/python${v}"						\
    "/usr/local/python/bin/python${v}"					\
    "/usr/local/python${v}/bin/python${v}"				\
    "/opt/bin/python${v}"						\
    "/opt/python/bin/python${v}"					\
    "/opt/python${v}/bin/python${v}"					\
    "/Library/Frameworks/Python.framework/Versions/${v}/bin/python"	\
    "/opt/local/bin/python${v}"						\
    "/sw/bin/python${v}"						\
    ;
  do
    if try_python "${p}"; then python="${p}"; break; fi;
  done;
  if [ -n "${python:-}" ]; then break; fi;
done;

if [ -z "${python:-}" ]; then
  echo "No suitable python found.";
  exit 1;
fi;

if [ -z "${caldav:-}" ]; then
  caldav="${wd}";
fi;

if [ -z "${caldavd_wrapper_command:-}" ]; then
  if [ "$(uname -s)" == "Darwin" ] && [ "$(uname -r | cut -d . -f 1)" -ge 9 ]; then
      caldavd_wrapper_command="launchctl bsexec /";
  else
      caldavd_wrapper_command="";
  fi;
fi;

    top="$(cd "${caldav}/.." && pwd -L)"
patches="${caldav}/lib-patches";
twisted="${top}/Twisted";
    dav="${twisted}/twisted/web2/dav"

if [ -z "${config:-}" ]; then
  config="${wd}/conf/caldavd-dev.plist";
fi;

conf_read_key ()
{
  local key="$1"; shift;

  # FIXME: This only works for simple values (no arrays, dicts)
  tr '\n' ' ' < "${config}"                                                    \
    | xpath "/plist/dict/*[preceding-sibling::key[1]='${key}'" 2>/dev/null \
    | sed -n 's|^<[^<][^<]*>\([^<]*\)</[^<][^<]*>.*$|\1|p';
}

if [ -n "${read_key}" ]; then
    conf_read_key "${read_key}";
    exit $?;
fi;

if "${kill}" || "${restart}"; then
    pidfile="$(conf_read_key "PIDFile")";
    if [ ! -r "${pidfile}" ]; then
        echo "Unreadable PID file: ${pidfile}";
        exit 1;
    fi;
    pid="$(cat "${pidfile}" | head -1)";
    if [ -z "${pid}" ]; then
        echo "No PID in PID file: ${pidfile}";
        exit 1;
    fi;
    echo "Killing process ${pid}";
    kill -TERM "${pid}";
    if ! "${restart}"; then
        exit 0;
    fi;
fi;

if [ -z "${PYTHONPATH:-}" ]; then
  user_python_path="";
else
  user_python_path=":${PYTHONPATH}";
fi;

       py_platform="$("${python}" -c "from distutils.util import get_platform; print get_platform()")";
        py_version="$(py_version "${python}")";
py_platform_libdir="lib.${py_platform}-${py_version}";

if [ -n "${install}" ] && ! echo "${install}" | grep '^/' > /dev/null; then
  install="$(pwd)/${install}";
fi;

svn_uri_base="$(svn info "${caldav}" --xml 2>/dev/null | sed -n 's|^.*<root>\(.*\)</root>.*$|\1|p')";

if [ -z "${svn_uri_base}" ]; then
  svn_uri_base="http://svn.calendarserver.org/repository/calendarserver";
fi;

export PYTHONPATH="${caldav}";

##
# Download and set up dependancies
##

run () {
  if "${print_path}"; then
    echo "${PYTHONPATH}";
    exit 0;
  fi;

  if ! "${setup_only}"; then
    if [ ! -f "${config}" ]; then
      echo "Missing config file: ${config}";
      echo "You might want to start by copying the test configuration:";
      echo "  cp conf/caldavd-test.plist conf/caldavd-dev.plist";
      exit 1;
    fi;

    cd "${wd}";
    if [ ! -d "${wd}/logs" ]; then
      mkdir "${wd}/logs";
    fi;
    exec ${caldavd_wrapper_command}          \
        "${caldav}/bin/caldavd" ${daemonize} \
        -f "${config}"                       \
        -T "${twisted}/bin/twistd"           \
        -P "${plugin_name}"                  \
        -t "${service_type}"                 \
        ${profile};
    cd /;
  fi;
}

apply_patches () {
  local name="$1"; shift;
  local path="$1"; shift;

  if [ -d "${patches}/${name}" ]; then
    echo "";
    echo "Applying patches to ${name} in ${path}...";

    cd "${path}";
    find "${patches}/${name}"                  \
        -type f                                \
        -name '*.patch'                        \
        -print                                 \
        -exec patch -p0 --forward -i '{}' ';';
    cd /;

  fi;

  echo ""
  echo "Removing build directory ${path}/build..."
  rm -rf "${path}/build";
  echo "Removing pyc files from ${path}..."
  find "${path}" -type f -name '*.pyc' -print0 | xargs -0 rm -f;
}

www_get () {
  if "${disable_setup}"; then return 0; fi;

  local name="$1"; shift;
  local path="$1"; shift;
  local  url="$1"; shift;

  local ext="$(echo "${url}" | sed 's|^.*\.\([^.]*\)$|\1|')";

  case "${ext}" in
    gz|tgz) decompress="gzip -d -c"; ;;
    bz2)    decompress="bzip2 -d -c"; ;;
    tar)    decompress="cat"; ;;
    *)
      echo "Unknown extension: ${ext}";
      exit 1;
      ;;
  esac;

  if "${force_setup}" || [ ! -d "${path}" ]; then
    echo "";
    echo "Downloading ${name}...";
    rm -rf "${path}";
    cd "$(dirname "${path}")";
    curl "${url}" | ${decompress} | tar xvf -;
    apply_patches "${name}" "${path}";
    cd /;
  fi;
}

svn_get () {
  if "${disable_setup}"; then return 0; fi;

  local     name="$1"; shift;
  local     path="$1"; shift;
  local      uri="$1"; shift;
  local revision="$1"; shift;

  if [ -d "${path}" ]; then
    wc_uri="$(svn info --xml "${path}" 2> /dev/null | sed -n 's|^.*<url>\(.*\)</url>.*$|\1|p')";

    if "${force_setup}"; then
      # Verify that we have a working copy checked out from the correct URI
      if [ "${wc_uri}" != "${uri}" ]; then
        echo "";
        echo "Current working copy (${path}) is from the wrong URI: ${wc_uri} != ${uri}";
        rm -rf "${path}";
        svn_get "${name}" "${path}" "${uri}" "${revision}";
        return $?;
      fi;

      echo "";

      echo "Reverting ${name}...";
      svn revert -R "${path}";

      echo "Updating ${name}...";
      svn update -r "${revision}" "${path}";

      apply_patches "${name}" "${path}";
    else
      if ! "${print_path}"; then
        # Verify that we have a working copy checked out from the correct URI
        if [ "${wc_uri}" != "${uri}" ]; then
          echo "";
          echo "Current working copy (${path}) is from the wrong URI: ${wc_uri} != ${uri}";
          echo "Performing repository switch for ${name}...";
          svn switch -r "${revision}" "${uri}" "${path}";

          apply_patches "${name}" "${path}";
        else
          svnversion="$(svnversion "${path}")";
          if [ "${svnversion%%[M:]*}" != "${revision}" ]; then
            echo "";
            echo "Updating ${name}...";
            svn update -r "${revision}" "${path}";

            apply_patches "${name}" "${path}";
          fi;
        fi;
      fi;
    fi;
  else
    echo "Checking out ${name}...";
    svn checkout -r "${revision}" "${uri}@${revision}" "${path}";

    apply_patches "${name}" "${path}";
  fi;
}

py_build () {
    local     name="$1"; shift;
    local     path="$1"; shift;
    local optional="$1"; shift;

    if ! "${disable_setup}" && (
        "${force_setup}" || [ ! -d "${path}/build/${py_platform_libdir}" ]
    ); then
      echo "";
      echo "Building ${name}...";
      cd "${path}";
      if ! "${python}" ./setup.py build --build-lib "build/${py_platform_libdir}" "$@"; then
          if "${optional}"; then
              echo "WARNING: ${name} failed to build.";
              echo "WARNING: ${name} is not required to run the server; continuing without it.";
          else
              return $?;
          fi;
      fi;
      cd /;
    fi;
}

py_install () {
  local name="$1"; shift;
  local path="$1"; shift;

  if [ -n "${install}" ]; then
    echo "";
    echo "Installing ${name}...";
    cd "${path}";
    "${python}" ./setup.py install "${install_flag}${install}";
    cd /;
  fi;
}

py_have_module () {
    local module="$1"; shift;

    PYTHONPATH="" "${python}" -c "import ${module}" > /dev/null 2>&1;
}

put_setuptools () {
    local where="$1"; shift;

    if ! "${disable_setup}"; then
      cp "${caldav}/support/setuptools-0.6c6-py2.5.egg" "${where}/";
    fi;
}

#
# Zope Interface
#

if ! py_have_module zope.interface; then
  zope="${top}/zope.interface-3.3.0";

  www_get "Zope Interface" "${zope}" http://www.zope.org/Products/ZopeInterface/3.3.0/zope.interface-3.3.0.tar.gz;
  py_build "Zope Interface" "${zope}" false;
  py_install "Zope Interface" "${zope}";

  export PYTHONPATH="${PYTHONPATH}:${zope}/build/${py_platform_libdir}";
fi;

#
# PyXML
#

if ! py_have_module xml.dom.ext; then
  xml="${top}/PyXML-0.8.4";

  www_get "PyXML" "${xml}" http://easynews.dl.sourceforge.net/sourceforge/pyxml/PyXML-0.8.4.tar.gz;
  py_build "PyXML" "${xml}" false;
  py_install "PyXML" "${xml}";

  export PYTHONPATH="${PYTHONPATH}:${xml}/build/${py_platform_libdir}";
fi;

#
# PyOpenSSL
#

if ! py_have_module OpenSSL; then
  ssl="${top}/pyOpenSSL-0.6";

  www_get "PyOpenSSL" "${ssl}" http://easynews.dl.sourceforge.net/sourceforge/pyopenssl/pyOpenSSL-0.6.tar.gz;
  py_build "PyOpenSSL" "${ssl}" false;
  py_install "PyOpenSSL" "${ssl}";

  export PYTHONPATH="${PYTHONPATH}:${ssl}/build/${py_platform_libdir}";
fi;

#
# PyKerberos
#

if type krb5-config > /dev/null; then
  if ! py_have_module kerberos; then
    kerberos="${top}/PyKerberos";

    svn_get "PyKerberos" "${kerberos}" "${svn_uri_base}/PyKerberos/tags/release/PyKerberos-1.0" 1793;
    py_build "PyKerberos" "${kerberos}" false; # FIXME: make optional
    py_install "PyKerberos" "${kerberos}";

    export PYTHONPATH="${PYTHONPATH}:${kerberos}/build/${py_platform_libdir}";
  fi;
fi;

#
# PyOpenDirectory
#

if [ "$(uname -s)" == "Darwin" ]; then
  if ! py_have_module opendirectory; then
    opendirectory="${top}/PyOpenDirectory";

    svn_get "PyOpenDirectory" "${opendirectory}" "${svn_uri_base}/PyOpenDirectory/tags/release/PyOpenDirectory-1.1" 2075;
    py_build "PyOpenDirectory" "${opendirectory}" false;
    py_install "PyOpenDirectory" "${opendirectory}";

    export PYTHONPATH="${PYTHONPATH}:${opendirectory}/build/${py_platform_libdir}";
  fi;
fi;

#
# xattr
#

if ! py_have_module xattr; then
  xattr="${top}/xattr";

  svn_get "xattr" "${xattr}" http://svn.red-bean.com/bob/xattr/releases/xattr-0.5 1013;
  put_setuptools "${xattr}";
  py_build "xattr" "${xattr}" false;
  py_install "xattr" "${xattr}";

  export PYTHONPATH="${PYTHONPATH}:${xattr}/build/${py_platform_libdir}";
fi;

#
# ctypes
#

if ! py_have_module ctypes; then
  ctypes="${top}/ctypes-1.0.0"

  www_get "ctypes" "${ctypes}" http://easynews.dl.sourceforge.net/sourceforge/ctypes/ctypes-1.0.0.tar.gz;
  py_build "ctypes" "${ctypes}" true;
  py_install "ctypes" "${ctypes}";

  export PYTHONPATH="${PYTHONPATH}:${ctypes}/build/${py_platform_libdir}";
fi;

#
# Twisted
#

case "${USER}" in
  wsanchez)
    proto="svn+ssh";
    ;;
  *)
    proto="svn";
    ;;
esac;
svn_uri="${proto}://svn.twistedmatrix.com/svn/Twisted/branches/dav-acl-1608-4";
svn_get "Twisted" "${twisted}" "${svn_uri}" 19773;

# No py_build step, since we tend to do edit Twisted, we want the sources in
# PYTHONPATH, not a build directory.

py_install "Twisted" "${twisted}";
 
export PYTHONPATH="${PYTHONPATH}:${twisted}";

# twisted.web2 doesn't get installed by default
if [ -n "${install}" ]; then
  echo "";
  echo "Installing Twisted.web2...";
  cd "${twisted}";
  "${python}" ./twisted/web2/topfiles/setup.py install "${install_flag}${install}";
  cd /;
fi;

#
# dateutil
#

if ! py_have_module dateutil; then
    dateutil="${top}/python-dateutil-1.2";

    www_get "dateutil" "${dateutil}" http://labix.org/download/python-dateutil/python-dateutil-1.2.tar.bz2;
    py_install "dateutil" "${dateutil}";

    export PYTHONPATH="${PYTHONPATH}:${dateutil}";
fi;

#
# vobject
#

vobject="${top}/vobject";

case "${USER}" in
  cyrusdaboo)
    base="svn+ssh://cdaboo@svn.osafoundation.org/svn";
    ;;
  *)
    base="http://svn.osafoundation.org";
    ;;
esac;
svn_uri="${base}/vobject/trunk";
svn_get "vObject" "${vobject}" "${svn_uri}" 193;
put_setuptools "${vobject}";

py_install "vObject" "${vobject}";

export PYTHONPATH="${PYTHONPATH}:${vobject}/src";

#
# PySQLite
#

if ! py_have_module sqlite3 && ! py_have_module pysqlite2; then
  pysqlite="${top}/pysqlite-2.3.2";

  www_get "PySQLite" "${pysqlite}" http://initd.org/pub/software/pysqlite/releases/2.3/2.3.2/pysqlite-2.3.2.tar.gz;
  py_build "PySQLite" "${pysqlite}" false;
  py_install "PySQLite" "${pysqlite}";

  export PYTHONPATH="${PYTHONPATH}:${pysqlite}/build/${py_platform_libdir}";
fi;

#
# PyDirector
#

if ! py_have_module pydirector; then
  pydirector="${top}/pydirector-1.0.0";
    
  www_get "PyDirector" "${pydirector}" http://easynews.dl.sourceforge.net/sourceforge/pythondirector/pydirector-1.0.0.tar.gz;
  put_setuptools "${pydirector}";
  py_build "PyDirector" "${pydirector}" false;
  py_install "PyDirector" "${pydirector}";

  export PYTHONPATH="${PYTHONPATH}:${pydirector}/build/${py_platform_libdir}";
fi;

#
# Calendar Server
#

py_install "Calendar Server" "${caldav}";

##
# Do home install
# This is a hack, but it's needed because installing with --home doesn't work for python-dateutil.
##

if [ -n "${install_home:-}" ]; then
  py_prefix="$("${python}" -c "import sys; print sys.prefix;")";
  py_libdir="$("${python}" -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1);")";

  install -d "${install_home}";
  install -d "${install_home}/bin";
  install -d "${install_home}/conf";
  install -d "${install_home}/lib/python";

  rsync -av "${install}${py_prefix}/bin/" "${install_home}/bin/";
  rsync -av "${install}${py_libdir}/" "${install_home}/lib/python/";
  rsync -av "${install}${py_prefix}/caldavd/" "${install_home}/caldavd/";

  rm -rf "${install}";
fi;

##
# Run the server
##

export PYTHONPATH="${PYTHONPATH}${user_python_path}";

run;
