/** * Send node to the servers. * * @param $to * @param ProtocolNode $node * @param null $id * * @return string Message ID. */ protected function sendMessageNode($to, $node, $id = null) { $msgId = $id == null ? $this->createMsgId() : $id; $to = $this->getJID($to); if ($node->getTag() == "body" || $node->getTag() == "enc") { $type = 'text'; } else { $type = 'media'; } $messageNode = new ProtocolNode("message", array('to' => $to, 'type' => $type, 'id' => $msgId, 't' => time(), 'notify' => $this->name), array($node), ""); $this->sendNode($messageNode); $this->logFile('info', '{type} message with id {id} sent to {to}', array('type' => $type, 'id' => $msgId, 'to' => ExtractNumber($to))); $this->eventManager()->fire("onSendMessage", array($this->phoneNumber, $to, $msgId, $node)); $this->waitForServer($msgId); return $msgId; }
/** * Will process the data from the server after it's been decrypted and parsed. * * This also provides a convenient method to use to unit test the event framework. * @param ProtocolNode $node * @param bool $autoReceipt * @param $type * * @throws Exception */ protected function processInboundDataNode(ProtocolNode $node, $autoReceipt = true, $type = "read") { $this->debugPrint($node->nodeString("rx ") . "\n"); $this->serverReceivedId = $node->getAttribute('id'); if ($node->getTag() == "challenge") { $this->processChallenge($node); } elseif ($node->getTag() == "failure") { $this->loginStatus = Constants::DISCONNECTED_STATUS; $this->eventManager()->fire("onLoginFailed", array($this->phoneNumber, $node->getChild(0)->getTag())); } elseif ($node->getTag() == "success") { if ($node->getAttribute("status") == "active") { $this->loginStatus = Constants::CONNECTED_STATUS; $challengeData = $node->getData(); file_put_contents($this->challengeFilename, $challengeData); $this->writer->setKey($this->outputKey); $this->eventManager()->fire("onLoginSuccess", array($this->phoneNumber, $node->getAttribute("kind"), $node->getAttribute("status"), $node->getAttribute("creation"), $node->getAttribute("expiration"))); } elseif ($node->getAttribute("status") == "expired") { $this->eventManager()->fire("onAccountExpired", array($this->phoneNumber, $node->getAttribute("kind"), $node->getAttribute("status"), $node->getAttribute("creation"), $node->getAttribute("expiration"))); } } elseif ($node->getTag() == 'ack' && $node->getAttribute("class") == "message") { $this->eventManager()->fire("onMessageReceivedServer", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('class'), $node->getAttribute('t'))); } elseif ($node->getTag() == 'receipt') { if ($node->hasChild("list")) { foreach ($node->getChild("list")->getChildren() as $child) { $this->eventManager()->fire("onMessageReceivedClient", array($this->phoneNumber, $node->getAttribute('from'), $child->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('participant'))); } } $this->eventManager()->fire("onMessageReceivedClient", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('participant'))); $this->sendAck($node, 'receipt'); } if ($node->getTag() == "message") { array_push($this->messageQueue, $node); if ($node->hasChild('x') && $this->lastId == $node->getAttribute('id')) { $this->sendNextMessage(); } if ($this->newMsgBind && ($node->getChild('body') || $node->getChild('media'))) { $this->newMsgBind->process($node); } if ($node->getAttribute("type") == "text" && $node->getChild('body') != null) { $author = $node->getAttribute("participant"); if ($autoReceipt) { $this->sendReceipt($node, $type, $author); } if ($author == "") { //private chat message $this->eventManager()->fire("onGetMessage", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute("notify"), $node->getChild("body")->getData())); if ($this->messageStore !== null) { $this->messageStore->saveMessage(ExtractNumber($node->getAttribute('from')), $this->phoneNumber, $node->getChild("body")->getData(), $node->getAttribute('id'), $node->getAttribute('t')); } } else { //group chat message $this->eventManager()->fire("onGetGroupMessage", array($this->phoneNumber, $node->getAttribute('from'), $author, $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute("notify"), $node->getChild("body")->getData())); if ($this->messageStore !== null) { $this->messageStore->saveMessage($author, $node->getAttribute('from'), $node->getChild("body")->getData(), $node->getAttribute('id'), $node->getAttribute('t')); } } } if ($node->getAttribute("type") == "text" && $node->getChild(0)->getTag() == 'enc') { // TODO if ($autoReceipt) { $this->sendReceipt($node, $type); } } if ($node->getAttribute("type") == "media" && $node->getChild('media') != null) { if ($node->getChild("media")->getAttribute('type') == 'image') { if ($node->getAttribute("participant") == null) { $this->eventManager()->fire("onGetImage", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('notify'), $node->getChild("media")->getAttribute('size'), $node->getChild("media")->getAttribute('url'), $node->getChild("media")->getAttribute('file'), $node->getChild("media")->getAttribute('mimetype'), $node->getChild("media")->getAttribute('filehash'), $node->getChild("media")->getAttribute('width'), $node->getChild("media")->getAttribute('height'), $node->getChild("media")->getData(), $node->getChild("media")->getAttribute('caption'))); } else { $this->eventManager()->fire("onGetGroupImage", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('participant'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('notify'), $node->getChild("media")->getAttribute('size'), $node->getChild("media")->getAttribute('url'), $node->getChild("media")->getAttribute('file'), $node->getChild("media")->getAttribute('mimetype'), $node->getChild("media")->getAttribute('filehash'), $node->getChild("media")->getAttribute('width'), $node->getChild("media")->getAttribute('height'), $node->getChild("media")->getData(), $node->getChild("media")->getAttribute('caption'))); } } elseif ($node->getChild("media")->getAttribute('type') == 'video') { if ($node->getAttribute("participant") == null) { $this->eventManager()->fire("onGetVideo", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('notify'), $node->getChild("media")->getAttribute('url'), $node->getChild("media")->getAttribute('file'), $node->getChild("media")->getAttribute('size'), $node->getChild("media")->getAttribute('mimetype'), $node->getChild("media")->getAttribute('filehash'), $node->getChild("media")->getAttribute('duration'), $node->getChild("media")->getAttribute('vcodec'), $node->getChild("media")->getAttribute('acodec'), $node->getChild("media")->getData(), $node->getChild("media")->getAttribute('caption'), $node->getChild("media")->getAttribute('width'), $node->getChild("media")->getAttribute('height'), $node->getChild("media")->getAttribute('fps'), $node->getChild("media")->getAttribute('vbitrate'), $node->getChild("media")->getAttribute('asampfreq'), $node->getChild("media")->getAttribute('asampfmt'), $node->getChild("media")->getAttribute('abitrate'))); } else { $this->eventManager()->fire("onGetGroupVideo", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('participant'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('notify'), $node->getChild("media")->getAttribute('url'), $node->getChild("media")->getAttribute('file'), $node->getChild("media")->getAttribute('size'), $node->getChild("media")->getAttribute('mimetype'), $node->getChild("media")->getAttribute('filehash'), $node->getChild("media")->getAttribute('duration'), $node->getChild("media")->getAttribute('vcodec'), $node->getChild("media")->getAttribute('acodec'), $node->getChild("media")->getData(), $node->getChild("media")->getAttribute('caption'), $node->getChild("media")->getAttribute('width'), $node->getChild("media")->getAttribute('height'), $node->getChild("media")->getAttribute('fps'), $node->getChild("media")->getAttribute('vbitrate'), $node->getChild("media")->getAttribute('asampfreq'), $node->getChild("media")->getAttribute('asampfmt'), $node->getChild("media")->getAttribute('abitrate'))); } } elseif ($node->getChild("media")->getAttribute('type') == 'audio') { $author = $node->getAttribute("participant"); $this->eventManager()->fire("onGetAudio", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('notify'), $node->getChild("media")->getAttribute('size'), $node->getChild("media")->getAttribute('url'), $node->getChild("media")->getAttribute('file'), $node->getChild("media")->getAttribute('mimetype'), $node->getChild("media")->getAttribute('filehash'), $node->getChild("media")->getAttribute('seconds'), $node->getChild("media")->getAttribute('acodec'), $author)); } elseif ($node->getChild("media")->getAttribute('type') == 'vcard') { if ($node->getChild("media")->hasChild('vcard')) { $name = $node->getChild("media")->getChild("vcard")->getAttribute('name'); $data = $node->getChild("media")->getChild("vcard")->getData(); } else { $name = "NO_NAME"; $data = $node->getChild("media")->getData(); } $author = $node->getAttribute("participant"); $this->eventManager()->fire("onGetvCard", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('notify'), $name, $data, $author)); } elseif ($node->getChild("media")->getAttribute('type') == 'location') { $url = $node->getChild("media")->getAttribute('url'); $name = $node->getChild("media")->getAttribute('name'); $author = $node->getAttribute("participant"); $this->eventManager()->fire("onGetLocation", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('notify'), $name, $node->getChild("media")->getAttribute('longitude'), $node->getChild("media")->getAttribute('latitude'), $url, $node->getChild("media")->getData(), $author)); } if ($autoReceipt) { $this->sendReceipt($node, $type); } } if ($node->getChild('received') != null) { $this->eventManager()->fire("onMessageReceivedClient", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('participant'))); } } if ($node->getTag() == "presence" && $node->getAttribute("status") == "dirty") { //clear dirty $categories = array(); if (count($node->getChildren()) > 0) { foreach ($node->getChildren() as $child) { if ($child->getTag() == "category") { $categories[] = $child->getAttribute("name"); } } } $this->sendClearDirty($categories); } if (strcmp($node->getTag(), "presence") == 0 && strncmp($node->getAttribute('from'), $this->phoneNumber, strlen($this->phoneNumber)) != 0 && strpos($node->getAttribute('from'), "-") === false) { $presence = array(); if ($node->getAttribute('type') == null) { $this->eventManager()->fire("onPresenceAvailable", array($this->phoneNumber, $node->getAttribute('from'))); } else { $this->eventManager()->fire("onPresenceUnavailable", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('last'))); } } if ($node->getTag() == "presence" && strncmp($node->getAttribute('from'), $this->phoneNumber, strlen($this->phoneNumber)) != 0 && strpos($node->getAttribute('from'), "-") !== false && $node->getAttribute('type') != null) { $groupId = Constants::parseJID($node->getAttribute('from')); if ($node->getAttribute('add') != null) { $this->eventManager()->fire("onGroupsParticipantsAdd", array($this->phoneNumber, $groupId, Constants::parseJID($node->getAttribute('add')))); } elseif ($node->getAttribute('remove') != null) { $this->eventManager()->fire("onGroupsParticipantsRemove", array($this->phoneNumber, $groupId, Constants::parseJID($node->getAttribute('remove')))); } } if (strcmp($node->getTag(), "chatstate") == 0 && strncmp($node->getAttribute('from'), $this->phoneNumber, strlen($this->phoneNumber)) != 0 && strpos($node->getAttribute('from'), "-") === false) { if ($node->getChild(0)->getTag() == "composing") { $this->eventManager()->fire("onMessageComposing", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), "composing", $node->getAttribute('t'))); } else { $this->eventManager()->fire("onMessagePaused", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), "paused", $node->getAttribute('t'))); } } if ($node->getTag() == "iq" && $node->getAttribute('type') == "get" && $node->getAttribute('xmlns') == "urn:xmpp:ping") { $this->eventManager()->fire("onPing", array($this->phoneNumber, $node->getAttribute('id'))); $this->sendPong($node->getAttribute('id')); } if ($node->getTag() == "iq" && $node->getChild("sync") != null) { //sync result $sync = $node->getChild('sync'); $existing = $sync->getChild("in"); $nonexisting = $sync->getChild("out"); //process existing first $existingUsers = array(); if (!empty($existing)) { foreach ($existing->getChildren() as $child) { $existingUsers[$child->getData()] = $child->getAttribute("jid"); } } //now process failed numbers $failedNumbers = array(); if (!empty($nonexisting)) { foreach ($nonexisting->getChildren() as $child) { $failedNumbers[] = str_replace('+', '', $child->getData()); } } $index = $sync->getAttribute("index"); $result = new SyncResult($index, $sync->getAttribute("sid"), $existingUsers, $failedNumbers); $this->eventManager()->fire("onGetSyncResult", array($result)); } if ($node->getTag() == "receipt") { $this->eventManager()->fire("onGetReceipt", array($node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('offline'), $node->getAttribute('retry'))); } if ($node->getTag() == "iq" && $node->getAttribute('type') == "result") { if ($node->getChild("query") != null) { if (isset($this->nodeId['privacy']) && $this->nodeId['privacy'] == $node->getAttribute('id')) { $listChild = $node->getChild(0)->getChild(0); foreach ($listChild->getChildren() as $child) { $blockedJids[] = $child->getAttribute('value'); } $this->eventManager()->fire("onGetPrivacyBlockedList", array($this->phoneNumber, $blockedJids)); return; } $this->eventManager()->fire("onGetRequestLastSeen", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getChild(0)->getAttribute('seconds'))); } if ($node->getChild("props") != null) { //server properties $props = array(); foreach ($node->getChild(0)->getChildren() as $child) { $props[$child->getAttribute("name")] = $child->getAttribute("value"); } $this->eventManager()->fire("onGetServerProperties", array($this->phoneNumber, $node->getChild(0)->getAttribute("version"), $props)); } if ($node->getChild("picture") != null) { $this->eventManager()->fire("onGetProfilePicture", array($this->phoneNumber, $node->getAttribute("from"), $node->getChild("picture")->getAttribute("type"), $node->getChild("picture")->getData())); } if ($node->getChild("media") != null || $node->getChild("duplicate") != null) { $this->processUploadResponse($node); } if (strpos($node->getAttribute("from"), Constants::WHATSAPP_GROUP_SERVER) !== false) { //There are multiple types of Group reponses. Also a valid group response can have NO children. //Events fired depend on text in the ID field. $groupList = array(); $groupNodes = array(); if ($node->getChild(0) != null && $node->getChild(0)->getChildren() != null) { foreach ($node->getChild(0)->getChildren() as $child) { $groupList[] = $child->getAttributes(); $groupNodes[] = $child; } } if (isset($this->nodeId['groupcreate']) && $this->nodeId['groupcreate'] == $node->getAttribute('id')) { $this->groupId = $node->getChild(0)->getAttribute('id'); $this->eventManager()->fire("onGroupsChatCreate", array($this->phoneNumber, $this->groupId)); } if (isset($this->nodeId['leavegroup']) && $this->nodeId['leavegroup'] == $node->getAttribute('id')) { $this->groupId = $node->getChild(0)->getChild(0)->getAttribute('id'); $this->eventManager()->fire("onGroupsChatEnd", array($this->phoneNumber, $this->groupId)); } if (isset($this->nodeId['getgroups']) && $this->nodeId['getgroups'] == $node->getAttribute('id')) { $this->eventManager()->fire("onGetGroups", array($this->phoneNumber, $groupList)); //getGroups returns a array of nodes which are exactly the same as from getGroupV2Info //so lets call this event, we have all data at hand, no need to call getGroupV2Info for every //group we are interested foreach ($groupNodes as $groupNode) { $this->handleGroupV2InfoResponse($groupNode, true); } } if (isset($this->nodeId['get_groupv2_info']) && $this->nodeId['get_groupv2_info'] == $node->getAttribute('id')) { $groupChild = $node->getChild(0); if ($groupChild != null) { $this->handleGroupV2InfoResponse($groupChild); } } } if (isset($this->nodeId['get_lists']) && $this->nodeId['get_lists'] == $node->getAttribute('id')) { $broadcastLists = array(); if ($node->getChild(0) != null) { $childArray = $node->getChildren(); foreach ($childArray as $list) { if ($list->getChildren() != null) { foreach ($list->getChildren() as $sublist) { $id = $sublist->getAttribute("id"); $name = $sublist->getAttribute("name"); $broadcastLists[$id]['name'] = $name; $recipients = array(); foreach ($sublist->getChildren() as $recipient) { array_push($recipients, $recipient->getAttribute('jid')); } $broadcastLists[$id]['recipients'] = $recipients; } } } } $this->eventManager()->fire("onGetBroadcastLists", array($this->phoneNumber, $broadcastLists)); } if ($node->getChild("pricing") != null) { $this->eventManager()->fire("onGetServicePricing", array($this->phoneNumber, $node->getChild(0)->getAttribute("price"), $node->getChild(0)->getAttribute("cost"), $node->getChild(0)->getAttribute("currency"), $node->getChild(0)->getAttribute("expiration"))); } if ($node->getChild("extend") != null) { $this->eventManager()->fire("onGetExtendAccount", array($this->phoneNumber, $node->getChild("account")->getAttribute("kind"), $node->getChild("account")->getAttribute("status"), $node->getChild("account")->getAttribute("creation"), $node->getChild("account")->getAttribute("expiration"))); } if ($node->getChild("normalize") != null) { $this->eventManager()->fire("onGetNormalizedJid", array($this->phoneNumber, $node->getChild(0)->getAttribute("result"))); } if ($node->getChild("status") != null) { $child = $node->getChild("status"); foreach ($child->getChildren() as $status) { $this->eventManager()->fire("onGetStatus", array($this->phoneNumber, $status->getAttribute("jid"), "requested", $node->getAttribute("id"), $status->getAttribute("t"), $status->getData())); } } } if ($node->getTag() == "iq" && $node->getAttribute('type') == "error") { $errorType = null; foreach ($this->nodeId as $type => $nodeID) { if ($nodeID == $node->getAttribute('id')) { $errorType = $type; break; } } $this->eventManager()->fire("onGetError", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getChild(0), $errorType)); } if ($node->getTag() == "message" && $node->getAttribute('type') == "media" && $node->getChild(0)->getAttribute('type') == "image") { $msgId = $this->createIqId(); $ackNode = new ProtocolNode("ack", array("url" => $node->getChild(0)->getAttribute('url')), null, null); $iqNode = new ProtocolNode("iq", array("id" => $msgId, "xmlns" => "w:m", "type" => "set", "to" => Constants::WHATSAPP_SERVER), array($ackNode), null); $this->sendNode($iqNode); } $children = $node->getChild(0); if ($node->getTag() == "stream:error" && !empty($children) && $node->getChild(0)->getTag() == "system-shutdown") { $this->eventManager()->fire("onStreamError", array($node->getChild(0)->getTag())); } if ($node->getTag() == "stream:error") { $this->eventManager()->fire("onStreamError", array($node->getChild(0)->getTag())); } if ($node->getTag() == "notification") { $name = $node->getAttribute("notify"); $type = $node->getAttribute("type"); switch ($type) { case "status": $this->eventManager()->fire("onGetStatus", array($this->phoneNumber, $node->getAttribute("from"), $node->getChild(0)->getTag(), $node->getAttribute("id"), $node->getAttribute("t"), $node->getChild(0)->getData())); break; case "picture": if ($node->hasChild('set')) { $this->eventManager()->fire("onProfilePictureChanged", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('t'))); } else { if ($node->hasChild('delete')) { $this->eventManager()->fire("onProfilePictureDeleted", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('t'))); } } //TODO break; case "contacts": $notification = $node->getChild(0)->getTag(); if ($notification == 'add') { $this->eventManager()->fire("onNumberWasAdded", array($this->phoneNumber, $node->getChild(0)->getAttribute('jid'))); } elseif ($notification == 'remove') { $this->eventManager()->fire("onNumberWasRemoved", array($this->phoneNumber, $node->getChild(0)->getAttribute('jid'))); } elseif ($notification == 'update') { $this->eventManager()->fire("onNumberWasUpdated", array($this->phoneNumber, $node->getChild(0)->getAttribute('jid'))); } break; case "encrypt": $value = $node->getChild(0)->getAttribute('value'); if (is_numeric($value)) { $this->eventManager()->fire("onGetKeysLeft", array($this->phoneNumber, $node->getChild(0)->getAttribute('value'))); } else { echo "Corrupt Stream: value " . $value . "is not numeric"; } break; case "w:gp2": if ($node->hasChild('remove')) { if ($node->getChild(0)->hasChild('participant')) { $this->eventManager()->fire("onGroupsParticipantsRemove", array($this->phoneNumber, $node->getAttribute('from'), $node->getChild(0)->getChild(0)->getAttribute('jid'))); } } else { if ($node->hasChild('add')) { $this->eventManager()->fire("onGroupsParticipantsAdd", array($this->phoneNumber, $node->getAttribute('from'), $node->getChild(0)->getChild(0)->getAttribute('jid'))); } else { if ($node->hasChild('create')) { $groupMembers = array(); foreach ($node->getChild(0)->getChild(0)->getChildren() as $cn) { $groupMembers[] = $cn->getAttribute('jid'); } $this->eventManager()->fire("onGroupisCreated", array($this->phoneNumber, $node->getChild(0)->getChild(0)->getAttribute('creator'), $node->getChild(0)->getChild(0)->getAttribute('id'), $node->getChild(0)->getChild(0)->getAttribute('subject'), $node->getAttribute('participant'), $node->getChild(0)->getChild(0)->getAttribute('creation'), $groupMembers)); } else { if ($node->hasChild('subject')) { $this->eventManager()->fire("onGetGroupsSubject", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('t'), $node->getAttribute('participant'), $node->getAttribute('notify'), $node->getChild(0)->getAttribute('subject'))); } else { if ($node->hasChild('promote')) { $promotedJIDs = array(); foreach ($node->getChild(0)->getChildren() as $cn) { $promotedJIDs[] = $cn->getAttribute('jid'); } $this->eventManager()->fire("onGroupsParticipantsPromote", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('t'), $node->getAttribute('participant'), $node->getAttribute('notify'), $promotedJIDs)); } else { if ($node->hasChild('modify')) { $this->eventManager()->fire("onGroupsParticipantChangedNumber", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('t'), $node->getAttribute('participant'), $node->getAttribute('notify'), $node->getChild(0)->getChild(0)->getAttribute('jid'))); } } } } } } break; case "account": if ($node->getChild(0)->getAttribute('author') == "") { $author = "Paypal"; } else { $author = $node->getChild(0)->getAttribute('author'); } $this->eventManager()->fire("onPaidAccount", array($this->phoneNumber, $author, $node->getChild(0)->getChild(0)->getAttribute('kind'), $node->getChild(0)->getChild(0)->getAttribute('status'), $node->getChild(0)->getChild(0)->getAttribute('creation'), $node->getChild(0)->getChild(0)->getAttribute('expiration'))); break; case "features": if ($node->getChild(0)->getChild(0) == "encrypt") { $this->eventManager()->fire("onGetFeature", array($this->phoneNumber, $node->getAttribute('from'), $node->getChild(0)->getChild(0)->getAttribute('value'))); } break; case "web": if ($node->getChild(0)->getTag() == 'action' && $node->getChild(0)->getAttribute('type') == 'sync') { $data = $node->getChild(0)->getChildren(); $this->eventManager()->fire("onWebSync", array($this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $data[0]->getData(), $data[1]->getData(), $data[2]->getData())); } break; default: throw new Exception("Method {$type} not implemented"); } $this->sendAck($node, 'notification'); } if ($node->getTag() == "call") { if ($node->getChild(0)->getTag() == "offer") { $callId = $node->getChild(0)->getAttribute("call-id"); $this->sendReceipt($node, null, null, $callId); $this->eventManager()->fire("onCallReceived", array($this->phoneNumber, $node->getAttribute("from"), $node->getAttribute("id"), $node->getAttribute("notify"), $node->getAttribute("t"), $node->getChild(0)->getAttribute("call-id"))); } else { $this->sendAck($node, 'call'); } } if ($node->getTag() == "ib") { foreach ($node->getChildren() as $child) { switch ($child->getTag()) { case "dirty": $this->sendClearDirty(array($child->getAttribute("type"))); break; case "account": $this->eventManager()->fire("onPaymentRecieved", array($this->phoneNumber, $child->getAttribute("kind"), $child->getAttribute("status"), $child->getAttribute("creation"), $child->getAttribute("expiration"))); break; case "offline": break; default: throw new Exception("ib handler for " . $child->getTag() . " not implemented"); } } } // Disconnect socket on stream error. if ($node->getTag() == "stream:error") { $this->disconnect(); } }
public function unsetPendingNode($jid) { unset($this->pending_nodes[ExtractNumber($jid)]); }
public function process($node) { if ($node->getAttribute("type") == 'text') { $text = $node->getChild('body'); $text = $text->getData(); $number = ExtractNumber($node->getAttribute("from")); $nickname = $number; echo "\n- " . $nickname . ": " . $text . " " . date('H:i') . "\n"; } }
public function decryptMessage($from, $ciphertext, $type, $id, $t, $retry_from = null, $skip_unpad = false) { $version = '1'; $this->parent->debugPrint("\n-> Decrypted Message: "); if ($type == 'pkmsg') { if (in_array(ExtractNumber($from), $this->parent->getv2Jids())) { $version = '2'; } try { $preKeyWhisperMessage = new PreKeyWhisperMessage(null, null, null, null, null, null, null, $ciphertext); $sessionCipher = $this->parent->getSessionCipher(ExtractNumber($from)); $plaintext = $sessionCipher->decryptPkmsg($preKeyWhisperMessage); if ($version == '2' && !$skip_unpad) { $plaintext = unpadV2Plaintext($plaintext); } $this->parent->debugPrint(parseText($plaintext) . "\n\n"); return $plaintext; } catch (Exception $e) { if ($e instanceof UntrustedIdentityException) { $this->parent->getAxolotlStore()->clearRecipient(ExtractNumber($from)); } $this->parent->debugPrint($e->getMessage() . ' - ' . $e->getFile() . ' - ' . $e->getLine()); // if ($e->getMessage() != "Null values!"){ $this->parent->debugPrint("Message {$id} could not be decrypted, sending retry.\n\n"); $participant = null; if ($retry_from != null) { if (strpos($retry_from, '-') !== false) { $participant = $from; } $from = $retry_from; } //$this->sendRetry($from, $id, $t, $participant); return false; //} } } elseif ($type == 'msg') { if (in_array(ExtractNumber($from), $this->parent->getv2Jids())) { $version = '2'; } try { $whisperMessage = new WhisperMessage(null, null, null, null, null, null, null, null, $ciphertext); $sessionCipher = $this->parent->getSessionCipher(ExtractNumber($from)); $plaintext = $sessionCipher->decryptMsg($whisperMessage); if ($version == '2' && !$skip_unpad) { $plaintext = unpadV2Plaintext($plaintext); } $this->parent->debugPrint(parseText($plaintext) . "\n\n"); return $plaintext; } catch (Exception $e) { $this->parent->debugPrint($e->getMessage() . ' - ' . $e->getFile() . ' - ' . $e->getLine()); $this->parent->debugPrint("Message {$id} could not be decrypted, sending retry.\n\n"); if ($retry_from != null) { $from = $retry_from; } //$this->sendRetry($from, $id, $t); return false; } } elseif ($type == 'skmsg') { if (in_array($from[1], $this->parent->v2Jids)) { $version = '2'; } try { $groupCipher = $this->parent->getGroupCipher(ExtractNumber($from[0]) . ':' . $from[1]); $plaintext = $groupCipher->decrypt($ciphertext); if ($version == '2' && !$skip_unpad) { $plaintext = unpadV2Plaintext($plaintext); } $this->parent->debugPrint("Message {$id} decrypted to " . parseText($plaintext) . "\n\n"); return $plaintext; } catch (Exception $e) { $this->parent->debugPrint($e->getMessage() . ' - ' . $e->getFile() . ' - ' . $e->getLine()); if ($retry_from != null) { $from = $retry_from; } $this->parent->sendRetry($this->node, $this->parent->getJID($from[0]), $id, $t); return false; } } }
public function Process() { if ($this->node->getChild("query") != null) { if (isset($this->parent->getNodeId()['privacy']) && $this->parent->getNodeId()['privacy'] == $this->node->getAttribute('id')) { $listChild = $this->node->getChild(0)->getChild(0); foreach ($listChild->getChildren() as $child) { $blockedJids[] = $child->getAttribute('value'); } $this->parent->eventManager()->fire("onGetPrivacyBlockedList", array($this->phoneNumber, $blockedJids)); return; } } if ($this->node->getAttribute('type') == "get" && $this->node->getAttribute('xmlns') == "urn:xmpp:ping") { $this->parent->eventManager()->fire("onPing", array($this->phoneNumber, $this->node->getAttribute('id'))); $this->parent->sendPong($this->node->getAttribute('id')); } if ($this->node->getChild("sync") != null) { //sync result $sync = $this->node->getChild('sync'); $existing = $sync->getChild("in"); $nonexisting = $sync->getChild("out"); //process existing first $existingUsers = array(); if (!empty($existing)) { foreach ($existing->getChildren() as $child) { $existingUsers[$child->getData()] = $child->getAttribute("jid"); } } //now process failed numbers $failedNumbers = array(); if (!empty($nonexisting)) { foreach ($nonexisting->getChildren() as $child) { $failedNumbers[] = str_replace('+', '', $child->getData()); } } $index = $sync->getAttribute("index"); $result = new SyncResult($index, $sync->getAttribute("sid"), $existingUsers, $failedNumbers); $this->parent->eventManager()->fire("onGetSyncResult", array($result)); } if ($this->node->getChild("props") != null) { //server properties $props = array(); foreach ($this->node->getChild(0)->getChildren() as $child) { $props[$child->getAttribute("name")] = $child->getAttribute("value"); } $this->parent->eventManager()->fire("onGetServerProperties", array($this->phoneNumber, $this->node->getChild(0)->getAttribute("version"), $props)); } if ($this->node->getChild("picture") != null) { $this->parent->eventManager()->fire("onGetProfilePicture", array($this->phoneNumber, $this->node->getAttribute("from"), $this->node->getChild("picture")->getAttribute("type"), $this->node->getChild("picture")->getData())); } if ($this->node->getChild("media") != null || $this->node->getChild("duplicate") != null) { $this->parent->processUploadResponse($this->node); } if (strpos($this->node->getAttribute("from"), Constants::WHATSAPP_GROUP_SERVER) !== false) { //There are multiple types of Group reponses. Also a valid group response can have NO children. //Events fired depend on text in the ID field. $groupList = array(); $groupNodes = array(); if ($this->node->getChild(0) != null && $this->node->getChild(0)->getChildren() != null) { foreach ($this->node->getChild(0)->getChildren() as $child) { $groupList[] = $child->getAttributes(); $groupNodes[] = $child; } } if (isset($this->parent->getNodeId()['groupcreate']) && $this->parent->getNodeId()['groupcreate'] == $this->node->getAttribute('id')) { $this->parent->setGroupId($this->node->getChild(0)->getAttribute('id')); $this->parent->eventManager()->fire("onGroupsChatCreate", array($this->phoneNumber, $this->node->getChild(0)->getAttribute('id'))); } if (isset($this->parent->getNodeId()['leavegroup']) && $this->parent->getNodeId()['leavegroup'] == $this->node->getAttribute('id')) { $this->parent->setGroupId($this->node->getChild(0)->getChild(0)->getAttribute('id')); $this->parent->eventManager()->fire("onGroupsChatEnd", array($this->phoneNumber, $this->node->getChild(0)->getChild(0)->getAttribute('id'))); } if (isset($this->parent->getNodeId()['getgroups']) && $this->parent->getNodeId()['getgroups'] == $this->node->getAttribute('id')) { $this->parent->eventManager()->fire("onGetGroups", array($this->phoneNumber, $groupList)); //getGroups returns a array of nodes which are exactly the same as from getGroupV2Info //so lets call this event, we have all data at hand, no need to call getGroupV2Info for every //group we are interested foreach ($groupNodes as $groupNode) { $this->handleGroupV2InfoResponse($groupNode, true); } } if (isset($this->parent->getNodeId()['get_groupv2_info']) && $this->parent->getNodeId()['get_groupv2_info'] == $this->node->getAttribute('id')) { $groupChild = $this->node->getChild(0); if ($groupChild != null) { $this->handleGroupV2InfoResponse($groupChild); } } } if (isset($this->parent->getNodeId()['get_lists']) && $this->parent->getNodeId()['get_lists'] == $this->node->getAttribute('id')) { $broadcastLists = array(); if ($this->node->getChild(0) != null) { $childArray = $this->node->getChildren(); foreach ($childArray as $list) { if ($list->getChildren() != null) { foreach ($list->getChildren() as $sublist) { $id = $sublist->getAttribute("id"); $name = $sublist->getAttribute("name"); $broadcastLists[$id]['name'] = $name; $recipients = array(); foreach ($sublist->getChildren() as $recipient) { array_push($recipients, $recipient->getAttribute('jid')); } $broadcastLists[$id]['recipients'] = $recipients; } } } } $this->parent->eventManager()->fire("onGetBroadcastLists", array($this->phoneNumber, $broadcastLists)); } if ($this->node->getChild("pricing") != null) { $this->parent->eventManager()->fire("onGetServicePricing", array($this->phoneNumber, $this->node->getChild(0)->getAttribute("price"), $this->node->getChild(0)->getAttribute("cost"), $this->node->getChild(0)->getAttribute("currency"), $this->node->getChild(0)->getAttribute("expiration"))); } if ($this->node->getChild("extend") != null) { $this->parent->eventManager()->fire("onGetExtendAccount", array($this->phoneNumber, $this->node->getChild("account")->getAttribute("kind"), $this->node->getChild("account")->getAttribute("status"), $this->node->getChild("account")->getAttribute("creation"), $this->node->getChild("account")->getAttribute("expiration"))); } if ($this->node->getChild("normalize") != null) { $this->parent->eventManager()->fire("onGetNormalizedJid", array($this->phoneNumber, $this->node->getChild(0)->getAttribute("result"))); } if ($this->node->getChild("status") != null) { $child = $this->node->getChild("status"); $childs = $child->getChildren(); if (isset($childs) && !is_null($childs)) { foreach ($childs as $status) { $this->parent->eventManager()->fire("onGetStatus", array($this->phoneNumber, $status->getAttribute("jid"), "requested", $this->node->getAttribute("id"), $status->getAttribute("t"), $status->getData())); } } } if ($this->node->getAttribute('type') == "error" && $this->node->getChild("error") != null) { $errorType = null; $this->parent->logFile('error', 'Iq error with {id} id', array('id' => $this->node->getAttribute('id'))); foreach ($this->parent->getNodeId() as $type => $nodeID) { if ($nodeID == $this->node->getAttribute('id')) { $errorType = $type; break; } } $nodeIds = $this->parent->getNodeId(); if (isset($nodeIds['sendcipherKeys']) && isset($nodeIds['sendcipherKeys']) == $this->node->getAttribute('id') && $this->node->getChild("error")->getAttribute('code') == "406") { $this->parent->sendSetPreKeys(); } $this->parent->eventManager()->fire("onGetError", array($this->phoneNumber, $this->node->getAttribute('from'), $this->node->getAttribute('id'), $this->node->getChild(0), $errorType)); } if (isset($this->parent->getNodeId()['cipherKeys']) && $this->parent->getNodeId()['cipherKeys'] == $this->node->getAttribute('id')) { $users = $this->node->getChild(0)->getChildren(); foreach ($users as $user) { $jid = $user->getAttribute("jid"); $registrationId = deAdjustId($user->getChild('registration')->getData()); $identityKey = new IdentityKey(new DjbECPublicKey($user->getChild('identity')->getData())); $signedPreKeyId = deAdjustId($user->getChild('skey')->getChild('id')->getData()); $signedPreKeyPub = new DjbECPublicKey($user->getChild('skey')->getChild('value')->getData()); $signedPreKeySig = $user->getChild('skey')->getChild('signature')->getData(); $preKeyId = deAdjustId($user->getChild('key')->getChild('id')->getData()); $preKeyPublic = new DjbECPublicKey($user->getChild('key')->getChild('value')->getData()); $preKeyBundle = new PreKeyBundle($registrationId, 1, $preKeyId, $preKeyPublic, $signedPreKeyId, $signedPreKeyPub, $signedPreKeySig, $identityKey); $sessionBuilder = new SessionBuilder($this->parent->getAxolotlStore(), $this->parent->getAxolotlStore(), $this->parent->getAxolotlStore(), $this->parent->getAxolotlStore(), ExtractNumber($jid), 1); $sessionBuilder->processPreKeyBundle($preKeyBundle); if (isset($this->parent->getPendingNodes()[ExtractNumber($jid)])) { foreach ($this->parent->getPendingNodes()[ExtractNumber($jid)] as $pendingNode) { $msgHandler = new MessageHandler($this->parent, $pendingNode); $msgHandler->Process(); } unset($this->parent->getPendingNodes()[ExtractNumber($jid)]); } $this->parent->sendPendingMessages($jid); } } }
public function process($node) { if ($node->getAttribute('type') == 'text') { $text = $node->getChild('body'); $text = $text->getData(); $number = ExtractNumber($node->getAttribute('from')); $nickname = findNicknameByPhone($number); echo "\n- " . $nickname . ': ' . $text . ' ' . date('H:i') . "\n"; } }
function decryptMessage($from, $ciphertext, $type, $id, $t, $retry_from = null, $skip_unpad = false) { $version = "1"; $this->parent->debugPrint("\n-> Decrypted Message: "); if ($type == "pkmsg") { if (in_array(ExtractNumber($from), $this->parent->getv2Jids())) { $version = "2"; } try { $preKeyWhisperMessage = new PreKeyWhisperMessage(null, null, null, null, null, null, null, $ciphertext); $sessionCipher = $this->parent->getSessionCipher(ExtractNumber($from)); $plaintext = $sessionCipher->decryptPkmsg($preKeyWhisperMessage); if ($version == "2" && !$skip_unpad) { $plaintext = unpadV2Plaintext($plaintext); } $this->parent->debugPrint(parseText($plaintext) . "\n\n"); return $plaintext; } catch (Exception $e) { $this->parent->debugPrint($e->getMessage() . " - " . $e->getFile() . " - " . $e->getLine()); if ($e->getMessage() != "Null values!") { $this->parent->debugPrint("Message {$id} could not be decrypted, sending retry.\n\n"); $participant = null; if ($retry_from != null) { if (strpos($retry_from, "-") !== false) { $participant = $from; } $from = $retry_from; } //$this->sendRetry($from, $id, $t, $participant); return false; } } } else { if ($type == "msg") { if (in_array(ExtractNumber($from), $this->parent->getv2Jids())) { $version = "2"; } try { $whisperMessage = new WhisperMessage(null, null, null, null, null, null, null, null, $ciphertext); $sessionCipher = $this->parent->getSessionCipher(ExtractNumber($from)); $plaintext = $sessionCipher->decryptMsg($whisperMessage); if ($version == "2" && !$skip_unpad) { $plaintext = unpadV2Plaintext($plaintext); } $this->parent->debugPrint(parseText($plaintext) . "\n\n"); return $plaintext; } catch (Exception $e) { $this->parent->debugPrint($e->getMessage() . " - " . $e->getFile() . " - " . $e->getLine()); $this->parent->debugPrint("Message {$id} could not be decrypted, sending retry.\n\n"); if ($retry_from != null) { $from = $retry_from; } //$this->sendRetry($from, $id, $t); return false; } } else { if ($type == "skmsg") { if (in_array($from[1], $this->v2Jids)) { $version = "2"; } try { $groupCipher = $this->parent->getGroupCipher(ExtractNumber($from[0]) . ":" . $from[1]); $plaintext = $groupCipher->decrypt($ciphertext); if ($version == "2" && !$skip_unpad) { $plaintext = unpadV2Plaintext($plaintext); } $this->parent->debugPrint("Message {$id} decrypted to " . parseText($plaintext) . "\n\n"); return $plaintext; } catch (Exception $e) { $this->parent->debugPrint($e->getMessage() . " - " . $e->getFile() . " - " . $e->getLine()); if ($retry_from != null) { $from = $retry_from; } $this->parent->sendRetry($this->parent->getJID($from[0]), $id, $t); return false; } } } } }
public function onGetAudio($mynumber, $from, $id, $type, $time, $name, $size, $url, $file, $mimeType, $fileHash, $duration, $acodec, $fromJID_ifGroup = null) { $number = ExtractNumber($from); $nickname = $number; $path = "data/media/{$nickname}/"; if (!file_exists($path)) { mkdir($path); } $filename = __DIR__ . DIRECTORY_SEPARATOR . $path . time() . ".jpg"; $data = file_get_contents($url); $fp = @fopen($filename, "w"); if ($fp) { fwrite($fp, $data); fclose($fp); } echo " < Received audio from {$nickname} >\n"; }
static function handleMessageReceived($mynumber, $from, $id, $type, $time, $name, $body) { $number = ExtractNumber($from); $contact = getUserInfo($number); if (!$contact) { addContact($number, $name); $contact = getUserInfo($number); $main_menu = GenieConstants::searchElement(GenieConstants::$MAIN_MENU_CONTEXT, GenieConstants::$REGISTRATION_MENU_CONTEXT, 'subMenu'); setMessageContext($main_menu, NULL, $contact['phone']); } //Handle ShortCut in request $requestArray = $data = preg_split('/\\s+/', $body); if ($requestArray[0][0] == '#') { self::handleShortCuts($contact, $requestArray); return; } $context = getMessageContext($number); $callBackFunction = 'initializeService'; if ($context['main_menu'] == -1) { self::handleGeneralConversation($body, $context, $contact); return; } if ($context['main_menu'] == 0) { if (GenieConstants::searchElement(GenieConstants::$MAIN_MENU_CONTEXT, $body, 'id') != NULL) { updateMessageContext($body, NULL, $contact['phone']); PubSub::publish(GenieConstants::$MAIN_MENU_CONTEXT[$body]->menuItem, $callBackFunction, $contact); } else { MessaggingController::sendMessage($contact, GenieConstants::$INVALID_SERVICE); MessaggingController::sendMessage($contact, GenieConstants::$MAIN_MENU_STRING); } return; } if ($context['main_menu'] != 0 && $context['main_menu'] != 8 && $context['sub_menu'] == NULL) { $subMenuKey = GenieConstants::searchElement(GenieConstants::$MAIN_MENU_CONTEXT[$context['main_menu']]->subMenu, $body, 'id'); $SubMenuDict = GenieConstants::$MAIN_MENU_CONTEXT[$context['main_menu']]->subMenu; if ($subMenuKey != NULL) { updateMessageContext($context['main_menu'], $body, $contact['phone']); $callBackFunction = $SubMenuDict[$subMenuKey]->callBackMethod; PubSub::publish(GenieConstants::$MAIN_MENU_CONTEXT[$context['main_menu']]->menuItem, $callBackFunction, $contact); } else { MessaggingController::sendMessage($contact, GenieConstants::$INVALID_SERVICE); PubSub::publish(GenieConstants::$MAIN_MENU_CONTEXT[$context['main_menu']]->menuItem, $callBackFunction, $contact); } return; } else { if ($context['main_menu'] != 0 && $context['main_menu'] != 8 && $context['sub_menu'] != NULL) { $SubMenuDict = GenieConstants::$MAIN_MENU_CONTEXT[$context['main_menu']]->subMenu; $callBackFunction = $SubMenuDict[$context['sub_menu']]->requestServer; PubSub::publish(GenieConstants::$MAIN_MENU_CONTEXT[$context['main_menu']]->menuItem, $callBackFunction, $contact, $body); return; } else { if ($context['sub_menu'] != NULL) { $callBackFunction = GenieConstants::$MAIN_MENU_CONTEXT[$context['main_menu']]->subMenu[$context['sub_menu']]->callBackMethod; } else { if (GenieConstants::$MAIN_MENU_CONTEXT[$context['main_menu']]->callBackMethod != NULL) { $callBackFunction = GenieConstants::$MAIN_MENU_CONTEXT[$context['main_menu']]->callBackMethod; } } } } PubSub::publish(GenieConstants::$MAIN_MENU_CONTEXT[$context['main_menu']]->menuItem, $callBackFunction, $contact, $body); }
/** * Will process the data from the server after it's been decrypted and parsed. * * This also provides a convenient method to use to unit test the event framework. * @param ProtocolNode $node * @param bool $autoReceipt * @param $type * * @throws \Exception */ protected function processInboundDataNode(ProtocolNode $node, $autoReceipt = true, $type = 'read') { $this->debugPrint($node->nodeString('rx ') . "\n"); $this->serverReceivedId = $node->getAttribute('id'); if ($node->getTag() == 'challenge') { $this->processChallenge($node); } elseif ($node->getTag() == 'failure') { $this->loginStatus = DISCONNECTED_STATUS; $this->eventsManager()->fire('onLoginFailed', [$this->phoneNumber, $node->getChild(0)->getTag()]); } elseif ($node->getTag() == 'success') { if ($node->getAttribute('status') == 'active') { $this->loginStatus = CONNECTED_STATUS; $challengeData = $node->getData(); file_put_contents($this->challengeFilename, $challengeData); $this->writer->setKey($this->outputKey); $this->eventsManager()->fire('onLoginSuccess', [$this->phoneNumber, $node->getAttribute('kind'), $node->getAttribute('status'), $node->getAttribute('creation'), $node->getAttribute('expiration')]); } elseif ($node->getAttribute('status') == 'expired') { $this->eventsManager()->fire('onAccountExpired', [$this->phoneNumber, $node->getAttribute('kind'), $node->getAttribute('status'), $node->getAttribute('creation'), $node->getAttribute('expiration')]); } } elseif ($node->getTag() == 'ack' && $node->getAttribute('class') == 'message') { $this->eventsManager()->fire('onMessageReceivedServer', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('class'), $node->getAttribute('t')]); } elseif ($node->getTag() == 'receipt') { if ($node->hasChild('list')) { foreach ($node->getChild('list')->getChildren() as $child) { $this->eventsManager()->fire('onMessageReceivedClient', [$this->phoneNumber, $node->getAttribute('from'), $child->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('participant')]); } } $this->eventsManager()->fire('onMessageReceivedClient', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('participant')]); $this->sendAck($node, 'receipt'); } if ($node->getTag() == 'message') { array_push($this->messageQueue, $node); if ($node->hasChild('x') && $this->lastId == $node->getAttribute('id')) { $this->sendNextMessage(); } if ($this->newMsgBind && ($node->getChild('body') || $node->getChild('media'))) { $this->newMsgBind->process($node); } if ($node->getAttribute('type') == 'text' && $node->getChild('body') != null) { $author = $node->getAttribute('participant'); if ($autoReceipt) { $this->sendReceipt($node, $type, $author); } if ($author == '') { //private chat message $this->eventsManager()->fire('onGetMessage', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('notify'), $node->getChild('body')->getData()]); if ($this->messageStore !== null) { $this->messageStore->saveMessage(ExtractNumber($node->getAttribute('from')), $this->phoneNumber, $node->getChild('body')->getData(), $node->getAttribute('id'), $node->getAttribute('t')); } } else { //group chat message $this->eventsManager()->fire('onGetGroupMessage', [$this->phoneNumber, $node->getAttribute('from'), $author, $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('notify'), $node->getChild('body')->getData()]); if ($this->messageStore !== null) { $this->messageStore->saveMessage($author, $node->getAttribute('from'), $node->getChild('body')->getData(), $node->getAttribute('id'), $node->getAttribute('t')); } } } if ($node->getAttribute('type') == 'text' && $node->getChild(0)->getTag() == 'enc') { // TODO if ($autoReceipt) { $this->sendReceipt($node, $type); } } if ($node->getAttribute('type') == 'media' && $node->getChild('media') != null) { if ($node->getChild('media')->getAttribute('type') == 'image') { if ($node->getAttribute('participant') == null) { $this->eventsManager()->fire('onGetImage', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('notify'), $node->getChild('media')->getAttribute('size'), $node->getChild('media')->getAttribute('url'), $node->getChild('media')->getAttribute('file'), $node->getChild('media')->getAttribute('mimetype'), $node->getChild('media')->getAttribute('filehash'), $node->getChild('media')->getAttribute('width'), $node->getChild('media')->getAttribute('height'), $node->getChild('media')->getData(), $node->getChild('media')->getAttribute('caption')]); } else { $this->eventsManager()->fire('onGetGroupImage', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('participant'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('notify'), $node->getChild('media')->getAttribute('size'), $node->getChild('media')->getAttribute('url'), $node->getChild('media')->getAttribute('file'), $node->getChild('media')->getAttribute('mimetype'), $node->getChild('media')->getAttribute('filehash'), $node->getChild('media')->getAttribute('width'), $node->getChild('media')->getAttribute('height'), $node->getChild('media')->getData(), $node->getChild('media')->getAttribute('caption')]); } } elseif ($node->getChild('media')->getAttribute('type') == 'video') { if ($node->getAttribute('participant') == null) { $this->eventsManager()->fire('onGetVideo', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('notify'), $node->getChild('media')->getAttribute('url'), $node->getChild('media')->getAttribute('file'), $node->getChild('media')->getAttribute('size'), $node->getChild('media')->getAttribute('mimetype'), $node->getChild('media')->getAttribute('filehash'), $node->getChild('media')->getAttribute('duration'), $node->getChild('media')->getAttribute('vcodec'), $node->getChild('media')->getAttribute('acodec'), $node->getChild('media')->getData(), $node->getChild('media')->getAttribute('caption')]); } else { $this->eventsManager()->fire('onGetGroupVideo', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('participant'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('notify'), $node->getChild('media')->getAttribute('url'), $node->getChild('media')->getAttribute('file'), $node->getChild('media')->getAttribute('size'), $node->getChild('media')->getAttribute('mimetype'), $node->getChild('media')->getAttribute('filehash'), $node->getChild('media')->getAttribute('duration'), $node->getChild('media')->getAttribute('vcodec'), $node->getChild('media')->getAttribute('acodec'), $node->getChild('media')->getData(), $node->getChild('media')->getAttribute('caption')]); } } elseif ($node->getChild('media')->getAttribute('type') == 'audio') { $author = $node->getAttribute('participant'); $this->eventsManager()->fire('onGetAudio', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('notify'), $node->getChild('media')->getAttribute('size'), $node->getChild('media')->getAttribute('url'), $node->getChild('media')->getAttribute('file'), $node->getChild('media')->getAttribute('mimetype'), $node->getChild('media')->getAttribute('filehash'), $node->getChild('media')->getAttribute('seconds'), $node->getChild('media')->getAttribute('acodec'), $author]); } elseif ($node->getChild('media')->getAttribute('type') == 'vcard') { if ($node->getChild('media')->hasChild('vcard')) { $name = $node->getChild('media')->getChild('vcard')->getAttribute('name'); $data = $node->getChild('media')->getChild('vcard')->getData(); } else { $name = 'NO_NAME'; $data = $node->getChild('media')->getData(); } $author = $node->getAttribute('participant'); $this->eventsManager()->fire('onGetvCard', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('notify'), $name, $data, $author]); } elseif ($node->getChild('media')->getAttribute('type') == 'location') { $url = $node->getChild('media')->getAttribute('url'); $name = $node->getChild('media')->getAttribute('name'); $author = $node->getAttribute('participant'); $this->eventsManager()->fire('onGetLocation', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('notify'), $name, $node->getChild('media')->getAttribute('longitude'), $node->getChild('media')->getAttribute('latitude'), $url, $node->getChild('media')->getData(), $author]); } if ($autoReceipt) { $this->sendReceipt($node, $type); } } if ($node->getChild('received') != null) { $this->eventsManager()->fire('onMessageReceivedClient', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('type'), $node->getAttribute('t'), $node->getAttribute('participant')]); } } if ($node->getTag() == 'presence' && $node->getAttribute('status') == 'dirty') { //clear dirty $categories = []; if (count($node->getChildren()) > 0) { foreach ($node->getChildren() as $child) { if ($child->getTag() == 'category') { $categories[] = $child->getAttribute('name'); } } } $this->sendClearDirty($categories); } if (strcmp($node->getTag(), 'presence') == 0 && strncmp($node->getAttribute('from'), $this->phoneNumber, strlen($this->phoneNumber)) != 0 && strpos($node->getAttribute('from'), '-') === false) { $presence = []; if ($node->getAttribute('type') == null) { $this->eventsManager()->fire('onPresenceAvailable', [$this->phoneNumber, $node->getAttribute('from')]); } else { $this->eventsManager()->fire('onPresenceUnavailable', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('last')]); } } if ($node->getTag() == 'presence' && strncmp($node->getAttribute('from'), $this->phoneNumber, strlen($this->phoneNumber)) != 0 && strpos($node->getAttribute('from'), '-') !== false && $node->getAttribute('type') != null) { $groupId = parseJID($node->getAttribute('from')); if ($node->getAttribute('add') != null) { $this->eventsManager()->fire('onGroupsParticipantsAdd', [$this->phoneNumber, $groupId, parseJID($node->getAttribute('add'))]); } elseif ($node->getAttribute('remove') != null) { $this->eventsManager()->fire('onGroupsParticipantsRemove', [$this->phoneNumber, $groupId, parseJID($node->getAttribute('remove'))]); } } if (strcmp($node->getTag(), 'chatstate') == 0 && strncmp($node->getAttribute('from'), $this->phoneNumber, strlen($this->phoneNumber)) != 0 && strpos($node->getAttribute('from'), '-') === false) { if ($node->getChild(0)->getTag() == 'composing') { $this->eventsManager()->fire('onMessageComposing', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), 'composing', $node->getAttribute('t')]); } else { $this->eventsManager()->fire('onMessagePaused', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), 'paused', $node->getAttribute('t')]); } } if ($node->getTag() == 'iq' && $node->getAttribute('type') == 'get' && $node->getAttribute('xmlns') == 'urn:xmpp:ping') { $this->eventsManager()->fire('onPing', [$this->phoneNumber, $node->getAttribute('id')]); $this->sendPong($node->getAttribute('id')); } if ($node->getTag() == 'iq' && $node->getChild('sync') != null) { //sync result $sync = $node->getChild('sync'); $existing = $sync->getChild('in'); $nonexisting = $sync->getChild('out'); //process existing first $existingUsers = []; if (!empty($existing)) { foreach ($existing->getChildren() as $child) { $existingUsers[$child->getData()] = $child->getAttribute('jid'); } } //now process failed numbers $failedNumbers = []; if (!empty($nonexisting)) { foreach ($nonexisting->getChildren() as $child) { $failedNumbers[] = str_replace('+', '', $child->getData()); } } $index = $sync->getAttribute('index'); $result = new SyncResult($index, $sync->getAttribute('sid'), $existingUsers, $failedNumbers); $this->eventsManager()->fire('onGetSyncResult', [$result]); } if ($node->getTag() == 'receipt') { $this->eventsManager()->fire('onGetReceipt', [$node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('offline'), $node->getAttribute('retry')]); } if ($node->getTag() == 'iq' && $node->getAttribute('type') == 'result') { if ($node->getChild('query') != null) { if (isset($this->nodeId['privacy']) && $this->nodeId['privacy'] == $node->getAttribute('id')) { $listChild = $node->getChild(0)->getChild(0); foreach ($listChild->getChildren() as $child) { $blockedJids[] = $child->getAttribute('value'); } $this->eventsManager()->fire('onGetPrivacyBlockedList', [$this->phoneNumber, $blockedJids]); return; } $this->eventsManager()->fire('onGetRequestLastSeen', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getChild(0)->getAttribute('seconds')]); } if ($node->getChild('props') != null) { //server properties $props = []; foreach ($node->getChild(0)->getChildren() as $child) { $props[$child->getAttribute('name')] = $child->getAttribute('value'); } $this->eventsManager()->fire('onGetServerProperties', [$this->phoneNumber, $node->getChild(0)->getAttribute('version'), $props]); } if ($node->getChild('picture') != null) { $this->eventsManager()->fire('onGetProfilePicture', [$this->phoneNumber, $node->getAttribute('from'), $node->getChild('picture')->getAttribute('type'), $node->getChild('picture')->getData()]); } if ($node->getChild('media') != null || $node->getChild('duplicate') != null) { $this->processUploadResponse($node); } if (strpos($node->getAttribute('from'), WHATSAPP_GROUP_SERVER) !== false) { //There are multiple types of Group reponses. Also a valid group response can have NO children. //Events fired depend on text in the ID field. $groupList = []; $groupNodes = []; if ($node->getChild(0) != null && $node->getChild(0)->getChildren() != null) { foreach ($node->getChild(0)->getChildren() as $child) { $groupList[] = $child->getAttributes(); $groupNodes[] = $child; } } if (isset($this->nodeId['groupcreate']) && $this->nodeId['groupcreate'] == $node->getAttribute('id')) { $this->groupId = $node->getChild(0)->getAttribute('id'); $this->eventsManager()->fire('onGroupsChatCreate', [$this->phoneNumber, $this->groupId]); } if (isset($this->nodeId['leavegroup']) && $this->nodeId['leavegroup'] == $node->getAttribute('id')) { $this->groupId = $node->getChild(0)->getChild(0)->getAttribute('id'); $this->eventsManager()->fire('onGroupsChatEnd', [$this->phoneNumber, $this->groupId]); } if (isset($this->nodeId['getgroups']) && $this->nodeId['getgroups'] == $node->getAttribute('id')) { $this->eventsManager()->fire('onGetGroups', [$this->phoneNumber, $groupList]); //getGroups returns a array of nodes which are exactly the same as from getGroupV2Info //so lets call this event, we have all data at hand, no need to call getGroupV2Info for every //group we are interested foreach ($groupNodes as $groupNode) { $this->handleGroupV2InfoResponse($groupNode, true); } } if (isset($this->nodeId['get_groupv2_info']) && $this->nodeId['get_groupv2_info'] == $node->getAttribute('id')) { $groupChild = $node->getChild(0); if ($groupChild != null) { $this->handleGroupV2InfoResponse($groupChild); } } } if (isset($this->nodeId['get_lists']) && $this->nodeId['get_lists'] == $node->getAttribute('id')) { $broadcastLists = []; if ($node->getChild(0) != null) { $childArray = $node->getChildren(); foreach ($childArray as $list) { if ($list->getChildren() != null) { foreach ($list->getChildren() as $sublist) { $id = $sublist->getAttribute('id'); $name = $sublist->getAttribute('name'); $broadcastLists[$id]['name'] = $name; $recipients = []; foreach ($sublist->getChildren() as $recipient) { array_push($recipients, $recipient->getAttribute('jid')); } $broadcastLists[$id]['recipients'] = $recipients; } } } } $this->eventsManager()->fire('onGetBroadcastLists', [$this->phoneNumber, $broadcastLists]); } if ($node->getChild('pricing') != null) { $this->eventsManager()->fire('onGetServicePricing', [$this->phoneNumber, $node->getChild(0)->getAttribute('price'), $node->getChild(0)->getAttribute('cost'), $node->getChild(0)->getAttribute('currency'), $node->getChild(0)->getAttribute('expiration')]); } if ($node->getChild('extend') != null) { $this->eventsManager()->fire('onGetExtendAccount', [$this->phoneNumber, $node->getChild('account')->getAttribute('kind'), $node->getChild('account')->getAttribute('status'), $node->getChild('account')->getAttribute('creation'), $node->getChild('account')->getAttribute('expiration')]); } if ($node->getChild('normalize') != null) { $this->eventsManager()->fire('onGetNormalizedJid', [$this->phoneNumber, $node->getChild(0)->getAttribute('result')]); } if ($node->getChild('status') != null) { $child = $node->getChild('status'); foreach ($child->getChildren() as $status) { $this->eventsManager()->fire('onGetStatus', [$this->phoneNumber, $status->getAttribute('jid'), 'requested', $node->getAttribute('id'), $status->getAttribute('t'), $status->getData()]); } } } if ($node->getTag() == 'iq' && $node->getAttribute('type') == 'error') { $errorType = null; foreach ($this->nodeId as $type => $nodeID) { if ($nodeID == $node->getAttribute('id')) { $errorType = $type; break; } } $this->eventsManager()->fire('onGetError', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getChild(0), $errorType]); } if ($node->getTag() == 'message' && $node->getAttribute('type') == 'media' && $node->getChild(0)->getAttribute('type') == 'image') { $msgId = $this->createIqId(); $ackNode = new ProtocolNode('ack', ['url' => $node->getChild(0)->getAttribute('url')]); $iqNode = new ProtocolNode('iq', ['id' => $msgId, 'xmlns' => 'w:m', 'type' => 'set', 'to' => WHATSAPP_SERVER], [$ackNode]); $this->sendNode($iqNode); } $children = $node->getChild(0); if ($node->getTag() == 'stream:error' && !empty($children) && $node->getChild(0)->getTag() == 'system-shutdown') { $this->eventsManager()->fire('onStreamError', [$node->getChild(0)->getTag()]); } if ($node->getTag() == 'stream:error') { $this->eventsManager()->fire('onStreamError', [$node->getChild(0)->getTag()]); } if ($node->getTag() == 'notification') { $name = $node->getAttribute('notify'); $type = $node->getAttribute('type'); switch ($type) { case 'status': $this->eventsManager()->fire('onGetStatus', [$this->phoneNumber, $node->getAttribute('from'), $node->getChild(0)->getTag(), $node->getAttribute('id'), $node->getAttribute('t'), $node->getChild(0)->getData()]); break; case 'picture': if ($node->hasChild('set')) { $this->eventsManager()->fire('onProfilePictureChanged', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('t')]); } else { if ($node->hasChild('delete')) { $this->eventsManager()->fire('onProfilePictureDeleted', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('t')]); } } //TODO break; case 'contacts': $notification = $node->getChild(0)->getTag(); if ($notification == 'add') { $this->eventsManager()->fire('onNumberWasAdded', [$this->phoneNumber, $node->getChild(0)->getAttribute('jid')]); } elseif ($notification == 'remove') { $this->eventsManager()->fire('onNumberWasRemoved', [$this->phoneNumber, $node->getChild(0)->getAttribute('jid')]); } elseif ($notification == 'update') { $this->eventsManager()->fire('onNumberWasUpdated', [$this->phoneNumber, $node->getChild(0)->getAttribute('jid')]); } break; case 'encrypt': $value = $node->getChild(0)->getAttribute('value'); if (is_numeric($value)) { $this->eventsManager()->fire('onGetKeysLeft', [$this->phoneNumber, $node->getChild(0)->getAttribute('value')]); } else { echo 'Corrupt Stream: value ' . $value . 'is not numeric'; } break; case 'w:gp2': if ($node->hasChild('remove')) { if ($node->getChild(0)->hasChild('participant')) { $this->eventsManager()->fire('onGroupsParticipantsRemove', [$this->phoneNumber, $node->getAttribute('from'), $node->getChild(0)->getChild(0)->getAttribute('jid')]); } } else { if ($node->hasChild('add')) { $this->eventsManager()->fire('onGroupsParticipantsAdd', [$this->phoneNumber, $node->getAttribute('from'), $node->getChild(0)->getChild(0)->getAttribute('jid')]); } else { if ($node->hasChild('create')) { $groupMembers = []; foreach ($node->getChild(0)->getChild(0)->getChildren() as $cn) { $groupMembers[] = $cn->getAttribute('jid'); } $this->eventsManager()->fire('onGroupisCreated', [$this->phoneNumber, $node->getChild(0)->getChild(0)->getAttribute('creator'), $node->getChild(0)->getChild(0)->getAttribute('id'), $node->getChild(0)->getChild(0)->getAttribute('subject'), $node->getAttribute('participant'), $node->getChild(0)->getChild(0)->getAttribute('creation'), $groupMembers]); } else { if ($node->hasChild('subject')) { $this->eventsManager()->fire('onGetGroupsSubject', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('t'), $node->getAttribute('participant'), $node->getAttribute('notify'), $node->getChild(0)->getAttribute('subject')]); } else { if ($node->hasChild('promote')) { $promotedJIDs = []; foreach ($node->getChild(0)->getChildren() as $cn) { $promotedJIDs[] = $cn->getAttribute('jid'); } $this->eventsManager()->fire('onGroupsParticipantsPromote', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('t'), $node->getAttribute('participant'), $node->getAttribute('notify'), $promotedJIDs]); } } } } } break; case 'account': if ($node->getChild(0)->getAttribute('author') == '') { $author = 'Paypal'; } else { $author = $node->getChild(0)->getAttribute('author'); } $this->eventsManager()->fire('onPaidAccount', [$this->phoneNumber, $author, $node->getChild(0)->getChild(0)->getAttribute('kind'), $node->getChild(0)->getChild(0)->getAttribute('status'), $node->getChild(0)->getChild(0)->getAttribute('creation'), $node->getChild(0)->getChild(0)->getAttribute('expiration')]); break; case 'features': if ($node->getChild(0)->getChild(0) == 'encrypt') { $this->eventsManager()->fire('onGetFeature', [$this->phoneNumber, $node->getAttribute('from'), $node->getChild(0)->getChild(0)->getAttribute('value')]); } break; case 'web': if ($node->getChild(0)->getTag() == 'action' && $node->getChild(0)->getAttribute('type') == 'sync') { $data = $node->getChild(0)->getChildren(); $this->eventsManager()->fire('onWebSync', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $data[0]->getData(), $data[1]->getData(), $data[2]->getData()]); } break; default: throw new \Exception('Method $type not implemented'); } $this->sendAck($node, 'notification'); } if ($node->getTag() == 'call') { if ($node->getChild(0)->getTag() == 'offer') { $callId = $node->getChild(0)->getAttribute('call-id'); $this->sendReceipt($node, null, null, $callId); $this->eventsManager()->fire('onCallReceived', [$this->phoneNumber, $node->getAttribute('from'), $node->getAttribute('id'), $node->getAttribute('notify'), $node->getAttribute('t'), $node->getChild(0)->getAttribute('call-id')]); } else { $this->sendAck($node, 'call'); } } if ($node->getTag() == 'ib') { foreach ($node->getChildren() as $child) { switch ($child->getTag()) { case 'dirty': $this->sendClearDirty([$child->getAttribute('type')]); break; case 'account': $this->eventsManager()->fire('onPaymentRecieved', [$this->phoneNumber, $child->getAttribute('kind'), $child->getAttribute('status'), $child->getAttribute('creation'), $child->getAttribute('expiration')]); break; case 'offline': break; default: throw new \Exception('ib handler for ' . $child->getTag() . ' not implemented'); } } } // Disconnect socket on stream error. if ($node->getTag() == 'stream:error') { $this->disconnect(); } }