/* 
   SQL Auditing Tools
   Copyright (C) Patrik Karlsson 2001
   
   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 "tds.h"
#include "sqlsamdump.h"
#include "sqllib.h"
#include "registry.h"
#include "constants.h"

const char *TEMPDIRS[5] = {"c:\\temp", "c:\\winnt\\temp", "d:\\temp", 
			   "e:\\temp", "c:\\"};

/*
  Check if extended sp exists
*/
int ifExistsXP(TDSSOCKET *tds, char *pXP) {

  int nResult;
  char *sQuery;
  int nRows = 0;

  sQuery = (char *) malloc (100);
  sprintf(sQuery, "select * from master..sysobjects where name=\'%s\'", pXP);

  if ( tds_submit_query(tds,sQuery) != TDS_FAIL ) {

    while( ( nResult = tds_process_result_tokens(tds) ) == TDS_SUCCEED) {
      while( tds_process_row_tokens(tds) ==TDS_SUCCEED ) {
	nRows ++;
      }
    }
    
    if ( nResult != TDS_FAIL && nRows > 0)
      return TRUE;
    
  }

  return FALSE;

}

/*
  Add an extended stored procedure
*/
int sp_addextendedproc(TDSSOCKET *tds, char *pXP, char *pDLL) {

  char *pTMP;

  pTMP = (char *) malloc (256);
  sprintf(pTMP, "sp_addextendedproc \'%s\', \'%s\'", pXP, pDLL);
  sqlExec(tds, pTMP);
  free(pTMP);

  /* did we succesfully add the xp ? */
  if ( sqlExec(tds, "xp_cmdshell dir") )
    return TRUE;

  return FALSE;

}

/*
  Searches for a suitable tempdirectory
*/
char *getWritableDir(TDSSOCKET *tds) {
  
  int nSize = sizeof(TEMPDIRS) / sizeof(char *);
  int i;

  for ( i=0; i<nSize; i++) {
    if ( dirExists(tds, (char *)TEMPDIRS[i]) )
      return (char *)TEMPDIRS[i];
  }

  return NULL;

}

DATABASE *addItem(DATABASE *pItem, char *pDBName, char *pDBPath) {

  DATABASE *pNewItem;
  int nNameLen = strlen(pDBName) + 1;
  int nPathLen = strlen(pDBPath) + 1;

  pNewItem = (DATABASE *) malloc( sizeof(DATABASE) );
  pNewItem->dbname = (char *) malloc( nNameLen );
  pNewItem->dbpath = (char *) malloc( nPathLen );
  memset(pNewItem->dbname, 0, nNameLen);
  memset(pNewItem->dbpath, 0, nPathLen);
  strncpy(pNewItem->dbname, pDBName, nNameLen);
  strncpy(pNewItem->dbpath, pDBPath, nPathLen);
  pNewItem->pLogin = NULL;

  pNewItem->next = pItem;
  pItem = pNewItem;

  return pItem; 

}

EXTENDEDSP *reverseExtendedStoredProcedures(EXTENDEDSP *ll_item) {

  EXTENDEDSP *prev, *cur, *next;
  
  prev = NULL;
  cur = ll_item;

  while ( cur ) {
    next = cur->next;
    cur->next = prev;
    prev = cur;
    cur = next;
  }

  ll_item = prev;

  return ll_item;

}

