a pointless commit to check on a problem Luke reported with CVS
[kai/samba.git] / source3 / lib / util.c
index 5c81a14c6637406d5961aae2b1d7e72aea2223e1..306e80c3073cf64b3d9e79fbba99c42ba7b0fa24 100644 (file)
@@ -2,7 +2,7 @@
    Unix SMB/Netbios implementation.
    Version 1.9.
    Samba utility functions
-   Copyright (C) Andrew Tridgell 1992-1997
+   Copyright (C) Andrew Tridgell 1992-1998
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -93,9 +93,9 @@ static BOOL stdout_logging = False;
 static char *filename_dos(char *path,char *buf);
 
 #if defined(SIGUSR2)
-/**************************************************************************** **
+/******************************************************************************
  catch a sigusr2 - decrease the debug log level.
- **************************************************************************** */
+ *****************************************************************************/
 int sig_usr2(void)
 {  
   BlockSignals( True, SIGUSR2);
@@ -170,7 +170,6 @@ reopen the log files
 ****************************************************************************/
 void reopen_logs(void)
 {
-  extern FILE *dbf;
   pstring fname;
   
   if (DEBUGLEVEL > 0)
@@ -888,6 +887,15 @@ int StrCaseCmp(char *s, char *t)
      asynchronous upper to lower mapping.
    */
 #if !defined(KANJI_WIN95_COMPATIBILITY)
+  /*
+   * For completeness we should put in equivalent code for code pages
+   * 949 (Korean hangul) and 950 (Big5 Traditional Chinese) here - but
+   * doubt anyone wants Samba to behave differently from Win95 and WinNT
+   * here. They both treat full width ascii characters as case senstive
+   * filenames (ie. they don't do the work we do here).
+   * JRA.
+   */
+
   if(lp_client_code_page() == KANJI_CODEPAGE)
   {
     /* Win95 treats full width ascii characters as case sensitive. */
@@ -952,6 +960,15 @@ int StrnCaseCmp(char *s, char *t, int n)
      asynchronous upper to lower mapping.
    */
 #if !defined(KANJI_WIN95_COMPATIBILITY)
+  /*
+   * For completeness we should put in equivalent code for code pages
+   * 949 (Korean hangul) and 950 (Big5 Traditional Chinese) here - but
+   * doubt anyone wants Samba to behave differently from Win95 and WinNT
+   * here. They both treat full width ascii characters as case senstive
+   * filenames (ie. they don't do the work we do here).
+   * JRA. 
+   */
+
   if(lp_client_code_page() == KANJI_CODEPAGE)
   {
     /* Win95 treats full width ascii characters as case sensitive. */
@@ -1059,6 +1076,15 @@ void strlower(char *s)
   while (*s)
   {
 #if !defined(KANJI_WIN95_COMPATIBILITY)
+  /*
+   * For completeness we should put in equivalent code for code pages
+   * 949 (Korean hangul) and 950 (Big5 Traditional Chinese) here - but
+   * doubt anyone wants Samba to behave differently from Win95 and WinNT
+   * here. They both treat full width ascii characters as case senstive
+   * filenames (ie. they don't do the work we do here).
+   * JRA. 
+   */
+
     if(lp_client_code_page() == KANJI_CODEPAGE)
     {
       /* Win95 treats full width ascii characters as case sensitive. */
@@ -1097,6 +1123,15 @@ void strupper(char *s)
   while (*s)
   {
 #if !defined(KANJI_WIN95_COMPATIBILITY)
+  /*
+   * For completeness we should put in equivalent code for code pages
+   * 949 (Korean hangul) and 950 (Big5 Traditional Chinese) here - but
+   * doubt anyone wants Samba to behave differently from Win95 and WinNT
+   * here. They both treat full width ascii characters as case senstive
+   * filenames (ie. they don't do the work we do here).
+   * JRA. 
+   */
+
     if(lp_client_code_page() == KANJI_CODEPAGE)
     {
       /* Win95 treats full width ascii characters as case sensitive. */
@@ -1158,6 +1193,15 @@ void string_replace(char *s,char oldc,char newc)
   while (*s)
   {
 #if !defined(KANJI_WIN95_COMPATIBILITY)
+  /*
+   * For completeness we should put in equivalent code for code pages
+   * 949 (Korean hangul) and 950 (Big5 Traditional Chinese) here - but
+   * doubt anyone wants Samba to behave differently from Win95 and WinNT
+   * here. They both treat full width ascii characters as case senstive
+   * filenames (ie. they don't do the work we do here).
+   * JRA. 
+   */
+
     if(lp_client_code_page() == KANJI_CODEPAGE)
     {
       /* Win95 treats full width ascii characters as case sensitive. */
@@ -1581,12 +1625,12 @@ BOOL reduce_name(char *s,char *dir,BOOL widelinks)
 #else
   pstring dir2;
   pstring wd;
-  pstring basename;
+  pstring base_name;
   pstring newname;
   char *p=NULL;
   BOOL relative = (*s != '/');
 
-  *dir2 = *wd = *basename = *newname = 0;
+  *dir2 = *wd = *base_name = *newname = 0;
 
   if (widelinks)
     {
@@ -1609,8 +1653,8 @@ BOOL reduce_name(char *s,char *dir,BOOL widelinks)
   /* remove any double slashes */
   string_sub(s,"//","/");
 
-  pstrcpy(basename,s);
-  p = strrchr(basename,'/');
+  pstrcpy(base_name,s);
+  p = strrchr(base_name,'/');
 
   if (!p)
     return(True);
@@ -1635,7 +1679,7 @@ BOOL reduce_name(char *s,char *dir,BOOL widelinks)
     }
 
 
-    if (p && (p != basename))
+    if (p && (p != base_name))
       {
        *p = 0;
        if (strcmp(p+1,".")==0)
@@ -1644,10 +1688,10 @@ BOOL reduce_name(char *s,char *dir,BOOL widelinks)
          *p = '/';
       }
 
-  if (ChDir(basename) != 0)
+  if (ChDir(base_name) != 0)
     {
       ChDir(wd);
-      DEBUG(3,("couldn't chdir for %s %s basename=%s\n",s,dir,basename));
+      DEBUG(3,("couldn't chdir for %s %s basename=%s\n",s,dir,base_name));
       return(False);
     }
 
@@ -1658,7 +1702,7 @@ BOOL reduce_name(char *s,char *dir,BOOL widelinks)
       return(False);
     }
 
-  if (p && (p != basename))
+  if (p && (p != base_name))
     {
       strcat(newname,"/");
       strcat(newname,p+1);
@@ -1784,6 +1828,15 @@ BOOL strhasupper(char *s)
   while (*s) 
   {
 #if !defined(KANJI_WIN95_COMPATIBILITY)
+  /*
+   * For completeness we should put in equivalent code for code pages
+   * 949 (Korean hangul) and 950 (Big5 Traditional Chinese) here - but
+   * doubt anyone wants Samba to behave differently from Win95 and WinNT
+   * here. They both treat full width ascii characters as case senstive
+   * filenames (ie. they don't do the work we do here).
+   * JRA. 
+   */
+
     if(lp_client_code_page() == KANJI_CODEPAGE)
     {
       /* Win95 treats full width ascii characters as case sensitive. */
@@ -1817,6 +1870,15 @@ BOOL strhaslower(char *s)
   while (*s) 
   {
 #if !defined(KANJI_WIN95_COMPATIBILITY)
+  /*
+   * For completeness we should put in equivalent code for code pages
+   * 949 (Korean hangul) and 950 (Big5 Traditional Chinese) here - but
+   * doubt anyone wants Samba to behave differently from Win95 and WinNT
+   * here. They both treat full width ascii characters as case senstive
+   * filenames (ie. they don't do the work we do here).
+   * JRA. 
+   */
+
     if(lp_client_code_page() == KANJI_CODEPAGE)
     {
       /* Win95 treats full width ascii characters as case sensitive. */
@@ -1858,6 +1920,15 @@ int count_chars(char *s,char c)
   int count=0;
 
 #if !defined(KANJI_WIN95_COMPATIBILITY)
+  /*
+   * For completeness we should put in equivalent code for code pages
+   * 949 (Korean hangul) and 950 (Big5 Traditional Chinese) here - but
+   * doubt anyone wants Samba to behave differently from Win95 and WinNT
+   * here. They both treat full width ascii characters as case senstive
+   * filenames (ie. they don't do the work we do here).
+   * JRA. 
+   */
+
   if(lp_client_code_page() == KANJI_CODEPAGE)
   {
     /* Win95 treats full width ascii characters as case sensitive. */
@@ -1990,6 +2061,10 @@ int write_socket(int fd,char *buf,int len)
   ret = write_data(fd,buf,len);
       
   DEBUG(6,("write_socket(%d,%d) wrote %d\n",fd,len,ret));
+  if(ret <= 0)
+    DEBUG(0,("write_socket: Error writing %d bytes to socket %d: ERRNO = %s\n", 
+       len, fd, strerror(errno) ));
+
   return(ret);
 }
 
@@ -1999,20 +2074,20 @@ read from a socket
 int read_udp_socket(int fd,char *buf,int len)
 {
   int ret;
-  struct sockaddr sock;
+  struct sockaddr_in sock;
   int socklen;
   
   socklen = sizeof(sock);
   bzero((char *)&sock,socklen);
   bzero((char *)&lastip,sizeof(lastip));
-  ret = recvfrom(fd,buf,len,0,&sock,&socklen);
+  ret = recvfrom(fd,buf,len,0,(struct sockaddr *)&sock,&socklen);
   if (ret <= 0) {
     DEBUG(2,("read socket failed. ERRNO=%s\n",strerror(errno)));
     return(0);
   }
 
-  lastip = *(struct in_addr *) &sock.sa_data[2];
-  lastport = ntohs(((struct sockaddr_in *)&sock)->sin_port);
+  lastip = sock.sin_addr;
+  lastport = ntohs(sock.sin_port);
 
   DEBUG(10,("read_udp_socket: lastip %s lastport %d read: %d\n",
              inet_ntoa(lastip), lastport, ret));
@@ -2485,29 +2560,31 @@ BOOL receive_local_message(int fd, char *buffer, int buffer_len, int timeout)
 }
 
 /****************************************************************************
- structure to hold a linked list of local udp messages.
+ structure to hold a linked list of local messages.
  for processing.
 ****************************************************************************/
 
-typedef struct _udp_message_list {
-   struct _udp_message_list *msg_next;
+typedef struct _message_list {
+   struct _message_list *msg_next;
    char *msg_buf;
    int msg_len;
-} udp_message_list;
+} pending_message_list;
 
-static udp_message_list *udp_msg_head = NULL;
+static pending_message_list *smb_msg_head = NULL;
 
 /****************************************************************************
- Function to push a linked list of local udp messages ready
+ Function to push a linked list of local messages ready
  for processing.
 ****************************************************************************/
-BOOL push_local_message(char *buf, int msg_len)
+
+static BOOL push_local_message(pending_message_list **pml, char *buf, int msg_len)
 {
-  udp_message_list *msg = (udp_message_list *)malloc(sizeof(udp_message_list));
+  pending_message_list *msg = (pending_message_list *)
+                               malloc(sizeof(pending_message_list));
 
   if(msg == NULL)
   {
-    DEBUG(0,("push_local_message: malloc fail (1)\n"));
+    DEBUG(0,("push_message: malloc fail (1)\n"));
     return False;
   }
 
@@ -2522,12 +2599,22 @@ BOOL push_local_message(char *buf, int msg_len)
   memcpy(msg->msg_buf, buf, msg_len);
   msg->msg_len = msg_len;
 
-  msg->msg_next = udp_msg_head;
-  udp_msg_head = msg;
+  msg->msg_next = *pml;
+  *pml = msg;
 
   return True;
 }
 
+/****************************************************************************
+ Function to push a linked list of local smb messages ready
+ for processing.
+****************************************************************************/
+
+BOOL push_smb_message(char *buf, int msg_len)
+{
+  return push_local_message(&smb_msg_head, buf, msg_len);
+}
+
 /****************************************************************************
   Do a select on an two fd's - with timeout. 
 
@@ -2535,6 +2622,10 @@ BOOL push_local_message(char *buf, int msg_len)
   queue (this can only happen during oplock break
   processing) return this first.
 
+  If a pending smb message has been pushed onto the
+  queue (this can only happen during oplock break
+  processing) return this next.
+
   If the first smbfd is ready then read an smb from it.
   if the second (loopback UDP) fd is ready then read a message
   from it and setup the buffer header to identify the length
@@ -2555,19 +2646,22 @@ BOOL receive_message_or_smb(int smbfd, int oplock_fd,
   *got_smb = False;
 
   /*
-   * Check to see if we already have a message on the udp queue.
+   * Check to see if we already have a message on the smb queue.
    * If so - copy and return it.
    */
-
-  if(udp_msg_head)
+  
+  if(smb_msg_head)
   {
-    udp_message_list *msg = udp_msg_head;
+    pending_message_list *msg = smb_msg_head;
     memcpy(buffer, msg->msg_buf, MIN(buffer_len, msg->msg_len));
-    udp_msg_head = msg->msg_next;
-
+    smb_msg_head = msg->msg_next;
+  
     /* Free the message we just copied. */
     free((char *)msg->msg_buf);
     free((char *)msg);
+    *got_smb = True;
+
+    DEBUG(5,("receive_message_or_smb: returning queued smb message.\n"));
     return True;
   }
 
@@ -3012,7 +3106,7 @@ void become_daemon(void)
 {
 #ifndef NO_FORK_DEBUG
   if (fork())
-    exit(0);
+    _exit(0);
 
   /* detach from the terminal */
 #ifdef USE_SETSID
@@ -3622,115 +3716,166 @@ void reset_globals_after_fork()
 /*******************************************************************
  return the DNS name of the client 
  ******************************************************************/
-char *client_name(void)
-{
-  extern int Client;
-  struct sockaddr sa;
-  struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa);
-  int     length = sizeof(sa);
-  static pstring name_buf;
-  struct hostent *hp;
+char *client_name(int fd)
+{
+       struct sockaddr sa;
+       struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa);
+       int     length = sizeof(sa);
+       static pstring name_buf;
+       struct hostent *hp;
+       static int last_fd=-1;
+       
+       if (global_client_name_done && last_fd == fd) 
+               return name_buf;
+       
+       last_fd = fd;
+       global_client_name_done = False;
+       
+       strcpy(name_buf,"UNKNOWN");
+       
+       if (fd == -1) {
+               return name_buf;
+       }
+       
+       if (getpeername(fd, &sa, &length) < 0) {
+               DEBUG(0,("getpeername failed\n"));
+               return name_buf;
+       }
+       
+       /* Look up the remote host name. */
+       if ((hp = gethostbyaddr((char *) &sockin->sin_addr,
+                               sizeof(sockin->sin_addr),
+                               AF_INET)) == 0) {
+               DEBUG(1,("Gethostbyaddr failed for %s\n",client_addr(fd)));
+               StrnCpy(name_buf,client_addr(fd),sizeof(name_buf) - 1);
+       } else {
+               StrnCpy(name_buf,(char *)hp->h_name,sizeof(name_buf) - 1);
+               if (!matchname(name_buf, sockin->sin_addr)) {
+                       DEBUG(0,("Matchname failed on %s %s\n",name_buf,client_addr(fd)));
+                       strcpy(name_buf,"UNKNOWN");
+               }
+       }
+       global_client_name_done = True;
+       return name_buf;
+}
 
-  if (global_client_name_done) 
-    return name_buf;
+/*******************************************************************
+ return the IP addr of the client as a string 
+ ******************************************************************/
+char *client_addr(int fd)
+{
+       struct sockaddr sa;
+       struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa);
+       int     length = sizeof(sa);
+       static fstring addr_buf;
+       static int last_fd = -1;
 
-  strcpy(name_buf,"UNKNOWN");
+       if (global_client_addr_done && fd == last_fd) 
+               return addr_buf;
 
-  if (Client == -1) {
-         return name_buf;
-  }
+       last_fd = fd;
+       global_client_addr_done = False;
 
-  if (getpeername(Client, &sa, &length) < 0) {
-    DEBUG(0,("getpeername failed\n"));
-    return name_buf;
-  }
+       strcpy(addr_buf,"0.0.0.0");
 
-  /* Look up the remote host name. */
-  if ((hp = gethostbyaddr((char *) &sockin->sin_addr,
-                         sizeof(sockin->sin_addr),
-                         AF_INET)) == 0) {
-    DEBUG(1,("Gethostbyaddr failed for %s\n",client_addr()));
-    StrnCpy(name_buf,client_addr(),sizeof(name_buf) - 1);
-  } else {
-    StrnCpy(name_buf,(char *)hp->h_name,sizeof(name_buf) - 1);
-    if (!matchname(name_buf, sockin->sin_addr)) {
-      DEBUG(0,("Matchname failed on %s %s\n",name_buf,client_addr()));
-      strcpy(name_buf,"UNKNOWN");
-    }
-  }
-  global_client_name_done = True;
-  return name_buf;
+       if (fd == -1) {
+               return addr_buf;
+       }
+       
+       if (getpeername(fd, &sa, &length) < 0) {
+               DEBUG(0,("getpeername failed\n"));
+               return addr_buf;
+       }
+       
+       fstrcpy(addr_buf,(char *)inet_ntoa(sockin->sin_addr));
+       
+       global_client_addr_done = True;
+       return addr_buf;
 }
 
 /*******************************************************************
- return the IP addr of the client as a string 
- ******************************************************************/
-char *client_addr(void)
-{
-  extern int Client;
-  struct sockaddr sa;
-  struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa);
-  int     length = sizeof(sa);
-  static fstring addr_buf;
+ Patch from jkf@soton.ac.uk
+ Split Luke's automount_server into YP lookup and string splitter
+ so can easily implement automount_path(). 
+ As we may end up doing both, cache the last YP result. 
+*******************************************************************/
 
-  if (global_client_addr_done) 
-    return addr_buf;
+#if (defined(NETGROUP) && defined(AUTOMOUNT))
+static char *automount_lookup(char *user_name)
+{
+  static fstring last_key = "";
+  static pstring last_value = "";
 
-  strcpy(addr_buf,"0.0.0.0");
+  int nis_error;        /* returned by yp all functions */
+  char *nis_result;     /* yp_match inits this */
+  int nis_result_len;  /* and set this */
+  char *nis_domain;     /* yp_get_default_domain inits this */
+  char *nis_map = (char *)lp_nis_home_map_name();
 
-  if (Client == -1) {
-         return addr_buf;
+  if ((nis_error = yp_get_default_domain(&nis_domain)) != 0)
+  {
+    DEBUG(3, ("YP Error: %s\n", yperr_string(nis_error)));
+    return last_value;
   }
 
-  if (getpeername(Client, &sa, &length) < 0) {
-    DEBUG(0,("getpeername failed\n"));
-    return addr_buf;
-  }
+  DEBUG(5, ("NIS Domain: %s\n", nis_domain));
 
-  fstrcpy(addr_buf,(char *)inet_ntoa(sockin->sin_addr));
+  if (!strcmp(user_name, last_key))
+  {
+    nis_result = last_value;
+    nis_result_len = strlen(last_value);
+    nis_error = 0;
+  }
+  else
+  {
+    if ((nis_error = yp_match(nis_domain, nis_map,
+                              user_name, strlen(user_name),
+                              &nis_result, &nis_result_len)) != 0)
+    {
+      DEBUG(3, ("YP Error: \"%s\" while looking up \"%s\" in map \"%s\"\n", 
+               yperr_string(nis_error), user_name, nis_map));
+    }
+    if (!nis_error && nis_result_len >= sizeof(pstring))
+    {
+      nis_result_len = sizeof(pstring)-1;
+    }
+    fstrcpy(last_key, user_name);
+    strncpy(last_value, nis_result, nis_result_len);
+    last_value[nis_result_len] = '\0';
+  }
 
-  global_client_addr_done = True;
-  return addr_buf;
+  DEBUG(4, ("YP Lookup: %s resulted in %s\n", user_name, last_value));
+  return last_value;
 }
+#endif
+
+/*******************************************************************
+ Patch from jkf@soton.ac.uk
+ This is Luke's original function with the NIS lookup code
+ moved out to a separate function.
+*******************************************************************/
 
 char *automount_server(char *user_name)
 {
        static pstring server_name;
 
 #if (defined(NETGROUP) && defined (AUTOMOUNT))
-       int nis_error;        /* returned by yp all functions */
-       char *nis_result;     /* yp_match inits this */
-       int nis_result_len;  /* and set this */
-       char *nis_domain;     /* yp_get_default_domain inits this */
-       char *nis_map = (char *)lp_nis_home_map_name();
        int home_server_len;
 
        /* set to default of local machine */
        pstrcpy(server_name, local_machine);
 
-       if ((nis_error = yp_get_default_domain(&nis_domain)) != 0)
-       {
-               DEBUG(3, ("YP Error: %s\n", yperr_string(nis_error)));
-       }
-
-       DEBUG(5, ("NIS Domain: %s\n", nis_domain));
-
-       if ((nis_error = yp_match(nis_domain, nis_map,
-                       user_name, strlen(user_name),
-                       &nis_result, &nis_result_len)) != 0)
-       {
-               DEBUG(3, ("YP Error: %s\n", yperr_string(nis_error)));
-       }
-
-       if (!nis_error && lp_nis_home_map())
+       if (lp_nis_home_map())
        {
-               home_server_len = strcspn(nis_result,":");
+               char *automount_value = automount_lookup(user_name);
+               home_server_len = strcspn(automount_value,":");
                DEBUG(5, ("NIS lookup succeeded.  Home server length: %d\n",home_server_len));
                if (home_server_len > sizeof(pstring))
                {
                        home_server_len = sizeof(pstring);
                }
-               strncpy(server_name, nis_result, home_server_len);
+               strncpy(server_name, automount_value, home_server_len);
+                server_name[home_server_len] = '\0';
        }
 #else
        /* use the local machine name instead of the auto-map server */
@@ -3742,6 +3887,44 @@ char *automount_server(char *user_name)
        return server_name;
 }
 
+/*******************************************************************
+ Patch from jkf@soton.ac.uk
+ Added this to implement %p (NIS auto-map version of %H)
+*******************************************************************/
+
+char *automount_path(char *user_name)
+{
+       static pstring server_path;
+
+#if (defined(NETGROUP) && defined (AUTOMOUNT))
+       char *home_path_start;
+
+       /* set to default of no string */
+       server_path[0] = 0;
+
+       if (lp_nis_home_map())
+       {
+               char *automount_value = automount_lookup(user_name);
+               home_path_start = strchr(automount_value,':');
+               if (home_path_start != NULL)
+               {
+                 DEBUG(5, ("NIS lookup succeeded.  Home path is: %s\n",
+                       home_path_start?(home_path_start+1):""));
+                 strcpy(server_path, home_path_start+1);
+               }
+       }
+#else
+       /* use the passwd entry instead of the auto-map server entry */
+       /* pstrcpy() copes with get_home_dir() returning NULL */
+       pstrcpy(server_path, get_home_dir(user_name));
+#endif
+
+       DEBUG(4,("Home server path: %s\n", server_path));
+
+       return server_path;
+}
+
+
 /*******************************************************************
 sub strings with useful parameters
 Rewritten by Stefaan A Eeckels <Stefaan.Eeckels@ecc.lu> and
@@ -3754,7 +3937,7 @@ void standard_sub_basic(char *str)
        struct passwd *pass;
        char *username = sam_logon_in_ssb ? samlogon_user : sesssetup_user;
 
-       for (s = str ; (p = strchr(s,'%')) != NULL ; s = p )
+       for (s = str ; s && *s && (p = strchr(s,'%')); s = p )
        {
                switch (*(p+1))
                {
@@ -3771,9 +3954,9 @@ void standard_sub_basic(char *str)
                                break;
                        }
                        case 'N' : string_sub(p,"%N", automount_server(username)); break;
-                       case 'I' : string_sub(p,"%I", client_addr()); break;
+                       case 'I' : string_sub(p,"%I", client_addr(Client)); break;
                        case 'L' : string_sub(p,"%L", local_machine); break;
-                       case 'M' : string_sub(p,"%M", client_name()); break;
+                       case 'M' : string_sub(p,"%M", client_name(Client)); break;
                        case 'R' : string_sub(p,"%R", remote_proto); break;
                        case 'T' : string_sub(p,"%T", timestring()); break;
                        case 'U' : string_sub(p,"%U", username); break;
@@ -3787,6 +3970,38 @@ void standard_sub_basic(char *str)
                        case 'h' : string_sub(p,"%h", myhostname); break;
                        case 'm' : string_sub(p,"%m", remote_machine); break;
                        case 'v' : string_sub(p,"%v", VERSION); break;
+                        case '$' : /* Expand environment variables */
+                        {
+                          /* Contributed by Branko Cibej <branko.cibej@hermes.si> */
+                          fstring envname;
+                          char *envval;
+                          char *q, *r;
+                          int copylen;
+                          if (*(p+2) != '(') { p+=2; break; }
+                          if ((q = strchr(p,')')) == NULL)
+                          {
+                            DEBUG(0,("standard_sub_basic: Unterminated environment \
+variable [%s]\n", p));
+                            p+=2; break;
+                          }
+                          r = p+3;
+                          copylen = MIN((q-r),(sizeof(envname)-1));
+                          strncpy(envname,r,copylen);
+                          envname[copylen] = '\0';
+                          if ((envval = getenv(envname)) == NULL)
+                          {
+                            DEBUG(0,("standard_sub_basic: Environment variable [%s] not set\n",
+                                     envname));
+                            p+=2; break;
+                          }
+                          copylen = MIN((q+1-p),(sizeof(envname)-1));
+                          strncpy(envname,p,copylen);
+                          envname[copylen] = '\0';
+                          string_sub(p,envname,envval);
+                          break;
+                        }
                        case '\0': p++; break; /* don't run off end if last character is % */
                        default  : p+=2; break;
                }
@@ -3840,11 +4055,20 @@ struct hostent *Get_Hostbyname(char *name)
       exit(0);
     }
 
+   
+  /* 
+   * This next test is redundent and causes some systems (with
+   * broken isalnum() calls) problems.
+   * JRA.
+   */
+
+#if 0
   if (!isalnum(*name2))
     {
       free(name2);
       return(NULL);
     }
+#endif /* 0 */
 
   ret = sys_gethostbyname(name2);
   if (ret != NULL)
@@ -4641,4 +4865,29 @@ char *tab_depth(int depth)
        return spaces;
 }
 
+/*****************************************************************
+ Convert a domain SID to an ascii string. (non-reentrant).
+*****************************************************************/
 
+/* BIG NOTE: this function only does SIDS where the identauth is not >= 2^32 */
+char *dom_sid_to_string(DOM_SID *sid)
+{
+  static pstring sidstr;
+  char subauth[16];
+  int i;
+  uint32 ia = (sid->id_auth[5]) +
+              (sid->id_auth[4] << 8 ) +
+              (sid->id_auth[3] << 16) +
+              (sid->id_auth[2] << 24);
+
+  sprintf(sidstr, "S-%d-%d", sid->sid_rev_num, ia);
+
+  for (i = 0; i < sid->num_auths; i++)
+  {
+    sprintf(subauth, "-%d", sid->sub_auths[i]);
+    strcat(sidstr, subauth);
+  }
+
+  DEBUG(7,("dom_sid_to_string returning %s\n", sidstr));
+  return sidstr;
+}