#!/bin/bash
#CDMI Tools (v0.3) by Salvatore Pinto - 05.08.2013

function usage {
  echo "Usage:
 $0 -e <endpoint> <action> [options]

<endpoint> is the endpoint of the CDMI service.

<action> is the action to be performed on the storage. Possible actions are:
  auth        Display auth information (for integration with other services)
  list        List contents of the container [default action]
  stat        Get info for the file (as CDMI metadata)
  publicurl   Retreive object public url (for public data access)
  get         Download file
  put         Upload a file (needs -T switch in the arguments)
  post        Update metadata (see post command options for the metadata list)
  delete      Delete a file (or container)
  mkdir       Create folder

Generic options:
  -o | --output <file>      Redirect output to file <file>
  --auth-file <file>        Local authorization file. Default authorization file is $_LOCAL_AUTH .
                            This file cointains the service authorization data. See sample authorization file for more info
  -a | --auth               Override authorization data (format shall be understandable by cURL command line).
  --cdmi-version <version>  Version of the CDMI standard to use (default $_CDMI_VERSION)
  --crl-update              Update CRL before creating a new proxy. Recommended if fetch-crl is not enabled in the cron (ex. on Windows machines)
  --ca-path <path>          Use the following CA path for HTTPs certificates verification (default to /etc/grid-security/certificates if present)
  -k | --insecure           Do not verify HTTPs certificate in the API calls (not recommended)

put command:
 -T | --upload-file <file>  File to upload [mandatory]

delete command:
  -r                        Enable recursive directory deletion

post command:
  -m <metadata>             Add metadata features. Metadata format is pipe separated <name>=<value> . Allowed metadata is
    readacl                   Read permission for containers. Allowed values are '.r:*' or '.'
    writeacl                  Write permission for containers. Allowed values are '.r:*' or '.'
    weblist                   Listing of container contents in web format. Allowed values are 'true' or 'false'
"
  exit 1
}

function error {
  ec="$1"
  dsc="$2"
  if [[ -z "$dsc" ]]; then
    dsc="$ec"
    ec=255
  fi
  [[ -z "$dsc" ]] && dsc="Unknow Error!!!"
  echo "ERROR: $dsc" 1>&2
  exit $ec
}

#Default arguments
_DEBUG=false
_ENDPOINT=
_PATH=
_AUTH=
_LOCAL_AUTH=$0.auth
_ACTION=
_POST_META=
_CDMI_VERSION=1.0.1
_FILE_TO_UPLOAD=
_FILE_OUTPUT=
_RECURSIVE=false
_CRL_UPDATE=false
_CURL_OPTS=

#Parse arguments
[[ -z "$1" ]] && usage
while [[ "$#" -gt 0 ]]; do
  case "$1" in
   -d | --debug) _DEBUG=true; shift 1 ;;
   -a | --auth) _AUTH="$2"; shift 2 ;;
   -A | --auth-file) _LOCAL_AUTH="$2"; shift 2 ;;
   -e | --endpoint) _ENDPOINT="${2%/}"; shift 2 ;;
   --cdmi-version) _CDMI_VERSION="$2"; shift 2 ;;
   -T | --upload-file) _FILE_TO_UPLOAD="$2"; shift 2 ;;
   -o | --output) _FILE_OUTPUT="$2"; shift 2 ;;
   -a | --action) _ACTION="$2"; shift 2 ;;
   -k | --insecure) _CURL_OPTS="$_CURL_OPTS -k"; shift 1;;
   --ca-path)  _CURL_OPTS="$_CURL_OPTS --capath $_CURL_OPTS"; shift 1;;
   -r) _RECURSIVE=true; shift 1 ;;
   -m) _POST_META="$2"; shift 2 ;;
   --crl-update) _CRL_UPDATE=true; shift 1 ;;
   -h | --help) usage ;;
   *)
     if [[ -z "$_ACTION" ]]; then
       _ACTION="$1"; shift 1;
     else
       _PATH="${_PATH%/}/$1"; shift 1;
     fi
    ;;
  esac
done

#Redirect output to a file if required
if [[ -n "$_FILE_OUTPUT" ]]; then
  exec 1>$_FILE_OUTPUT
fi

