/* getAccountName.cpp
 *
 * Copyright (c) 2008, Charles S. Wilson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/* must include before windows.h */
#include "getopt.h"

#include <windows.h>
#include <iostream>
#include "Win32Error.h"
#include "lookupAccountName.h"
#include "../version.h"

#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>

static const std::string VERSION = VERSION_STRING;
static const std::string PROGNAME= "getAccountName";

BOOL getGuestAccountName(LPTSTR *accountName);
BOOL getAdminAccountName(LPTSTR *accountName);

void usage  (std::ostream& os, char* progname, int exitCode);
void help   (std::ostream& os, char* progname, int exitCode);
void license(std::ostream& os, char* progname, int exitCode);
void version(std::ostream& os, char* progname, int exitCode);

int main(int argc, char** argv)
{
  LPTSTR accountName;
  int c;
  int opt_guest = 0;
  int opt_admin = 0;
  int opt_bynum = -1;
  BOOL rc;

  opterr = 0;
  while (1)
  {
    static struct option long_options[] =
    {
      {"guest", no_argument, 0, 'g'},
      {"admin", no_argument, 0, 'a'},
      {"number", required_argument, 0, 'n'},
      {"help",    no_argument, 0, 'h'},
      {"version", no_argument, 0, 'V'},
      {"license", no_argument, 0, 'L'},
      {0, 0, 0, 0}
    };
    int option_index = 0;
    c = getopt_long(argc, argv, ":gan:hVL", long_options, &option_index);

    if (c == -1)
      break;

    switch (c)
    {
      case 'h':
        help(std::cout, argv[0], 0);
        /* not reached */
        break;
      case 'V':
        version(std::cout, argv[0], 0);
        /* not reached */
        break;
      case 'L':
        license(std::cout, argv[0], 0);
        /* not reached */
        break;

      case 'g':
        opt_guest = 1;
        break;

      case 'a':
        opt_admin = 1;
        break;

      case 'n':
        {
          char* endp;
          long val = strtol(optarg, &endp, 10);
          if (optarg == endp)
          {
            std::cerr << "unable to parse argument (must be integer): \""
                      << optarg << "\"" << std::endl;
            usage(std::cerr, argv[0], 1);
            /* not reached */
          }
          if ((val < 0) || (val > (94)))
          {
            std::cerr << "argument " << val << " out of range [0,94]." << std::endl; 
            usage(std::cerr, argv[0], 1);
            /* not reached */
          }
          opt_bynum = static_cast<int>(val);
        }
        break;

      case ':':
        std::cerr << "option -" << std::string(1, optopt) << " missing required arguments" << std::endl;
        usage (std::cerr, argv[0], 1);
        /* not reached */
        break;

      case '?':
        {
          std::string optopt_str;
          if (std::isprint(optopt)) {
            optopt_str = "`-" + std::string(1, optopt) + "'";
          } else {
            std::ostringstream ostr;
            ostr << "character 0x" << std::hex << std::setfill('0')
                 << std::setw(2) << static_cast<int>(optopt);
            optopt_str = ostr.str();
          }
          std::cerr << "unknown option " << optopt_str << std::endl;
          usage (std::cerr, argv[0], 1);
        }
        /* not reached */
        break;

      default:
        std::cerr << "internal getopt error" << std::endl;
        exit(1);
    }
  }

  c = ((opt_bynum != -1) + (opt_admin == 1) + (opt_guest == 1));
  if (c != 1)
  {
    std::cerr << "Exactly one of `-g', `-a', or `-n NUM' must be specified." << std::endl;
    usage(std::cerr, argv[0], 1);
    /* not reached */
  }

  if (optind < argc)
  {
    std::cerr << "additional arguments ignored: ";
    while (optind < argc)
    {
      std::cerr << "\"" << argv[optind++];
      if (optind < argc)
        std::cerr << "\", ";
      else
        std::cerr << "\"" << std::endl;
    }
    usage(std::cerr, argv[0], 1);
    /* not reached */
  }
 
  rc = FALSE;
  if (opt_guest)
  {
    rc = getGuestAccountName(&accountName);
  }
  else if (opt_admin)
  {
    rc = getAdminAccountName(&accountName);
  }
  else
  {
    rc = lookupWellKnownAccountName(&accountName, 
        static_cast<WELL_KNOWN_SID_TYPE>(opt_bynum));
  }
  if (rc)
  {
    DWORD dwErr = ::GetLastError();
    CWin32Error e = dwErr;
    std::string emsg(e);
    std::cerr << "Unable to obtain account name: "
              << emsg << std::endl;
    return 1;
  }
  std::cout << accountName << std::endl;
  free(accountName);
  return 0;
}

