#!/usr/bin/env ruby

require 'time'

require 'active_samba_ldap'
require 'active_samba_ldap/command'

include ActiveSambaLdap::GetTextSupport

argv, opts, options = ActiveSambaLdap::Command.parse_options do |opts, options|
  options.ou = nil
  options.computer_account = false
  options.uid = nil
  options.gid = nil
  options.supplementary_groups = []
  options.create_group = true
  options.home_directory = nil
  options.home_directory_mode = nil
  options.shell = nil
  options.comment = nil
  options.setup_home_directory = true
  options.skeleton_directory = nil
  options.time = 0

  options.expire_date = nil
  options.can_change_password = nil
  options.must_change_password = nil
  options.samba_home_unc = nil
  options.samba_home_drive = nil
  options.samba_logon_script = nil
  options.samba_profile_path = nil
  options.samba_account_flags = nil
  options.common_name = nil
  options.given_name = nil
  options.surname = nil
  options.mail_addresses = nil
  options.mail_to_addresses = nil

  opts.banner += " USER_NAME"

  opts.on("-o", "--ou=OU",
          _("add the user in the organizational unit OU"),
          _("(relative to the user suffix)")) do |ou|
    if /^ou=/ =~ ou
      options.ou = ou
    else
      options.ou = "ou=#{ou}"
    end
  end
  opts.on("-c", "--[no-]computer-account",
          _("is a Windows Workstation"),
          _("(otherwise, Windows user)"),
          "(#{options.computer_account})") {|options.computer_account|}
  opts.on("-u", "--uid=UID", Integer, _("uid")) {|options.uid|}
  opts.on("-g", "--gid=GID", _("gid")) {|options.gid|}
  opts.on("-G", "--groups=GID1,GID2,GID3", Array,
          _("supplementary groups (comma separated)")) do |groups|
    options.supplementary_groups = groups
  end
  opts.on("--[no-]create-group",
          _("create a group for the user"),
          "(#{options.create_group})") {|options.create_group|}
  opts.on("-c", "--comment=COMMENT",
          _("set the GECOS field for the new user account")) {|options.comment|}
  opts.on("-s", "--shell=SHELL", _("shell")) {|options.shell|}
  opts.on("-G", "--given-name=NAME", _("given name")) {|options.given_name|}
  opts.on("-N", "--common-name=NAME",
          _("common name")) {|options.common_name|}
  opts.on("-S", "--surname=NAME", _("surname")) {|options.surname|}

  opts.on("-d", "--home-directory=HOME_DIR",
          _("home directory")) {|options.home_directory|}
  opts.on("--home-directory-mode=MODE",
          _("permission of home directory")) {|options.home_directory_mode|}
  opts.on("--[no-]setup-home-directory",
          _("setup home directory"),
          "(#{options.setup_home_directory})") {|options.setup_home_directory|}
  opts.on("-k", "--skel=DIR", "--skeleton-directory=DIR",
          _("skeleton directory")) {|options.skeleton_directory|}

  opts.on("--time=TIME", Integer,
          _("wait TIME seconds before exiting"),
          "(#{options.time})") {|options.time|}

  opts.separator("")
  opts.separator(_("For samba accounts:"))

  opts.on("-e", "--expire-date=DATE", _("expire date")) do |date|
    options.expire_date = Time.parse(date)
  end
  opts.on("-C", "--[no-]can-change-password",
          _("can change password")) do |bool|
    options.can_change_password = bool
  end
  opts.on("-M", "--[no-]must-change-password",
          _("must change password")) do |bool|
    options.must_change_password = bool
  end
  opts.on("--samba-home-path=UNC",
          _("sambaHomePath"),
          _("(SMB home share, like '\\\\PDC\\user'")) do |unc|
    options.samba_home_unc = unc
  end
  opts.on("--samba-home-drive=DRIVE",
          _("sambaHomeDrive"),
          _("(letter associated with home share, like 'H:')")) do |drive|
    options.samba_home_drive = drive
  end
  opts.on("--samba-logon-script=SCRIPT",
          _("sambaLogonScript"),
          _("(DOS script to execute on login)")) do |script|
    options.samba_logon_script = script
  end
  opts.on("--samba-profile-path=PATH",
          _("sambaProfilePath"),
          _("(profile directory, like '\\\\PDC\\profiles\\user')")) do |path|
    options.samba_profile_path = path
  end
  opts.on("--samba-account-flags=FLAGS",
          _("sambaAcctFlags"),
          _("(samba account control bits, " \
            "like '[NDHTUMWSLXI]')")) {|options.samba_account_flags|}

