App-Netdisco

 view release on metacpan or  search on metacpan

lib/App/Netdisco/Util/SNMPText.pm  view on Meta::CPAN

  return [ map { decode_snmp_data($_, $device, $field_label) } @$data ]
    if $type eq 'ARRAY';
  return { map { $_ => decode_snmp_data($data->{$_}, $device, $field_label) } keys %$data }
    if $type eq 'HASH';
  return $data if $type;

  return decode_snmp_text($data, $device, $field_label);
}

=head2 decode_cli_extra( $extra )

Decodes the C<-e> text given to C<netdisco-do> for a C<cf_> action, which
arrives as raw octets, with no device charsets. Undef and empty values are
returned unchanged.

=cut

sub decode_cli_extra {
  my ($extra) = @_;
  return decode_text_with($extra, []);
}

=head2 looks_garbled( $string )

True when the string holds a character from U+00C2 to U+00F4 followed by one
from U+0080 to U+00BF, which is how UTF-8 octets look when read as Latin-1,
and its characters read as octets are valid UTF-8. Correct text such as an
accented letter before a non-breaking space or a closing guillemet fails the
second test.

=cut

sub looks_garbled {
  my ($string) = @_;
  return 0 unless defined $string and $string =~ m/[\x{C2}-\x{F4}][\x{80}-\x{BF}]/;
  my $bytes = try { Encode::encode('iso-8859-1', $string, $CHECK) };
  return 0 unless defined $bytes;
  return (defined try { Encode::decode('UTF-8', $bytes, $CHECK) }) ? 1 : 0;
}

=head2 ungarble( $string, \@charsets )

Returns the text that C<$string> would have been had its characters been read
as octets, using C<decode_text_with>. Returns undef when the string holds a
character which Latin-1 cannot encode, so it cannot be garbled in that way.

=cut

sub ungarble {
  my ($string, $charsets) = @_;
  my $bytes = try { Encode::encode('iso-8859-1', $string, $CHECK) };
  return undef unless defined $bytes;
  return scalar decode_text_with($bytes, $charsets || []);
}

=head2 scan_config_encoding( $config )

Walks hashes (keys and values) and arrays in C<$config> and returns a list of
C<< { path => 'discover_no[2]', value => ..., suggestion => ... } >> for every
string which C<looks_garbled>. Other references are skipped. Reads only the
data it is given. Under the settings which hold device credentials
(C<device_auth>, C<snmp_auth>, C<community>, C<community_rw>) the value and
suggestion are C<(hidden)>.

=cut

my $CREDENTIAL_SETTINGS = qr/^(?:device_auth|snmp_auth|community|community_rw)\b/;

sub scan_config_encoding {
  my ($config) = @_;
  my @findings;
  _walk_config($config, '', \@findings);
  foreach my $finding (grep { $_->{path} =~ $CREDENTIAL_SETTINGS } @findings) {
    $finding->{$_} = '(hidden)' for qw/value suggestion/;
  }
  return @findings;
}

sub _walk_config {
  my ($node, $path, $findings) = @_;
  my $type = ref $node;

  if (!$type) {
    return unless looks_garbled($node);
    push @$findings, { path => $path, value => $node,
                       suggestion => ungarble($node, []) };
  }
  elsif ($type eq 'ARRAY') {
    _walk_config($node->[$_], "$path\[$_]", $findings) for 0 .. $#$node;
  }
  elsif ($type eq 'HASH') {
    foreach my $key (sort keys %$node) {
      my $child = length $path ? "$path.$key" : $key;
      _walk_config($key, "$child (key)", $findings) if looks_garbled($key);
      _walk_config($node->{$key}, $child, $findings);
    }
  }
}

=head2 scan_stored_encoding( $schema, $limit )

Reads the database only. Returns findings for stored text that looks garbled
or holds U+FFFD, in C<device.name>, C<description>, C<vendor>, C<os> and
C<os_ver>, C<device_port.descr>, C<device_vlan.description> and
C<device_port_ssid.ssid>. Each finding is
C<< { ip, column, value, suggestion } >> where C<suggestion> is undef for
U+FFFD, whose original octets are not recoverable. At most C<$limit>
findings are returned per column, followed by C<< { column, more => $count } >>
for the rest.

=cut

my @STORED_COLUMNS = (
  [Device => 'device', 'name'], [Device => 'device', 'description'],
  [Device => 'device', 'vendor'], [Device => 'device', 'os'],
  [Device => 'device', 'os_ver'], [DevicePort => 'device_port', 'descr'],
  [DeviceVlan => 'device_vlan', 'description'],
  [DevicePortSsid => 'device_port_ssid', 'ssid'],
);

sub scan_stored_encoding {



( run in 1.096 second using v1.01-cache-2.11-cpan-3f962061493 )