00001: <?php
00002: /**
00003: * Copyright (c) 2009-2010, Laurent Laville <pear@laurent-laville.org>
00004: * Bertrand Mansion <bmansion@mamasam.com>
00005: *
00006: * All rights reserved.
00007: *
00008: * Redistribution and use in source and binary forms, with or without
00009: * modification, are permitted provided that the following conditions
00010: * are met:
00011: *
00012: * * Redistributions of source code must retain the above copyright
00013: * notice, this list of conditions and the following disclaimer.
00014: * * Redistributions in binary form must reproduce the above copyright
00015: * notice, this list of conditions and the following disclaimer in the
00016: * documentation and/or other materials provided with the distribution.
00017: * * Neither the name of the authors nor the names of its contributors
00018: * may be used to endorse or promote products derived from this software
00019: * without specific prior written permission.
00020: *
00021: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00022: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00023: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00024: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
00025: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00026: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00027: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00028: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00029: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00030: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00031: * POSSIBILITY OF SUCH DAMAGE.
00032: *
00033: * PHP version 5
00034: *
00035: * @category Networking
00036: * @package Net_Growl
00037: * @author Laurent Laville <pear@laurent-laville.org>
00038: * @author Bertrand Mansion <bmansion@mamasam.com>
00039: * @license http://www.opensource.org/licenses/bsd-license.php BSD
00040: * @version CVS: $Id:$
00041: * @link http://growl.laurent-laville.org/
00042: * @since File available since Release 0.9.0
00043: */
00044:
00045: require_once 'Net/Growl/Exception.php';
00046:
00047: // Lazy loading allowed by a custom __autoload function
00048: spl_autoload_register(array('Net_Growl', 'autoload'));
00049:
00050: // Converts standard error into exception
00051: set_error_handler(array('Net_Growl', 'errorHandler'));
00052:
00053: /**
00054: * Sends notifications to {@link http://growl.info Growl}
00055: *
00056: * This package makes it possible to easily send a notification from
00057: * your PHP script to {@link http://growl.info Growl}.
00058: *
00059: * Growl is a global notification system for Mac OS X.
00060: * Any application can send a notification to Growl, which will display
00061: * an attractive message on your screen. Growl currently works with a
00062: * growing number of applications.
00063: *
00064: * The class provides the following capabilities:
00065: * - Register your PHP application in Growl.
00066: * - Let Growl know what kind of notifications to expect.
00067: * - Notify Growl.
00068: * - Set a maximum number of notifications to be displayed (beware the loops !).
00069: *
00070: * @category Networking
00071: * @package Net_Growl
00072: * @author Laurent Laville <pear@laurent-laville.org>
00073: * @author Bertrand Mansion <bmansion@mamasam.com>
00074: * @license http://www.opensource.org/licenses/bsd-license.php BSD
00075: * @version Release: @package_version@
00076: * @link http://growl.laurent-laville.org/
00077: * @link http://growl.info Growl Homepage
00078: * @since Class available since Release 0.9.0
00079: */
00080: class Net_Growl
00081: {
00082: /**
00083: * Growl default UDP port
00084: */
00085: const UDP_PORT = 9887;
00086:
00087: /**
00088: * Growl default GNTP port
00089: */
00090: const GNTP_PORT = 23053;
00091:
00092: /**
00093: * Growl priorities
00094: */
00095: const PRIORITY_LOW = -2;
00096: const PRIORITY_MODERATE = -1;
00097: const PRIORITY_NORMAL = 0;
00098: const PRIORITY_HIGH = 1;
00099: const PRIORITY_EMERGENCY = 2;
00100:
00101: /**
00102: * PHP application object
00103: *
00104: * This is usually a Net_Growl_Application object but can really be
00105: * any other object as long as Net_Growl_Application methods are
00106: * implemented.
00107: *
00108: * @var object
00109: */
00110: private $_application;
00111:
00112: /**
00113: * Application is registered
00114: * @var bool
00115: */
00116: protected $isRegistered = false;
00117:
00118: /**
00119: * Net_Growl connection options
00120: * @var array
00121: */
00122: protected $options = array(
00123: 'host' => '127.0.0.1',
00124: 'port' => self::UDP_PORT,
00125: 'protocol' => 'udp',
00126: 'timeout' => 30,
00127: 'context' => array(),
00128: 'passwordHashAlgorithm' => 'MD5',
00129: 'encryptionAlgorithm' => 'NONE',
00130: 'debug' => false
00131: );
00132:
00133: /**
00134: * Current number of notification being displayed on user desktop
00135: * @var int
00136: */
00137: protected $growlNotificationCount = 0;
00138:
00139: /**
00140: * Maximum number of notification to be displayed on user desktop
00141: * @var int
00142: */
00143: private $_growlNotificationLimit = 0;
00144:
00145: /**
00146: * Handle to the log file.
00147: * @var resource
00148: * @since 2.0.0b2
00149: */
00150: private $_fp = false;
00151:
00152: /**
00153: * Notification callback results
00154: *
00155: * @var array
00156: * @since 2.0.0b2
00157: */
00158: protected $growlNotificationCallback = array();
00159:
00160: /**
00161: * Notification unique instance
00162: * @var object
00163: * @since 2.1.0
00164: * @see singleton, reset
00165: */
00166: protected static $instance = null;
00167:
00168: /**
00169: * Singleton
00170: *
00171: * Makes sure there is only one Growl connection open.
00172: *
00173: * @param mixed &$application Can be either a Net_Growl_Application object
00174: * or the application name string
00175: * @param array $notifications List of notification types
00176: * @param string $password (optional) Password for Growl
00177: * @param array $options (optional) List of options : 'host', 'port',
00178: * 'protocol', 'timeout' for Growl socket server.
00179: * 'passwordHashAlgorithm', 'encryptionAlgorithm'
00180: * to secure communications.
00181: * 'debug' to know what data are sent and received.
00182: *
00183: * @return object Net_Growl
00184: * @throws Net_Growl_Exception if class handler does not exists
00185: */
00186: public static final function singleton(&$application, $notifications,
00187: $password = '', $options = array()
00188: ) {
00189: if (self::$instance === null) {
00190: if (isset($options['protocol'])) {
00191: if ($options['protocol'] == 'tcp') {
00192: $protocol = 'gntp';
00193: } else {
00194: $protocol = $options['protocol'];
00195: }
00196: } else {
00197: $protocol = 'udp';
00198: }
00199: $class = 'Net_Growl_' . ucfirst($protocol);
00200:
00201: if (class_exists($class, true)) {
00202: self::$instance = new $class(
00203: $application, $notifications, $password, $options
00204: );
00205: } else {
00206: $message = 'Cannot find class "'.$class.'"';
00207: throw new Net_Growl_Exception($message);
00208: }
00209: }
00210: return self::$instance;
00211: }
00212:
00213: /**
00214: * Resettable Singleton Solution
00215: *
00216: * @return void
00217: * @link http://sebastian-bergmann.de/archives/882-guid.html
00218: * Testing Code That Uses Singletons
00219: * @since 2.1.0
00220: */
00221: public static final function reset()
00222: {
00223: self::$instance = null;
00224: }
00225:
00226: /**
00227: * Constructor
00228: *
00229: * This method instantiate a new Net_Growl object and opens a socket connection
00230: * to the specified Growl socket server.
00231: * Currently, only UDP is supported by Growl.
00232: * The constructor registers a shutdown function {@link Net_Growl::_Net_Growl()}
00233: * that closes the socket if it is open.
00234: *
00235: * Example 1.
00236: * <code>
00237: * require_once 'Net/Growl.php';
00238: *
00239: * $notifications = array('Errors', 'Messages');
00240: * $growl = Net_Growl::singleton('My application', $notification);
00241: * $growl->notify( 'Messages',
00242: * 'My notification title',
00243: * 'My notification description');
00244: * </code>
00245: *
00246: * @param mixed &$application Can be either a Net_Growl_Application object
00247: * or the application name string
00248: * @param array $notifications (optional) List of notification types
00249: * @param string $password (optional) Password for Growl
00250: * @param array $options (optional) List of options : 'host', 'port',
00251: * 'protocol', 'timeout' for Growl socket server.
00252: * 'passwordHashAlgorithm', 'encryptionAlgorithm'
00253: * to secure communications.
00254: * 'debug' to know what data are sent and received.
00255: *
00256: * @return void
00257: */
00258: protected function __construct(&$application, $notifications = array(),
00259: $password = '', $options = array()
00260: ) {
00261: foreach ($options as $k => $v) {
00262: if (isset($this->options[$k])) {
00263: $this->options[$k] = $v;
00264: }
00265: }
00266: $timeout = $this->options['timeout'];
00267: if (!is_int($timeout)) {
00268: // get default timeout (in seconds) for socket based streams.
00269: $timeout = ini_get('default_socket_timeout');
00270: }
00271: if (!is_int($timeout)) {
00272: // if default timeout not available on php.ini, then use this one
00273: $timeout = 30;
00274: }
00275: $this->options['timeout'] = $timeout;
00276:
00277: if (is_string($application)) {
00278: if (isset($options['AppIcon'])) {
00279: $icon = $options['AppIcon'];
00280: } else {
00281: $icon = '';
00282: }
00283: $this->_application = new Net_Growl_Application(
00284: $application, $notifications, $password, $icon
00285: );
00286: } elseif (is_object($application)) {
00287: $this->_application = $application;
00288: }
00289:
00290: if (is_string($this->options['debug'])) {
00291: $this->_fp = fopen($this->options['debug'], 'a');
00292: }
00293: }
00294:
00295: /**
00296: * Destructor
00297: *
00298: * @since 2.0.0b2
00299: */
00300: public function __destruct()
00301: {
00302: if (is_resource($this->_fp)) {
00303: fclose($this->_fp);
00304: }
00305: }
00306:
00307: /**
00308: * Limit the number of notifications
00309: *
00310: * This method limits the number of notifications to be displayed on
00311: * the Growl user desktop. By default, there is no limit. It is used
00312: * mostly to prevent problem with notifications within loops.
00313: *
00314: * @param int $max Maximum number of notifications
00315: *
00316: * @return void
00317: */
00318: public function setNotificationLimit($max)
00319: {
00320: $this->_growlNotificationLimit = $max;
00321: }
00322:
00323: /**
00324: * Returns the registered application object
00325: *
00326: * @return object Application
00327: * @see Net_Growl_Application
00328: */
00329: public function getApplication()
00330: {
00331: return $this->_application;
00332: }
00333:
00334: /**
00335: * Sends a application register to Growl
00336: *
00337: * @return Net_Growl_Response
00338: * @throws Net_Growl_Exception if REGISTER failed
00339: */
00340: public function register()
00341: {
00342: return $this->sendRegister();
00343: }
00344:
00345: /**
00346: * Sends a notification to Growl
00347: *
00348: * Growl notifications have a name, a title, a description and
00349: * a few options, depending on the kind of display plugin you use.
00350: * The bubble plugin is recommended, until there is a plugin more
00351: * appropriate for these kind of notifications.
00352: *
00353: * The current options supported by most Growl plugins are:
00354: * <pre>
00355: * array('priority' => 0, 'sticky' => false)
00356: * </pre>
00357: * - sticky: whether the bubble stays on screen until the user clicks on it.
00358: * - priority: a number from -2 (low) to 2 (high), default is 0 (normal).
00359: *
00360: * @param string $name Notification name
00361: * @param string $title Notification title
00362: * @param string $description (optional) Notification description
00363: * @param string $options (optional) few Notification options
00364: *
00365: * @return Net_Growl_Response | FALSE
00366: * @throws Net_Growl_Exception if NOTIFY failed
00367: */
00368: public function notify($name, $title, $description = '', $options = array())
00369: {
00370: if ($this->_growlNotificationLimit > 0
00371: && $this->growlNotificationCount >= $this->_growlNotificationLimit
00372: ) {
00373: // limit reached: no more notification displayed on user desktop
00374: return false;
00375: }
00376:
00377: if (!$this->isRegistered) {
00378: $this->sendRegister();
00379: }
00380: return $this->sendNotify($name, $title, $description, $options);
00381: }
00382:
00383: /**
00384: * Send request to remote server
00385: *
00386: * @param string $method Either REGISTER, NOTIFY
00387: * @param mixed $data Data block to send
00388: * @param bool $callback (optional) Socket callback request
00389: *
00390: * @return Net_Growl_Response | TRUE
00391: * @throws Net_Growl_Exception if remote server communication failure
00392: */
00393: protected function sendRequest($method, $data, $callback = false)
00394: {
00395: // @codeCoverageIgnoreStart
00396: $addr = $this->options['protocol'] . '://' . $this->options['host'];
00397:
00398: $this->debug(
00399: $addr . ':' .
00400: $this->options['port'] . ' ' .
00401: $this->options['timeout']
00402: );
00403:
00404: // open connection
00405: if (is_array($this->options['context'])
00406: && function_exists('stream_context_create')
00407: ) {
00408: $context = stream_context_create($this->options['context']);
00409:
00410: if (function_exists('stream_socket_client')) {
00411: $flags = STREAM_CLIENT_CONNECT;
00412: $addr = $addr . ':' . $this->options['port'];
00413: $sh = @stream_socket_client(
00414: $addr, $errno, $errstr,
00415: $this->options['timeout'], $flags, $context
00416: );
00417: } else {
00418: $sh = @fsockopen(
00419: $addr, $this->options['port'],
00420: $errno, $errstr, $$this->options['timeout'], $context
00421: );
00422: }
00423: } else {
00424: $sh = @fsockopen(
00425: $addr, $this->options['port'],
00426: $errno, $errstr, $$this->options['timeout']
00427: );
00428: }
00429:
00430: if ($sh === false) {
00431: $this->debug($errstr, 'error');
00432: $error = 'Could not connect to Growl Server.';
00433: throw new Net_Growl_Exception($error);
00434: }
00435: stream_set_timeout($sh, $this->options['timeout'], 0);
00436:
00437: $this->debug($data);
00438: $res = fwrite($sh, $data, mb_strlen($data));
00439:
00440: if ($res === false) {
00441: $error = 'Could not send data to Growl Server.';
00442: throw new Net_Growl_Exception($error);
00443: }
00444:
00445: switch ($this->options['protocol']) {
00446: case 'tcp':
00447: // read GNTP response
00448: $line = $this->_readLine($sh);
00449: $this->debug($line);
00450: $response = new Net_Growl_Response($line);
00451: $statusOK = ($response->getStatus() == 'OK');
00452: while (mb_strlen($line) > 0) {
00453: $line = $this->_readLine($sh);
00454: $response->appendBody($line."\r\n");
00455: if (is_resource($this->_fp)) {
00456: $this->debug($line);
00457: }
00458: }
00459:
00460: if ($statusOK
00461: && $callback === true
00462: && $method == 'NOTIFY'
00463: ) {
00464: // read GNTP socket Callback response
00465: $line = $this->_readLine($sh);
00466: $this->debug($line);
00467: if (preg_match('/^GNTP\/1.0 -(\w+).*$/', $line, $resp)) {
00468: $res = ($resp[1] == 'CALLBACK');
00469: if ($res) {
00470: while (mb_strlen($line) > 0) {
00471: $line = $this->_readLine($sh);
00472: $this->debug($line);
00473: $eon = true;
00474:
00475: $nid = preg_match(
00476: '/^Notification-ID: (.*)$/',
00477: $line, $resp
00478: );
00479: if ($nid) {
00480: $eon = false;
00481: }
00482:
00483: $ncr = preg_match(
00484: '/^Notification-Callback-Result: (.*)$/',
00485: $line, $resp
00486: );
00487: if ($ncr) {
00488: $this->growlNotificationCallback[] = $resp[1];
00489: $eon = false;
00490: }
00491:
00492: $ncc = preg_match(
00493: '/^Notification-Callback-Context: (.*)$/',
00494: $line, $resp
00495: );
00496: if ($ncc) {
00497: $this->growlNotificationCallback[] = $resp[1];
00498: $eon = false;
00499: }
00500:
00501: $ncct = preg_match(
00502: '/^Notification-Callback-Context-Type: (.*)$/',
00503: $line, $resp
00504: );
00505: if ($ncct) {
00506: $this->growlNotificationCallback[] = $resp[1];
00507: $eon = false;
00508: }
00509:
00510: $nct = preg_match(
00511: '/^Notification-Callback-Timestamp: (.*)$/',
00512: $line, $resp
00513: );
00514: if ($nct) {
00515: $this->growlNotificationCallback[] = $resp[1];
00516: $eon = false;
00517: }
00518:
00519: if ($eon) {
00520: break;
00521: }
00522: }
00523: }
00524: }
00525:
00526: if (is_resource($this->_fp)) {
00527: while (mb_strlen($line) > 0) {
00528: $line = $this->_readLine($sh);
00529: $this->debug($line);
00530: }
00531: }
00532: }
00533: break;
00534: case 'udp':
00535: $statusOK = $response = true;
00536: break;
00537: }
00538:
00539: switch (strtoupper($method)) {
00540: case 'REGISTER':
00541: if ($statusOK) {
00542: $this->isRegistered = true;
00543: }
00544: break;
00545: case 'NOTIFY':
00546: if ($statusOK) {
00547: $this->growlNotificationCount++;
00548: }
00549: break;
00550: }
00551:
00552: // close connection
00553: fclose($sh);
00554:
00555: return $response;
00556: // @codeCoverageIgnoreEnd
00557: }
00558:
00559: /**
00560: * Returns Growl default icon logo binary data
00561: * Decodes data encoded with MIME base64
00562: *
00563: * @param bool $return (optional) If used and set to FALSE,
00564: * getDefaultGrowlIcon() will output the binary
00565: * representation instead of return it
00566: *
00567: * @return string
00568: */
00569: public function getDefaultGrowlIcon($return = true)
00570: {
00571: $growl_logo
00572: = 'iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAA'
00573: . 'AARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAA'
00574: . 'OpgAABdwnLpRPAAAAAlwSFlzAAALEgAACxIB0t1+/AAACthJREFUaEPtmAlUlPUa'
00575: . 'h1UUkX0RFBEQwaUCd1D2xWGbGWTYZd9lkFUEEQzFJTKyrFtamZZbmZaZpbaZaZqV'
00576: . 'W4taaqam5kIqatpyQZ/7zpCde7rZ7Z60vOfwnfOe/8c5H9/8nnef6dCh/Wr3QLsH'
00577: . '2j3waw9ER0whOmyC3jQRVcSqn+D/xksxyhmEB2hRhZQzJrRCbAKxkWVihaRqqu98'
00578: . 'kOSYKUyZOI+spHqiQsvRRJYTp6oSkFQSQ4zJT0i4syEWPL6SK5cvcPTgl2x6eT0r'
00579: . '5i1jfFYN8VElxI4eSF2GMYXJuXc2RFGGhoqcMJY9XM5nby7jw1fXUlv2AFkaP05t'
00580: . '82R1o+OdDaAr2LjQwcSFDqJ+Qh4tVy5weO8R1i0fC9cS+P4LbxoK+93ZEAXpZWQn'
00581: . 'JvDEo0tou85Da5WcWrgSzdE3BlCdl/T3QuTGKchL0PymiKzYMSSrPHl7/VZOn/iC'
00582: . 'H67OEfGVYhXQkgHN4ax/zPWvA0iMWYBydBUKPy1B3kWEjFIQH2REaqgxKQojksMc'
00583: . 'yYhJ/0VQVrQvyaE2TMhR8cJTobS2lIv4yT9DlML1XC7uGUyNtvj2Q0SH1xLql89o'
00584: . 'Xy2B3oX4exXhMzwLpa8jSaOtxGyIDbIgyrcLaj87UtQRZKmdyVZak6M058h+yX1q'
00585: . 'xSaJ6dLo5/Oiimdnjrm9AEkxj6DwySfAcxzKkGJSYgqJDFDiM1RN0PABxIdYkBBi'
00586: . 'g9rXklBPC0KGmaD2MSJLaUO2qjtPT3cXwRPEqn8WfwNAonEtk40LA28vQFRoFQXp'
00587: . 'M3lu0Wt8dfAoLT9c4uAnm3lr7XIq8pUoRxoR629NRWIvVs0ZwuQcV5TeFmQqbckR'
00588: . 'gIUCcPzjdL7Znc3xHWnseyuJs3vzpA6kFshj1yrv2wswuXweTSeP0Xz2AAc+eZdN'
00589: . 'r6/k6KG9+r5yX00aqQoL1sz34dzeMSIqlxOfxktt2JAe0Z34YGs8+poQ7u1GjGIw'
00590: . 'kb79ifAdSEGCDx+skrS6lMHBV28zgK6nL130HtrMyYxVBZOuHkLF+Eo2rF1DpsqJ'
00591: . 'HRvCBKVYrEQKU86ftEwe50p8oBUqH0tMu3XC1NgQe1sLbCxNcOhhhcddfZmYHUzT'
00592: . 'Jg3nN3rd3gj81haZnjCOSG8HGie6tfV1ytoALmu53lzIlheCJa0s9RFw7tmVzgYd'
00593: . 'MDMWEIGxMuuCq3NPEtXebFukpGXPqL8eIC+lgqTATmxbOYrWM2nsWafmxTkBzC71'
00594: . 'ojLTiyfv9SU13I64YCsiRllgZ9UZI8OOmHTriJV5V9xcHAj3H8zWpVFwMpzVD//3'
00595: . 'gZYS10hB9qJbBztujC07n/Vg3gQ3nOy6YWFiKCliiqO9LXe5OeDtYUPQUDOSFDYC'
00596: . 'YYmDrSGWpgZYWxjRy86StEg3Dr/kC58Hcm5nIDMqZ9xUXNt6XoIqWEtOyvQ/B1GY'
00597: . 'UUlOwlgyIu1ZUOUk26U95sYd6dqlA9bmhpIedtzd34l+fXrg4WpCpEQgcbQ10dKp'
00598: . 'fDzMpMCteKTEmc+WDObaTgE4nybpV86XH6koTosgN2XyLwILUsZRmpPPh9s+Y9+n'
00599: . 'h3nq0ecoyCijsvTp/w0iLXm5rAVhMnGNRUAXxvgaydCylF5vy9xiJ33B6lLEsLNA'
00600: . 'WBjT37W3mBMDXaz1z8UH2+gjERtkzdwSJ9jtDV9Hyk6U01Y/16R+KGXbmiDCRxig'
00601: . '8Tckxt+AREUPUqL82Lp5i77rXTnzNdMm1lGunfnHAKIjphIwMp/hHpkytLzkC4kZ'
00602: . 'Y/wsCR9pjmKEOVHSZdLCuvNYiSNztA4ovcxwdzJkoLMFI9z7oI0dwIbGu9Fq7IgR'
00603: . 'yJhAax4tFYCvpHO1jmvrXq3jf7H5s0YwepiZ/lm1nxXKgAHS9dyp1caw8tnnObH7'
00604: . 'PZ6eM5eKotm/D5CVuojAkbkybTNR+OcToywkITKMsQoZWMmOVKW7MHfiPezZEMr+'
00605: . '9yP57J3RcErDlS/U7HvNn/cWDmHbYj8ubFfDwXCW17miCRCAAGupGWc4Kt5vzRfP'
00606: . 'FwqEzkrYujZCP8l1Ez1eij8m0J7CjHSqCpJoKFVQXVzOrCkzmT2tAW1K4s0B8jIX'
00607: . '4zssg/SEKaxY+ioH9u3j8sVv2bj+eR6vu4cfm5JpbUqRlpmuX8i4JlP1O7FmMb0Y'
00608: . 'sWsi7qds+CELzsVx6GVP8qNs9Sk0NbMXx9eNhG81cFXe810qO14PJ0Nlr5/gCSHW'
00609: . 'AmrBrHJ3Vi6toFRbzcTCIiaXTWLyhHoeqq9iyUz3mwMEeOZSWtBI8/lL+ry7cX15'
00610: . 'YB/vrg6WP3WhF7HfZ9O8J5Fty9S8MjeCzQvVfL0lgX+eFLB/yjOtBTKZBeRSMj9+'
00611: . 'rGBJjZu+XlZNd2PnU3dz/o0RHN/gxTO1bgQOMsXXw1SfYmOlVmICLJk3xYPrPxSw'
00612: . 'euUkyfkGxudMo2H6VA7tSObkluG/DVA2fhUN9fO51NxM0+njbN+0kU3rN7Br+0ds'
00613: . 'fec9/jHVh8tNcXqIA2/HUZTkhcJ3EH6eAxnu7kzAMCcaS4ZzdX+SQEiBfi8RaJb7'
00614: . 'I0qOvuhF09s+tO4KYOcCd2pS7PF3N8XcpBPdDDvJaYBLr676ljtW1vDx0T05/mmM'
00615: . 'fNYsjhxaz8a1RTR/FQ0XfNm8LOzmEdi+9QtefnEXs+pXU1u5/JcH6+99lZJsLfcV'
00616: . '2rDlmSFkqvvT16kXjr1ssLMxpYdYzx42DBrQmzWNo+BELFxMhCY5T0gt7FcIiIrt'
00617: . 'S4PI1dwlbdYOU5Nuuvdj0KnNOorZWnbWR6Iktgcnd4RIOqZKuorwpgA448+P+wez'
00618: . 'sHHWH+tCN/sxSqtxYZBLB1kNDPTrgbmxAWYmXelhbSKpYMU66Twck0I9J+JPyYd/'
00619: . 'IwDHVXyw2J/oEHdGDnPX70M2VmYYyJqhWzV0prvXAQ3tb0KVROijxe5c3D2S64d8'
00620: . '5P8DaDngxevz/f6c+BtQ1flqNH6GBMi0VUs7nZHrwOr73Tj9+nA4JF3phFK8JuJP'
00621: . 'y7pwRs31g5FMyx2Iax8HBt/dl7v6OeoBboi/cXaSKOj2Jl0aVSTZU5NqzxypnTcf'
00622: . '6c+Kul63RvwNiLqieAo1pkwSb13cNEK8Hii7TUSb6NPi9TM/n01RXP1E2nCoE7bd'
00623: . 'LenTuzu9e1phZmr0HwC6VNJFwXOgCTXpvbi/0Ill01xEvAMPT516awF0INV5YRRp'
00624: . 'TDj22jDxeDicVYl4MfE6p3T3EomzEoGTKp6sdqenrTnWslZbmHbFsEsnPUCXf0sf'
00625: . 'nfhBfbux9oH+XN4jE/uID8c2eLDowTm3XvyNSFSke/JYmTWH1g7j+ufSas9I/jfp'
00626: . 'IET8KbnXnQJ2dXeIpEUfrGTd6GxgQKeOHfSm83qXzh1lKTQkLbQ72+brashfJvYI'
00627: . 'Plr2Oz3/Vv5iXFOYwISE7iyaZM/hNUNp3Svp9KV0kYNSD8dkdTgSKoJC+W6rPw+O'
00628: . 'd2Wwm7RQyXVr8870czSSCWzF4+XOnHxpCGwfxuFXBrBitur2ef1m8JPyIpmYaMW8'
00629: . 'MjvelcI7snIIV94ZScv7Plz/QLbPXf60bPbmwyfv4alKF6ZmO3BvlgNPVDrzxoMu'
00630: . 'vPmgAwtrh/71wn8NVFccR/lYJ2rTLHi02JrnptjzRmMfts/TTeJ+vD/PlXWznXly'
00631: . 'oh0zcy2ZmWfPrCJvHpre8PeL/zVMQ0059WXJ1BVGUKcNpn58EDOKFbLvjGH2pBzm'
00632: . 'zrj5l5lbmebt72r3QLsH2j3Q7oF2D9xxHvgXsaxDNYPEU7QAAAAASUVORK5CYII=';
00633:
00634: $data = base64_decode($growl_logo);
00635:
00636: if ($return === false) {
00637: // @codeCoverageIgnoreStart
00638: if (headers_sent()) {
00639: return;
00640: }
00641: header('content-type: image/png');
00642: echo $data;
00643: exit();
00644: // @codeCoverageIgnoreEnd
00645: } else {
00646: return $data;
00647: }
00648: }
00649:
00650: /**
00651: * Logs GNTP IN/OUT messages
00652: *
00653: * @param string $message String containing the message to log
00654: * @param string $priority (optional) String containing a priority name
00655: *
00656: * @return void
00657: */
00658: protected function debug($message, $priority = 'debug')
00659: {
00660: if (is_resource($this->_fp)
00661: && mb_strlen($message) > 0
00662: ) {
00663: fwrite(
00664: $this->_fp,
00665: date("Y-m-d H:i:s") . " [$priority] - " . $message . "\n"
00666: );
00667: }
00668: }
00669:
00670: /**
00671: * Converts standard error into exception
00672: *
00673: * @param int $errno contains the level of the error raised
00674: * @param string $errstr contains the error message
00675: * @param string $errfile contains the filename that the error was raised in
00676: * @param int $errline contains the line number the error was raised at
00677: *
00678: * @return void
00679: * @throws ErrorException when a standard error occured with severity level
00680: * we are asking for (uses error_reporting)
00681: * @since 2.1.0
00682: */
00683: public function errorHandler($errno, $errstr, $errfile, $errline)
00684: {
00685: // Only catch errors we are asking for
00686: if ((error_reporting() & $errno) == 0) {
00687: return;
00688: }
00689: throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
00690: }
00691:
00692: /**
00693: * Autoloader for PEAR compatible classes
00694: *
00695: * @param string $class Class name
00696: *
00697: * @return void
00698: * @throws Net_Growl_Exception if class handler cannot be loaded
00699: */
00700: public static function autoload($class)
00701: {
00702: try {
00703: $path = str_replace('_', '/', $class .'.php');
00704: include_once $path;
00705: }
00706: catch (ErrorException $e) {
00707: $message = 'Cannot load class "'.$class.'"';
00708: throw new Net_Growl_Exception($message);
00709: }
00710: }
00711:
00712: /**
00713: * Read until either the end of the socket or a newline, whichever
00714: * comes first. Strips the trailing newline from the returned data.
00715: *
00716: * @param mixed $fp a file pointer resource
00717: *
00718: * @return All available data up to a newline, without that
00719: * newline, or until the end of the socket,
00720: * @throws Net_Growl_Exception if not connected
00721: */
00722: private function _readLine($fp)
00723: {
00724: // @codeCoverageIgnoreStart
00725: if (!is_resource($fp)) {
00726: throw new Net_Growl_Exception('not connected');
00727: }
00728:
00729: $line = '';
00730: $timeout = time() + $this->options['timeout'];
00731: while (!feof($fp) && (time() < $timeout)) {
00732: $line .= @fgets($fp);
00733: if (mb_substr($line, -1) == "\n" && mb_strlen($line) > 0) {
00734: break;
00735: }
00736: }
00737: return rtrim($line, "\r\n");
00738: // @codeCoverageIgnoreEnd
00739: }
00740: }
00741: ?>