#If prensent, load authorization file
if [[ -n "$_LOCAL_AUTH" && -e "$_LOCAL_AUTH" ]]; then
  $_DEBUG && echo "[DEBUG] Loading authorization file" >&2
  source "$_LOCAL_AUTH"
  #If present, not expired and related to the same endpoint, load token cache file infomration
  _LOCAL_AUTH_CACHE="${_LOCAL_AUTH}.cache"
  if [[ -e "$_LOCAL_AUTH_CACHE" ]]; then
    CACHE_EXPIRE_TIME="`sed -n 's/^CACHE_EXPIRE_TIME="\\(.*\\)"/\1/p' $_LOCAL_AUTH_CACHE`"
    CACHE_RELATED_ENDPOINT="`sed -n 's/^CACHE_RELATED_ENDPOINT="\\(.*\\)"/\1/p' $_LOCAL_AUTH_CACHE`"
    if [[ -n "$CACHE_EXPIRE_TIME" && `date +%s` -lt `date -d "$CACHE_EXPIRE_TIME" +%s` && "$CACHE_RELATED_ENDPOINT" == "$_ENDPOINT" ]]; then
      $_DEBUG && echo "[DEBUG] Loading cached authorization file data" >&2
      source $_LOCAL_AUTH_CACHE
    fi
  fi
fi

#Authorization
$_DEBUG && echo "[DEBUG] Initializing authorization" >&2
if [[ "$AUTH_TYPE" == "basic" ]]; then
  #HTTP basic authentication
  $_DEBUG && echo "[DEBUG] using HTTP basic authentication: $AUTH_USERNAME / ***********" >&2
  AUTH="Authorization: Basic `echo -n "$AUTH_USERNAME:$AUTH_PASSWORD" | base64`"
elif [[ "$AUTH_TYPE" == "token" ]]; then
  #HTTP token authentication
  $_DEBUG && echo "[DEBUG] using HTTP token authentication: $AUTH_TOKEN" >&2
  AUTH="X-Auth-Token: $AUTH_TOKEN"
elif [[ "$AUTH_TYPE" == "keystone-basic" ]]; then
  #Keystone authentication (with username/password)
  $_DEBUG && echo "[DEBUG] using keystone (username/password) authentication" >&2
  if [[ -n "$AUTH_TOKEN" ]]; then
    #Token is still valid, just use it
    $_DEBUG && echo "[DEBUG] valid token found ( $AUTH_TOKEN ). using it." >&2
    AUTH="X-Auth-Token: $AUTH_TOKEN"
  else
    #Token is not valid anymore, re-generate it
    $_DEBUG && echo "[DEBUG] no valid token found, creating a new one." >&2
    error 127 "Not jet implemented"
  fi