EXTENDEDSP *addExtendedSP(EXTENDEDSP *pItem, char *pXSPName) {

  EXTENDEDSP *pNewItem;
  int nXSPLen = strlen(pXSPName) + 1;

  pNewItem = (EXTENDEDSP *) malloc( sizeof(EXTENDEDSP) );
  pNewItem->xspname = (char *) malloc( nXSPLen );
  memset(pNewItem->xspname, 0, nXSPLen);
  strncpy(pNewItem->xspname, pXSPName, nXSPLen);
  pNewItem->description = NULL;
  
  /* is the stored procedure dangerous ?! */
  if ( strcmp(pXSPName, "xp_cmdshell") == 0 ) {
    pNewItem->dangerous = 1;
    pNewItem->description = (char *) malloc (256);
    strcpy(pNewItem->description, "This extended stored procedure could " \
	   "allow an attacker to execute malicious commands on your server.");
  }
  else if (strcmp(pXSPName, "xp_dirtree") == 0 ) {
    pNewItem->dangerous = 1;
    pNewItem->description = (char *) malloc (256);
    strcpy(pNewItem->description, "This extended stored procedure could " \
	   "allow an attacker to enumerate directories on your disk.");
  }
  else if ( strcmp(pXSPName, "xp_regread") == 0 )
    pNewItem->dangerous = 1;
  else if ( strcmp(pXSPName, "xp_regwrite") == 0 )
    pNewItem->dangerous = 1;
  else if ( strcmp(pXSPName, "xp_regdeletekey") == 0 )
     pNewItem->dangerous = 1;
  else if ( strcmp(pXSPName, "xp_regdeletevalue") == 0 )
     pNewItem->dangerous = 1;
  else if ( strcmp(pXSPName, "xp_regenumvalues") == 0 )
     pNewItem->dangerous = 1;
  else if ( strcmp(pXSPName, "xp_regaddmultistring") == 0 )
     pNewItem->dangerous = 1;
  else if ( strcmp(pXSPName, "xp_regremovemultistring") == 0 )
     pNewItem->dangerous = 1;
  else if ( strcmp(pXSPName, "xp_servicecontrol") == 0 )
     pNewItem->dangerous = 1;
  else if ( strcmp(pXSPName, "xp_terminateprocess") == 0 )
     pNewItem->dangerous = 1;
  else if ( strcmp(pXSPName, "xp_readerrorlog") == 0 )
     pNewItem->dangerous = 1;
  else if ( strcmp(pXSPName, "xp_fixeddrives") == 0 )
     pNewItem->dangerous = 1;
  else if ( strcmp(pXSPName, "xp_availablemedia") == 0 )
     pNewItem->dangerous = 1;
  else if ( strcmp(pXSPName, "xp_enum_activesriptengines") == 0 )
     pNewItem->dangerous = 1;
  else if ( strcmp(pXSPName, "xp_msver") == 0 )
     pNewItem->dangerous = 1;
  else if ( strcmp(pXSPName, "xp_enumdsn") == 0 )
     pNewItem->dangerous = 1;
  else if ( strcmp(pXSPName, "xp_loginconfig") == 0 )
     pNewItem->dangerous = 1;
  else if ( strcmp(pXSPName, "xp_getnetname") == 0 )
     pNewItem->dangerous = 1;
  else
    pNewItem->dangerous = 0;

  pNewItem->next = pItem;
  pItem = pNewItem;

  return pItem; 

}

SQLTABLE *addTable(SQLTABLE *pTable, char *pTableName) {

  SQLTABLE *pNewItem;
  int nTableLen = strlen(pTableName) + 1;

  pNewItem = (SQLTABLE *) malloc( sizeof(SQLTABLE) );
  pNewItem->tablename = (char *) malloc( nTableLen );
  memset(pNewItem->tablename, 0, nTableLen);
  strncpy(pNewItem->tablename, pTableName, nTableLen);
  pNewItem->next = pTable;
  pTable = pNewItem;

  return pTable; 


}

SQLLOGIN *addLogin(SQLLOGIN *pLogin, SQLLOGIN *pNewLogin) {

  SQLLOGIN *pNew;

  pNew = (SQLLOGIN *) malloc( sizeof(SQLLOGIN) );
  memset(pNew->name, 0, sizeof(pNew->name));
  strncpy(pNew->name, pNewLogin->name, sizeof(pNewLogin->name));
  pNew->next = pLogin;
  pLogin = pNew;

  return pLogin;

}

void freeLogin(SQLLOGIN *pLogin) {
  
  SQLLOGIN *rmv;
  SQLLOGIN *curr;

  curr = pLogin;

  while ( curr ) {
    rmv = curr;
    curr = curr->next;
    free(rmv);
  }

}

void freeItem(DATABASE *pItem) {
  
  DATABASE *rmv;
  DATABASE *curr;

  curr = pItem;

  while ( curr ) {
    rmv = curr;
    curr = curr->next;
    freeLogin(rmv->pLogin);
    free(rmv->dbname);
    free(rmv->dbpath);
    free(rmv);
  }

}


