#!/usr/bin/perl
#
# add_user
#
# Add one or more users to the XML directory
#
#

use strict;
use warnings;
use Getopt::Long;
use Data::Dumper;

## Useful items
my $path_sep;
my $config_path;
my @dir_elem;
my $user_template = &get_user_template;
my $new_user_count = 0;

## Command line args
my $users;
my $domain;
my $dirpath;
my $help;

## Misc items somewhat related to cmd line args
my $start;
my $end; 
my $user;

## Check for Windows vs. *nix
if ( $^O =~ m/^win/i ) {
  ## Detected Windows (probably)
  $path_sep = "\\";  # single backslash (\)
  use File::Spec::Win32;
} else {
  $path_sep = '/';   # single slash (/)
  use File::Spec;
}

GetOptions(
		   'h'           => \$help,
		   'help'        => \$help,
		   'domain=s'    => \$domain,
		   'users=s'     => \$users,
		   'confpath=s'  => \$config_path,
);

if ( $help ) { 
  usage();
  exit(0);
}

if ( ! $domain ) {
  $domain='default';
}

## Validate users if specified on command line
if ( $users ) {
  ($start,$end) = split /-/,$users;
  if ( ! $start && ! $end ) {
	die "Please specify both a start and end range, separated by a hyphen:\n add_user --users=xxxx-yyyy\n";
  }

  unless ( $start =~ m/[1-9]\d+/ ) {
	die "Start of range '$start' is not numeric or is too short\n";
  }

  unless ( $end =~ m/[1-9]\d+/ ) {
	die "End of range '$end' is not numberic or is too short\n";
  }

  if ( $end <= $start ) {
	die "End of range needs to be greater than start of range\n";
  }
} else {
  ## Look for user id in $ARGV[0]
  if ( ! $ARGV[0] ) {
	die "You must specify user id as a command line argument to add user, or use the --users option.\n";
  }
  unless ( $ARGV[0] =~ m/^[1-9]\d+$/ ) {
	die "User id must be numeric, be at least 2 digits long, and cannot begin with the digit zero.\n"
  }
  $user = $ARGV[0];
}

if ( ! $config_path ) {
  $config_path = '/usr/local/freeswitch/conf';
} 

## Check to make sure the directories in question exists
unless ( -d $config_path ) {
  die "Configuration path '$config_path' does not exist.\n";
}

my $directory_path = $config_path . $path_sep . 'directory';
unless ( -d $directory_path ) {
  die "Directory path '$directory_path' does not exist.\n";
}

## Now check domain pathname and test existence
if ( ! $domain ) {
  $domain = 'default';
}

## Full directory path includes the domain name 
my $full_dir_path = $directory_path . $path_sep . $domain;
unless ( -d $full_dir_path ) {
  die "Full path to directory and domain '$full_dir_path' does not exist. \n";
}

unless ( -w $full_dir_path ) {
  die "This user does not have write access to '$full_dir_path'.\n";
}
print "\n";

## Regexp assemble items to show user what a PCRE might look like for his new users
my $ra_present;
my $ra_new;
my $ra_all;
eval { require Regexp::Assemble; };
if ( ! $@ ) {
  ## If Regexp::Assemble is available flag it for later building regexes
  $ra_present = 'true';
  $ra_new = Regexp::Assemble->new(    # new user regex
								  reduce => 1,
								  flags  => 0,
);  
  $ra_all = Regexp::Assemble->new(    # all users regex w/ new users thrown in
								  reduce => 1,
								  flags  => 0,
);
}

## If we're this far then we can read in the existing users and put them in a hash
##  Later we can check hash to avoid adding duplicate users
my %current_users;
my @CURRENT_USER_FILES = glob($full_dir_path . $path_sep . '*.xml');
foreach ( @CURRENT_USER_FILES ) {
  #print "User: $_\n";
  open(FILEIN,'<',$_);
  while(<FILEIN>) {
	next unless  m/user id|number-alias/;
	m/user id="(\d+)"/;
	my $user_id = $1;
	if ( ! $user_id ) {
	  m/alias="(\d+)"/;
	  $user_id = $1;
	}

	next unless $user_id;
	$current_users{$user_id}++;

	if ( $ra_present && $user_id =~ m/^\d+$/ ) {
	  #print "Adding $user_id to \$re_all...\n";
	  $ra_all->add($user_id)->anchor_line_begin->anchor_line_end;
	}
	last;
  }
  close(FILEIN);
}

#print Dumper(%current_users) . "\n";
if ( $start && $end ) {
  ## Add range of users
  foreach $user ($start .. $end) {
	&add_user($user);
  }
} else {
  ## Add single user
  &add_user($user);
}