elif [[ "$AUTH_TYPE" == "keystone-voms" || "$AUTH_TYPE" == "keystone-myproxy" ]]; then
  #Keystone authentication (with VOMS proxy certificate)
  $_DEBUG && echo "[DEBUG] using keystone (VOMS) authentication" >&2

  #Set CA PATH
  if [[ "${_CURL_OPTS//--capath/}" == "$_CURL_OPTS" ]]; then
    [[ -z "$AUTH_KEYSTONE_CAPATH" ]] && AUTH_KEYSTONE_CAPATH=/etc/grid-security/certificates
    _CURL_OPTS="$_CURL_OPTS --capath $AUTH_KEYSTONE_CAPATH"
  fi

  if [[ "$_ACTION" == "auth" ]]; then
    #If action is auth, to recreate a new token
    AUTH_TOKEN=
  fi

  #Get token (if not defined already from the cache)
  if [[ -n "$AUTH_TOKEN" ]]; then
    #Token is still valid, just use it
    $_DEBUG && echo "[DEBUG] valid token found in the cache ( $AUTH_TOKEN ). using it." >&2
    AUTH="X-Auth-Token: $AUTH_TOKEN"
  else
    #Token is not valid anymore, re-generate it
    $_DEBUG && echo "[DEBUG] no valid token found, creating a new one." >&2
    
    if $_CRL_UPDATE; then
      #Run fetch-crl if specified, this is required for VOMS tools
      $_DEBUG && echo "[DEBUG] Updating CRLs..." >&2
      fetch-crl &>/dev/null
    fi

    #Get proxy credential, if any
    if [[ -n "$AUTH_KEYSTONE_PROXY" ]]; then
      $_DEBUG && echo "[DEBUG] checking proxy validity." >&2
      #Check proxy certificate validity and VO name
      voms-proxy-info -file "$AUTH_KEYSTONE_PROXY" -exists
      [[ $? -ne 0 ]] && error 192 "Specified proxy certificate is not valid anymore. Please generate a new proxy or specify the user certificate."
      VONAME="`voms-proxy-info -file "$AUTH_KEYSTONE_PROXY" -all | gawk '{if($1=="VO")print $3}'`"
      [[ $? -ne 0 ]] && error 192 "Specified proxy certificate is not valid anymore. Please generate a new proxy or specify the user certificate."

      #Check the proxy VO extensions validity (if specified)
      if [[ -n "$AUTH_KEYSTONE_VOMSEXT" && "$VONAME" != "$AUTH_KEYSTONE_VOMSEXT" ]]; then
        #No valid VOMS extensions provided with the proxy, re-create a new proxy
        $_DEBUG && echo "[DEBUG] No valid VOMS extensions provided in the proxy. Create a new temporary proxy for autentication." >&2

        TMP_OPTS="-rfc -n"
        [[ -n "$X509_VOMSES" ]] && TMP_OPTS="$TMP_OPTS -vomses $X509_VOMSES"
        export X509_USER_PROXY="$AUTH_KEYSTONE_PROXY"
        voms-proxy-init -voms "$AUTH_KEYSTONE_VOMSEXT" -out "$AUTH_KEYSTONE_PROXY.vomsext" $TMP_OPTS &>/dev/null
        if [[ $? -ne 0 ]]; then
          rm -f "$AUTH_KEYSTONE_PROXY.vomsext"
          voms-proxy-init -voms "$AUTH_KEYSTONE_VOMSEXT" -out "$AUTH_KEYSTONE_PROXY.vomsext" $TMP_OPTS -debug
          error 192 "Failed to generate temporary user proxy."
        fi
        AUTH_KEYSTONE_PROXY=$AUTH_KEYSTONE_PROXY.vomsext
        export X509_USER_PROXY=
      fi
    elif [[ -n "$AUTH_KEYSTONE_CERT" && -n "$AUTH_KEYSTONE_KEY" && -n "$AUTH_KEYSTONE_VOMSEXT" ]]; then
      $_DEBUG && echo "[DEBUG] generating temporary proxy." >&2
      AUTH_KEYSTONE_PROXY="$0.tempproxy"

      #Generate a proxy from the certificate (with VOMS extensions)
      TMP_OPTS="-rfc"
      [[ -n "$X509_VOMSES" ]] && TMP_OPTS="$TMP_OPTS -vomses $X509_VOMSES"
      voms-proxy-init -cert "$AUTH_KEYSTONE_CERT" -key "$AUTH_KEYSTONE_KEY" -voms "$AUTH_KEYSTONE_VOMSEXT" -out "$AUTH_KEYSTONE_PROXY" $TMP_OPTS &>/dev/null
      if [[ $? -ne 0 ]]; then
	voms-proxy-info -file "$AUTH_KEYSTONE_PROXY" -exists
        if [[ $? -ne 0 ]]; then
	  rm -f "$AUTH_KEYSTONE_PROXY"
          voms-proxy-init -cert "$AUTH_KEYSTONE_CERT" -key "$AUTH_KEYSTONE_KEY" -voms "$AUTH_KEYSTONE_VOMSEXT" -out "$AUTH_KEYSTONE_PROXY" $TMP_OPTS -debug
	  error 192 "Failed to generate temporary user proxy."
	fi
      fi
    elif [[ -n "$AUTH_MYPROXY_SERVER" && -n "$AUTH_USERNAME" && -n "$AUTH_KEYSTONE_VOMSEXT" ]]; then
      $_DEBUG && echo "[DEBUG] generating temporary proxy from myproxy." >&2
      AUTH_KEYSTONE_PROXY="$0.tempproxy"

      #Generate a proxy from the myproxy (with VOMS extensions)
      AUTH_MYPROXY_SERVER="-s ${AUTH_MYPROXY_SERVER/:/ -p }"
      if [[ -n "$AUTH_PASSWORD" ]]; then
        echo "$AUTH_PASSWORD" | myproxy-logon -S $AUTH_MYPROXY_SERVER -l $AUTH_USERNAME -o "${AUTH_KEYSTONE_PROXY}m" > /dev/null
        res="${PIPESTATUS[1]}"
      else
        myproxy-logon $AUTH_MYPROXY_SERVER -l $AUTH_USERNAME -o "${AUTH_KEYSTONE_PROXY}m"
        res=$?
      fi
      if [[ $res -ne 0 ]]; then
        error 193 "Failed to get proxy from myproxy. Are credentials wrong?"
      fi

      #Sign the proxy with VOMS extensions
      TMP_OPTS="-rfc -n"
      [[ -n "$X509_VOMSES" ]] && TMP_OPTS="$TMP_OPTS -vomses $X509_VOMSES"
      export X509_USER_PROXY="${AUTH_KEYSTONE_PROXY}m"
      voms-proxy-init -voms "$AUTH_KEYSTONE_VOMSEXT" -out "$AUTH_KEYSTONE_PROXY" $TMP_OPTS &>/dev/null
      if [[ $? -ne 0 ]]; then
        voms-proxy-info -file "$AUTH_KEYSTONE_PROXY" -exists
        if [[ $? -ne 0 ]]; then
          rm -f "$AUTH_KEYSTONE_PROXY"
          voms-proxy-init -voms "$AUTH_KEYSTONE_VOMSEXT" -out "$AUTH_KEYSTONE_PROXY" $TMP_OPTS -debug
          error 192 "Failed to sign temporary user proxy."
        fi
      fi
      rm -f "${AUTH_KEYSTONE_PROXY}m"
      export X509_USER_PROXY=

    else
      error 194 "Please setup a proxy or certificate for Keystone authentication and VOMS extension in the bcdmi.auth file"
    fi

    #Get Keystone URI
    if [[ -z "$AUTH_KEYSTONE_URI" ]]; then
      #Try to get the keystone url from the endpoint
      AUTH_KEYSTONE_URI=`curl $_CURL_OPTS -v -H "X-CDMI-Specification-Version: $_CDMI_VERSION" "${_ENDPOINT}" 2>&1 | sed -n "s|< www-authenticate *: *Keystone uri *= *[\"\\\']*\([^\"\\\']*\).*$|\1|Ip"`
      #Try to get the keystone URI for OpenStack CDMI implementation
      [[ -z "$AUTH_KEYSTONE_URI" ]] && AUTH_KEYSTONE_URI=`curl $_CURL_OPTS -v -H "X-CDMI-Specification-Version: $_CDMI_VERSION" "${_ENDPOINT%/AUTH_*}/AUTH_testauth" 2>&1 | sed -n "s|< www-authenticate *: *Keystone uri *= *[\"\\\']*\([^\"\\\']*\).*$|\1|Ip"`
      [[ -z "$AUTH_KEYSTONE_URI" ]] && AUTH_KEYSTONE_URI=`curl $_CURL_OPTS -v -H "X-CDMI-Specification-Version: $_CDMI_VERSION" "${_ENDPOINT%/cdmi/AUTH_*}/cdmi/AUTH_testauth" 2>&1 | sed -n "s|< www-authenticate *: *Keystone uri *= *[\"\\\']*\([^\"\\\']*\).*$|\1|Ip"`
      [[ -z "$AUTH_KEYSTONE_URI" ]] && error 197 "Impossible to get Keystone URI. Please specify a custom one in the autorization file."
    fi
    AUTH_KEYSTONE_URI="${AUTH_KEYSTONE_URI%/}"; AUTH_KEYSTONE_URI="${AUTH_KEYSTONE_URI%/v2.0}/v2.0"

    #Get tenant
    if [[ -z "$AUTH_KEYSTONE_TENANT" ]]; then
      #Tenant name if not specified, get tenants list
      $_DEBUG && echo "[DEBUG] tenant name not specified. getting unscoped token" >&2
      TMPSTR="`curl $_CURL_OPTS -s -S -X POST "$AUTH_KEYSTONE_URI/tokens" -H 'Accept: application/json' -H 'Content-Type: application/json' -d '{"auth":{"voms":true}}' --cert "$AUTH_KEYSTONE_PROXY" --key "$AUTH_KEYSTONE_PROXY" --cacert "$AUTH_KEYSTONE_PROXY" | gawk 'BEGIN{RS="\\""}{if ($0=="expires") {getline;getline;gsub("T"," ",$0);gsub("Z","",$0);printf $0","}if($0=="id"){getline;getline;printf $0;exit}}'`"
      AUTH_TOKEN="${TMPSTR#*,}"
      [[ -z "$AUTH_TOKEN" ]] && error 201 "failed to get unscoped token. Are you sure your certificate VOMS extension can access the service?"
      AUTH="X-Auth-Token: $AUTH_TOKEN"

      #get default tenant name
      $_DEBUG && echo "[DEBUG] getting the tenants lists for the user via unskopen token" >&2
      AUTH_KEYSTONE_TENANT=`curl $_CURL_OPTS -s -S "$AUTH_KEYSTONE_URI/tenants" -H "$AUTH" -H 'Accept: application/json' -H 'Content-Type: application/json' | gawk 'BEGIN{RS="\""}{if($0=="name"){getline;getline;printf $0","}}'`
      AUTH_KEYSTONE_TENANT=${AUTH_KEYSTONE_TENANT%,}
      if [[ "${AUTH_KEYSTONE_TENANT//,/}" != "$AUTH_KEYSTONE_TENANT" ]]; then
        #user belongs to more than one tenant, check to which tenants he can autenticate to with this proxy
        $_DEBUG && echo "[DEBUG] user belongs to more than one tenant, tryying to get the tenant associated to the VOMS signature" >&2
        
        allowed_tenants=
        for tenant in ${AUTH_KEYSTONE_TENANT//,/ }; do
          TMPSTR="`curl $_CURL_OPTS -s -S -X POST "$AUTH_KEYSTONE_URI/tokens" -H 'Accept: application/json' -H 'Content-Type: application/json' -d '{"auth":{"voms":true,"tenantName":"'"$tenant"'"}}' --cert "$AUTH_KEYSTONE_PROXY" --key $AUTH_KEYSTONE_PROXY --cacert "$AUTH_KEYSTONE_PROXY" | gawk 'BEGIN{RS="\\""}{if ($0=="expires") {getline;getline;gsub("T"," ",$0);gsub("Z","",$0);printf $0","}if($0=="id"){getline;getline;printf $0;exit}}'`"
          AUTH_TOKEN="${TMPSTR#*,}"
          [[ -n "$AUTH_TOKEN" ]] && allowed_tenants="$allowed_tenants,$tenant"
        done
        AUTH_KEYSTONE_TENANT="${allowed_tenants#,}"
        
        if [[ "${AUTH_KEYSTONE_TENANT//,/}" != "$AUTH_KEYSTONE_TENANT" ]]; then
          if [[ "$_ACTION" == "auth" ]]; then
            AUTH_KEYSTONE_TENANT=
          else
            error 203 "User belong to more than one tenant. You need to specify one in the authorization file. Possible tenants are: $AUTH_KEYSTONE_TENANT . Use auth to get the details of the allowed tenants."
          fi
        fi
        $_DEBUG && echo "[DEBUG] Got default tenant (from VOMS signature): $AUTH_KEYSTONE_TENANT" >&2
      else
        #user belong to only one tenant, using it as default
        $_DEBUG && echo "[DEBUG] Got default tenant: $AUTH_KEYSTONE_TENANT" >&2
      fi
    fi

    if [[ -z "$AUTH_KEYSTONE_TENANT" ]]; then
      echo "WARNING: Failed to get a default tenant. You need to specify one in the authorization file. The list of allowed tenants is provided via the auth command."
    else
      #Request scoped token (and get as return the endpoint path also, if any)
      TMPSTR="`curl $_CURL_OPTS -s -S -X POST "$AUTH_KEYSTONE_URI/tokens" -H 'Accept: application/json' -H 'Content-Type: application/json' -d '{"auth":{"voms":true,"tenantName":"'"$AUTH_KEYSTONE_TENANT"'"}}' --cert "$AUTH_KEYSTONE_PROXY" --key $AUTH_KEYSTONE_PROXY --cacert "$AUTH_KEYSTONE_PROXY" | gawk -v e="$_ENDPOINT" 'BEGIN{RS="\\\""}{if ($0=="expires"){getline;getline;gsub("T"," ",$0);gsub("Z","",$0);expd=$0}if($0=="id"&&idd==""){getline;getline;idd=$0}if($0=="publicURL"){getline;getline;if($0~"^"e){gsub("^"e,"",$0);ptd=$0};}}END{if(idd!=""&&expd!="")print expd","idd","ptd}'`"
      AUTH_TOKEN_EXPIRE_TIME="${TMPSTR%%,*}"
      AUTH_TOKEN="${TMPSTR#*,}"; AUTH_TOKEN="${AUTH_TOKEN%,*}"
      AUTH_TOKEN_PATH="${TMPSTR##*,}"
      [[ -z "$AUTH_TOKEN" ]] && error 202 "failed to get scoped token for tenant $AUTH_KEYSTONE_TENANT"
      $_DEBUG && echo "[DEBUG] got scoped token $AUTH_TOKEN (expire on $AUTH_TOKEN_EXPIRE_TIME)." >&2
      $_DEBUG && [[ -n "$AUTH_TOKEN_PATH" ]] && echo "[DEBUG] got additional endpoint scoped path: $AUTH_TOKEN_PATH" >&2
      AUTH="X-Auth-Token: $AUTH_TOKEN"

      #Convert endpoint from SWIFT to CDMI interface (only for openstack, does nothing for other CDMI implementations)
      [[ "$AUTH_TOKEN_PATH" == "${AUTH_TOKEN_PATH#/v1/}" ]] || AUTH_TOKEN_PATH="/cdmi/${AUTH_TOKEN_PATH#/v1/}"
 
      #If the proxy is temporary, delete it
      [[ "$AUTH_KEYSTONE_PROXY" == "$0.tempproxy" ]] && rm -f "$0.tempproxy"
    fi

    #Save data to a cache. So you do not have to autenticate each time you perform a request. The cache file expires when the token expires
    if [[ -n "$_LOCAL_AUTH_CACHE" ]]; then
      $_DEBUG && echo "[DEBUG] saving authorization token to cache." >&2
      cat << EOF > $_LOCAL_AUTH_CACHE
