home *** CD-ROM | disk | FTP | other *** search
/ isnet Internet / Isnet Internet CD.iso / prog / hiz / 09 / 09.exe / adynware.exe / perl / lib / site / Net / PH.pm < prev    next >
Encoding:
Perl POD Document  |  1999-12-28  |  18.5 KB  |  832 lines

  1.  
  2. package Net::PH;
  3.  
  4. require 5.001;
  5.  
  6. use strict;
  7. use vars qw(@ISA $VERSION);
  8. use Carp;
  9.  
  10. use Socket 1.3;
  11. use IO::Socket;
  12. use Net::Cmd;
  13. use Net::Config;
  14.  
  15. $VERSION = do { my @r=(q$Revision: 2.17 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r};
  16. @ISA     = qw(Exporter Net::Cmd IO::Socket::INET);
  17.  
  18. sub new
  19. {
  20.  my $pkg  = shift;
  21.  my $host = shift if @_ % 2;
  22.  my %arg  = @_; 
  23.  my $hosts = defined $host ? [ $host ] : $NetConfig{ph_hosts};
  24.  my $ph;
  25.  
  26.  my $h;
  27.  foreach $h (@{$hosts})
  28.   {
  29.    $ph = $pkg->SUPER::new(PeerAddr => ($host = $h), 
  30.               PeerPort => $arg{Port} || 'csnet-ns(105)',
  31.               Proto    => 'tcp',
  32.               Timeout  => defined $arg{Timeout}
  33.                     ? $arg{Timeout}
  34.                     : 120
  35.              ) and last;
  36.   }
  37.  
  38.  return undef
  39.     unless defined $ph;
  40.  
  41.  ${*$ph}{'net_ph_host'} = $host;
  42.  
  43.  $ph->autoflush(1);
  44.  
  45.  $ph->debug(exists $arg{Debug} ? $arg{Debug} : undef);
  46.  
  47.  $ph;
  48. }
  49.  
  50. sub status
  51. {
  52.  my $ph = shift;
  53.  
  54.  $ph->command('status')->response;
  55.  $ph->code;
  56. }
  57.  
  58. sub login
  59. {
  60.  my $ph = shift;
  61.  my($user,$pass,$encrypted) = @_;
  62.  my $resp;
  63.  
  64.  $resp = $ph->command("login",$user)->response;
  65.  
  66.  if(defined($pass) && $resp == CMD_MORE)
  67.   {
  68.    if($encrypted)
  69.     {
  70.      my $challenge_str = $ph->message;
  71.      chomp($challenge_str);
  72.      Net::PH::crypt::crypt_start($pass);
  73.      my $cryptstr = Net::PH::crypt::encryptit($challenge_str);
  74.  
  75.      $ph->command("answer", $cryptstr);
  76.     }
  77.    else
  78.     {
  79.      $ph->command("clear", $pass);
  80.     }
  81.    $resp = $ph->response;
  82.   }
  83.  
  84.  $resp == CMD_OK;
  85. }
  86.  
  87. sub logout
  88. {
  89.  my $ph = shift;
  90.  
  91.  $ph->command("logout")->response == CMD_OK;
  92. }
  93.  
  94. sub id
  95. {
  96.  my $ph = shift;
  97.  my $id = @_ ? shift : $<;
  98.  
  99.  $ph->command("id",$id)->response == CMD_OK;
  100. }
  101.  
  102. sub siteinfo
  103. {
  104.  my $ph = shift;
  105.  
  106.  $ph->command("siteinfo");
  107.  
  108.  my $ln;
  109.  my %resp;
  110.  my $cur_num = 0;
  111.  
  112.  while(defined($ln = $ph->getline))
  113.   {
  114.    $ph->debug_print(0,$ln)
  115.      if ($ph->debug & 2);
  116.    chomp($ln);
  117.    my($code,$num,$tag,$data);
  118.  
  119.    if($ln =~ /^-(\d+):(\d+):(?:\s*([^:]+):)?\s*(.*)/o)
  120.     {
  121.      ($code,$num,$tag,$data) = ($1, $2, $3 || "",$4);
  122.      $resp{$tag} = bless [$code, $num, $tag, $data], "Net::PH::Result";
  123.     }
  124.    else
  125.     {
  126.      $ph->set_status($ph->parse_response($ln));
  127.      return \%resp;
  128.     }
  129.   }
  130.  
  131.  return undef;
  132. }
  133.  
  134. sub query
  135. {
  136.  my $ph = shift;
  137.  my $search = shift;
  138.  
  139.  my($k,$v);
  140.  
  141.  my @args = ('query', _arg_hash($search));
  142.  
  143.  push(@args,'return',_arg_list( shift ))
  144.     if @_;
  145.  
  146.  unless($ph->command(@args)->response == CMD_INFO)
  147.   {
  148.    return $ph->code == 501
  149.     ? []
  150.     : undef;
  151.   }
  152.  
  153.  my $ln;
  154.  my @resp;
  155.  my $cur_num = 0;
  156.  
  157.  my($last_tag);
  158.  
  159.  while(defined($ln = $ph->getline))
  160.   {
  161.    $ph->debug_print(0,$ln)
  162.      if ($ph->debug & 2);
  163.    chomp($ln);
  164.    my($code,$idx,$num,$tag,$data);
  165.  
  166.    if($ln =~ /^-(\d+):(\d+):\s*([^:]*):\s*(.*)/o)
  167.     {
  168.      ($code,$idx,$tag,$data) = ($1,$2,$3,$4);
  169.      my $num = $idx - 1;
  170.  
  171.      $resp[$num] ||= {};
  172.  
  173.      $tag = $last_tag
  174.     unless(length($tag));
  175.  
  176.      $last_tag = $tag;
  177.  
  178.      if(exists($resp[$num]->{$tag}))
  179.       {
  180.        $resp[$num]->{$tag}->[3] .= "\n" . $data;
  181.       }
  182.      else
  183.       {
  184.        $resp[$num]->{$tag} = bless [$code, $idx, $tag, $data], "Net::PH::Result";
  185.       }
  186.     }
  187.    else
  188.     {
  189.      $ph->set_status($ph->parse_response($ln));
  190.      return \@resp;
  191.     }
  192.   }
  193.  
  194.  return undef;
  195. }
  196.  
  197. sub change
  198. {
  199.  my $ph = shift;
  200.  my $search = shift;
  201.  my $make = shift;
  202.  
  203.  $ph->command(
  204.     "change", _arg_hash($search),
  205.     "make",   _arg_hash($make)
  206.  )->response == CMD_OK;
  207. }
  208.  
  209. sub _arg_hash
  210. {
  211.  my $hash = shift;
  212.  
  213.  return $hash
  214.     unless(ref($hash));
  215.  
  216.  my($k,$v);
  217.  my @r;
  218.  
  219.  while(($k,$v) = each %$hash)
  220.   {
  221.    my $a = $v;
  222.    $a =~ s/\n/\\n/sog;
  223.    $a =~ s/\t/\\t/sog;
  224.    $a = '"' . $a . '"'
  225.     if $a =~ /\W/;
  226.    push(@r, "$k=$a");   
  227.   }
  228.  join(" ", @r);
  229. }
  230.  
  231. sub _arg_list
  232. {
  233.  my $arr = shift;
  234.  
  235.  return $arr
  236.     unless(ref($arr));
  237.  
  238.  my $v;
  239.  my @r;
  240.  
  241.  foreach $v (@$arr)
  242.   {
  243.    my $a = $v;
  244.    $a =~ s/\n/\\n/sog;
  245.    $a =~ s/\t/\\t/sog;
  246.    $a = '"' . $a . '"'
  247.     if $a =~ /\W/;
  248.    push(@r, $a);   
  249.   }
  250.  
  251.  join(" ",@r);
  252. }
  253.  
  254. sub add
  255. {
  256.  my $ph = shift;
  257.  my $arg = @_ > 1 ? { @_ } : shift;
  258.  
  259.  $ph->command('add', _arg_hash($arg))->response == CMD_OK;
  260. }
  261.  
  262. sub delete
  263. {
  264.  my $ph = shift;
  265.  my $arg = @_ > 1 ? { @_ } : shift;
  266.  
  267.  $ph->command('delete', _arg_hash($arg))->response == CMD_OK;
  268. }
  269.  
  270. sub force
  271. {
  272.  my $ph = shift; 
  273.  my $search = shift;
  274.  my $force = shift;
  275.  
  276.  $ph->command(
  277.     "change", _arg_hash($search),
  278.     "force",  _arg_hash($force)
  279.  )->response == CMD_OK;
  280. }
  281.  
  282.  
  283. sub fields
  284. {
  285.  my $ph = shift;
  286.  
  287.  $ph->command("fields", _arg_list(\@_));
  288.  
  289.  my $ln;
  290.  my %resp;
  291.  my $cur_num = 0;
  292.  
  293.  while(defined($ln = $ph->getline))
  294.   {
  295.    $ph->debug_print(0,$ln)
  296.      if ($ph->debug & 2);
  297.    chomp($ln);
  298.    my($code,$num,$tag,$data,$last_tag);
  299.  
  300.    if($ln =~ /^-(\d+):(\d+):\s*([^:]*):\s*(.*)/o)
  301.     {
  302.      ($code,$num,$tag,$data) = ($1,$2,$3,$4);
  303.  
  304.      $tag = $last_tag
  305.     unless(length($tag));
  306.  
  307.      $last_tag = $tag;
  308.  
  309.      if(exists $resp{$tag})
  310.       {
  311.        $resp{$tag}->[3] .= "\n" . $data;
  312.       }
  313.      else
  314.       {
  315.        $resp{$tag} = bless [$code, $num, $tag, $data], "Net::PH::Result";
  316.       }
  317.     }
  318.    else
  319.     {
  320.      $ph->set_status($ph->parse_response($ln));
  321.      return \%resp;
  322.     }
  323.   }
  324.  return undef;
  325. }
  326.  
  327. sub quit
  328. {
  329.  my $ph = shift;
  330.  
  331.  $ph->close
  332.     if $ph->command("quit")->response == CMD_OK;
  333. }
  334.  
  335.  
  336. sub parse_response
  337. {
  338.  return ()
  339.     unless $_[1] =~ s/^(-?)(\d\d\d):?//o;
  340.  ($2, $1 eq "-");
  341. }
  342.  
  343. sub debug_text { $_[2] =~ /^(clear)/i ? "$1 ....\n" : $_[2]; }
  344.  
  345. package Net::PH::Result;
  346.  
  347. sub code  { shift->[0] }
  348. sub value { shift->[1] }
  349. sub field { shift->[2] }
  350. sub text  { shift->[3] }
  351.  
  352. package Net::PH::crypt;
  353.  
  354.  
  355.  
  356. use Math::BigInt;
  357. use    integer;
  358.  
  359.  
  360. use vars qw($c1 $c2 $c3 $c4 @cr $n1 $n2
  361.         $ROTORSZ $MASK $ERR $REPORT $BUGZ
  362.         @t1 @t2 @t3 $i $k );
  363.  
  364. BEGIN {
  365.     $ROTORSZ = 256;
  366.     $MASK = 255;
  367.     $BUGZ = 0;        # to set debug output level
  368.     $ERR = "";
  369.     $REPORT = "$ERR\nPlease report this bug to bseib\@purdue.edu.\n";
  370. }
  371.  
  372.  
  373. sub crypt_start # (char *pass)
  374. {
  375.     my    $pw = $_[0] if $_[0];
  376.     my    ($ic, $i, $k, $temp, $random, $buf, $lbuf);
  377.     my    $seed;
  378.     my    ($signed_seed, $sign);
  379.     my    $b32 = new Math::BigInt '4294967296';
  380.     my    $b31 = new Math::BigInt '2147483648';
  381.  
  382.     $n1 = 0;
  383.     $n2 = 0;
  384.  
  385.     for ($i = 0; $i < $ROTORSZ; $i++) {
  386.         $t1[$i] = $t2[$i] = $t3[$i] = 0;
  387.     }
  388.  
  389.     $buf = crypt($pw, $pw);    # should return a 13 char str to $buf
  390.     $lbuf = length($buf);
  391.     return(-1) if ($lbuf <= 0); # caller didn't supply a passwd
  392.     $seed = new Math::BigInt '123';        # where did 123 come from, Steve? :-)
  393.     for ($i = 0; $i < $lbuf; $i++) {
  394.         $seed = ($seed * ord(substr($buf,$i,1)) + $i) % $b32;
  395.     }
  396.     for ($i = 0; $i < $ROTORSZ; $i++) {
  397.         $t1[$i] = $i;
  398.     }
  399.  
  400.     for ($i = 0; $i < $ROTORSZ; $i++) {
  401.         print STDERR "\n" if ($BUGZ > 1);
  402.         $seed = (5 * $seed + ord(substr($buf,($i % $lbuf),1))) % $b32;
  403.         printf(STDERR "seed: %08lx\n",$seed) if ($BUGZ > 1);
  404.         if ($seed >= $b31) {
  405.             $sign = -1;
  406.             $signed_seed = ($seed - $b32);
  407.         } else {
  408.             $sign = 1;
  409.             $signed_seed = ($seed);
  410.         }
  411.         printf(STDERR "sgsd: %08lx\n",$signed_seed) if ($BUGZ > 1);
  412.         $random = $sign * int($signed_seed % '65521');
  413.         printf(STDERR "ran1: %08lx\n",$random) if ($BUGZ > 1);
  414.         $k = $ROTORSZ - 1 - $i;
  415.         $ic = ($random & $MASK) % ($k + 1);
  416.         printf(STDERR " ic1: %08lx\n",$ic) if ($BUGZ > 1);
  417.         $random = ($random >> 8) & $MASK;
  418.         printf(STDERR "ran2: %08lx\n",$random) if ($BUGZ > 1);
  419.         $temp = $t1[$k];
  420.         $t1[$k] = $t1[$ic];
  421.         $t1[$ic] = $temp;
  422.         next if ($t3[$k] != 0);
  423.         unless ($k) {
  424.             $ERR = "[0] Can't % by zero. \$k=$k";
  425.             die $REPORT;
  426.         }
  427.         $ic = ($random & $MASK) % $k;
  428.         printf(STDERR " ic2: %08lx\n",$ic) if ($BUGZ > 1);
  429.         while ($t3[$ic] != 0) {
  430.             unless ($k) {
  431.                 $ERR = "[1] Can't % by zero. \$k=$k";
  432.                 die $REPORT;
  433.             }
  434.             $ic = ($ic + 1) % $k;
  435.             printf(STDERR " ic3: %08lx\n",$ic) if ($BUGZ > 1);
  436.         }
  437.         $t3[$k] = $ic;
  438.         $t3[$ic] = $k;
  439.     }
  440.     for ($i = 0; $i < $ROTORSZ; $i++) {
  441.         $t2[$t1[$i] & $MASK] = $i;
  442.     }
  443.  
  444.     &print_t(@t1) if $BUGZ;
  445.     &print_t(@t2) if $BUGZ;
  446.     &print_t(@t3) if $BUGZ;
  447.  
  448.     undef;
  449. }
  450.  
  451.  
  452. sub encryptit
  453. {
  454.     my    $plain_str = $_[0] if $_[0];
  455.     my    $crypt_str;
  456.     my    ($x, @cr);
  457.  
  458.     print STDERR $plain_str,"\n" if $BUGZ;
  459.  
  460.     for ($i=0;$i<length($plain_str);$i++) {
  461.         $x = ord(substr($plain_str,$i,1)) + $n1;
  462.         $x = $t1[$x & $MASK] + $n2;
  463.         $x = $t3[$x & $MASK] - $n2;
  464.         $x = $t2[$x & $MASK] - $n1;
  465.         $x = ($x & $MASK);
  466.         push (@cr, $x);
  467.         $n1 = ($n1 + 1) % $ROTORSZ;
  468.         $n2 = ($n2 + 1) % $ROTORSZ unless ($n1);
  469.     }
  470.  
  471.     $crypt_str =  &encode(@cr);
  472.     return (length($crypt_str),$crypt_str);
  473. }
  474.  
  475.  
  476. sub encode
  477. {
  478.     my    @cr = @_ if @_;        # the crypt char list;
  479.     my    $str;
  480.     my    ($c1, $c2, $c3, $c4);
  481.     my    @ts;                # stands for "threesome"
  482.  
  483.     $str = &ENC($#cr + 1);        # length byte
  484.  
  485.     @ts = splice(@cr,0,3);        # grab first three from list
  486.     while ($#ts == 2) {            # right size
  487.         $c1 = int(  $ts[0] / 4);
  488.         $c2 = int( ($ts[0] % 4) * 16 + (int($ts[1] / 16) % 16) );
  489.         $c3 = int( ($ts[1] % 16) * 4 + (int($ts[2] / 64) % 4 ) );
  490.         $c4 = int( ($ts[2] % 64) );
  491.         $str = $str . &ENC($c1) . &ENC($c2) . &ENC($c3) . &ENC($c4);
  492.         @ts = splice(@cr,0,3);    # grab next three from list
  493.     }
  494.  
  495.     $str;    # return encoded string
  496. }
  497.  
  498.  
  499. sub ENC {
  500.     my    $c = $_[0] if $_[0];
  501.     return sprintf("%c",(($c % 64) + ord('#')) );
  502. }
  503.  
  504. sub SetDebugMode {
  505.     $BUGZ = $_[0];
  506. }
  507.  
  508. sub print_t {
  509.     my $i = 0;
  510.     my @t = @_;
  511.     foreach (@t) {
  512.         printf(STDERR "%02x",$_);
  513.         unless (++$i % 32) {
  514.             print STDERR "\n";
  515.         } else {
  516.             print STDERR ":" unless ($i % 4);
  517.         }
  518.     }
  519.     print STDERR "\n";
  520. }
  521.  
  522. 1;
  523.  
  524. __END__
  525.  
  526. =head1 NAME
  527.  
  528. Net::PH - CCSO Nameserver Client class
  529.  
  530. =head1 SYNOPSIS
  531.  
  532.     use Net::PH;
  533.     
  534.     $ph = Net::PH->new("some.host.name",
  535.                        Port    => 105,
  536.                        Timeout => 120,
  537.                        Debug   => 0);
  538.  
  539.     if($ph) {
  540.         $q = $ph->query({ field1 => "value1" },
  541.                         [qw(name address pobox)]);
  542.     
  543.         if($q) {
  544.         }
  545.     }
  546.     
  547.     
  548.     if($ph) {
  549.         $q = $ph->query('field1=value1',
  550.                         'name address pobox');
  551.     
  552.         if($q) {
  553.         }
  554.     }
  555.  
  556. =head1 DESCRIPTION
  557.  
  558. C<Net::PH> is a class implementing a simple Nameserver/PH client in Perl
  559. as described in the CCSO Nameserver -- Server-Client Protocol. Like other
  560. modules in the Net:: family the C<Net::PH> object inherits methods from
  561. C<Net::Cmd>.
  562.  
  563. =head1 CONSTRUCTOR
  564.  
  565. =over 4
  566.  
  567. =item new ( [ HOST ] [, OPTIONS ])
  568.  
  569.     $ph = Net::PH->new("some.host.name",
  570.                        Port    => 105,
  571.                        Timeout => 120,
  572.                        Debug   => 0
  573.                       );
  574.  
  575. This is the constructor for a new Net::PH object. C<HOST> is the
  576. name of the remote host to which a PH connection is required.
  577.  
  578. If C<HOST> is not given, then the C<SNPP_Host> specified in C<Net::Config>
  579. will be used.
  580.  
  581. C<OPTIONS> is an optional list of named options which are passed in
  582. a hash like fashion, using key and value pairs. Possible options are:-
  583.  
  584. B<Port> - Port number to connect to on remote host.
  585.  
  586. B<Timeout> - Maximum time, in seconds, to wait for a response from the
  587. Nameserver, a value of zero will cause all IO operations to block.
  588. (default: 120)
  589.  
  590. B<Debug> - Enable the printing of debugging information to STDERR
  591.  
  592. =back
  593.  
  594. =head1 METHODS
  595.  
  596. Unless otherwise stated all methods return either a I<true> or I<false>
  597. value, with I<true> meaning that the operation was a success. When a method
  598. states that it returns a value, failure will be returned as I<undef> or an
  599. empty list.
  600.  
  601. =over 4
  602.  
  603. =item query( SEARCH [, RETURN ] )
  604.  
  605.     $q = $ph->query({ name => $myname },
  606.             [qw(name email schedule)]);
  607.     
  608.     foreach $handle (@{$q}) {
  609.     foreach $field (keys %{$handle}) {
  610.             $c = ${$handle}{$field}->code;
  611.             $v = ${$handle}{$field}->value;
  612.             $f = ${$handle}{$field}->field;
  613.             $t = ${$handle}{$field}->text;
  614.             print "field:[$field] [$c][$v][$f][$t]\n" ;
  615.     }
  616.     }
  617.  
  618.     
  619.  
  620. Search the database and return fields from all matching entries.
  621.  
  622. The C<SEARCH> argument is a reference to a HASH which contains field/value
  623. pairs which will be passed to the Nameserver as the search criteria.
  624.  
  625. C<RETURN> is optional, but if given it should be a reference to a list which
  626. contains field names to be returned.
  627.  
  628. The alternative syntax is to pass strings instead of references, for example
  629.  
  630.     $q = $ph->query('name=myname',
  631.             'name email schedule');
  632.  
  633. The C<SEARCH> argument is a string that is passed to the Nameserver as the 
  634. search criteria.
  635.  
  636. C<RETURN> is optional, but if given it should be a string which will
  637. contain field names to be returned.
  638.  
  639. Each match from the server will be returned as a HASH where the keys are the
  640. field names and the values are C<Net::PH:Result> objects (I<code>, I<value>, 
  641. I<field>, I<text>).
  642.  
  643. Returns a reference to an ARRAY which contains references to HASHs, one
  644. per match from the server.
  645.  
  646. =item change( SEARCH , MAKE )
  647.  
  648.     $r = $ph->change({ email => "*.domain.name" },
  649.                      { schedule => "busy");
  650.  
  651. Change field values for matching entries.
  652.  
  653. The C<SEARCH> argument is a reference to a HASH which contains field/value
  654. pairs which will be passed to the Nameserver as the search criteria.
  655.  
  656. The C<MAKE> argument is a reference to a HASH which contains field/value
  657. pairs which will be passed to the Nameserver that
  658. will set new values to designated fields.
  659.  
  660. The alternative syntax is to pass strings instead of references, for example
  661.  
  662.     $r = $ph->change('email="*.domain.name"',
  663.                      'schedule="busy"');
  664.  
  665. The C<SEARCH> argument is a string to be passed to the Nameserver as the 
  666. search criteria.
  667.  
  668. The C<MAKE> argument is a string to be passed to the Nameserver that
  669. will set new values to designated fields.
  670.  
  671. Upon success all entries that match the search criteria will have
  672. the field values, given in the Make argument, changed.
  673.  
  674. =item login( USER, PASS [, ENCRYPT ])
  675.  
  676.     $r = $ph->login('username','password',1);
  677.  
  678. Enter login mode using C<USER> and C<PASS>. If C<ENCRYPT> is given and
  679. is I<true> then the password will be used to encrypt a challenge text 
  680. string provided by the server, and the encrypted string will be sent back
  681. to the server. If C<ENCRYPT> is not given, or I<false> the the password 
  682. will be sent in clear text (I<this is not recommended>)
  683.  
  684. =item logout()
  685.  
  686.     $r = $ph->logout();
  687.  
  688. Exit login mode and return to anonymous mode.
  689.  
  690. =item fields( [ FIELD_LIST ] )
  691.  
  692.     $fields = $ph->fields();
  693.     foreach $field (keys %{$fields}) {
  694.         $c = ${$fields}{$field}->code;
  695.         $v = ${$fields}{$field}->value;
  696.         $f = ${$fields}{$field}->field;
  697.         $t = ${$fields}{$field}->text;
  698.         print "field:[$field] [$c][$v][$f][$t]\n";
  699.     }
  700.  
  701. Returns a reference to a HASH. The keys of the HASH are the field names
  702. and the values are C<Net::PH:Result> objects (I<code>, I<value>, I<field>,
  703. I<text>).
  704.  
  705. C<FIELD_LIST> is a string that lists the fields for which info will be
  706. returned.
  707.  
  708. =item add( FIELD_VALUES )
  709.  
  710.     $r = $ph->add( { name => $name, phone => $phone });
  711.  
  712. This method is used to add new entries to the Nameserver database. You
  713. must successfully call L<login> before this method can be used.
  714.  
  715. B<Note> that this method adds new entries to the database. To modify
  716. an existing entry use L<change>.
  717.  
  718. C<FIELD_VALUES> is a reference to a HASH which contains field/value
  719. pairs which will be passed to the Nameserver and will be used to 
  720. initialize the new entry.
  721.  
  722. The alternative syntax is to pass a string instead of a reference, for example
  723.  
  724.     $r = $ph->add('name=myname phone=myphone');
  725.  
  726. C<FIELD_VALUES> is a string that consists of field/value pairs which the
  727. new entry will contain.
  728.  
  729. =item delete( FIELD_VALUES )
  730.  
  731.     $r = $ph->delete('name=myname phone=myphone');
  732.  
  733. This method is used to delete existing entries from the Nameserver database.
  734. You must successfully call L<login> before this method can be used.
  735.  
  736. B<Note> that this method deletes entries to the database. To modify
  737. an existing entry use L<change>.
  738.  
  739. C<FIELD_VALUES> is a string that serves as the search criteria for the
  740. records to be deleted. Any entry in the database which matches this search 
  741. criteria will be deleted.
  742.  
  743. =item id( [ ID ] )
  744.  
  745.     $r = $ph->id('709');
  746.  
  747. Sends C<ID> to the Nameserver, which will enter this into its
  748. logs. If C<ID> is not given then the UID of the user running the
  749. process will be sent.
  750.  
  751. =item status()
  752.  
  753. Returns the current status of the Nameserver.
  754.  
  755. =item siteinfo()
  756.  
  757.     $siteinfo = $ph->siteinfo();
  758.     foreach $field (keys %{$siteinfo}) {
  759.         $c = ${$siteinfo}{$field}->code;
  760.         $v = ${$siteinfo}{$field}->value;
  761.         $f = ${$siteinfo}{$field}->field;
  762.         $t = ${$siteinfo}{$field}->text;
  763.         print "field:[$field] [$c][$v][$f][$t]\n";
  764.     }
  765.  
  766. Returns a reference to a HASH containing information about the server's 
  767. site. The keys of the HASH are the field names and values are
  768. C<Net::PH:Result> objects (I<code>, I<value>, I<field>, I<text>).
  769.  
  770. =item quit()
  771.  
  772.     $r = $ph->quit();
  773.  
  774. Quit the connection
  775.  
  776. =back
  777.  
  778. =head1 Q&A
  779.  
  780. How do I get the values of a Net::PH::Result object?
  781.  
  782.     foreach $handle (@{$q}) {
  783.         foreach $field (keys %{$handle}) {
  784.             $my_code  = ${$q}{$field}->code;
  785.             $my_value = ${$q}{$field}->value;
  786.             $my_field = ${$q}{$field}->field;
  787.             $my_text  = ${$q}{$field}->text;
  788.         }
  789.     }
  790.  
  791. How do I get a count of the returned matches to my query?
  792.  
  793.     $my_count = scalar(@{$query_result});
  794.  
  795. How do I get the status code and message of the last C<$ph> command?
  796.  
  797.     $status_code    = $ph->code;
  798.     $status_message = $ph->message;
  799.  
  800. =head1 SEE ALSO
  801.  
  802. L<Net::Cmd>
  803.  
  804. =head1 AUTHORS
  805.  
  806. Graham Barr <gbarr@ti.com>
  807. Alex Hristov <hristov@slb.com>
  808.  
  809. =head1 ACKNOWLEDGMENTS
  810.  
  811. Password encryption code ported to perl by Broc Seib <bseib@purdue.edu>,
  812. Purdue University Computing Center.
  813.  
  814. Otis Gospodnetic <otisg@panther.middlebury.edu> suggested
  815. passing parameters as string constants. Some queries cannot be 
  816. executed when passing parameters as string references.
  817.  
  818.         Example: query first_name last_name email="*.domain"
  819.  
  820. =head1 COPYRIGHT
  821.  
  822. The encryption code is based upon cryptit.c, Copyright (C) 1988 by
  823. Steven Dorner and the University of Illinois Board of Trustees,
  824. and by CSNET.
  825.  
  826. All other code is Copyright (c) 1996-1997 Graham Barr <gbarr@ti.com>
  827. and Alex Hristov <hristov@slb.com>. All rights reserved. This program is
  828. free software; you can redistribute it and/or modify it under the same
  829. terms as Perl itself.
  830.  
  831. =cut
  832.