+ my $self = shift;
+
+ $self->{group} = undef; # belt and braces
+ delete $channels{$self->{call}};
+}
+
+# is it an ak1a cluster ?
+sub is_ak1a
+{
+ my $self = shift;
+ return $self->{'sort'} eq 'A';
+}
+
+# is it a user?
+sub is_user
+{
+ my $self = shift;
+ return $self->{'sort'} eq 'U';
+}
+
+# is it a connect type
+sub is_connect
+{
+ my $self = shift;
+ return $self->{'sort'} eq 'C';
+}
+
+# handle out going messages, immediately without waiting for the select to drop
+# this could, in theory, block
+sub send_now
+{
+ my $self = shift;
+ my $conn = $self->{conn};
+ my $sort = shift;
+ my $call = $self->{call};
+
+ for (@_) {
+ chomp;
+ $conn->send_now("$sort$call|$_") if $conn;
+ dbg('chan', "-> $sort $call $_") if $conn;
+ }
+ $self->{t} = time;
+}
+
+#
+# the normal output routine
+#
+sub send # this is always later and always data
+{
+ my $self = shift;
+ my $conn = $self->{conn};
+ my $call = $self->{call};
+
+ for (@_) {
+ chomp;
+ $conn->send_later("D$call|$_") if $conn;
+ dbg('chan', "-> D $call $_") if $conn;
+ }
+ $self->{t} = time;
+}
+
+# send a file (always later)
+sub send_file
+{
+ my ($self, $fn) = @_;
+ my $call = $self->{call};
+ my $conn = $self->{conn};
+ my @buf;
+
+ open(F, $fn) or die "can't open $fn for sending file ($!)";
+ @buf = <F>;
+ close(F);
+ $self->send(@buf);
+}
+
+# this will implement language independence (in time)
+sub msg
+{
+ my $self = shift;
+ return DXM::msg($self->{lang}, @_);
+}
+
+# change the state of the channel - lots of scope for debugging here :-)
+sub state
+{
+ my $self = shift;
+ if (@_) {
+ $self->{oldstate} = $self->{state};
+ $self->{state} = shift;
+ $self->{func} = '' unless defined $self->{func};
+ dbg('state', "$self->{call} channel func $self->{func} state $self->{oldstate} -> $self->{state}\n");
+ }
+ return $self->{state};
+}
+
+# disconnect this channel
+sub disconnect
+{
+ my $self = shift;
+ my $user = $self->{user};
+ my $conn = $self->{conn};
+ my $call = $self->{call};
+
+ $self->finish();
+ $conn->send_now("Z$call|bye") if $conn; # this will cause 'client' to disconnect
+ $user->close() if defined $user;
+ $conn->disconnect() if $conn;
+ $self->del();
+}
+
+#
+# just close all the socket connections down without any fiddling about, cleaning, being
+# nice to other processes and otherwise telling them what is going on.
+#
+# This is for the benefit of forked processes to prepare for starting new programs, they
+# don't want or need all this baggage.
+#
+
+sub closeall
+{
+ my $ref;
+ foreach $ref (values %channels) {
+ $ref->{conn}->disconnect() if $ref->{conn};
+ }
+}
+
+# various access routines
+
+#
+# return a list of valid elements
+#
+
+sub fields
+{
+ return keys(%valid);
+}
+
+#
+# return a prompt for a field
+#
+
+sub field_prompt
+{
+ my ($self, $ele) = @_;
+ return $valid{$ele};
+}
+
+no strict;
+sub AUTOLOAD
+{
+ my $self = shift;
+ my $name = $AUTOLOAD;
+ return if $name =~ /::DESTROY$/;
+ $name =~ s/.*:://o;
+
+ confess "Non-existant field '$AUTOLOAD'" if !$valid{$name};
+ @_ ? $self->{$name} = shift : $self->{$name} ;