#   opts.on("--mail-addresses=ADDRESS1,ADDRESS2,ADDRESS3",
#           Array,
#           _("mailAddresses (comma separated)")) {|options.mail_addresses|}
#   opts.on("--mail-to-addresses=ADDRESS1,ADDRESS2,ADDRESS3",
#           Array,
#           _("mailToAddresses (forward address)"),
#           _("(comma separated)")) do |addresses|
#     options.mail_to_addresses = addresses
#   end
end

name = nil
if argv.size == 1
  name = argv.first
else
  $stderr.puts opts
  exit 1
end

unless Process.uid.zero?
  $stderr.puts(_("need root authority."))
  exit 1
end

ActiveSambaLdap::Base.establish_connection("update")


class User < ActiveSambaLdap::User
  ldap_mapping
end

class Computer < ActiveSambaLdap::Computer
  ldap_mapping
end

class Group < ActiveSambaLdap::Group
  ldap_mapping
end

class UnixIdPool < ActiveSambaLdap::UnixIdPool
  ldap_mapping
end

if options.computer_account
  member_class = Computer
  member_type = _("computer")
  name = name.chomp("$") + "$"
else
  member_class = User
  member_type = _("user")
end

unless member_class.valid_name?(name)
  $stderr.puts(_("illegal %s name: %s") % [name, member_type])
  exit 1
end

if member_class.exists?(name)
  $stderr.puts(_("%s already exists: %s") % [member_type, name])
  exit 1
end

create_options = {
  :uid => [name, options.ou].compact.join(","),
  :uid_number => options.uid,
  :gid_number => options.gid,
  :create_group => options.create_group,
  :group_class => Group,
  :home_directory => options.home_directory,
  :login_shell => options.shell,
  :given_name => options.given_name,
  :cn => options.common_name,
  :sn => options.surname,
  :gecos => options.comment,
  :samba_acct_flags => options.samba_account_flags,
}

if !create_options[:cn] and options.given_name and options.surname
  create_options[:cn] = "#{options.given_name} #{options.surname}"
end

if options.computer_account
  create_options[:description] = "Computer"
  create_options[:gecos] ||= "Computer"
else
  create_options.merge!(:can_change_password => options.can_change_password,
                        :must_change_password => options.must_change_password,
                        :user_logon_script => options.samba_logon_script,
                        :user_home_unc => options.samba_home_unc,
                        :user_home_drive => options.samba_home_drive,
                        :user_profile => options.samba_profile_path)
  if options.expire_date
    create_options[:samba_kickoff_time] = options.expire_date.to_i.to_s
  end
end

member = nil
begin
  member = member_class.create(create_options)
rescue ActiveSambaLdap::UidNumberAlreadyExists
  $stderr.puts(_("UID already exists: %s") % uid_number)
  exit 1
rescue ActiveSambaLdap::GidNumberDoesNotExist,
  ActiveSambaLdap::GroupDoesNotExist,
  ActiveSambaLdap::GroupDoesNotHaveSambaSID
  $stderr.puts $!
  exit 1
end

unless member.errors.empty?
  member.errors.each_full do |message|
    $stderr.puts(message)
  end
  exit 1
end

if options.setup_home_directory
  begin
    setup_options = {
      :mode => options.home_directory_mode,
      :skeleton_directory => options.skeleton_directory,
    }
    member.setup_home_directory(setup_options)
  rescue SystemCallError
    $stderr.puts $!
    exit 1
  end
end

[member.gid_number, *options.supplementary_groups].each do |group|
  group = Group.find_by_name_or_gid_number(group)
  if options.computer_account
    group.computers << member
  else
    group.users << member
  end
end

ActiveSambaLdap::Base.restart_nscd

ActiveSambaLdap::Base.clear_active_connections!