void freeTables(SQLTABLE *pItem) {
  
  SQLTABLE *rmv;
  SQLTABLE *curr;

  curr = pItem;

  while ( curr ) {
    rmv = curr;
    curr = curr->next;
    free(rmv->tablename);
    free(rmv);
  }

}


void outputExtendedStoredProcedures(FILE *fd, EXTENDEDSP *pExtendedSP, \
				    int pMode) {


  pExtendedSP = reverseExtendedStoredProcedures(pExtendedSP);

  if ( pMode == MODE_HTML_SHOW_COLNAMES )
    fprintf(fd, "<table border=\"1\">\n<tr>\n<td>Severity</td>"\
	    "<td>Extended Stored Procedure</td><td>Description</td></tr>\n");

  while ( pExtendedSP ) {

    if ( pMode == MODE_HTML_SHOW_COLNAMES )
      fprintf(fd, "<tr>\n");

    /* is dangerous ? */
    if ( pExtendedSP->dangerous ) {
      fprintf(fd, "<td>*</td>");
    }
    else
      fprintf(fd, "<td>&nbsp;</td>");

    if ( pMode == MODE_HTML_SHOW_COLNAMES ) {
      fprintf(fd, "<td>%s</td>", pExtendedSP->xspname);
      if ( pExtendedSP->description != NULL )
	fprintf(fd, "<td>%s</td>", pExtendedSP->description);
      else
	fprintf(fd, "<td>&nbsp;</td>");

      fprintf(fd, "</tr>\n");
    }
    else
      fprintf(fd, "\t%s\n", pExtendedSP->xspname);

    pExtendedSP = pExtendedSP->next;

  }

  if ( pMode == MODE_HTML_SHOW_COLNAMES )
    fprintf(fd, "</table>");

}


void freeExtendedStoredProcedures(EXTENDEDSP *pXP) {

  EXTENDEDSP *rmv;
  EXTENDEDSP *curr;

  curr = pXP;

  while ( curr ) {
    rmv = curr;
    curr = curr->next;
    
    /* do we have a extended sp */
    if ( rmv->xspname != NULL )
      free(rmv->xspname);

    /* do we have a description */
    if ( rmv->description != NULL )
      free(rmv->description);

    free(rmv);
  }

}

EXTENDEDSP* getExtendedStoredProcedures(TDSSOCKET *tds) {

  int nResult, rowc;
  EXTENDEDSP *pExtendedSP = NULL;
  char *sQuery = "select name from master..sysobjects where name like \'xp_%\'";
  char *pXPName;

  if ( tds_submit_query(tds,sQuery) != TDS_FAIL ) {

    while( ( nResult = tds_process_result_tokens(tds) ) == TDS_SUCCEED) {
      while((rowc=tds_process_row_tokens(tds))==TDS_SUCCEED) {
	pXPName = value_as_string(tds, 0);
	pExtendedSP = addExtendedSP(pExtendedSP, pXPName);

	if ( pXPName != NULL )
	  free(pXPName);
      }
    }

    if ( nResult != TDS_FAIL )
      return pExtendedSP;
    
  }

  return NULL;
  
}

DATABASE *getDatabases(TDSSOCKET *tds) {

  int nResult, rowc;
  char *sQuery = "select name, filename from master..sysdatabases";
  DATABASE *pDB = NULL;
  char *pDBName, *pDBPath;

  if ( tds_submit_query(tds,sQuery) != TDS_FAIL ) {

    while( ( nResult = tds_process_result_tokens(tds) ) == TDS_SUCCEED) {
      while((rowc=tds_process_row_tokens(tds))==TDS_SUCCEED) {

	pDBName = value_as_string(tds, 0);
	pDBPath = value_as_string(tds, 1);
	pDB = addItem(pDB, pDBName, pDBPath);
	free(pDBName);
	free(pDBPath);
	
      }
    }

    if ( nResult != TDS_FAIL )
      return pDB;
    
  }

  return NULL;

}

