1<?php 2 3/* vim: set expandtab tabstop=4 shiftwidth=4: */ 4 5/** 6 * Net_Dict 7 * 8 * PHP Versions 4 and 5 9 * 10 * LICENSE: This source file is subject to version 2.02 of the PHP license 11 * that is available through the world-wide-web at the following URI: 12 * http://www.php.net/license/2_02.txt. If you did not receive a copy of 13 * the PHP License and are unable to obtain it through the web, please 14 * send a note to license@php.net so we can mail you a copy immediately. 15 * 16 * @category Networking 17 * @package Net_Dict 18 * @author Chandrashekhar Bhosle <cnb@freedomink.org> 19 * @author Ian Eure <ieure@php.net> 20 * @copyright 2002 Chandrashekhar Bhosle 21 * @copyright 2005 - 2006 Ian Eure 22 * @license http://www.php.net/license/2_02.txt PHP License 2.02 23 * @version CVS: $Revision: 296454 $ 24 * @link http://pear.php.net/package/Net_Dict 25 */ 26 27require_once 'PEAR.php'; 28require_once 'Net/Socket.php'; 29 30 31define('NET_DICT_SERVER', 'dict.org'); 32define('NET_DICT_PORT', '2628'); 33 34/** 35 * The main Net_Dict class 36 * 37 * Net_Dict is a PHP interface for talking to dictd servers. 38 * 39 * @category Networking 40 * @package Net_Dict 41 * @author Chandrashekhar Bhosle <cnb@freedomink.org> 42 * @license http://www.php.net/license/2_02.txt PHP License v2.02 43 * @version Release: @package_version@ 44 * @link http://pear.php.net/package/Net_Dict 45 */ 46class Net_Dict 47{ 48 /** 49 * Default DICT server name 50 * 51 * @var string 52 */ 53 var $server = NET_DICT_SERVER; 54 55 /** 56 * Default DICT Port 57 * 58 * @var int 59 */ 60 var $port = NET_DICT_PORT; 61 62 /** 63 * Socket object 64 * 65 * @var object 66 */ 67 var $_socket; 68 69 /** 70 * Server Information 71 * 72 * @var string 73 */ 74 var $servinfo; 75 76 /** 77 * if caching is on or off 78 * 79 * @var boolean 80 */ 81 var $caching = false; 82 83 /** 84 * PEAR Cache 85 * 86 * @var object 87 */ 88 var $cache; 89 90 91 /** 92 * Gets definitions for the specified word in the specified database. 93 * 94 * @param string $word specified word 95 * @param string $database specified database 96 * 97 * @return mixed Array of definitions if sucessful, else PEAR_Error 98 */ 99 function define($word, $database = '*') 100 { 101 if ($this->caching) { 102 if ($defines = $this->cache->get($word, 'Net_Dict_Defs')) { 103 return $defines; 104 } 105 } 106 107 if (!is_object($this->_socket)) { 108 $res = $this->connect(); 109 if (PEAR::isError($res)) { 110 return $res; 111 } 112 } 113 114 $resp = $this->_sendCmd("DEFINE $database '$word'"); 115 116 if (PEAR::isError($resp)) { 117 return $resp; 118 } 119 120 list($num) = explode(' ', $resp['text'], 2); 121 122 for ($i = 0; $i < $num; $i++) { 123 $resp = $this->_socket->readLine(); 124 125 preg_match( 126 "/(\d{3})\s+?\"(.+)?\"\s+?(\S+)\s+?\"(.+)?\"/", 127 $resp, 128 $matches 129 ); 130 131 $defines[$i]['response'] = $resp; 132 $defines[$i]['word'] = $matches[2]; 133 $defines[$i]['database'] = $matches[3]; 134 $defines[$i]['description'] = $matches[4]; 135 136 $resp = $this->_getMultiline(); 137 138 $defines[$i]['definition'] = $resp['text']; 139 } 140 141 $this->readLine(); /* discard status */ 142 143 if ($this->caching) { 144 $this->cache->save($word, $defines, 0, 'Net_Dict_Defs'); 145 } 146 147 return $defines; 148 } 149 150 /** 151 * Searches an index for the dictionary, and reports words 152 * which were found using a particular strategy. 153 * 154 * @param string $word word to search for 155 * @param string $strategy strategy used, defaults to 'substring' 156 * @param string $database database to search, all if not specified. 157 * 158 * @return mixed Array of matches if successful, else PEAR_Error 159 */ 160 function match($word, $strategy = 'substring', $database = '*') 161 { 162 $resp = $this->_sendCmd("MATCH $database $strategy '$word'"); 163 164 if (PEAR::isError($resp)) { 165 return $resp; 166 } 167 168 $resp = $this->_getMultiLine(); 169 170 $this->readLine(); /* discard status */ 171 172 preg_match_all("/(\S+)?\s\"(.+?)\"/", $resp['text'], $matches); 173 174 for ($i = 0; $i < count($matches[0]); $i++) { 175 $matched[$i]['database'] = $matches[1][$i]; 176 $matched[$i]['word'] = $matches[2][$i]; 177 } 178 179 return $matched; 180 } 181 182 /** 183 * Gets list of available databases 184 * 185 * @return mixed Array of databases if successful, else PEAR_Error 186 */ 187 function showDatabases() 188 { 189 $resp = $this->_sendCmd('SHOW DB'); 190 191 if (PEAR::isError($resp)) { 192 return $resp; 193 } 194 195 $resp = $this->_getMultiLine(); 196 197 $this->readLine(); /* discard status */ 198 199 preg_match_all("/(\S+)?\s+?\"(.+?)\"/", $resp['text'], $matches); 200 201 for ($i = 0; $i < count($matches[0]); $i++) { 202 $databases[$i]['database'] = $matches[1][$i]; 203 $databases[$i]['description'] = $matches[2][$i]; 204 } 205 206 return $databases; 207 } 208 209 /** 210 * Gets a list of available strategies 211 * 212 * @return mixed Array of strategies if successful, else PEAR_Error 213 */ 214 function showStrategies() 215 { 216 $resp = $this->_sendCmd('SHOW STRAT'); 217 218 if (PEAR::isError($resp)) { 219 return $resp; 220 } 221 222 $resp = $this->_getMultiLine(); 223 224 $this->readLine(); /* discard status */ 225 226 preg_match_all("/(\S+)?\s+?\"(.+?)\"/", $resp['text'], $matches); 227 228 for ($i = 0; $i < count($matches[0]); $i++) { 229 $strategies[$i]['strategy'] = $matches[1][$i]; 230 $strategies[$i]['description'] = $matches[2][$i]; 231 } 232 233 return $strategies; 234 } 235 236 /** 237 * Gets source, copyright, and licensing information about the 238 * specified database. 239 * 240 * @param string $database database name 241 * 242 * @return mixed string if successful, else PEAR_Error 243 */ 244 function showInfo($database) 245 { 246 return $this->simpleQuery('SHOW INFO ' . $database); 247 } 248 249 /** 250 * Gets local server information written by the local administrator. 251 * This could include information about local databases or strategies, 252 * or administrative information such as who to contact for access to 253 * databases requiring authentication. 254 * 255 * @return mixed string if sucessful, else PEAR_Error 256 */ 257 function showServer() 258 { 259 return $this->simpleQuery('SHOW SERVER'); 260 } 261 262 /** 263 * Allows the client to provide information about itself 264 * for possible logging and statistical purposes. All clients SHOULD 265 * send this command after connecting to the server. All DICT servers 266 * MUST implement this command (note, though, that the server doesn't 267 * have to do anything with the information provided by the client). 268 * 269 * @param string $text defaults to 'cnb' 270 * 271 * @return mixed string if successful, else PEAR_Error 272 */ 273 function client($text = 'cnb') 274 { 275 $this->_sendCmd('CLIENT ' . $text); 276 } 277 278 /** 279 * Display some server-specific timing or debugging information. This 280 * information may be useful in debugging or tuning a DICT server. All 281 * DICT servers MUST implement this command (note, though, that the text 282 * part of the response is not specified and may be omitted). 283 * 284 * @return mixed string if successful, else PEAR_Error 285 */ 286 function status() 287 { 288 $resp = $this->_sendCmd('STATUS'); 289 return $resp['text']; 290 } 291 292 /** 293 * Provides a short summary of commands that are understood by this 294 * implementation of the DICT server. The help text will be presented 295 * as a textual response, terminated by a single period on a line by 296 * itself. All DICT servers MUST implement this command. 297 * 298 * @return mixed string on success, else PEAR_Error 299 */ 300 function help() 301 { 302 return $this->simpleQuery('HELP'); 303 } 304 305 /** 306 * This command is used by the client to cleanly exit the server. 307 * All DICT servers MUST implement this command. 308 * 309 * @return mixed string on success, else PEAR_Error 310 */ 311 function quit() 312 { 313 return $this->_sendCmd('QUIT'); 314 } 315 316 /** 317 * Requests that all text responses be prefaced by a MIME header 318 * [RFC2045] followed by a single blank line (CRLF). 319 * 320 * @return mixed 321 * @todo Implement this method 322 */ 323 function optionMIME() 324 { 325 } 326 327 /** 328 * The client can authenticate itself to the server using a username and 329 * password. The authentication-string will be computed as in the APOP 330 * protocol discussed in [RFC1939]. 331 * 332 * @param string $user username 333 * @param string $auth password 334 * 335 * @return mixed 336 * @todo Implement this method. 337 */ 338 function auth($user, $auth) 339 { 340 } 341 342 /** 343 * The Simple Authentication and Security Layer (SASL) is currently 344 * being developed [RFC2222]. The DICT protocol reserves the SASLAUTH 345 * and SASLRESP commands for this method of authentication. 346 * 347 * @param string $mechanism mechanism used 348 * @param string $initial_response initial response 349 * 350 * @return mixed 351 * 352 * @todo Implement this method. 353 */ 354 function SASLAuth($mechanism, $initial_response) 355 { 356 } 357 358 /** 359 * The client will send all responses using the SASLRESP command and a 360 * BASE64-encoded parameter. 361 * 362 * @param string $response the response 363 * 364 * @return mixed 365 * @todo Implement this method. 366 */ 367 function SASLResp($response) 368 { 369 } 370 371 /** 372 * Connects to a dict server and sets up a socket 373 * 374 * @param string $server dict server 375 * @param integer $port port to connect to 376 * 377 * @return mixed true on success, else PEAR_Error 378 */ 379 function connect($server = '', $port = 0) 380 { 381 $s = new Net_Socket; 382 383 if (empty($server)) { 384 $server = $this->server; 385 } 386 387 if (0 == $port) { 388 $port = $this->port; 389 } 390 391 $err = $s->connect($server, $port); 392 393 if (PEAR::isError($err)) { 394 395 return $err; 396 } 397 398 $banner = $s->readLine(); 399 $resp['code'] = substr($banner, 0, 3); 400 $resp['text'] = ltrim(substr($banner, 3)); 401 402 if (!Net_Dict::isOK($resp)) { 403 return new PEAR_Error($resp['text'], 404 $resp['code']); 405 } 406 407 $reg = array(); 408 preg_match("/\d{3} (.*) <(.*)> <(.*)>/", $banner, $reg); 409 $this->servinfo["signature"] = $reg[1]; 410 $this->servinfo["capabilities"] = explode(".", $reg[2]); 411 $this->servinfo["msg-id"] = $reg[3]; 412 413 $this->_socket =& $s; 414 415 return true; 416 } 417 418 /** 419 * Disconnect from the dict server 420 * 421 * @see Net_Socket::disconnect() 422 * @return mixed Net_Socket::disconnect()'s return value 423 * @author Ian Eure <ieure@php.net> 424 */ 425 function disconnect() 426 { 427 if (isset($this->_socket)) { 428 return $this->_socket->disconnect(); 429 } 430 return new PEAR_Error('not connected'); 431 } 432 433 /** 434 * Sets the server and port of dict server 435 * 436 * @param string $server server address 437 * @param int $port port number to use 438 * 439 * @return void 440 */ 441 function setServer($server, $port = 0) 442 { 443 $this->server = $server; 444 445 if (0 < $port) { 446 $this->port = $port; 447 } 448 } 449 450 /** 451 * Sets caching on or off and provides the cache type and parameters 452 * 453 * @param boolean $flag true if caching is required 454 * @param string $container name of container 455 * @param array $container_options options 456 * 457 * @return void 458 */ 459 function setCache($flag = false, $container = '', $container_options = '') 460 { 461 $this->caching = $flag; 462 463 if ($this->caching) { 464 include_once 'Cache.php'; 465 466 if (is_object($this->cache)) { 467 unset($this->cache); 468 } 469 470 $this->cache = new Cache($container, $container_options); 471 } 472 } 473 474 /** 475 * Sends a command, checks the reponse, and 476 * if good returns the reponse, other wise 477 * returns false. 478 * 479 * @param string $cmd Command to send (\r\n will be appended) 480 * 481 * @return mixed First line of response if successful, otherwise false 482 */ 483 function _sendCmd($cmd) 484 { 485 $result = $this->_socket->writeLine($cmd); 486 487 if (PEAR::isError($result) && $result) { 488 return $result; 489 } 490 491 $data = $this->_socket->readLine(); 492 493 if (PEAR::isError($data)) { 494 return $data; 495 } 496 497 $resp['code'] = substr($data, 0, 3); 498 $resp['text'] = ltrim(substr($data, 3)); 499 500 if (!Net_Dict::isOK($resp)) { 501 return new PEAR_Error($resp['text'], 502 $resp['code']); 503 } 504 505 return $resp; 506 } 507 508 /** 509 * Reads a multiline reponse and returns the data 510 * 511 * @return mixed string on success or PEAR_Error 512 */ 513 function _getMultiline() 514 { 515 $data = ''; 516 while (($tmp = $this->readLine()) != '.') { 517 if (substr($tmp, 0, 2) == '..') { 518 $tmp = substr($tmp, 1); 519 } 520 $data .= $tmp . "\r\n"; 521 } 522 523 $resp['text'] = substr($data, 0, -2); 524 525 return $resp; 526 } 527 528 /** 529 * Alias to Net_Socket::readLine(); 530 * 531 * @see Net_Socket::readLine(); 532 533 * @return All available data up to a newline, without that 534 * newline, or until the end of the socket, or a PEAR_Error if 535 * not connected. 536 */ 537 function readLine() 538 { 539 return $this->_socket->readLine(); 540 } 541 542 /** 543 * Runs a generic dict query 544 * 545 * @param string $query dict query 546 * 547 * @return mixed string on success, else PEAR_Error 548 */ 549 function simpleQuery($query) 550 { 551 $resp = $this->_sendCmd($query); 552 553 if (PEAR::isError($resp)) { 554 return $resp; 555 } 556 557 $resp = $this->_getMultiLine(); 558 559 $this->readLine(); /* discard status */ 560 561 return $resp['text']; 562 } 563 564 /** 565 * Checks if a response code is positive 566 * 567 * @param array $resp response code 568 * 569 * @return boolean 570 */ 571 function isOK($resp) 572 { 573 $positives = array(1, 2, 3); 574 575 return in_array(substr($resp['code'], 0, 1), $positives); 576 } 577} 578 579?> 580