#This cache file is automatically generated. It will expire on $AUTH_TOKEN_EXPIRE_TIME . Delete it if you have problems in the authentication.
CACHE_EXPIRE_TIME="$AUTH_TOKEN_EXPIRE_TIME"
CACHE_RELATED_ENDPOINT="$_ENDPOINT"
AUTH_TOKEN="$AUTH_TOKEN"
AUTH_TOKEN_PATH="$AUTH_TOKEN_PATH"
AUTH_KEYSTONE_URI="$AUTH_KEYSTONE_URI"
AUTH_KEYSTONE_TENANT="$AUTH_KEYSTONE_TENANT"
EOF
    fi
  fi

  #If keystone defines a token path for the service, add it to the endpoint
  if [[ -n "$AUTH_TOKEN_PATH" ]]; then
    _ENDPOINT="${_ENDPOINT%/}/${AUTH_TOKEN_PATH#/}"
  fi
elif [[ -n "$_AUTH" ]]; then
  $_DEBUG && echo "[DEBUG] Manual authorization string specified. Using it." >&2
  AUTH="$_AUTH"
else
  error 242 "No authorization method specified. Please setup a local authorization file."
fi

#Add the specified path to the endpoint, to get the object full URI
_ENDPOINT="${_ENDPOINT%/}/${_PATH#/}"

