]> git.mxchange.org Git - hub.git/blob - application/hub/main/nodes/class_BaseHubNode.php
398f32aa8bd90aa509e1ee16d45b5e6d0c8d17d4
[hub.git] / application / hub / main / nodes / class_BaseHubNode.php
1 <?php
2 /**
3  * A general hub node class
4  *
5  * @author              Roland Haeder <webmaster@shipsimu.org>
6  * @version             0.0.0
7  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2014 Hub Developer Team
8  * @license             GNU GPL 3.0 or any newer version
9  * @link                http://www.shipsimu.org
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 class BaseHubNode extends BaseHubSystem implements Updateable, AddableCriteria {
25         /**
26          * Node types
27          */
28         const NODE_TYPE_BOOT    = 'boot';
29         const NODE_TYPE_MASTER  = 'master';
30         const NODE_TYPE_LIST    = 'list';
31         const NODE_TYPE_REGULAR = 'regular';
32
33         // Exception constants
34         const EXCEPTION_HUB_ALREADY_ANNOUNCED = 0xe00;
35
36         // Other constants
37         const OBJECT_LIST_SEPARATOR = ',';
38
39         /**
40          * Universal node locator of bootstrap node
41          */
42         private $bootUnl = '';
43
44         /**
45          * Whether this node is anncounced (keep on FALSE!)
46          * @deprecated
47          */
48         private $hubIsAnnounced = FALSE;
49
50         /**
51          * Whether this hub is active (default: FALSE)
52          */
53         private $isActive = FALSE;
54
55         /**
56          * Whether this node accepts announcements (default: FALSE)
57          */
58         private $acceptAnnouncements = FALSE;
59
60         /**
61          * Whether this node accepts DHT bootstrap requests (default: FALSE)
62          */
63         private $acceptDhtBootstrap = FALSE;
64
65         /**
66          * Protected constructor
67          *
68          * @param       $className      Name of the class
69          * @return      void
70          */
71         protected function __construct ($className) {
72                 // Call parent constructor
73                 parent::__construct($className);
74
75                 // Get a wrapper instance
76                 $wrapperInstance = DatabaseWrapperFactory::createWrapperByConfiguredName('node_info_db_wrapper_class');
77
78                 // Set it here
79                 $this->setWrapperInstance($wrapperInstance);
80
81                 // Get a crypto instance
82                 $cryptoInstance = ObjectFactory::createObjectByConfiguredName('crypto_class');
83
84                 // Set it here
85                 $this->setCryptoInstance($cryptoInstance);
86
87                 // Init state which sets the state to 'init'
88                 $this->initState();
89         }
90
91         /**
92          * Initializes the node's state which sets it to 'init'
93          *
94          * @return      void
95          */
96         private function initState() {
97                 // Get the state factory and create the initial state.
98                 NodeStateFactory::createNodeStateInstanceByName('init', $this);
99         }
100
101         /**
102          * Generates a private key and hashes it (for speeding up things)
103          *
104          * @param       $searchInstance         An instance of a LocalSearchCriteria class
105          * @return void
106          */
107         private function generatePrivateKeyAndHash (LocalSearchCriteria $searchInstance) {
108                 // Get an RNG instance
109                 $rngInstance = ObjectFactory::createObjectByConfiguredName('rng_class');
110
111                 // Generate a pseudo-random string
112                 $randomString = $rngInstance->randomString(255);
113
114                 // Hash and encrypt the string so we become a node id (also documented as "hub id")
115                 $this->setPrivateKey($this->getCryptoInstance()->encryptString($randomString));
116                 $this->setPrivateKeyHash($this->getCryptoInstance()->hashString($this->getPrivateKey()));
117
118                 // Register the node id with our wrapper
119                 $this->getWrapperInstance()->registerPrivateKey($this, $this->getRequestInstance(), $searchInstance);
120
121                 // Output message
122                 self::createDebugInstance(__CLASS__)->debugOutput('BOOTSTRAP: Created new private key with hash: ' . $this->getPrivateKeyHash() . '');
123         }
124
125         /**
126          * Generates a random string from various data inluding UUID if PECL
127          * extension uuid is installed.
128          *
129          * @param       $length                 Length of the random part
130          * @return      $randomString   Random string
131          * @todo        Make this code more generic and move it to CryptoHelper or
132          */
133         protected function generateRamdomString ($length) {
134                 // Get an RNG instance
135                 $rngInstance = ObjectFactory::createObjectByConfiguredName('rng_class');
136
137                 // Generate a pseudo-random string
138                 $randomString = $rngInstance->randomString($length) . ':' . $this->getBootUniversalNodeLocator() . ':' . $this->getRequestInstance()->getRequestElement('mode');
139
140                 // Add UUID for even more entropy for the hasher
141                 $randomString .= $this->getCryptoInstance()->createUuid();
142
143                 // Return it
144                 return $randomString;
145         }
146
147         /**
148          * Getter for boot IP/port combination
149          *
150          * @return      $bootUnl        The IP/port combination of the boot node
151          */
152         protected final function getBootUniversalNodeLocator () {
153                 return $this->bootUnl;
154         }
155
156         /**
157          * Checks whether the given IP address matches one of the bootstrap nodes
158          *
159          * @param       $remoteAddr             IP address to checkout against our bootstrapping list
160          * @return      $isFound                Whether the IP is found
161          */
162         protected function ifAddressMatchesBootstrapNodes ($remoteAddr) {
163                 // By default nothing is found
164                 $isFound = FALSE;
165
166                 // Run through all configured IPs
167                 foreach (explode(BaseHubSystem::BOOTSTRAP_NODES_SEPARATOR, $this->getConfigInstance()->getConfigEntry('hub_bootstrap_nodes')) as $unl) {
168                         // @TODO Unfinished
169                         die(__METHOD__ . ':' . print_r($this, TRUE));
170                         die(__METHOD__ . ': unl=' . $unl . ',remoteAddr=' . $remoteAddr);
171
172                         // Does it match?
173                         if ($unl == $remoteAddr) {
174                                 // Found it!
175                                 $isFound = TRUE;
176
177                                 // Remember the UNL
178                                 $this->bootUnl = $unl;
179
180                                 // Output message
181                                 self::createDebugInstance(__CLASS__)->debugOutput('BOOTSTRAP: ' . __FUNCTION__ . '[' . __METHOD__ . ':' . __LINE__ . ']: IP matches remote address ' . $unl->__toString() . '.');
182
183                                 // Stop further searching
184                                 break;
185                         } elseif ($unl == $this->getConfigInstance()->getConfigEntry('node_listen_addr')) {
186                                 /*
187                                  * IP matches listen address. At this point we really don't care
188                                  * if we can really listen on that address
189                                  */
190                                 $isFound = TRUE;
191
192                                 // Remember the port number
193                                 $this->bootUnl = $unl;
194
195                                 // Output message
196                                 self::createDebugInstance(__CLASS__)->debugOutput('BOOTSTRAP: ' . __FUNCTION__ . '[' . __METHOD__ . ':' . __LINE__ . ']: IP matches listen address ' . $unl->__toString() . '.');
197
198                                 // Stop further searching
199                                 break;
200                         }
201                 } // END - foreach
202
203                 // Return the result
204                 return $isFound;
205         }
206
207         /**
208          * Tries to detect own UNL (Universal Node Locator)
209          *
210          * @return      $unl    Node's own universal node locator
211          */
212         public function detectOwnUniversalNodeLocator () {
213                 // Get the UNL array back
214                 $unlArray = $this->getUniversalNodeLocatorArray();
215
216                 // @TODO
217                 die(__METHOD__ . ':unlArray=' . print_r($unlArray, TRUE));
218         }
219
220         /**
221          * Outputs the console teaser. This should only be executed on startup or
222          * full restarts. This method generates some space around the teaser.
223          *
224          * @return      void
225          */
226         public function outputConsoleTeaser () {
227                 // Get the app instance (for shortening our code)
228                 $app = $this->getApplicationInstance();
229
230                 // Output all lines
231                 self::createDebugInstance(__CLASS__)->debugOutput(' ');
232                 self::createDebugInstance(__CLASS__)->debugOutput($app->getAppName() . ' v' . $app->getAppVersion() . ' - ' . $this->getRequestInstance()->getRequestElement('mode') . ' mode active');
233                 self::createDebugInstance(__CLASS__)->debugOutput('Copyright (c) 2007 - 2008 Roland Haeder, 2009 - 2014 Hub Developer Team');
234                 self::createDebugInstance(__CLASS__)->debugOutput(' ');
235                 self::createDebugInstance(__CLASS__)->debugOutput('This program comes with ABSOLUTELY NO WARRANTY; for details see docs/COPYING.');
236                 self::createDebugInstance(__CLASS__)->debugOutput('This is free software, and you are welcome to redistribute it under certain');
237                 self::createDebugInstance(__CLASS__)->debugOutput('conditions; see docs/COPYING for details.');
238                 self::createDebugInstance(__CLASS__)->debugOutput(' ');
239         }
240
241         /**
242          * Generic method to acquire a hub-id. On first run this generates a new one
243          * based on many pseudo-random data. On any later run, unless the id
244          * got not removed from database, it will be restored from the database.
245          *
246          * @param       $requestInstance        A Requestable class
247          * @param       $responseInstance       A Responseable class
248          * @return      void
249          */
250         public function bootstrapAcquireNodeId (Requestable $requestInstance, Responseable $responseInstance) {
251                 // Is there a node id?
252                 if ($this->getWrapperInstance()->ifNodeDataIsFound($this)) {
253                         // Get the node id from result and set it
254                         $this->setNodeId($this->getField(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_ID));
255
256                         // Output message
257                         self::createDebugInstance(__CLASS__)->debugOutput('BOOTSTRAP: Re-using found node-id: ' . $this->getNodeId() . '');
258                 } else {
259                         // Get an RNG instance
260                         $rngInstance = ObjectFactory::createObjectByConfiguredName('rng_class');
261
262                         // Generate a pseudo-random string
263                         $randomString = $rngInstance->randomString(255);
264
265                         // Hash and encrypt the string so we become a node id (also documented as "hub id")
266                         $this->setNodeId($this->getCryptoInstance()->hashString($this->getCryptoInstance()->encryptString($randomString)));
267
268                         // Register the node id with our wrapper
269                         $this->getWrapperInstance()->registerNodeId($this, $this->getRequestInstance());
270
271                         // Output message
272                         self::createDebugInstance(__CLASS__)->debugOutput('BOOTSTRAP: Created new node-id: ' . $this->getNodeId() . '');
273                 }
274         }
275
276         /**
277          * Generates a session id which will be sent to the other hubs and peers
278          *
279          * @return      void
280          */
281         public function bootstrapGenerateSessionId () {
282                 // Now get a search criteria instance
283                 $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
284
285                 // Search for the node number one which is hard-coded the default
286                 $searchInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_NR  , 1);
287                 $searchInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_MODE, $this->getRequestInstance()->getRequestElement('mode'));
288                 $searchInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_ID  , $this->getNodeId());
289                 $searchInstance->setLimit(1);
290
291                 // Remember it for later usage
292                 $this->setSearchInstance($searchInstance);
293
294                 // Get a random string
295                 $randomString = $this->generateRamdomString(255);
296
297                 // Hash and encrypt the string so we become a "node id" aka Hub-Id
298                 $this->setSessionId($this->getCryptoInstance()->hashString($this->getCryptoInstance()->encryptString($randomString)));
299
300                 // Register the node id with our wrapper
301                 $this->getWrapperInstance()->registerSessionId($this, $this->getRequestInstance(), $searchInstance);
302
303                 // Output message
304                 self::createDebugInstance(__CLASS__)->debugOutput('BOOTSTRAP: Created new session-id: ' . $this->getSessionId() . '');
305
306                 // Change the state because the node has auired a hub id
307                 $this->getStateInstance()->nodeGeneratedSessionId();
308         }
309
310         /**
311          * Generate a private key for en-/decryption
312          *
313          * @return      void
314          */
315         public function bootstrapGeneratePrivateKey () {
316                 // Is it valid?
317                 if ($this->getWrapperInstance()->ifNodeDataIsFound($this)) {
318                         // Is the element set?
319                         if (is_null($this->getField(NodeInformationDatabaseWrapper::DB_COLUMN_PRIVATE_KEY))) {
320                                 /*
321                                  * Auto-generate the private key for e.g. out-dated database
322                                  * "tables". This allows a smooth update for the underlaying
323                                  * database table.
324                                  */
325                                 $this->generatePrivateKeyAndHash($this->getSearchInstance());
326                         } else {
327                                 // Get the node id from result and set it
328                                 $this->setPrivateKey(base64_decode($this->getField(NodeInformationDatabaseWrapper::DB_COLUMN_PRIVATE_KEY)));
329                                 $this->setPrivateKeyHash($this->getField(NodeInformationDatabaseWrapper::DB_COLUMN_PRIVATE_KEY_HASH));
330
331                                 // Output message
332                                 self::createDebugInstance(__CLASS__)->debugOutput('BOOTSTRAP: Re-using found private key hash: ' . $this->getPrivateKeyHash() . '');
333                         }
334                 } else {
335                         /*
336                          * Generate it in a private method (no confusion with 'private
337                          * method access' and 'private key' here! ;-)).
338                          */
339                         $this->generatePrivateKeyAndHash($this->getSearchInstance());
340                 }
341         }
342
343         /**
344          * Adds hub data elements to a given dataset instance
345          *
346          * @param       $criteriaInstance       An instance of a storeable criteria
347          * @param       $requestInstance        An instance of a Requestable class
348          * @return      void
349          */
350         public function addElementsToDataSet (StoreableCriteria $criteriaInstance, Requestable $requestInstance = NULL) {
351                 // Make sure request instance is set as it is not optional
352                 assert($requestInstance instanceof Requestable);
353
354                 // Add node number and type
355                 $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_NR  , 1);
356                 $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_MODE, $requestInstance->getRequestElement('mode'));
357
358                 // Add the node id
359                 $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_ID, $this->getNodeId());
360
361                 // Add the session id if acquired
362                 if ($this->getSessionId() != '') {
363                         $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_SESSION_ID, $this->getSessionId());
364                 } // END - if
365
366                 // Add the private key if acquired
367                 if ($this->getPrivateKey() != '') {
368                         $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_PRIVATE_KEY, base64_encode($this->getPrivateKey()));
369                         $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_PRIVATE_KEY_HASH, $this->getPrivateKeyHash());
370                 } // END - if
371         }
372
373         /**
374          * Updates a given field with new value
375          *
376          * @param       $fieldName              Field to update
377          * @param       $fieldValue             New value to store
378          * @return      void
379          * @throws      DatabaseUpdateSupportException  If this class does not support database updates
380          * @todo        Try to make this method more generic so we can move it in BaseFrameworkSystem
381          */
382         public function updateDatabaseField ($fieldName, $fieldValue) {
383                 // Unfinished
384                 $this->partialStub('Unfinished!');
385                 return;
386
387                 // Get a critieria instance
388                 $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
389
390                 // Add search criteria
391                 $searchInstance->addCriteria(UserDatabaseWrapper::DB_COLUMN_USERNAME, $this->getUserName());
392                 $searchInstance->setLimit(1);
393
394                 // Now get another criteria
395                 $updateInstance = ObjectFactory::createObjectByConfiguredName('update_criteria_class');
396
397                 // Add criteria entry which we shall update
398                 $updateInstance->addCriteria($fieldName, $fieldValue);
399
400                 // Add the search criteria for searching for the right entry
401                 $updateInstance->setSearchInstance($searchInstance);
402
403                 // Set wrapper class name
404                 $updateInstance->setWrapperConfigEntry('user_db_wrapper_class');
405
406                 // Remember the update in database result
407                 $this->getResultInstance()->add2UpdateQueue($updateInstance);
408         }
409
410         /**
411          * Announces this hub to the upper (bootstrap or list) hubs. After this is
412          * successfully done the given task is unregistered from the handler. This
413          * might look a bit overloaded here but the announcement phase isn't a
414          * simple "Hello there" message, it may later on also contain more
415          * informations like the object list.
416          *
417          * @param       $taskInstance   The task instance running this announcement
418          * @return      void
419          * @throws      NodeAlreadyAnnouncedException   If this hub is already announced
420          * @todo        Change the first if() block to check for a specific state
421          */
422         public function announceToUpperNodes (Taskable $taskInstance) {
423                 // Is this hub node announced?
424                 if ($this->hubIsAnnounced === TRUE) {
425                         // Already announced!
426                         throw new NodeAlreadyAnnouncedException($this, self::EXCEPTION_HUB_ALREADY_ANNOUNCED);
427                 } // END - if
428
429                 // Debug output
430                 self::createDebugInstance(__CLASS__)->debugOutput('HUB-Announcement: START (taskInstance=' . $taskInstance->__toString(). ')');
431
432                 // Get a helper instance
433                 $helperInstance = ObjectFactory::createObjectByConfiguredName('node_announcement_helper_class');
434
435                 // Load the announcement descriptor
436                 $helperInstance->loadDescriptorXml($this);
437
438                 // Compile all variables
439                 $helperInstance->getTemplateInstance()->compileConfigInVariables();
440
441                 // "Publish" the descriptor by sending it to the bootstrap/list nodes
442                 $helperInstance->sendPackage($this);
443
444                 // Change the state, this should be the last line except debug output
445                 $this->getStateInstance()->nodeAnnouncingToUpperHubs();
446
447                 // Debug output
448                 self::createDebugInstance(__CLASS__)->debugOutput('HUB-Announcement: FINISHED');
449         }
450
451         /**
452          * Does a self-connect attempt on the public IP address. This should make
453          * it sure, we are reachable from outside world. For this kind of package we
454          * don't need that overload we have in the announcement phase.
455          *
456          * @param       $taskInstance   The task instance running this announcement
457          * @return      void
458          */
459         public function doSelfConnection (Taskable $taskInstance) {
460                 // Debug output
461                 self::createDebugInstance(__CLASS__)->debugOutput('NODE[' . __METHOD__ . ':' . __LINE__ . ']: Self Connection: START (taskInstance=' . $taskInstance->__toString(). ')');
462
463                 // Get a helper instance
464                 $helperInstance = ObjectFactory::createObjectByConfiguredName('node_self_connect_helper_class', array($this));
465
466                 // Load the descriptor (XML) file
467                 $helperInstance->loadDescriptorXml($this);
468
469                 // Compile all variables
470                 $helperInstance->getTemplateInstance()->compileConfigInVariables();
471
472                 // And send the package away
473                 $helperInstance->sendPackage($this);
474
475                 // Debug output
476                 self::createDebugInstance(__CLASS__)->debugOutput('NODE[' . __METHOD__ . ':' . __LINE__ . ']: Self Connection: FINISHED');
477         }
478
479         /**
480          * Activates the hub by doing some final preparation and setting
481          * $hubIsActive to TRUE.
482          *
483          * @param       $requestInstance        A Requestable class
484          * @param       $responseInstance       A Responseable class
485          * @return      void
486          */
487         public function activateNode (Requestable $requestInstance, Responseable $responseInstance) {
488                 // Checks whether a listener is still active and shuts it down if one
489                 // is still listening.
490                 if (($this->determineIfListenerIsActive()) && ($this->isNodeActive())) {
491                         // Shutdown them down before they can hurt anything
492                         $this->shutdownListenerPool();
493                 } // END - if
494
495                 // Get the controller here
496                 $controllerInstance = Registry::getRegistry()->getInstance('controller');
497
498                 // Run all filters for the hub activation
499                 $controllerInstance->executeActivationFilters($requestInstance, $responseInstance);
500
501                 // ----------------------- Last step from here ------------------------
502                 // Activate the hub. This is ALWAYS the last step in this method
503                 $this->getStateInstance()->nodeIsActivated();
504                 // ---------------------- Last step until here ------------------------
505         }
506
507         /**
508          * Initializes the listener pool (class)
509          *
510          * @return      void
511          */
512         public function initializeListenerPool () {
513                 // Debug output
514                 self::createDebugInstance(__CLASS__)->debugOutput('NODE[' . __METHOD__ . ':' . __LINE__ . ']: Initialize listener: START');
515
516                 // Get a new pool instance
517                 $this->setListenerPoolInstance(ObjectFactory::createObjectByConfiguredName('listener_pool_class', array($this)));
518
519                 // Get an instance of the low-level listener
520                 $listenerInstance = ObjectFactory::createObjectByConfiguredName('tcp_listener_class', array($this));
521
522                 // Setup address and port
523                 $listenerInstance->setListenAddressByConfiguration('node_listen_addr');
524
525                 /*
526                  * All nodes can now use the same configuration entry because it can be
527                  * customized in config-local.php.
528                  */
529                 $listenerInstance->setListenPortByConfiguration('node_listen_port');
530
531                 // Initialize the listener
532                 $listenerInstance->initListener();
533
534                 // Get a decorator class
535                 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('node_tcp_listener_class', array($listenerInstance));
536
537                 // Add this listener to the pool
538                 $this->getListenerPoolInstance()->addListener($decoratorInstance);
539
540                 // Get a decorator class
541                 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('client_tcp_listener_class', array($listenerInstance));
542
543                 // Add this listener to the pool
544                 $this->getListenerPoolInstance()->addListener($decoratorInstance);
545
546                 // Get an instance of the low-level listener
547                 $listenerInstance = ObjectFactory::createObjectByConfiguredName('udp_listener_class', array($this));
548
549                 // Setup address and port
550                 $listenerInstance->setListenAddressByConfiguration('node_listen_addr');
551
552                 /*
553                  * All nodes can now use the same configuration entry because it can be
554                  * customized in config-local.php.
555                  */
556                 $listenerInstance->setListenPortByConfiguration('node_listen_port');
557
558                 // Initialize the listener
559                 $listenerInstance->initListener();
560
561                 // Get a decorator class
562                 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('node_udp_listener_class', array($listenerInstance));
563
564                 // Add this listener to the pool
565                 $this->getListenerPoolInstance()->addListener($decoratorInstance);
566
567                 // Get a decorator class
568                 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('client_udp_listener_class', array($listenerInstance));
569
570                 // Add this listener to the pool
571                 $this->getListenerPoolInstance()->addListener($decoratorInstance);
572
573                 // Debug output
574                 self::createDebugInstance(__CLASS__)->debugOutput('NODE[' . __METHOD__ . ':' . __LINE__ . ']: Initialize listener: FINISHED.');
575         }
576
577         /**
578          * Getter for isActive attribute
579          *
580          * @return      $isActive       Whether the hub is active
581          */
582         public final function isNodeActive () {
583                 return $this->isActive;
584         }
585
586         /**
587          * Enables (default) or disables isActive flag
588          *
589          * @param       $isActive       Whether the hub is active
590          * @return      void
591          */
592         public final function enableIsActive ($isActive = TRUE) {
593                 $this->isActive = (bool) $isActive;
594         }
595
596         /**
597          * Checks whether this node accepts announcements
598          *
599          * @return      $acceptAnnouncements    Whether this node accepts announcements
600          */
601         public final function isAcceptingAnnouncements () {
602                 // Check it (this node must be active and not shutdown!)
603                 $acceptAnnouncements = (($this->acceptAnnouncements === TRUE) && ($this->isNodeActive()));
604
605                 // Return it
606                 return $acceptAnnouncements;
607         }
608
609         /**
610          * Checks whether this node accepts DHT bootstrap requests
611          *
612          * @return      $acceptDhtBootstrap     Whether this node accepts DHT bootstrap requests
613          */
614         public final function isAcceptingDhtBootstrap () {
615                 // Check it (this node must be active and not shutdown!)
616                 $acceptDhtBootstrap = (($this->acceptDhtBootstrap === TRUE) && ($this->isNodeActive()));
617
618                 // Return it
619                 return $acceptDhtBootstrap;
620         }
621
622         /**
623          * Checks whether this node has attempted to announce itself
624          *
625          * @return      $hasAnnounced   Whether this node has attempted to announce itself
626          * @todo        Add checking if this node has been announced to the sender node
627          */
628         public function ifNodeIsAnnouncing () {
629                 // Debug message
630                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('NODE[' . __METHOD__ . ':' . __LINE__ . ']: ifNodeIsAnnouncing(): state=' . $this->getStateInstance()->getStateName());
631
632                 // Simply check the state of this node
633                 $hasAnnounced = ($this->getStateInstance() instanceof NodeAnnouncingState);
634
635                 // Debug message
636                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('NODE[' . __METHOD__ . ':' . __LINE__ . ']: ifNodeIsAnnouncing(): hasAnnounced=' . intval($hasAnnounced));
637
638                 // Return it
639                 return $hasAnnounced;
640         }
641
642         /**
643          * Checks whether this node has attempted to announce itself and completed it
644          *
645          * @return      $hasAnnouncementCompleted       Whether this node has attempted to announce itself and completed it
646          * @todo        Add checking if this node has been announced to the sender node
647          */
648         public function ifNodeHasAnnouncementCompleted () {
649                 // Debug message
650                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('NODE[' . __METHOD__ . ':' . __LINE__ . ']: ifNodeHasAnnouncementCompleted(): state=' . $this->getStateInstance()->getStateName());
651
652                 // Simply check the state of this node
653                 $hasAnnouncementCompleted = ($this->getStateInstance() instanceof NodeAnnouncementCompletedState);
654
655                 // Debug message
656                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('NODE[' . __METHOD__ . ':' . __LINE__ . ']: ifNodeHasAnnouncementCompleted(): hasAnnouncementCompleted=' . intval($hasAnnouncementCompleted));
657
658                 // Return it
659                 return $hasAnnouncementCompleted;
660         }
661
662         /**
663          * Enables whether this node accepts announcements
664          *
665          * @param       $acceptAnnouncements    Whether this node accepts announcements (default: TRUE)
666          * @return      void
667          */
668         protected final function enableAcceptingAnnouncements ($acceptAnnouncements = TRUE) {
669                 $this->acceptAnnouncements = $acceptAnnouncements;
670         }
671
672         /**
673          * Enables whether this node accepts DHT bootstrap requests
674          *
675          * @param       $acceptDhtBootstrap     Whether this node accepts DHT bootstrap requests (default: TRUE)
676          * @return      void
677          */
678         public final function enableAcceptDhtBootstrap ($acceptDhtBootstrap = TRUE) {
679                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('NODE[' . __METHOD__ . ':' . __LINE__ . ']: Enabling DHT bootstrap requests ...');
680                 $this->acceptDhtBootstrap = $acceptDhtBootstrap;
681         }
682
683         /**
684          * Checks wether this node is accepting node-list requests
685          *
686          * @return      $acceptsRequest         Wether this node accepts node-list requests
687          */
688         public function isAcceptingNodeListRequests () {
689                 /*
690                  * Only 'regular' nodes does not accept such requests, checking
691                  * HubRegularNode is faster, but if e.g. HubRegularI2PNode will be
692                  * added then the next check will be TRUE.
693                  */
694                 $acceptsRequest = ((!$this instanceof HubRegularNode) && ($this->getRequestInstance()->getRequestElement('mode') != self::NODE_TYPE_REGULAR));
695
696                 // Return it
697                 return $acceptsRequest;
698         }
699
700         /**
701          * Determines an instance of a LocateableNode class
702          *
703          * @return      $unlInstance    An instance of a LocateableNode class for this node
704          */
705         public function determineUniversalNodeLocator () {
706                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('NODE[' . __METHOD__ . ':' . __LINE__ . ']: CALLED!');
707                 // Determine UNL based on this node:
708                 // 1) Get discovery class
709                 $discoveryInstance = ObjectFactory::createObjectByConfiguredName('unl_discovery_class');
710
711                 // 2) "Determine it
712                 $unlInstance = $discoveryInstance->discoverUniversalNodeLocatorByNode($this);
713
714                 // 3) Return it
715                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('NODE[' . __METHOD__ . ':' . __LINE__ . ']: unlInstance= ' . $unlInstance->__toString() . ' - EXIT!');
716                 return $unlInstance;
717         }
718
719         /**
720          * "Getter" for an array of an instance of a LocateableNode class
721          *
722          * @return      $unlArray       An array from an instance of a LocateableNode class for this node
723          */
724         public final function getUniversalNodeLocatorArray () {
725                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('NODE[' . __METHOD__ . ':' . __LINE__ . ']: CALLED!');
726
727                 // Get the Universal Node Locator (UNL) instance
728                 $unlInstance = $this->determineUniversalNodeLocator();
729
730                 // @TODO Unfinished
731                 die(__METHOD__ . ':unlInstance[' . gettype($unlInstance) . ']=' . print_r($unlInstance, TRUE));
732
733                 // Return it
734                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('NODE[' . __METHOD__ . ':' . __LINE__ . ']: EXIT!');
735                 return $unlArray;
736         }
737
738         /**
739          * Updates/refreshes node data (e.g. status).
740          *
741          * @return      void
742          * @todo        Find more to do here
743          */
744         public function updateNodeData () {
745                 // Set some dummy configuration entries, e.g. node_status
746                 $this->getConfigInstance()->setConfigEntry('node_status', $this->getStateInstance()->getStateName());
747         }
748
749         /**
750          * Handles message answer by given data array
751          *
752          * @param       $messageData            A valid answer message data array
753          * @param       $packageInstance        An instance of a Receivable class
754          * @return      void
755          * @todo        Handle thrown exception
756          */
757         public function handleAnswerStatusByMessageData (array $messageData, Receivable $packageInstance) {
758                 // Is it not empty?
759                 assert(!empty($messageData[BaseXmlAnswerTemplateEngine::ANSWER_STATUS]));
760
761                 // Construct configuration entry for handling class' name
762                 $classConfigEntry = strtolower($messageData[NetworkPackage::MESSAGE_ARRAY_TYPE] . '_status_' . $messageData[BaseXmlAnswerTemplateEngine::ANSWER_STATUS]) . '_handler_class';
763
764                 // Try to get a class
765                 $handlerInstance = ObjectFactory::createObjectByConfiguredName($classConfigEntry);
766
767                 // Handle it there
768                 $handlerInstance->handleAnswerMessageData($messageData, $packageInstance);
769         }
770
771         /**
772          * "Getter" for an array of all accepted object types
773          *
774          * @return      $objectList             Array of all accepted object types
775          */
776         public function getListFromAcceptedObjectTypes () {
777                 // Get registry instance
778                 $objectRegistryInstance = ObjectTypeRegistryFactory::createObjectTypeRegistryInstance();
779
780                 // Get all entries
781                 $objectList = $objectRegistryInstance->getEntries(XmlObjectRegistryTemplateEngine::OBJECT_TYPE_DATA_NAME);
782
783                 // ... and return it
784                 return $objectList;
785         }
786
787         /**
788          * Adds all required elements from given array into data set instance
789          *
790          * @param       $dataSetInstance        An instance of a StoreableCriteria class
791          * @param       $nodeData                       An array with valid node data
792          * @return      void
793          */
794         public function addArrayToDataSet (StoreableCriteria $dataSetInstance, array $nodeData) {
795                 // Add all data the array provides
796                 foreach (NodeDistributedHashTableDatabaseWrapper::getAllElements() as $element) {
797                         // Is the element there?
798                         if (isset($nodeData[$element])) {
799                                 // Add it
800                                 $dataSetInstance->addCriteria($element, $nodeData[$element]);
801                         } else {
802                                 // Output warning message
803                                 /* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('NODE[' . __METHOD__ . ':' . __LINE__ . ']: addArrayToDataSet(): Element ' . $element . ' not found in nodeData array.');
804                         }
805                 } // END - foreac
806         }
807 }
808
809 // [EOF]
810 ?>