#!/usr/bin/env ruby

require 'time'

require 'active_samba_ldap'
require 'active_samba_ldap/command'

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.canonical_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", "--canonical-name=NAME",
          "canonical name") {|options.canonical_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

member_class = options.computer_account ? Computer : User

member_type = member_class.name.downcase

if options.computer_account
  name = name.chomp("$") + "$"
end

unless member_class.valid_name?(name)
  $stderr.puts "'#{name}' is illegal #{member_type} name"
  exit 1
end

if member_class.exists?(name)
  $stderr.puts "#{member_type} '#{name}' already exists."
  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.canonical_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 '#{uid_number}' already exists"
  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!