BOOL getGuestAccountName(LPTSTR *accountName)
{
  /* first try local guest, but if that fails look for domain guest */
  /* return TRUE (error) if both fail                               */
  return (lookupWellKnownAccountName(accountName, WinAccountGuestSid) &&
          lookupWellKnownAccountName(accountName, WinAccountDomainGuestsSid));
}
BOOL getAdminAccountName(LPTSTR *accountName)
{
  return lookupWellKnownAccountName(accountName, WinAccountAdministratorSid);
}

void usage  (std::ostream& os, char* progname, int exitCode)
{
  std::string name(progname ? progname : PROGNAME);
  os << "Usage: " << name << " [-hVL] [-g|-a|-n INT]" << std::endl;
  exit(exitCode);
}

void help   (std::ostream& os, char* progname, int exitCode)
{
  static const std::string helpText = 
"returns the (possibly localized) name of the requested account\n"
"\n"
"   --help|-h           print this help\n"
"   --version|-V        print version information\n"
"   --license|-L        print license information\n"
"   --guest|-g          print name of Guest account\n"
"   --admin|-a          print name of Administrator account\n"
"   --number|-n NUM     print name of the account specified by the supplied\n"
"                       integer. This number should be in the range [0,78]\n"
"                       inclusive. See WELL_KNOWN_SID_TYPE documentation\n"
"                       matching these numbers to specific account types.\n"
"\n"
"Exactly one of the options `-g', `-a', or `-n INT' may be specified.\n"
"\n"
"NOTE: this application works only on Windows XP or above\n";

  std::string name(progname ? progname : PROGNAME);
  os << name << " [-hVL] [-g|-a|-n INT]" << std::endl;
  os << helpText << std::endl;
  exit(exitCode);
}

void license(std::ostream& os, char* progname, int exitCode)
{
  static const std::string licenseText =
"Copyright (c) 2008, Charles S. Wilson\n"
"\n"
"Permission is hereby granted, free of charge, to any person obtaining a copy\n"
"of this software and associated documentation files (the \"Software\"), to deal\n"
"in the Software without restriction, including without limitation the rights\n"
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n"
"copies of the Software, and to permit persons to whom the Software is\n"
"furnished to do so, subject to the following conditions:\n"
"\n"
"The above copyright notice and this permission notice shall be included in\n"
"all copies or substantial portions of the Software.\n"
"\n"
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n"
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n"
"THE SOFTWARE.\n"
"\n"
"Portions of this software are provided under independent licensing terms:\n"
"Win32Error:  (c) 2001 Ajit Jadhav, BSD-style license\n"
"getopt:      (c) 1989--2001 Free Software Foundation (GNU C Library), LGPLv2";

  std::string name(progname ? progname : PROGNAME);
  os << name << " version " << VERSION << std::endl;
  os << licenseText << std::endl;
  exit(exitCode);
}
void version(std::ostream& os, char* progname, int exitCode)
{
  std::string name(progname ? progname : PROGNAME);
  os << name << " version " << VERSION << std::endl;
  exit(exitCode);
}

