#ifdef SMB_PASSWD
/* 
   Unix SMB/Netbios implementation.
   Version 1.9.
   SMB parameters and setup
   Copyright (C) Andrew Tridgell 1992-1995
   Modified by Jeremy Allison 1995.
   
   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
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "includes.h"
#include "loadparm.h"

extern int DEBUGLEVEL;


/*
 * Routine to search the smbpasswd file for an
 * entry matching the username.
 */
struct smb_passwd *get_smbpwnam(char *name)
{
  /* Static buffers we will return. */
  static struct smb_passwd pw_buf;
  static pstring user_name;
  static unsigned char smbpwd[16];
  char linebuf[256];
  unsigned char c;
  unsigned char *p;
  long uidval;
  long linebuf_len;
  unsigned char lonybble, hinybble;
  int i;
  FILE *fp;
  int lockfd;
  char *pfile = lp_smb_passwd_file();

  if (!*pfile) {
    DEBUG(0,("No SMB password file set\n"));
    return(NULL);
  }

  DEBUG(10,("get_smbpwnam: opening file %s\n", pfile));
  
  fp = fopen(pfile,"r");
  
  if(fp == NULL) 
    {
      DEBUG(0,("get_smbpwnam: unable to open file %s\n", pfile));
      return NULL;
    }
  
  if((lockfd = file_lock(pfile,5))<0) {
    DEBUG(0,("get_smbpwnam: unable to lock file %s\n", pfile));
    fclose(fp);
    return NULL;
  }

  /* make sure it is only rw by the owner */
  chmod(pfile,0600);
  
  /* We have a read lock on the file. */
  /* Scan the file, a line at a time and
     check if the name matches. */
  while(!feof(fp)) 
    {
      linebuf[0] = '\0';

      fgets(linebuf, 256, fp);
      if(ferror(fp))
	{
	  fclose(fp);
	  file_unlock(lockfd);
	  return NULL;
	}

      /* Check if the string is terminated with a newline -
	 if not then we must keep reading and discard until
	 we get one.
	 */
      linebuf_len = strlen(linebuf);
      if(linebuf[linebuf_len-1] != '\n')
	{
	  c = '\0';
	  while(!ferror(fp) && !feof(fp))
	    {
	      c = fgetc(fp);
	      if(c == '\n')
		break;
	    }
	}
      else
	linebuf[linebuf_len-1] = '\0';

#ifdef DEBUG_PASSWORD
      DEBUG(100, ("get_smbpwnam: got line |%s|\n", linebuf));
#endif
      if((linebuf[0] == 0) && feof(fp))
	{
	  DEBUG(4,("get_smbpwnam: end of file reached\n"));
	  break;
	}
      /* The line we have should be of the form :-
	 
	 username:uid:[32hex bytes]:....other flags presently ignored....
	 
	 */

      if(linebuf[0] == '#' || linebuf[0] == '\0')
	{
	  DEBUG(6,("get_smbpwnam: skipping comment or blank line\n"));
	  continue;
	}
      p = (unsigned char *)strchr(linebuf, ':');
      if( p == NULL)
	{
	  DEBUG(0,("get_smbpwnam: malformed password entry (no :)\n"));
	  continue;
	}
      /* As 256 is shorter than a pstring we don't
	 need to check length here - if this ever changes.... */
      strncpy(user_name, linebuf, PTR_DIFF(p,linebuf));
      user_name[PTR_DIFF(p,linebuf)] = '\0';
      if(!strequal(user_name, name))
	continue;

      /* User name matches - get uid and password */
      p++;			/* Go past ':' */
      if(!isdigit(*p)) {
	DEBUG(0,("get_smbpwnam: malformed password entry (uid not number)\n"));
	fclose(fp);
	file_unlock(lockfd);
	return NULL;
      }

      uidval = atoi((char *)p);
      while (*p && isdigit(*p)) p++;
      if(*p != ':') {
	DEBUG(0,("get_smbpwnam: malformed password entry (no : after uid)\n"));
	fclose(fp);
	file_unlock(lockfd);
	return NULL;
      }

      /* Now get the password value - this should be 32 hex digits which
	 are the ascii representations of a 16 byte string. Get two at
	 a time and put them into the password.
	 */
      p++;
      if(*p == '*' || *p == 'X') {
	/* Password deliberately invalid - end here. */
	DEBUG(10,("get_smbpwnam: entry invalidated for user %s\n",user_name));
	fclose(fp);
	file_unlock(lockfd);
	return NULL;
      }
      if(linebuf_len < (PTR_DIFF(p,linebuf) + 33)) {
	DEBUG(0,("get_smbpwnam: malformed password entry (passwd too short)\n"));
	fclose(fp);
	file_unlock(lockfd);
	return(False);
      }

      if(p[32] != ':') {
	DEBUG(0,("get_smbpwnam: malformed password entry (no terminating :)\n"));
	fclose(fp);
	file_unlock(lockfd);
	return NULL;
      }

      if (strequal((char *)p,"NO PASSWORD")) {
	pw_buf.smb_passwd = NULL;
      } else {	
	char *hexchars = "0123456789ABCDEF";
	char *p1,*p2;
	for(i = 0; i < 32; i += 2)
	  {
	    hinybble = toupper(p[i]);
	    lonybble = toupper(p[i+1]);
	    
	    p1 = strchr(hexchars,hinybble);
	    p2 = strchr(hexchars,lonybble);
	    if (!p1 || !p2) {
	      DEBUG(0,("Malformed password entry (non hex chars)\n"));
	      fclose(fp);
	      file_unlock(lockfd);
	      return NULL;
	    }

	    hinybble = PTR_DIFF(p1,hexchars);
	    lonybble = PTR_DIFF(p2,hexchars);

	    smbpwd[i/2] = (hinybble << 4) | lonybble;
	  }
	pw_buf.smb_passwd = smbpwd;
      }
      pw_buf.smb_name = user_name;
      pw_buf.smb_userid = uidval;
      fclose(fp);
      file_unlock(lockfd);
      DEBUG(5,("get_smbpwname: returning passwd entry for user %s, uid %d\n",
		user_name, uidval));
      return &pw_buf;
    }
  
  fclose(fp);
  file_unlock(lockfd);
  return NULL;
}
#else
void smbpass_dummy(void){} /* To avoid compiler complaints */
#endif