$_DEBUG && echo "[DEBUG] Performing query $_ACTION..." >&2
if [[ "$_ACTION" == "auth" ]]; then
  #Only display auth information (and tenants list)
  echo "Auth type: $AUTH_TYPE"
  echo "User auth header: ${AUTH#-H }"
  if [[ "$AUTH_TYPE" == "keystone-voms" || "$AUTH_TYPE" == "keystone-basic" || "$AUTH_TYPE" == "keystone-myproxy" ]]; then
    [[ -n "$AUTH_KEYSTONE_URI" ]] && echo "Keystone URI: $AUTH_KEYSTONE_URI" || echo "Keystone URI: (none)"
    [[ -n "$AUTH_KEYSTONE_TENANT" ]] && echo "Tenant associated: $AUTH_KEYSTONE_TENANT" || echo "Tenant associated: (none)"
    [[ -n "$AUTH_KEYSTONE_TENANT" && -n "$_ENDPOINT" ]] && echo "Tenant associated endpoint: $_ENDPOINT" || echo "Tenant associated endpoint: (none)"
    if [[ -n "$AUTH_KEYSTONE_URI" ]]; then
      echo "Tenants list:"
      echo "tenant name,tenant id,tenant description"
      curl $_CURL_OPTS -s -S "$AUTH_KEYSTONE_URI/tenants" -H "$AUTH" -H 'Accept: application/json' -H 'Content-Type: application/json' | gawk 'BEGIN{RS="\""}{if($0=="name"){getline;getline;tmn=$0;}if($0=="id"){getline;getline;tmid=$0;}if($0=="description"){getline;getline;tmd=$0;}if((tmn!="")&&(tmid!="")&&(tmd!="")){print tmn","tmid","tmd;tmn="";tmid="";tmd=""}}'
    fi
  fi