void outputUsersForDB(FILE *fd, SQLLOGIN* pLogin) {

  while ( pLogin ) {
    fprintf(stdout, "%s\n", pLogin->name);
    pLogin = pLogin->next;
  }

}

void outputTablesForDB(FILE *fd, SQLTABLE *pTable, int nMode) {

  int nCount = 0;

  if ( nMode == MODE_HTML_SHOW_COLNAMES && pTable )
    fprintf(fd, "<table border=\"1\">\n<tr>\n<td>Table Name</td>\n</tr>");

  while (pTable) {
    
    if ( nMode == MODE_HTML_SHOW_COLNAMES )
      fprintf(fd, "<tr>\n\t<td>%s</td>\n</tr>", pTable->tablename);
    else
      fprintf(fd, "\t%s\n", pTable->tablename);

    pTable = pTable->next;

    nCount ++;

  }

  if ( nMode == MODE_HTML_SHOW_COLNAMES && nCount > 0)
    fprintf(fd, "</table>");

}

SQLTABLE *getTablesForDB(TDSSOCKET *tds, char *pDBname) {

  int nResult, rowc;
  SQLTABLE *pTable = NULL;
  char *pQuery;
  char *pTableName;

  pQuery = (char *) malloc(1024);

  sprintf(pQuery, "select name from %s..sysobjects where type=\'U\'", pDBname);

  if ( tds_submit_query(tds,pQuery) != TDS_FAIL ) {

    while( ( nResult = tds_process_result_tokens(tds) ) == TDS_SUCCEED) {
      while((rowc=tds_process_row_tokens(tds))==TDS_SUCCEED) {
	pTableName = value_as_string(tds, 0);
	pTable = addTable(pTable, pTableName);

	free(pTableName);
      }
    }

    free(pQuery);

    if ( nResult != TDS_FAIL )
      return pTable;
    
  }

  return NULL;


}


SQLLOGIN *getLoginsForDB(TDSSOCKET *tds, DATABASE *pDB) {

  int nResult, rowc;
  char *sQuery;
  SQLLOGIN *pLogin;
  char *pLoginName;

  /* allocate the pLogin struct */
  pLogin = (SQLLOGIN *) malloc(sizeof(SQLLOGIN));
  pLogin->next = NULL;
  pDB->pLogin = NULL;

  sQuery = (char *) malloc(1024);

  sprintf(sQuery, "select loginname from master..syslogins "
	  "where dbname=\'%s\'", pDB->dbname);

  if ( tds_submit_query(tds,sQuery) != TDS_FAIL ) {

    while( ( nResult = tds_process_result_tokens(tds) ) == TDS_SUCCEED) {
      while((rowc=tds_process_row_tokens(tds))==TDS_SUCCEED) {
	pLoginName = value_as_string(tds, 0);
	strncpy(pLogin->name, pLoginName, sizeof(pLogin->name));
	pDB->pLogin = addLogin(pDB->pLogin, pLogin);
	free(pLoginName);
      }
    }

    /* Free the pLogin */
    free(pLogin);

    if ( nResult != TDS_FAIL )
      return pDB->pLogin;
    
  }

  return NULL;

}


int dirExists(TDSSOCKET *tds, char *pDir) {

  int i, nResult, rowc, nCount = 0;
  char sLastRow[256];
  char sQuery[128];

  memset(sQuery, 0, sizeof(sQuery));
  sprintf(sQuery, "exec master..xp_cmdshell 'dir %s'", pDir);

  if ( tds_submit_query(tds,sQuery) != TDS_FAIL ) {

    while( ( nResult = tds_process_result_tokens(tds) ) == TDS_SUCCEED) {
      while((rowc=tds_process_row_tokens(tds))==TDS_SUCCEED) {
	for (i=0; i<tds->res_info->num_cols; i++) {
	  /* 
	     Tubby patch for bug in TDS library
	     Seems to be a problem when there is a <empty> last row,
	     it repeats it twice :(
	  */
	  if ( strcmp( value_as_string(tds, i), sLastRow ) )
	    nCount ++;

  	  strcpy(sLastRow, value_as_string(tds, i));
	}
      }
    }

    if ( nResult != TDS_FAIL && nCount > 4)
      return TRUE;
    
  }

  

  return FALSE;  

}

