/* 
   Unix SMB/CIFS implementation.
   Username handling
   Copyright (C) Andrew Tridgell 1992-1998
   Copyright (C) Jeremy Allison 1997-2001.
   Copyright (C) Volker Lendecke 2006
   
   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 3 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, see <http://www.gnu.org/licenses/>.
*/

#include "includes.h"
#include "smbd/globals.h"

/*******************************************************************
 Map a username from a dos name to a unix name by looking in the username
 map. Note that this modifies the name in place.
 This is the main function that should be called *once* on
 any incoming or new username - in order to canonicalize the name.
 This is being done to de-couple the case conversions from the user mapping
 function. Previously, the map_username was being called
 every time Get_Pwnam_alloc was called.
 Returns True if username was changed, false otherwise.
********************************************************************/

static const char *get_last_from(void)
{
	if (!last_from) {
		return "";
	}
	return last_from;
}

static const char *get_last_to(void)
{
	if (!last_to) {
		return "";
	}
	return last_to;
}

static bool set_last_from_to(const char *from, const char *to)
{
	char *orig_from = last_from;
	char *orig_to = last_to;

	last_from = SMB_STRDUP(from);
	last_to = SMB_STRDUP(to);

	SAFE_FREE(orig_from);
	SAFE_FREE(orig_to);

	if (!last_from || !last_to) {
		SAFE_FREE(last_from);
		SAFE_FREE(last_to);
		return false;
	}
	return true;
}

bool map_username(fstring user)
{
	XFILE *f;
	char *mapfile = lp_username_map();
	char *s;
	char buf[512];
	bool mapped_user = False;
	char *cmd = lp_username_map_script();

	if (!*user)
		return false;

	if (strequal(user,get_last_to()))
		return false;

	if (strequal(user,get_last_from())) {
		DEBUG(3,("Mapped user %s to %s\n",user,get_last_to()));
		fstrcpy(user,get_last_to());
		return true;
	}

	/* first try the username map script */

	if ( *cmd ) {
		char **qlines;
		char *command = NULL;
		int numlines, ret, fd;

		command = talloc_asprintf(talloc_tos(),
					"%s \"%s\"",
					cmd,
					user);
		if (!command) {
			return false;
		}

		DEBUG(10,("Running [%s]\n", command));
		ret = smbrun(command, &fd);
		DEBUGADD(10,("returned [%d]\n", ret));

		if ( ret != 0 ) {
			if (fd != -1)
				close(fd);
			return False;
		}

		numlines = 0;
		qlines = fd_lines_load(fd, &numlines,0, NULL);
		DEBUGADD(10,("Lines returned = [%d]\n", numlines));
		close(fd);

		/* should be either no lines or a single line with the mapped username */

		if (numlines && qlines) {
			DEBUG(3,("Mapped user %s to %s\n", user, qlines[0] ));
			fstrcpy( user, qlines[0] );
		}

		TALLOC_FREE(qlines);

		return numlines != 0;
	}

	/* ok.  let's try the mapfile */
	if (!*mapfile)
		return False;

	f = x_fopen(mapfile,O_RDONLY, 0);
	if (!f) {
		DEBUG(0,("can't open username map %s. Error %s\n",mapfile, strerror(errno) ));
		return False;
	}

	DEBUG(4,("Scanning username map %s\n",mapfile));

	while((s=fgets_slash(buf,sizeof(buf),f))!=NULL) {
		char *unixname = s;
		char *dosname = strchr_m(unixname,'=');
		char **dosuserlist;
		bool return_if_mapped = False;

		if (!dosname)
			continue;

		*dosname++ = 0;

		while (isspace((int)*unixname))
			unixname++;

		if ('!' == *unixname) {
			return_if_mapped = True;
			unixname++;
			while (*unixname && isspace((int)*unixname))
				unixname++;
		}

		if (!*unixname || strchr_m("#;",*unixname))
			continue;

		{
			int l = strlen(unixname);
			while (l && isspace((int)unixname[l-1])) {
				unixname[l-1] = 0;
				l--;
			}
		}

		/* skip lines like 'user = ' */

		dosuserlist = str_list_make_v3(talloc_tos(), dosname, NULL);
		if (!dosuserlist) {
			DEBUG(0,("Bad username map entry.  Unable to build user list.  Ignoring.\n"));
			continue;
		}

		if (strchr_m(dosname,'*') ||
		    user_in_list(user, (const char **)dosuserlist)) {
			DEBUG(3,("Mapped user %s to %s\n",user,unixname));
			mapped_user = True;

			set_last_from_to(user, unixname);
			fstrcpy( user, unixname );

			if ( return_if_mapped ) {
				TALLOC_FREE(dosuserlist);
				x_fclose(f);
				return True;
			}
		}

		TALLOC_FREE(dosuserlist);
	}

	x_fclose(f);

	/*
	 * Setup the last_from and last_to as an optimization so
	 * that we don't scan the file again for the same user.
	 */

	set_last_from_to(user, user);

	return mapped_user;
}