elif [[ -z "$_ACTION" || "$_ACTION" == "list" ]]; then
  #List contents of the container
  curl $_CURL_OPTS -s -S "$_ENDPOINT" -H "$AUTH" -H "X-CDMI-Specification-Version: $_CDMI_VERSION" | gawk 'BEGIN{RS="\""}{if($0=="children"){while($0!~"]|}"){getline;if($0~"]|}")break;getline;gsub(/ /,"%20",$0);print $0}}}'
elif [[ "$_ACTION" == "get" ]]; then
  #Get content of the file
  curl $_CURL_OPTS -s -S "$_ENDPOINT" -H "$AUTH"
elif [[ "$_ACTION" == "put" ]]; then
  [[ -z "$_FILE_TO_UPLOAD" ]] && error 243 "You need to specify a file to upload."
  curl $_CURL_OPTS "$_ENDPOINT" -H "$AUTH" -H 'Content-Type: application/binary' -T "$_FILE_TO_UPLOAD"
elif [[ "$_ACTION" == "stat" ]]; then
  curl $_CURL_OPTS -s -S "$_ENDPOINT" -H "X-CDMI-Specification-Version: $_CDMI_VERSION" -H "$AUTH"
elif [[ "$_ACTION" == "publicurl" ]]; then
  echo "$_ENDPOINT"