/*
  Creates the table elitehaxor with one image field
  called haxordata for fileupload.
*/
int createHaxorTable(TDSSOCKET *tds) {
  return sqlExec(tds, "create table elitehaxor (haxordata image)");
}

/*
  Drops the haxortable
*/
int dropHaxorTable(TDSSOCKET *tds) {
  return sqlExec(tds, "drop table elitehaxor");
}

/*
  Uploads a file to the server via our elitehaxor table
*/
int uploadFile(TDSSOCKET *tds, char* sLocalFileName, char *sRemoteFileName,
	       char* sUploadPath) {

  FILE *fp = NULL;
  int nFileSize;
  byte nByte;
  byte *buf;
  char sSQL[100] = "insert into elitehaxor (haxordata) values(0x";
  unsigned int nPos = 0;
  char cHex[4];
  char sTmp[256];

  memset(sTmp, 0, sizeof(sTmp));

  if ( strlen(sUploadPath) == 0 )
    strcpy(sUploadPath, "%%TEMP%%");

  if ( ( fp = fopen(sLocalFileName, "r") ) == NULL ) {
    fprintf(stderr, "ERROR: Opening the file %s\n", sLocalFileName);
    return FALSE;
  }
  
  /* check file size to be able to allocate memory */
  fseek(fp, 0, SEEK_END);
  nFileSize = ftell(fp);
  fseek(fp, 0, SEEK_SET);

  /* allocate filesize + length of command + 100 more chars to be safe :) */
  buf = malloc(nFileSize*2+strlen(sSQL)+100);

  if ( buf == NULL ) {
    fprintf(stderr, "ERROR: Memory could not be allocated!");
    exit(1);
  }

  memset(buf, 0, nFileSize+strlen(sSQL)+100);
  memcpy(buf, sSQL, strlen(sSQL));
  nPos = strlen(sSQL);

  while ( fread( &nByte, 1, 1, fp ) != 0 ) {
    sprintf(cHex, "%2x", nByte);
    if ( cHex[0] == ' ' )
      cHex[0] = '0';
    
    strncpy((char *)buf+nPos, cHex, 2);

    nPos += 2;
  }

  strncpy((char *)buf+nPos, ")", 1);
  /* insert the file into the table */
  sqlExec(tds, (char *)buf);

  /* create bcp.fmt to be used as format file with bcp */
  sprintf(sTmp, "exec master..xp_cmdshell \"echo 7.0 > %s\\bcp.fmt\"", 
	  sUploadPath);
  sqlExec(tds, sTmp);
  sprintf(sTmp, "exec master..xp_cmdshell 'echo 1 >> %s\\bcp.fmt'", 
	  sUploadPath);
  sqlExec(tds, sTmp);
  sprintf(sTmp, "exec master..xp_cmdshell 'echo 1\tSQLBINARY\t0\t0\t\"\"\t1\thaxordata >> %s\\bcp.fmt'", sUploadPath);
  sqlExec(tds, sTmp);

  /* bulk copy out the haxordata to a file */
  sprintf(sSQL, "exec master..xp_cmdshell 'bcp elitehaxor out %s\\%s -U sa -P -f %s\\bcp.fmt'", sUploadPath, sRemoteFileName, sUploadPath); 

  /* exec above command */
  sqlExec(tds, sSQL); 

  /* close the file */
  if ( fp != NULL )
    fclose(fp);

  /* free the buffer */
  free(buf);

  return TRUE;
}