print "\nOperation complete. ";
if ( $new_user_count == 0 ) {
  print "No users added.\n";
  exit(0);
} else {
  printf "%d user%s added.\n", $new_user_count, $new_user_count==1 ? "" : "s";
  print "Be sure to reloadxml.\n\n";
}

if ( $ra_present ) {
  print "Regular expression information:\n\n";
  ## Regexp::Assemble adds some stuff we really don't need
  ## These lines just make the regexp pattern a bit more readable
  my $tmp = $ra_new->as_string;
  $tmp =~ s/\?://g;
  $tmp =~ s/^\(\?\-xism:\^/^(/;
  $tmp =~ s/\$\)$/)\$/;
  $tmp =~ s/\\d/[0-9]/g;  # [0-9] is sometimes easier to read than \d
  print "            Sample regex for all new users: " . $tmp . "\n";
  $tmp = $ra_all->as_string;
  $tmp =~ s/\?://g;
  $tmp =~ s/^\(\?\-xism:\^/^(/;
  $tmp =~ s/\$\)$/)\$/;
  $tmp =~ s/\\d/[0-9]/g;  # [0-9] is sometimes easier to read than \d
  print "Sample regex for all new AND current users: " . $tmp . "\n\n";
  print "In the default configuration you can modify the expression in the condition for 'Local_Extension'.\n";
  print ""
} else {
  print "If CPAN module Regexp::Assemble were installed this program would be able to suggest a regex for your new users.\n"
}

exit(0);

sub add_user {
  my $user_id = shift;
  if ( exists( $current_users{$user_id} ) ) {
	warn "User id $user_id already exists, skipping...\n";
  } else {
	my $new_user = $user_template;
	$new_user =~ s/__USERID__/$user_id/g;
	#print "Adding user id '$user_id' with this XML:\n";
	#print $new_user . "\n";

	## Attempt to create the user file
	my $user_file_name = $full_dir_path . $path_sep . $user_id . '.xml';

	## Does it already exist?
	if ( -f $user_file_name ) {
	  warn "$user_file_name exists, skipping...\n";
	}
	my $fh;
	open($fh,'>',$user_file_name);
	if ( ! $fh ) {
	  warn "Unable to open '$user_file_name' - $!\n";
	  warn "Skipping...\n";
	  next;
	}

	print $fh $new_user;
	close($fh);
	print "Added $user_id in file $user_file_name \n";
	$new_user_count++;
	if ( $ra_present ) {
	  $ra_new->add($user_id)->anchor_line_begin->anchor_line_end;
	  $ra_all->add($user_id)->anchor_line_begin->anchor_line_end;
	}
  }

}

sub get_user_template {
  my $templ = <<ENDUSERTEMPLATE;
<include>
  <user id="__USERID__">
    <params>
      <param name="password" value="\$\${default_password}"/>
      <param name="vm-password" value="__USERID__"/>
    </params>
    <variables>
      <variable name="toll_allow" value="domestic,international,local"/>
      <variable name="accountcode" value="__USERID__"/>
      <variable name="user_context" value="default"/>
      <variable name="effective_caller_id_name" value="Extension __USERID__"/>
      <variable name="effective_caller_id_number" value="__USERID__"/>
      <variable name="outbound_caller_id_name" value="\$\${outbound_caller_name}"/>
      <variable name="outbound_caller_id_number" value="\$\${outbound_caller_id}"/>
      <variable name="callgroup" value="techsupport"/>
    </variables>
  </user>
</include>

ENDUSERTEMPLATE

  return $templ;
}

sub usage {
  print <<ENDUSAGE;

            FreeSWITCH add user utility

Adds one or more users to the FreeSWITCH directory using XML files.


 Syntax:

 add_user <user_id> [--domain=<domain_name>] [--confpath=<configuration_path>]
 add_user --users=<start_user_id>-<end_user_id> [--domain=<domain_name>] [--confpath=<configuration_path>]

In its simplest form, add_user will simply add the user_id specified at the command line.
By default, users are added to the "default" domain. Use the --domain option to specify
a different domain for the user(s) that are added.

To specify a range of user IDs use the --users option. Separate the beginning and
end of the range with a hyphen (-) character.

By default add_user will look for the XML directory in its default location of
/usr/local/freeswitch/conf/directory. Use the --confpath (configuration path)
option to specify an alternate directory location.


 NOTES: 

add_user assumes
User IDs must be numeric and cannot begin with zero.
User IDs must be at least two digits long and have no specific length limit.
If a user ID exists it will be skipped.
If a domain specified does not exist no users will be created.

ENDUSAGE

}