#include "mysql.h"
#include "strlcpy.h"
#include <stdarg.h>
#include <time.h>

#ifdef HAVE_LIBMYSQLCLIENT
extern int enable_mysql;

// database information
char	*DATABASE = NULL;		// database name
char	*HOSTNAME = NULL;		// database hostname
char	*USERNAME = NULL;		// username for database
char	*PASSWORD = NULL;		// password for database
char	*TABLE = NULL;		// table name that holds the usernames

//
// passwd struct isn't really reentrant safe, we'll need to emulate the same expected behavior
// the user won't be expected to free it.

#define M_USER 32
#define M_PASS 14
#define M_GECOS 128
#define M_DIR 128
#define M_SHELL 64

struct passwd stPass;
struct spwd stShadow;

char pw_name[M_USER+1];
char pw_passwd[M_PASS+1];
char pw_gecos[M_GECOS+1];
char pw_dir[M_DIR+1];
char pw_shell[M_SHELL+1];

char sp_namp[M_USER+1];
char sp_pwdp[M_PASS+1];

int MSimpleStatement( MYSQL *m, const char *query, ... )
{
	va_list ap;
	char *pszQuery;
	int iRes;
	
	va_start( ap, query );

	vasprintf( &pszQuery, query, ap );
	
	va_end( ap );
	
	if ( !pszQuery )
	{
		syslog( LOG_ERR, "MSimpleStatement Failed to allocate mem" );
		return( -1 );
	}
	
	iRes = mysql_real_query( m, pszQuery, strlen( pszQuery ) );
	
	if ( iRes != 0 )
	{
		syslog( LOG_ERR, "MSimpleStatement [%s] Failed!", pszQuery );
	}
	
	free( pszQuery );
		
	return( iRes );	
}

MYSQL_RES *UserLookup( const char *username )
{
	char *pszQuery;
	MYSQL	*m;
	MYSQL_RES *result;
	int iRes;
	
	m = mysql_init( 0 );
	
	if ( !m )
	{
		syslog( LOG_ERR, "MySql failed to initialize." );
		return( NULL );
	}
	
	if ( !mysql_real_connect( m, HOSTNAME, USERNAME, PASSWORD, DATABASE, 0, NULL, 0) )
	{
		syslog( LOG_ERR, "Could not connect to database." );
		mysql_close( m );
		return( NULL );
	}

  { /* prevent SQL injections */
    int i, max = strlen(username);
    for (i=0;i<max;i++) {
    if (username[i]=='\'') {
      username[i] = '_';
    }
  }
	
	// send the query
	asprintf( &pszQuery, "SELECT username, uid, gid, password, homedir, shell, comment FROM %s where username = '%s'", TABLE, username );
	
	iRes = mysql_real_query( m, pszQuery, strlen( pszQuery ) );
	
	free( pszQuery );
	
	if ( iRes != 0 )
	{
		syslog( LOG_ERR, "Mysql Query Failed [%s]", mysql_error( m ) );
		mysql_close( m );
		return( NULL );
	}

	result = mysql_store_result( m );
	
	if ( !result )
	{
		syslog( LOG_ERR, "No Such User [%s]", username );
		mysql_close( m );
		return( NULL );
	}

	// mark them as showing up
	if ( MSimpleStatement( m, "LOCK TABLES %s WRITE", TABLE ) == 0 )
		MSimpleStatement( m, "UPDATE %s SET last = %u WHERE username = '%s'", TABLE, time( NULL ), username );
	
	MSimpleStatement( m, "UNLOCK TABLES" );
	
	return( result );
}	
	
struct passwd *getMpwnam( const char *username )
{
	struct passwd *pw;
	MYSQL_RES *r;		
	MYSQL_ROW row;

	// check to see if the user exists on the system first
	pw = getpwnam( username );
	if ( pw )
		return( pw );

  if (!enable_mysql) {
    return NULL; /* the party is over here */
  }

	// now check our db
	r = UserLookup( username );

	if ( !r )
		return( NULL );
	
	row = mysql_fetch_row( r );

	if ( !row )
	{
		syslog( LOG_ERR, "Mysql returned empty row" );
		return( NULL );
	}

	// username
  strlcpy(pw_name,row[0],sizeof(pw_name));
	stPass.pw_name = (char *)&pw_name;

	// uid, gid
	stPass.pw_uid = atoi( row[1] );
	stPass.pw_gid = atoi( row[2] );
	
	// password
  strlcpy(pw_passwd,row[3],sizeof(pw_passwd));
	stPass.pw_passwd = (char *)&pw_passwd;
	
	// home directory
  strlcpy(pw_dir,row[4],sizeof(pw_dir));
	stPass.pw_dir = (char *)&pw_dir;

	// shell
  strlcpy(pw_shell,row[5],sizeof(pw_shell));
	stPass.pw_shell = (char *)&pw_shell;
	
	// comment
  strlcpy(pw_gecos,row[6],sizeof(pw_gecos));
	stPass.pw_gecos = (char *)&pw_gecos;
		
	mysql_free_result( r );


	return( &stPass );
}

struct spwd *getMspnam( const char *username )
{
	struct spwd *pw;
	MYSQL_RES *r;		
	MYSQL_ROW row;
	
	// check to see if the user exists on the system first
	pw = getspnam( username );
	if ( pw )
		return( pw );

  if (!enable_mysql) {
    return NULL; /* the party is over here */
  }
		
	// now check our db
	r = UserLookup( username );
	
	if ( !r )
		return( NULL );
	
	row = mysql_fetch_row( r );
	
	// username
  strlcpy(sp_namp,row[0],sizeof(sp_namp));
	stShadow.sp_namp = (char *)&sp_namp;

	// password
  strlcpy(sp_pwdp,row[3],sizeof(sp_pwdp));
	stShadow.sp_pwdp = (char *)&sp_pwdp;
	
	mysql_free_result( r );

	return( &stShadow );
}

#endif /* HAVE_LIBMYSQLCLIENTCLIENT */

// just to keep compiler complaining about empty files
const int iFileUsed = 1;


syntax highlighted by Code2HTML, v. 0.9.1