+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item send_email
+
+This is the FS::svc_acct job-queue-able version. It still uses
+FS::Misc::send_email under-the-hood.
+
+=cut
+
+sub send_email {
+ my %opt = @_;
+
+ eval "use FS::Misc qw(send_email)";
+ die $@ if $@;
+
+ $opt{mimetype} ||= 'text/plain';
+ $opt{mimetype} .= '; charset="iso-8859-1"' unless $opt{mimetype} =~ /charset/;
+
+ my $error = send_email(
+ 'from' => $opt{from},
+ 'to' => $opt{to},
+ 'subject' => $opt{subject},
+ 'content-type' => $opt{mimetype},
+ 'body' => [ map "$_\n", split("\n", $opt{body}) ],
+ );
+ die $error if $error;
+}
+
+=item check_and_rebuild_fuzzyfiles
+
+=cut
+
+sub check_and_rebuild_fuzzyfiles {
+ my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc;
+ -e "$dir/svc_acct.username"
+ or &rebuild_fuzzyfiles;
+}
+
+=item rebuild_fuzzyfiles
+
+=cut
+
+sub rebuild_fuzzyfiles {
+
+ use Fcntl qw(:flock);
+
+ my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc;
+
+ #username
+
+ open(USERNAMELOCK,">>$dir/svc_acct.username")
+ or die "can't open $dir/svc_acct.username: $!";
+ flock(USERNAMELOCK,LOCK_EX)
+ or die "can't lock $dir/svc_acct.username: $!";
+
+ my @all_username = map $_->getfield('username'), qsearch('svc_acct', {});
+
+ open (USERNAMECACHE,">$dir/svc_acct.username.tmp")
+ or die "can't open $dir/svc_acct.username.tmp: $!";
+ print USERNAMECACHE join("\n", @all_username), "\n";
+ close USERNAMECACHE or die "can't close $dir/svc_acct.username.tmp: $!";
+
+ rename "$dir/svc_acct.username.tmp", "$dir/svc_acct.username";
+ close USERNAMELOCK;
+
+}
+
+=item all_username
+
+=cut
+
+sub all_username {
+ my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc;
+ open(USERNAMECACHE,"<$dir/svc_acct.username")
+ or die "can't open $dir/svc_acct.username: $!";
+ my @array = map { chomp; $_; } <USERNAMECACHE>;
+ close USERNAMECACHE;
+ \@array;
+}
+
+=item append_fuzzyfiles USERNAME
+
+=cut
+
+sub append_fuzzyfiles {
+ my $username = shift;
+
+ &check_and_rebuild_fuzzyfiles;
+
+ use Fcntl qw(:flock);
+
+ my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc;
+
+ open(USERNAME,">>$dir/svc_acct.username")
+ or die "can't open $dir/svc_acct.username: $!";
+ flock(USERNAME,LOCK_EX)
+ or die "can't lock $dir/svc_acct.username: $!";
+
+ print USERNAME "$username\n";
+
+ flock(USERNAME,LOCK_UN)
+ or die "can't unlock $dir/svc_acct.username: $!";
+ close USERNAME;
+
+ 1;
+}
+
+
+
+=item radius_usergroup_selector GROUPS_ARRAYREF [ SELECTNAME ]
+
+=cut
+
+sub radius_usergroup_selector {
+ my $sel_groups = shift;
+ my %sel_groups = map { $_=>1 } @$sel_groups;
+
+ my $selectname = shift || 'radius_usergroup';
+
+ my $dbh = dbh;
+ my $sth = $dbh->prepare(
+ 'SELECT DISTINCT(groupname) FROM radius_usergroup ORDER BY groupname'
+ ) or die $dbh->errstr;
+ $sth->execute() or die $sth->errstr;
+ my @all_groups = map { $_->[0] } @{$sth->fetchall_arrayref};
+
+ my $html = <<END;
+ <SCRIPT>
+ function ${selectname}_doadd(object) {
+ var myvalue = object.${selectname}_add.value;
+ var optionName = new Option(myvalue,myvalue,false,true);
+ var length = object.$selectname.length;
+ object.$selectname.options[length] = optionName;
+ object.${selectname}_add.value = "";
+ }
+ </SCRIPT>
+ <SELECT MULTIPLE NAME="$selectname">
+END
+
+ foreach my $group ( @all_groups ) {
+ $html .= '<OPTION';
+ if ( $sel_groups{$group} ) {
+ $html .= ' SELECTED';
+ $sel_groups{$group} = 0;
+ }
+ $html .= ">$group</OPTION>\n";