elif [[ "$_ACTION" == "delete" ]]; then
  if $_RECURSIVE; then
    if [[ "${_ENDPOINT%/}" == "$_ENDPOINT" ]]; then
      #This is not a folder, delete the file
      curl $_CURL_OPTS -s -S "$_ENDPOINT" -X DELETE -H "X-CDMI-Specification-Version: $_CDMI_VERSION" -H "$AUTH"
    else
      #This is a folder, list the contents and delete all the contents recursively
      for f in `curl $_CURL_OPTS -s -S "$_ENDPOINT" -H "$AUTH" -H "X-CDMI-Specification-Version: $_CDMI_VERSION" | gawk 'BEGIN{RS="\""}{if($0=="children"){while($0!~"]|}"){getline;if($0~"]|}")break;getline;gsub(/ /,"%20",$0);print $0}}}'`; do
        if [[ "${f%/}" == "$f" ]]; then
          curl $_CURL_OPTS -s -S "$_ENDPOINT$f" -X DELETE -H "X-CDMI-Specification-Version: $_CDMI_VERSION" -H "$AUTH"
        else
          $0 -r -e "$_ENDPOINT" delete $f
        fi
      done
      #Then delete the folder itself
      curl $_CURL_OPTS -s -S "$_ENDPOINT" -H "X-CDMI-Specification-Version: $_CDMI_VERSION" -X DELETE -H "$AUTH"
    fi
  else
    curl $_CURL_OPTS -s -S "$_ENDPOINT" -H "X-CDMI-Specification-Version: $_CDMI_VERSION" -X DELETE -H "$AUTH"
  fi
elif [[ "$_ACTION" == "mkdir" ]]; then
   curl $_CURL_OPTS -X PUT "${_ENDPOINT%/}/" -H "X-CDMI-Specification-Version: $_CDMI_VERSION" -H "$AUTH" -H 'Content-Type: application/cdmi-container' -H 'Content-Length: 0'
elif [[ "$_ACTION" == "post" ]]; then
  #WARNING. This uses SWIFT API, because there is still no CDMI ACL implementation in SWIFT
  _ENDPOINT="${_ENDPOINT/\/cdmi\///v1/}" #switch to SWIFT API
  
  for META in ${_POST_META//|/ }; do
    META_CODE="${META%%=*}"
    META_VALUE="${META#*=}"
    case "$META_CODE" in
      readacl) META_CODE="X-Container-Read" ;;
      writeacl) META_CODE="X-Container-Write" ;;
      weblist) META_CODE="X-Container-Meta-Web-Listings" ;;
      *) error 125 "Metadata code $META_CODE not valid"
    esac
    curl $_CURL_OPTS -s -S "$_ENDPOINT" -X POST -H "$AUTH" -H "$META_CODE: $META_VALUE" -H 'Accept-Encoding: identity'
  done
else
  error 150 "Action $_ACTION is not recognized."
fi

exit 0