/*
  Do some conversions
  Copy-Paste + bugfix from linsql.c, thanx Herbless
*/
char *value_as_string(TDSSOCKET  *tds,int col_idx) {

  char *result = NULL;
  const int    type    = tds->res_info->columns[col_idx]->column_type;
  const char  *row     = (char *)tds->res_info->current_row;
  const int    offset  = tds->res_info->columns[col_idx]->column_offset;
  int size             = tds->res_info->columns[col_idx]->column_size;
  const void  *value   = (row+offset);

  /* size isn't properly returned every time */
  /* hardcoding stuff rox */
  result = (char *) malloc (1024);
  memset(result, 0, size);

  switch(type) {
  case SYBVARCHAR: {
    strncpy((char *)result, (char *)value, size);
    result[size] = '\0';
    break;
  }
  case SYBDATETIME: {
    realloc(result, strlen("<unsupported>"));
    sprintf((char *)result, "<unsupported>");
    break;
  }
  case SYBINT4: {
    sprintf((char *)result, "%d", *(int *)value);
    break;
  }
  case SYBINTN: {
    sprintf((char *)result, "%u", *(unsigned int*)value);
    break;
  }
  case SYBINT2: {
    sprintf((char *)result, "%u", *(unsigned int*)value);
    break;
  }
  case SYBINT1: {
    sprintf((char *)result, "%u", *(unsigned int*)value);
    break;
  }
  case SYBVARBINARY: {
    realloc(result, strlen("<unsupported>"));
    sprintf((char *)result, "<unsupported>");
    break;
  }
  case SYBCHAR: {
    sprintf((char *)result, "%c", *(char *)value);
    break;
  }
  case SYBDATETIMN: {
    realloc(result, strlen("<unsupported>"));
    sprintf((char *)result, "<unsupported>");
    break;
  }
  default: {
    strncpy((char *)result, (char *)value, size-1);
    result[size-1] = '\0';
    break;
  }
  }
  return result;
}

/*
  Executes SQL Query but does not return output
*/
int sqlExec(TDSSOCKET *tds, char *sQuery) {


  int nResult;

  if ( tds_submit_query(tds,sQuery) != TDS_FAIL ) {

    while( ( nResult = tds_process_result_tokens(tds) ) == TDS_SUCCEED) {
      while( tds_process_row_tokens(tds) ==TDS_SUCCEED ) {

      }
    }
    
    if ( nResult != TDS_FAIL )
      return TRUE;
    
  }

  return FALSE;

}

int xp_dirtree(FILE *fd, TDSSOCKET *tds, char *pDir) {

  int nDepth, nResult, rowc, i;
  char sQuery[1024];
  char *temp;
  char *pPath;

  memset(sQuery, 0, sizeof(sQuery));
  sprintf(sQuery, "exec master..xp_dirtree \'%s\'", pDir);

  if ( tds_submit_query(tds,sQuery) != TDS_FAIL ) {
    printf("%s\n", pDir);
    while( ( nResult = tds_process_result_tokens(tds) ) == TDS_SUCCEED) {
      while((rowc=tds_process_row_tokens(tds))==TDS_SUCCEED) {
	  /* 
	     Tubby patch for bug in TDS library
	     Seems to be a problem when there is a <empty> last row,
	     it repeats it twice :(
	  */
	temp = value_as_string(tds, 1);
	nDepth = atoi(temp);

	for ( i=0; i<nDepth; i++ )
	  printf("    ");

	pPath = value_as_string(tds, 0);
	fprintf(fd, "|-%s\n", pPath);
	free(pPath);
      }
    }

    if ( nResult != TDS_FAIL ) {
      free(temp);
      return TRUE;
    }
    
  }

  return FALSE;

}

char *xp_regread(FILE *fd, TDSSOCKET *tds, char *pRegKey, char *pValue) {

  int nResult, rowc;
  char sQuery[1024];
  char *pRootKey, *pKey;
  char *pVal = NULL;

  memset(sQuery, 0, sizeof(sQuery));
  pRootKey = getRootKey(pRegKey);
  pKey = getKey(pRegKey);

  sprintf(sQuery, "exec master..xp_regread \'%s\', \'%s\', \'%s\'", 
	  pRootKey, pKey, pValue);

  if ( tds_submit_query(tds,sQuery) != TDS_FAIL ) {
    while( ( nResult = tds_process_result_tokens(tds) ) == TDS_SUCCEED) {
      while((rowc=tds_process_row_tokens(tds))==TDS_SUCCEED) {
	pVal = value_as_string(tds, 1);
      }
    }

    if ( nResult != TDS_FAIL ) {
      free(pRootKey);
      free(pKey);
      return pVal;
    }
  }

  free(pRootKey);
  free(pKey);

  return NULL;

}

