text: /*
  Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
  See COPYRIGHT for license information.
  */
  
  
  #include "config.h"
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #ifdef HAVE_UNISTD_H
  #include <unistd.h>
  #endif
  #ifdef HAVE_STDARG_H
  #include <stdarg.h>
  #endif
  #include "netcdf.h"
  #include "ncbytes.h"
  #include "ncuri.h"
  #include "ncauth.h"
  #include "nclog.h"
  #include "ncwinpath.h"
  
  #ifdef _MSC_VER
  #include <windows.h>
  #endif
  
  #undef MEMCHECK
  #define MEMCHECK(x) if((x)==NULL) {goto nomem;} else {}
  
  /* Define the curl flag defaults in envv style */
  static const char* AUTHDEFAULTS[] = {
  "HTTP.TIMEOUT","1800", /*seconds */ /* Long but not infinite */
  NULL
  };
  
  /* Forward */
  static int setauthfield(NCauth* auth, const char* flag, const char* value);
  static void setdefaults(NCauth*);
  
  /**************************************************/
  /* External Entry Points */
  
  int
  NC_parseproxy(NCauth* auth, const char* surl)
  {
      int ret = NC_NOERR;
      NCURI* uri = NULL;
      if(surl == NULL || strlen(surl) == 0)
  	return (NC_NOERR); /* nothing there*/
      if(ncuriparse(surl,&uri) != NCU_OK)
  	return (NC_EURL);
      auth->proxy.user = uri->user;
      auth->proxy.pwd = uri->password;
      auth->proxy.host = strdup(uri->host);
      if(uri->port != NULL)
          auth->proxy.port = atoi(uri->port);
      else
          auth->proxy.port = 80;
      return (ret);
  }
  
  char*
  NC_combinehostport(NCURI* uri)
  {
      size_t len;
      char* host = NULL;
      char* port = NULL;
      char* hp = NULL;
      if(uri == NULL) return NULL;
      host = uri->host;
      port = uri->port;
      if(uri == NULL || host == NULL) return NULL;
      if(port != NULL && strlen(port) == 0) port = NULL;
      len = strlen(host);
      if(port != NULL) len += (1+strlen(port));
      hp = (char*)malloc(len+1);
      if(hp == NULL) return NULL;
      strncpy(hp,host,len);
      if(port != NULL) {
  	strncat(hp,":",len);
  	strncat(hp,port,len);
      }
      return hp;
  }
  
  int
  NC_authsetup(NCauth* auth, NCURI* uri)
  {
      int ret = NC_NOERR;
      char* uri_hostport = NULL;
  
      if(uri != NULL)
        uri_hostport = NC_combinehostport(uri);
      else
        return NC_EDAP; /* Generic EDAP error. */
      setdefaults(auth);
  
      /* Note, we still must do this function even if
         ncrc_getglobalstate()->rc.ignore is set in order
         to getinfo e.g. host+port  from url
      */
  
      setauthfield(auth,"HTTP.DEFLATE",
  		      NC_rclookup("HTTP.DEFLATE",uri_hostport));
      setauthfield(auth,"HTTP.VERBOSE",
  			NC_rclookup("HTTP.VERBOSE",uri_hostport));
      setauthfield(auth,"HTTP.TIMEOUT",
  			NC_rclookup("HTTP.TIMEOUT",uri_hostport));
      setauthfield(auth,"HTTP.USERAGENT",
  			NC_rclookup("HTTP.USERAGENT",uri_hostport));
      setauthfield(auth,"HTTP.COOKIEFILE",
  			NC_rclookup("HTTP.COOKIEFILE",uri_hostport));
      setauthfield(auth,"HTTP.COOKIE_FILE",
  			NC_rclookup("HTTP.COOKIE_FILE",uri_hostport));
      setauthfield(auth,"HTTP.COOKIEJAR",
  			NC_rclookup("HTTP.COOKIEJAR",uri_hostport));
      setauthfield(auth,"HTTP.COOKIE_JAR",
  			NC_rclookup("HTTP.COOKIE_JAR",uri_hostport));
      setauthfield(auth,"HTTP.PROXY.SERVER",
  			NC_rclookup("HTTP.PROXY.SERVER",uri_hostport));
      setauthfield(auth,"HTTP.PROXY_SERVER",
  			NC_rclookup("HTTP.PROXY_SERVER",uri_hostport));
      setauthfield(auth,"HTTP.SSL.VALIDATE",
  			NC_rclookup("HTTP.SSL.VALIDATE",uri_hostport));
      setauthfield(auth,"HTTP.SSL.CERTIFICATE",
  			NC_rclookup("HTTP.SSL.CERTIFICATE",uri_hostport));
      setauthfield(auth,"HTTP.SSL.KEY",
  			NC_rclookup("HTTP.SSL.KEY",uri_hostport));
      setauthfield(auth,"HTTP.SSL.KEYPASSWORD",
  			NC_rclookup("HTTP.SSL.KEYPASSWORD",uri_hostport));
      setauthfield(auth,"HTTP.SSL.CAINFO",
  			NC_rclookup("HTTP.SSL.CAINFO",uri_hostport));
      setauthfield(auth,"HTTP.SSL.CAPATH",
  			NC_rclookup("HTTP.SSL.CAPATH",uri_hostport));
      setauthfield(auth,"HTTP.SSL.VERIFYPEER",
  			NC_rclookup("HTTP.SSL.VERIFYPEER",uri_hostport));
      setauthfield(auth,"HTTP.NETRC",
  			NC_rclookup("HTTP.NETRC",uri_hostport));
  
      { /* Handle various cases for user + password */
        /* First, see if the user+pwd was in the original url */
        char* user = NULL;
        char* pwd = NULL;
        if(uri->user != NULL && uri->password != NULL) {
  	    user = uri->user;
  	    pwd = uri->password;
        } else {
     	    user = NC_rclookup("HTTP.CREDENTIALS.USER",uri_hostport);
  	    pwd = NC_rclookup("HTTP.CREDENTIALS.PASSWORD",uri_hostport);
        }
        if(user != NULL && pwd != NULL) {
          user = strdup(user); /* so we can consistently reclaim */
          pwd = strdup(pwd);
        } else {
  	    /* Could not get user and pwd, so try USERPASSWORD */
  	    const char* userpwd = NC_rclookup("HTTP.CREDENTIALS.USERPASSWORD",uri_hostport);
  	    if(userpwd != NULL) {
            ret = NC_parsecredentials(userpwd,&user,&pwd);
            if(ret) {nullfree(uri_hostport); return ret;}
  	    }
        }
        setauthfield(auth,"HTTP.CREDENTIALS.USERNAME",user);
        setauthfield(auth,"HTTP.CREDENTIALS.PASSWORD",pwd);
        nullfree(user);
        nullfree(pwd);
        nullfree(uri_hostport);
      }
      return (ret);
  }
  
  void
  NC_authclear(NCauth* auth)
  {
      if(auth->curlflags.cookiejarcreated) {
  #ifdef _MSC_VER
          DeleteFile(auth->curlflags.cookiejar);
  #else
          remove(auth->curlflags.cookiejar);
  #endif
      }
      nullfree(auth->curlflags.useragent);
      nullfree(auth->curlflags.cookiejar);
      nullfree(auth->curlflags.netrc);
      nullfree(auth->ssl.certificate);
      nullfree(auth->ssl.key);
      nullfree(auth->ssl.keypasswd);
      nullfree(auth->ssl.cainfo);
      nullfree(auth->ssl.capath);
      nullfree(auth->proxy.host);
      nullfree(auth->proxy.user);
      nullfree(auth->proxy.pwd);
      nullfree(auth->creds.user);
      nullfree(auth->creds.pwd);
  }
  
  /**************************************************/
  
  static int
  setauthfield(NCauth* auth, const char* flag, const char* value)
  {
      int ret = NC_NOERR;
      if(value == NULL) goto done;
      if(strcmp(flag,"HTTP.DEFLATE")==0) {
          if(atoi(value)) auth->curlflags.compress = 1;
  #ifdef D4DEBUG
          nclog(NCLOGNOTE,"HTTP.DEFLATE: %ld", infoflags.compress);
  #endif
      }
      if(strcmp(flag,"HTTP.VERBOSE")==0) {
          if(atoi(value)) auth->curlflags.verbose = 1;
  #ifdef D4DEBUG
              nclog(NCLOGNOTE,"HTTP.VERBOSE: %ld", auth->curlflags.verbose);
  #endif
      }
      if(strcmp(flag,"HTTP.TIMEOUT")==0) {
          if(atoi(value)) auth->curlflags.timeout = atoi(value);
  #ifdef D4DEBUG
              nclog(NCLOGNOTE,"HTTP.TIMEOUT: %ld", auth->curlflags.timeout);
  #endif
      }
      if(strcmp(flag,"HTTP.USERAGENT")==0) {
          if(atoi(value)) auth->curlflags.useragent = strdup(value);
          MEMCHECK(auth->curlflags.useragent);
  #ifdef D4DEBUG
              nclog(NCLOGNOTE,"HTTP.USERAGENT: %s", auth->curlflags.useragent);
  #endif
      }
      if(
  	strcmp(flag,"HTTP.COOKIEFILE")==0
          || strcmp(flag,"HTTP.COOKIE_FILE")==0
          || strcmp(flag,"HTTP.COOKIEJAR")==0
          || strcmp(flag,"HTTP.COOKIE_JAR")==0
        ) {
  	nullfree(auth->curlflags.cookiejar);
          auth->curlflags.cookiejar = strdup(value);
          MEMCHECK(auth->curlflags.cookiejar);
  #ifdef D4DEBUG
              nclog(NCLOGNOTE,"HTTP.COOKIEJAR: %s", auth->curlflags.cookiejar);
  #endif
      }
      if(strcmp(flag,"HTTP.PROXY.SERVER")==0 || strcmp(flag,"HTTP.PROXY_SERVER")==0) {
          ret = NC_parseproxy(auth,value);
          if(ret != NC_NOERR) goto done;
  #ifdef D4DEBUG
              nclog(NCLOGNOTE,"HTTP.PROXY.SERVER: %s", value);
  #endif
      }
      if(strcmp(flag,"HTTP.SSL.VALIDATE")==0) {
          if(atoi(value)) {
  	    auth->ssl.verifypeer = 1;
  	    auth->ssl.verifyhost = 1;
  #ifdef D4DEBUG
                  nclog(NCLOGNOTE,"HTTP.SSL.VALIDATE: %ld", 1);
  #endif
  	}
      }
  
      if(strcmp(flag,"HTTP.SSL.CERTIFICATE")==0) {
  	nullfree(auth->ssl.certificate);
          auth->ssl.certificate = strdup(value);
          MEMCHECK(auth->ssl.certificate);
  #ifdef D4DEBUG
              nclog(NCLOGNOTE,"HTTP.SSL.CERTIFICATE: %s", auth->ssl.certificate);
  #endif
      }
  
      if(strcmp(flag,"HTTP.SSL.KEY")==0) {
  	nullfree(auth->ssl.key);
          auth->ssl.key = strdup(value);
          MEMCHECK(auth->ssl.key);
  #ifdef D4DEBUG
              nclog(NCLOGNOTE,"HTTP.SSL.KEY: %s", auth->ssl.key);
  #endif
      }
  
      if(strcmp(flag,"HTTP.SSL.KEYPASSWORD")==0) {
  	nullfree(auth->ssl.keypasswd) ;
          auth->ssl.keypasswd = strdup(value);
          MEMCHECK(auth->ssl.keypasswd);
  #ifdef D4DEBUG
              nclog(NCLOGNOTE,"HTTP.SSL.KEYPASSWORD: %s", auth->ssl.keypasswd);
  #endif
      }
  
      if(strcmp(flag,"HTTP.SSL.CAINFO")==0) {
  	nullfree(auth->ssl.cainfo) ;
          auth->ssl.cainfo = strdup(value);
          MEMCHECK(auth->ssl.cainfo);
  #ifdef D4DEBUG
              nclog(NCLOGNOTE,"HTTP.SSL.CAINFO: %s", auth->ssl.cainfo);
  #endif
      }
  
      if(strcmp(flag,"HTTP.SSL.CAPATH")==0) {
  	nullfree(auth->ssl.capath) ;
          auth->ssl.capath = strdup(value);
          MEMCHECK(auth->ssl.capath);
  #ifdef D4DEBUG
              nclog(NCLOGNOTE,"HTTP.SSL.CAPATH: %s", auth->ssl.capath);
  #endif
      }
  
      if(strcmp(flag,"HTTP.SSL.VERIFYPEER")==0) {
          const char* s = value;
          int tf = 0;
          if(s == NULL || strcmp(s,"0")==0 || strcasecmp(s,"false")==0)
              tf = 0;
          else if(strcmp(s,"1")==0 || strcasecmp(s,"true")==0)
              tf = 1;
          else
              tf = 1; /* default if not null */
          auth->ssl.verifypeer = tf;
  #ifdef D4DEBUG
              nclog(NCLOGNOTE,"HTTP.SSL.VERIFYPEER: %d", auth->ssl.verifypeer);
  #endif
      }
  
      if(strcmp(flag,"HTTP.NETRC")==0) {
          nullfree(auth->curlflags.netrc);
          auth->curlflags.netrc = strdup(value);
          MEMCHECK(auth->curlflags.netrc);
  #ifdef D4DEBUG
              nclog(NCLOGNOTE,"HTTP.NETRC: %s", auth->curlflags.netrc);
  #endif
      }
  
      if(strcmp(flag,"HTTP.CREDENTIALS.USERNAME")==0) {
          nullfree(auth->creds.user);
          auth->creds.user = strdup(value);
          MEMCHECK(auth->creds.user);
      }
      if(strcmp(flag,"HTTP.CREDENTIALS.PASSWORD")==0) {
          nullfree(auth->creds.pwd);
          auth->creds.pwd = strdup(value);
          MEMCHECK(auth->creds.pwd);
      }
  
  done:
      return (ret);
  
  nomem:
      return (NC_ENOMEM);
  }
  
  /*
  Given form user:pwd, parse into user and pwd
  and do %xx unescaping
  */
  int
  NC_parsecredentials(const char* userpwd, char** userp, char** pwdp)
  {
    char* user = NULL;
    char* pwd = NULL;
  
    if(userpwd == NULL)
  	return NC_EINVAL;
    user = strdup(userpwd);
    if(user == NULL)
  	return NC_ENOMEM;
    pwd = strchr(user,':');
    if(pwd == NULL) {
      free(user);
  	return NC_EINVAL;
    }
    *pwd = '\0';
    pwd++;
    if(userp)
  	*userp = ncuridecode(user);
    if(pwdp)
  	*pwdp = ncuridecode(pwd);
    free(user);
    return NC_NOERR;
  }
  
  static void
  setdefaults(NCauth* auth)
  {
      int ret = NC_NOERR;
      const char** p;
      for(p=AUTHDEFAULTS;*p;p+=2) {
  	ret = setauthfield(auth,p[0],p[1]);
  	if(ret) {
              nclog(NCLOGERR, "RC file defaulting failed for: %s=%s",p[0],p[1]);
  	}
      }
  }
  