James Cone's efforts to add another netrc parsing "mode"
authorDaniel Stenberg <daniel@haxx.se>
Tue, 21 May 2002 22:17:19 +0000 (22:17 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 21 May 2002 22:17:19 +0000 (22:17 +0000)
lib/memdebug.c
lib/netrc.c
lib/netrc.h
lib/url.c
lib/urldata.h

index f46d23b2a3e5cd9561a2e472b54d912327654ac8..6a9ee7bc5bd294d1f58e13dfb7c994f4420bf617 100644 (file)
@@ -49,7 +49,9 @@
 
 struct memdebug {
   int size;
-  char mem[1];
+  double mem[1];
+  /* I'm hoping this is the thing with the strictest alignment
+   * requirements.  That also means we waste some space :-( */
 };
 
 /*
index 46f22551167ba4ba6966fe2d5304a129e5c03f69..e3b4a4aea3ae5f8cd77ad45d94b175339b94fded 100644 (file)
@@ -78,12 +78,15 @@ int Curl_parsenetrc(char *host,
   FILE *file;
   char netrcbuffer[256];
   int retcode=1;
+
+  int specific_login = (login[0] != 0);
   
   char *home = NULL; 
   int state=NOTHING;
 
-  char state_login=0;
-  char state_password=0;
+  char state_login=0;      /* Found a login keyword */
+  char state_password=0;   /* Found a password keyword */
+  char state_our_login=0;  /* With specific_login, found *our* login name */
 
 #define NETRC DOT_CHAR "netrc"
 
@@ -116,6 +119,30 @@ int Curl_parsenetrc(char *host,
 
   sprintf(netrcbuffer, "%s%s%s", home, DIR_CHAR, NETRC);
 
+#ifdef MALLOCDEBUG
+  {
+    /* This is a hack to allow testing.
+     * If compiled with --enable-debug and CURL_DEBUG_NETRC is defined,
+     * then it's the path to a substitute .netrc for testing purposes *only* */
+
+    char *override = curl_getenv("CURL_DEBUG_NETRC");
+
+    if (override != NULL) {
+      printf("NETRC: overridden .netrc file: %s\n", home);
+
+      if (strlen(override)+1 > sizeof(netrcbuffer)) {
+        free(override);
+        if(NULL==pw)
+          free(home);
+
+        return -1;
+      }
+      strcpy(netrcbuffer, override);
+      free(override);
+    }
+  }
+#endif /* MALLOCDEBUG */
+
   file = fopen(netrcbuffer, "r");
   if(file) {
     char *tok;
@@ -123,6 +150,10 @@ int Curl_parsenetrc(char *host,
     while(fgets(netrcbuffer, sizeof(netrcbuffer), file)) {
       tok=strtok_r(netrcbuffer, " \t\n", &tok_buf);
       while(tok) {
+
+        if (login[0] && password[0])
+          goto done;
+
        switch(state) {
        case NOTHING:
          if(strequal("machine", tok)) {
@@ -149,17 +180,23 @@ int Curl_parsenetrc(char *host,
        case HOSTVALID:
          /* we are now parsing sub-keywords concerning "our" host */
          if(state_login) {
-           strncpy(login, tok, LOGINSIZE-1);
+            if (specific_login) {
+              state_our_login = strequal(login, tok);
+            }else{
+              strncpy(login, tok, LOGINSIZE-1);
 #ifdef _NETRC_DEBUG
-           printf("LOGIN: %s\n", login);
+             printf("LOGIN: %s\n", login);
 #endif
+            }
            state_login=0;
          }
          else if(state_password) {
-           strncpy(password, tok, PASSWORDSIZE-1);
+            if (state_our_login || !specific_login) {
+              strncpy(password, tok, PASSWORDSIZE-1);
 #ifdef _NETRC_DEBUG
-           printf("PASSWORD: %s\n", password);
+              printf("PASSWORD: %s\n", password);
 #endif
+            }
            state_password=0;
          }
          else if(strequal("login", tok))
@@ -169,13 +206,16 @@ int Curl_parsenetrc(char *host,
          else if(strequal("machine", tok)) {
            /* ok, there's machine here go => */
            state = HOSTFOUND;
+            state_our_login = 0;
          }
          break;
        } /* switch (state) */
+
        tok = strtok_r(NULL, " \t\n", &tok_buf);
       } /* while (tok) */
     } /* while fgets() */
 
+done:
     fclose(file);
   }
 
index 5c98571e784a573124e41e87566437bec39deb82..4aa3c953c5c8019ed4e215fa0d61c9c09ccbb9d1 100644 (file)
@@ -25,4 +25,9 @@
 int Curl_parsenetrc(char *host,
                     char *login,
                     char *password);
+  /* Assume: password[0]=0, host[0] != 0.
+   * If login[0] = 0, search for login and password within a machine section
+   * in the netrc.
+   * If login[0] != 0, search for password within machine and login.
+   */
 #endif
index 4b20c131a4372658d730e6ae1c0f91a2178b4850..8ea4b7c4fef0ba23700a242390ad28be96c8d240 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -441,7 +441,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...)
     /*
      * Parse the $HOME/.netrc file
      */
-    data->set.use_netrc = va_arg(param, long)?TRUE:FALSE;
+    data->set.use_netrc = va_arg(param, long);
     break;
   case CURLOPT_FOLLOWLOCATION:
     /*
@@ -1351,7 +1351,6 @@ static CURLcode CreateConnection(struct SessionHandle *data,
   char resumerange[40]="";
   struct connectdata *conn;
   struct connectdata *conn_temp;
-  char endbracket;
   int urllen;
   Curl_addrinfo *hostaddr;
 #ifdef HAVE_ALARM
@@ -1406,7 +1405,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
      connections, so we set this to force-close. Protocols that support
      this need to set this to FALSE in their "curl_do" functions. */
   conn->bits.close = TRUE;
-  
+
   /* inherite initial knowledge from the data struct */
   conn->bits.user_passwd = data->set.userpwd?1:0;
   conn->bits.proxy_user_passwd = data->set.proxyuserpwd?1:0;
@@ -1533,35 +1532,12 @@ static CURLcode CreateConnection(struct SessionHandle *data,
 
   buf = data->state.buffer; /* this is our buffer */
 
-  /*************************************************************
-   * Take care of user and password authentication stuff
-   *************************************************************/
-
-  if(conn->bits.user_passwd && !data->set.use_netrc) {
-    data->state.user[0] =0;
-    data->state.passwd[0]=0;
-
-    if(*data->set.userpwd != ':') {
-      /* the name is given, get user+password */
-      sscanf(data->set.userpwd, "%127[^:]:%127[^\n]",
-             data->state.user, data->state.passwd);
-    }
-    else
-      /* no name given, get the password only */
-      sscanf(data->set.userpwd+1, "%127[^\n]", data->state.passwd);
-
-    /* check for password, if no ask for one */
-    if( !data->state.passwd[0] ) {
-      if(!data->set.fpasswd || 
-         data->set.fpasswd(data->set.passwd_client,
-                           "password:", data->state.passwd,
-                           sizeof(data->state.passwd)))
-      {
-        failf(data, "Bad password from password callback");
-        return CURLE_BAD_PASSWORD_ENTERED;
-      }
-    }
-  }
+  /*
+   * So if the URL was A://B/C,
+   *   conn->protostr is A
+   *   conn->gname is B
+   *   conn->path is /C
+   */
 
   /*************************************************************
    * Take care of proxy authentication stuff
@@ -1843,7 +1819,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
     }
     if(type) {
       char command;
-      *type=0;
+      *type=0;                     /* it was in the middle of the hostname */
       command = toupper(type[6]);
       switch(command) {
       case 'A': /* ASCII mode */
@@ -1911,86 +1887,6 @@ static CURLcode CreateConnection(struct SessionHandle *data,
     return CURLE_UNSUPPORTED_PROTOCOL;
   }
 
-  /*************************************************************
-   * .netrc scanning coming up
-   *************************************************************/
-  if(data->set.use_netrc) {
-    if(Curl_parsenetrc(conn->hostname,
-                       data->state.user,
-                       data->state.passwd)) {
-      infof(data, "Couldn't find host %s in the .netrc file, using defaults",
-            conn->hostname);
-    }
-    else
-      conn->bits.user_passwd = 1; /* enable user+password */
-
-    /* weather we failed or not, we don't know which fields that were filled
-       in anyway */
-    if(!data->state.user[0])
-      strcpy(data->state.user, CURL_DEFAULT_USER);
-    if(!data->state.passwd[0])
-      strcpy(data->state.passwd, CURL_DEFAULT_PASSWORD);
-  }
-  else if(!(conn->bits.user_passwd) &&
-         (conn->protocol & (PROT_FTP|PROT_HTTP)) ) {
-    /* This is a FTP or HTTP URL, and we haven't got the user+password in
-     * the extra parameter, we will now try to extract the possible
-     * user+password pair in a string like:
-     * ftp://user:password@ftp.my.site:8021/README */
-    char *ptr=NULL; /* assign to remove possible warnings */
-    if((ptr=strchr(conn->name, '@'))) {
-      /* there's a user+password given here, to the left of the @ */
-
-      data->state.user[0] =0;
-      data->state.passwd[0]=0;
-
-      if(*conn->name != ':') {
-        /* the name is given, get user+password */
-        sscanf(conn->name, "%127[^:@]:%127[^@]",
-               data->state.user, data->state.passwd);
-      }
-      else
-        /* no name given, get the password only */
-        sscanf(conn->name+1, "%127[^@]", data->state.passwd);
-
-      if(data->state.user[0]) {
-        char *newname=curl_unescape(data->state.user, 0);
-        if(strlen(newname) < sizeof(data->state.user)) {
-          strcpy(data->state.user, newname);
-        }
-        /* if the new name is longer than accepted, then just use
-           the unconverted name, it'll be wrong but what the heck */
-        free(newname);
-      }
-
-      /* check for password, if no ask for one */
-      if( !data->state.passwd[0] ) {
-        if(!data->set.fpasswd ||
-           data->set.fpasswd(data->set.passwd_client,
-                             "password:", data->state.passwd,
-                             sizeof(data->state.passwd))) {
-          failf(data, "Bad password from password callback");
-          return CURLE_BAD_PASSWORD_ENTERED;
-        }
-      }
-      else {
-        /* we have a password found in the URL, decode it! */
-        char *newpasswd=curl_unescape(data->state.passwd, 0);
-        if(strlen(newpasswd) < sizeof(data->state.passwd)) {
-          strcpy(data->state.passwd, newpasswd);
-        }
-        free(newpasswd);
-      }
-
-      conn->name = ++ptr;
-      conn->bits.user_passwd=TRUE; /* enable user+password */
-    }
-    else {
-      strcpy(data->state.user, CURL_DEFAULT_USER);
-      strcpy(data->state.passwd, CURL_DEFAULT_PASSWORD);
-    }
-  }
-
   /*************************************************************
    * Figure out the remote port number
    *
@@ -1999,29 +1895,32 @@ static CURLcode CreateConnection(struct SessionHandle *data,
    *
    * To be able to detect port number flawlessly, we must not confuse them
    * IPv6-specified addresses in the [0::1] style. (RFC2732)
+   *
+   * The conn->name is currently [user:passwd@]host[:port] where host could
+   * be a hostname, IPv4 address or IPv6 address.
    *************************************************************/
 
-  if((1 == sscanf(conn->name, "[%*39[0-9a-fA-F:.]%c", &endbracket)) &&
-     (']' == endbracket)) {
-    /* This is a (IPv6-style) specified IP-address. We support _any_
-       IP within brackets to be really generic. */
-    conn->name++; /* pass the starting bracket */
+  tmp = strrchr(conn->name, ':');
 
-    tmp = strchr(conn->name, ']');
-    *tmp = 0; /* zero terminate */
+  if (tmp) {
+    char *rest;
+    unsigned long port;
 
-    tmp++; /* pass the ending bracket */
-    if(':' != *tmp)
-      tmp = NULL; /* no port number available */
-  }
-  else {
-    /* traditional IPv4-style port-extracting */
-    tmp = strchr(conn->name, ':');
-  }
+    port=strtoul(tmp+1, &rest, 10);  /* Port number must be decimal */
 
-  if (tmp) {
-    *tmp++ = '\0'; /* cut off the name there */
-    conn->remote_port = atoi(tmp);
+    if (rest != (tmp+1) && *rest == '\0') {
+      /* The colon really did have only digits after it,
+       * so it is either a port number or a mistake */
+
+      if (port > 0xffff) {   /* Single unix standard says port numbers are
+                              * 16 bits long */
+        failf(data, "Port number too large: %lu", port);
+        return CURLE_URL_MALFORMAT;
+      }
+
+      *tmp = '\0'; /* cut off the name there */
+      conn->remote_port = port;
+    }
   }
 
   if(data->change.proxy) {
@@ -2075,6 +1974,138 @@ static CURLcode CreateConnection(struct SessionHandle *data,
     free(proxydup); /* free the duplicate pointer and not the modified */
   }
 
+  /*************************************************************
+   * Take care of user and password authentication stuff
+   *************************************************************/
+
+  /*
+   * Inputs: data->set.userpwd   (CURLOPT_USERPWD)
+   *         data->set.fpasswd   (CURLOPT_PASSWDFUNCTION)
+   *         data->set.use_netrc (CURLOPT_NETRC)
+   *         conn->hostname
+   *         netrc file
+   *         hard-coded defaults
+   *
+   * Outputs: (almost :- all currently undefined)
+   *          conn->bits.user_passwd  - non-zero if non-default passwords exist
+   *          conn->state.user        - non-zero length if defined
+   *          conn->state.passwd      -   ditto
+   *          conn->hostname          - remove user name and password
+   */
+
+  /* At this point, we're hoping all the other special cases have
+   * been taken care of, so conn->hostname is at most
+   *    [user[:password]]@]hostname
+   *
+   * We need somewhere to put the embedded details, so do that first.
+   */
+
+  data->state.user[0] =0;   /* to make everything well-defined */
+  data->state.passwd[0]=0;
+  
+  if (conn->protocol & (PROT_FTP|PROT_HTTP)) {
+    /* This is a FTP or HTTP URL, we will now try to extract the possible
+     * user+password pair in a string like:
+     * ftp://user:password@ftp.my.site:8021/README */
+    char *ptr=strchr(conn->name, '@');
+    char *userpass = conn->name;
+    if(ptr != NULL) {
+      /* there's a user+password given here, to the left of the @ */
+
+      conn->name = conn->hostname = ++ptr;
+
+      /* So the hostname is sane.  Only bother interpreting the
+       * results if we could care.  It could still be wasted
+       * work because it might be overtaken by the programmatically
+       * set user/passwd, but doing that first adds more cases here :-(
+       */
+
+      if (data->set.use_netrc != CURL_NETRC_REQUIRED) {
+        /* We could use the one in the URL */
+
+        conn->bits.user_passwd = 1; /* enable user+password */
+
+        if(*userpass != ':') {
+          /* the name is given, get user+password */
+          sscanf(userpass, "%127[^:@]:%127[^@]",
+                 data->state.user, data->state.passwd);
+        }
+        else
+          /* no name given, get the password only */
+          sscanf(userpass, ":%127[^@]", data->state.passwd);
+
+        if(data->state.user[0]) {
+          char *newname=curl_unescape(data->state.user, 0);
+          if(strlen(newname) < sizeof(data->state.user)) {
+            strcpy(data->state.user, newname);
+          }
+          /* if the new name is longer than accepted, then just use
+             the unconverted name, it'll be wrong but what the heck */
+          free(newname);
+        }
+        if (data->state.passwd[0]) {
+          /* we have a password found in the URL, decode it! */
+          char *newpasswd=curl_unescape(data->state.passwd, 0);
+          if(strlen(newpasswd) < sizeof(data->state.passwd)) {
+            strcpy(data->state.passwd, newpasswd);
+          }
+          free(newpasswd);
+        }
+      }
+    }
+  }
+
+  /* Programmatically set password:
+   *   - always applies, if available
+   *   - takes precedence over the values we just set above
+   * so scribble it over the top.
+   * User-supplied passwords are assumed not to need unescaping.
+   *
+   * user_password is set in "inherite initial knowledge' above,
+   * so it doesn't have to be set in this block
+   */
+  if (data->set.userpwd != NULL) {
+    if(*data->set.userpwd != ':') {
+      /* the name is given, get user+password */
+      sscanf(data->set.userpwd, "%127[^:]:%127[^\n]",
+             data->state.user, data->state.passwd);
+    }
+    else
+      /* no name given, get the password only */
+      sscanf(data->set.userpwd+1, "%127[^\n]", data->state.passwd);
+  }
+
+  if (data->set.use_netrc != CURL_NETRC_IGNORED &&
+      data->state.passwd[0] == '\0' ) {  /* need passwd */
+    if(Curl_parsenetrc(conn->hostname,
+                       data->state.user,
+                       data->state.passwd)) {
+      infof(data, "Couldn't find host %s in the .netrc file, using defaults",
+            conn->hostname);
+    } else
+      conn->bits.user_passwd = 1; /* enable user+password */
+  }
+
+  /* if we have a user but no password, ask for one */
+  if(conn->bits.user_passwd &&
+     !data->state.passwd[0] ) {
+    if(!data->set.fpasswd ||
+      data->set.fpasswd(data->set.passwd_client,
+                       "password:", data->state.passwd,
+                           sizeof(data->state.passwd)))
+      return CURLE_BAD_PASSWORD_ENTERED;
+  }
+
+  /* So we could have a password but no user; that's just too bad. */
+
+  /* If our protocol needs a password and we have none, use the defaults */
+  if ( (conn->protocol & (PROT_FTP|PROT_HTTP)) &&
+       !conn->bits.user_passwd) {
+    strcpy(data->state.user, CURL_DEFAULT_USER);
+    strcpy(data->state.passwd, CURL_DEFAULT_PASSWORD);
+    /* This is the default password, so DON'T set conn->bits.user_passwd */
+  }
+
   /*************************************************************
    * Check the current list of connections to see if we can
    * re-use an already existing one or if we have to create a
index ff2a997a0277347f3ec41834bc8ebe5353e1f0ac..209bf531dcf97ed17f68ba7fd39a81c474617df9 100644 (file)
@@ -650,7 +650,8 @@ struct UserDefined {
   bool no_body;
   bool set_port;
   bool upload;
-  bool use_netrc;
+  enum CURL_NETRC_OPTION
+       use_netrc;        /* defined in include/curl.h */
   bool verbose;
   bool krb4;             /* kerberos4 connection requested */
   bool reuse_forbid;     /* forbidden to be reused, close after use */