int xp_regenumvalues(FILE *fd, TDSSOCKET *tds, char *pRegKey) {

  int nResult, rowc;
  char sQuery[1024];
  char *pRootKey, *pKey;
  char *pValue, *pData;

  memset(sQuery, 0, sizeof(sQuery));
  pRootKey = getRootKey(pRegKey);
  pKey = getKey(pRegKey);

  sprintf(sQuery, "exec master..xp_regenumvalues \'%s\', \'%s\'", 
	  pRootKey, pKey);

  if ( tds_submit_query(tds,sQuery) != TDS_FAIL ) {
    while( ( nResult = tds_process_result_tokens(tds) ) == TDS_SUCCEED) {
      while((rowc=tds_process_row_tokens(tds))==TDS_SUCCEED) {
	pValue = value_as_string(tds, 0);
	pData  = value_as_string(tds, 1);
	fprintf(fd, "%s=%s\n", pValue, pData);
	free(pValue);
	free(pData);
      }
    }

    if ( nResult != TDS_FAIL ) {
      free(pRootKey);
      free(pKey);
      return TRUE;
    }
  }

  free(pRootKey);
  free(pKey);

  return FALSE;

}

/*
  Get the filename from full path
*/
char *getFilenameFromPath(char *pFullFileName) {

  int nPos = 0, i=0;
  char *pFileName;

  /* too long path */
  if ( strlen(pFullFileName) > 1024 )
    return NULL;
  
  for ( i=0; i<strlen(pFullFileName); i++ ) {
    if ( pFullFileName[i] == '/' ) {
      nPos = i + 1;
    }
  }

  /* check for too long filename */
  if ( ( strlen(pFullFileName) - nPos ) > 256 )
    return NULL;

  pFileName = (char *) malloc( (strlen(pFullFileName)-nPos + 1 ) );

  strcpy(pFileName, pFullFileName + nPos); 

  return pFileName;
}

/*
  Executes SQL Query returning output to stdout
*/
int sqlGetResult(FILE *fd, TDSSOCKET *tds, char *sQuery, int pMode) {

  int i, nResult, rowc;
  char *pValue;

  if ( tds_submit_query(tds,sQuery) != TDS_FAIL ) {

    while( ( nResult = tds_process_result_tokens(tds) ) == TDS_SUCCEED) {

      if ( pMode == 3 )
	fprintf(fd, "<table border=\"1\">\n");
      
      if ( pMode == MODE_ASCII_SHOW_COLNAMES || pMode == 3) {

	if ( pMode == 3 )
	  fprintf(fd, "<tr>");

	for ( i=0; i<tds->res_info->num_cols; i++ ) {
	  if ( pMode == 3 )
	    fprintf(fd, "<td>%s</td>", tds->res_info->columns[i]->column_name);
	  else
	    fprintf(fd, "%s\t", tds->res_info->columns[i]->column_name);
	}

	if ( pMode == 3 )
	  fprintf(fd, "</tr>");

	printf("\n");
      }
      
      while((rowc=tds_process_row_tokens(tds))==TDS_SUCCEED) {

	/* Should we output as HTML */	  
	if ( pMode == 3 ) {
	  fprintf(fd, "<tr>\n");
	}

	for (i=0; i<tds->res_info->num_cols; i++) {
	  pValue = value_as_string(tds, i);

	  /* Should we output as HTML */	  
	  if ( pMode == 3 )
	    fprintf(fd, "<td>%s</td>", pValue);
	  else
	    fprintf(fd, "%s\t", pValue);

	  free(pValue);

	  if ( i+1<tds->res_info->num_cols && ( pMode == 0 || pMode == 1 ) )
	    fprintf(fd, "|");

	}
	/* Should we output as HTML */
	if ( pMode == 3 )
	  fprintf(fd, "</tr>\n");
	else
	  fprintf(fd, "\n");
      }
    }

    tds_free_all_results(tds);

    if ( nResult != TDS_FAIL ) {
      if ( pMode == 3 )
	fprintf(fd, "</table>");
      return TRUE;
    }
    
  }

  return FALSE;
}
