diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..62556abc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+/bootstrap/compiled.php
+/vendor
+/.idea
+/nbproject
+composer.phar
+.env.*.php
+.env.php
+.DS_Store
+Thumbs.db
+*.dat
+
+*.db
diff --git a/EVENTS.md b/EVENTS.md
deleted file mode 100755
index 5c02ecc0..00000000
--- a/EVENTS.md
+++ /dev/null
@@ -1,47 +0,0 @@
-Available events and arguments
-==============================
-See events/WhatsAppEventListener.php.
-
-How to bind a callback to an event
-==================================
-
-# Create a WhatsAppEventListener class and implement the method you
-# would like to handle:
-```php
-require 'events/WhatsAppEventListenerBase.php';
-
-class MyEventListener extends WhatsAppEventListenerBase {
- function onGetMessage(
- $phone, // The user phone number including the country code.
- $from, // The sender JID.
- $msgid, // The message id.
- $type, // The message type.
- $time, // The unix time when send message notification.
- $name, // The sender name.
- $message // The message.
- ) {
- print( "onGetMessage(" . $phone . ", " . $from . ", " . $msgid . ", " . $type . ", " . $time . ", " . $name . ", " . $message . ")\n" );
- }
-}
-```
-# Require your new class;
-```php
-require 'MyEventListener.php';
-```
-# Create an instance of WhastProt.
-```php
-$w = new WhatsProt($userPhone, $userIdentity, $userName, $debug);
-```
-# Add your event listener.
-```php
-w->eventManager()->addEventListener(new MyEventListener());
-```
-# Connect to WhatsApp servers.
-```php
-$w->connect();
-```
-# Login to WhatsApp
-```php
-$w->loginWithPassword($password);
-```
-[...]
diff --git a/README.md b/README.md
index 59a6eec3..a93afda2 100644
--- a/README.md
+++ b/README.md
@@ -1,60 +1,31 @@
-# WhatsAPI
+# Chat API [](https://packagist.org/packages/whatsapp/chat-api) [](https://packagist.org/packages/whatsapp/chat-api) [](https://packagist.org/packages/whatsapp/chat-api)
Interface to WhatsApp Messenger
-----------
-
-### Update March 15th, 2014
-
-Sources are back after brief downtime due to [DCMA takedown](https://github.com/github/dmca/blob/master/2014-02-12-WhatsApp.md).
-
-### Note July 30th, 2013
-*New policy:*
-
-*I no longer provide support to users who are trying to send bulk messages using this API (i.e. a large amount of messages and not the built-in bulk message functionality).*
-*Sending advertisments on WhatsApp goes directly against their EULA and I have no way of determining whether the user is trying to send spam, advertising or sending mass messages to "opt-in users".*
-*And I also don't want to waste the little spare time that I have on trying to figure out ways to fuck up this beautiful ad-free platform called WhatsApp by enabling people to send spam.*
-*Everyone is free to use this API but there will be no more issue reports about being blocked after sending messages to semi-random users.*
-
-*In the famous words of Heath Ledger as the Joker (taken completely out of context by me):*
+**Read the [wiki](https://github.com/WHAnonymous/Chat-API/wiki)** and previous issues before opening a new one! Maybe your issue is already answered.
-**It's not about the money, it's about sending a message.**
+For new WhatsApp updates check **[WhatsApp incoming updates log](https://github.com/WHAnonymous/Chat-API/wiki/WhatsApp-incoming-updates)**
-*\- [shirioko](https://github.com/shirioko)*
+**Do you like this project? Support it by donating BTC** to 1HcEX9foem5atreEg1gbQ6xVeosN5WEcjs 
----------
+### Installation
-### Note July 14th, 2013
-*Events renamed:*
-- *A large number of events have been renamed in the event handling system to better match the recent method names.*
-- *All event names and parameters have been listed in the EVENTS.md file*
+```sh
+composer require whatsapp/chat-api
+```
+### Special thanks
-----------
-
-### Note July 10th, 2013
-*Another massive overhaul in the code:*
-- *MAJOR RENAMING OF MOST METHODS!! Old legacy code will break, we are sorry but it is necessary to provide a cleaner interface. Please check the new code.*
-- *Methods renamed to give a more consistent feel to the API - all methods are now camelCase watch out for typo's!*
-- *Initial movement towards bringing the code into alignment with PSR-2 (http://www.php-fig.org/psr/2/)*
-- *There is absolutely NO, NONE, NADA, ZIP, 100% FREE of any need to use/enter a MAC address or IMEI in this code. DO NOT TRY!*
-- *[New Android token used](https://github.com/karolsarnacki/whatsapp/commit/55d8233b852ecd9f6a6f845586e91e6fadbd0c44#L1L20) as WP7 one appears to no longer work. Long live the WP7 token?*
-
-----------
-
+- [CODeRUS](https://github.com/CODeRUS)
+- [tgalal](https://github.com/tgalal)
+- [SikiFn](https://github.com/SikiFn)
+- [0xTryCatch](https://github.com/0xTryCatch)
+- [Shirioko](https://github.com/shirioko)
-### Note June 18th, 2013
-
-*Big overhaul in the code. Big thanks to:*
-- *[Ali Hubail](https://github.com/hubail) and*
-- *[Ahmed Moh'd](http://fb.com/ahmed.mhd) for making this project happen (and adding me as a member)*
-- *[Jannik Vogel](https://github.com/JayFoxRox) for helping me retrieve the latest WhatsApp token, someone should write a book about it some day..*
-- *[Tarek Galal](https://github.com/tgalal) for providing the latest WhatsApp functionality in yowsup*
-- *[Atans](https://github.com/atans) and*
-- *[Jonathan Williamson](https://github.com/jonnywilliamson) for additional fixes*
-
-*\- [shirioko](https://github.com/shirioko)*
+Also Ahmed Moh'd (fb.com/ahmed.mhd) and Ali Hubail (@hubail) for making this project possible.
+And everyone that contributes to it.
----------
@@ -63,72 +34,18 @@ According to [the company](http://www.whatsapp.com/):
> “WhatsApp Messenger is a cross-platform mobile messenger that replaces SMS and works through the existing internet data plan of your device. WhatsApp is available for iPhone, BlackBerry, Android, Windows Phone, Nokia Symbian60 & S40 phones. Because WhatsApp Messenger uses the same internet data plan that you use for email and web browsing, there is no cost to message and stay in touch with your friends.”
-Late 2011 numbers: 1 billion messages per day, ~20 million users.
-
-### Modified XMPP
-WhatsApp uses some sort of customized XMPP server, named internally as FunXMPP, which is basically some extended proprietary version.
-
-### Login procedure
-Much like XMPP, WhatsApp uses JID (jabber id) and password to successfully login to the service. The password is generated by the server and received upon registration.
-
-
-The JID is a concatenation between your country’s code and mobile number.
-
-Initial login uses Digest Access Authentication.
-
-### Message sending
-Messages are basically sent as TCP packets, following WhatsApp’s own format (unlike what’s defined in XMPP RFCs).
-
-Messages are application level encrypted using RC4 keystreams
-
-### Multimedia Message sending
-Photos, Videos and Audio files shared with WhatsApp contacts are HTTP-uploaded to a server before being sent to the recipient(s) along with Base64 thumbnail of media file (if applicable) along with the generated HTTP link as the message body.
-
-### Event system
-WhatsApi uses an event manager (created by [facine](https://github.com/facine)) which allows you to respond to certain events.
-
-List of events and example code on how to bind an event handler:
-https://github.com/shirioko/WhatsAPI/wiki/WhatsApi-events
-
-# FAQ
-
-
-- **What’s with the hex chars floating all over the code?**
-
- Mostly WhatsApp’s proprietary control chars/commands, or formatted data according to their server’s specifications, stored in predefined dictionaries within the clients.
-
-- **What’s your future development plans?**
-
- We don’t have any.
-
-- **Would it run over the web?**
-
- We’ve tested a slightly-modified version on top of Tornado Web Server and worked like a charm, however, building a chat client is a bit tricky, do your research.
-
-- **Can I receive chats?**
-
- Indeed, using the same socket-receiving mechanism. But you have to parse the incoming data. Parsing functions aren’t included in this release, maybe in the next one?
-
-- **I think the code is messy.**
-
- It’s working.
-
-- **How can I obtain my password?**
-
- Register a number using WhatsAPI or intercept your phone's password using MissVenom
-
-# NOTES
-
-- This proof of concept is extensible to contain every feature that make a fully-fledged client, similar to the official ones, actually could be even better.
-
-- During the two weeks of analysis of service mechanisms, we stumbled upon serious design and security flaws (they fixed some of them since 2011). For a company with such massive user base, we expected better practises and engineering.
+Jan. 2015: 30 billion messages per day, ~700 million users.
# License
-MIT - refer to the source code for the extra line.
+MIT
+
+# Terms and conditions
-# Venomous
+- You will NOT use this API for marketing purposes (spam, massive sending...).
+- We do NOT give support to anyone that wants this API to send massive messages or similar.
+- We reserve the right to block any user of this repository that does not meet these conditions.
-Team of Bahraini Developers.
+## Legal
-Ahmed Moh'd ([fb.com/ahmed.mhd](https://www.facebook.com/ahmed.mhd)) and Ali Hubail ([@hubail](https://twitter.com/hubail)) contributed to this release.
+This code is in no way affiliated with, authorized, maintained, sponsored or endorsed by WhatsApp or any of its affiliates or subsidiaries. This is an independent and unofficial API. Use at your own risk.
diff --git a/classesMD5.txt b/classesMD5.txt
deleted file mode 100644
index f8d11075..00000000
--- a/classesMD5.txt
+++ /dev/null
@@ -1,110 +0,0 @@
-===============================
-= CLASSES MD5 =
-= B64 ENCODED =
-===============================
-
-
-WhatsApp/2.11.407 Android/4.3 Device/GalaxyS3
-xOyKd7AoN0uoos7Fkeup5w==
-
-WhatsApp/2.11.404 Android/4.3 Device/GalaxyS3
-UwR/p2qFH6trUCtAwVFvlw==
-
-WhatsApp/2.11.395 Android/4.3 Device/GalaxyS3 [*] Safe version
-P3b9TfNFCkkzPoVzZnm+BA==
-
-WhatsApp/2.11.394 Android/4.3 Device/GalaxyS3
-EkA+wIj+E8R7ncRihvTbJQ==
-
-WhatsApp/2.11.393 Android/4.3 Device/GalaxyS3
-39ykQ+QRBQ3V4y5u7qnqDA==
-
-WhatsApp/2.11.391 Android/4.3 Device/GalaxyS3
-08io6i/9AExMEsEar18G5w==
-
-WhatsApp/2.11.390 Android/4.3 Device/GalaxyS3
-ueXcdj5E56OZ0ABxU/YasQ==
-
-WhatsApp/2.11.388 Android/4.3 Device/GalaxyS3
-KSb52iVfhyJMXxuGse8avw==
-
-WhatsApp/2.11.383 Android/4.3 Device/GalaxyS3
-2IVWfReKm2m+1ORLg3gQtg==
-
-WhatsApp/2.11.382 Android/4.3 Device/GalaxyS3
-IJWjujh8Eds1Ew1pjWZWvg==
-
-WhatsApp/2.11.380 Android/4.3 Device/GalaxyS3
-b9ztDlCAK+k27UGaZkM7nw==
-
-WhatsApp/2.11.378 Android/4.3 Device/GalaxyS3 [*] 5 Sept 2014. PS.
-oCtjlSonS+4H16h9HW6nNA==
-
-WhatsApp/2.11.375 Android/4.3 Device/GalaxyS3
-ZLvs/rzS8qU90wZLE/1MgQ==
-
-WhatsApp/2.11.372 Android/4.3 Device/GalaxyS3
-eAaY/5A1G1nadV0TLtmdFQ==
-
-WhatsApp/2.11.371 Android/4.3 Device/GalaxyS3
-ScUKonjwWeLYhKqH8uV4HA==
-
-WhatsApp/2.11.365 Android/4.3 Device/GalaxyS3
-GUfilO0O5J4ZEMWcQKN7mg==
-
-WhatsApp/2.11.355 Android/4.3 Device/GalaxyS3
-w5mQTrb0Tky4s6Ln6RFMVQ==
-
-WhatsApp/2.11.354 Android/4.3 Device/GalaxyS3
-MPLa6ffv7Z/WNGS60PpxJQ==
-
-WhatsApp/2.11.348 Android/4.3 Device/GalaxyS3
-vszYr764HF2FQpdbqTdr/w==
-
-WhatsApp/2.11.339 Android/4.0.4 Device/GalaxyS3
-iV15qOB/jPIidogqfJ/oJA==
-
-WhatsApp/2.11.301 Android/4.3 Device/GalaxyS3
-pZ3J/O+F3HXOyx8YixzvPQ==
-
-WhatsApp/2.11.272 Android/4.0.4 Device/GalaxyS2
-kI6dipxOAk055AWZxknGqg==
-
-WhatsApp/2.11.264 Android/4.0.4 Device/GalaxyS2
-opf5/xMP2qdqHJ+7d4/LwA==
-
-WhatsApp/2.11.254 Android/4.0.4 Device/GalaxyS2
-ueKiLIlXuHtGzw2oxoZvfg==
-
-WhatsApp/2.11.224 Android/4.4.2 Device/GalaxyS4
-ZGdhtLiFqomD5ovSpQ+Odw==
-
-WhatsApp/2.11.209 Android/4.4.2 Device/GalaxyS4
-+XW/7rCZDX9T7YrGQqTmcg==
-
-WhatsApp/2.11.200 Android/4.4.2 Device/GalaxyS4
-8PZch1s/VRHIKujxCxOXig==
-
-WhatsApp/2.11.151 Android/4.2.1 Device/GalaxyS3
-94bjoO7brhy/QJZRceJHYw==
-
-WhatsApp/2.11.144 Android/4.2.1 Device/GalaxyS3
-QtHrjQzwQjujOOOd1oObgQ==
-
-WhatsApp/2.11.139 Android/4.2.1 Device/GalaxyS3
-rzoCoMEYKQ6zoUWINKC9oQ==
-
-WhatsApp/2.11.134 Android/4.2.1 Device/GalaxyS3
-r4WQV17nVTl3+uFlF9mvEg==
-
-WhatsApp/2.11.125 Android/4.2.1 Device/GalaxyS3
-JqOGtSEKUWEu/jxzCB6Bdw==
-
-WhatsApp/2.11.113 Android/4.2.1 Device/GalaxyS3
-MOpsiNsR+nHEv0dFc3dqmA==
-
-WhatsApp/2.11.102 Android/4.2.1 Device/GalaxyS3
-zo7YXXvrrRqpikOi/CveTw==
-
-WhatsApp/2.11.69 Android/4.2.1 Device/GalaxyS3
-30CnAF22oY+2PUD5pcJGqw==
diff --git a/composer.json b/composer.json
new file mode 100644
index 00000000..5bf39337
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,37 @@
+{
+ "name": "whatsapp/chat-api",
+ "description": "The PHP WhatsApp library",
+ "license": "MIT",
+ "keywords": [
+ "WhatsApp",
+ "WhatsAPI",
+ "Chat-API"
+ ],
+ "authors": [
+ {
+ "role": "Contributors",
+ "name": "Chat API Contributing Team",
+ "homepage": "https://github.com/WHAnonymous/Chat-API/graphs/contributors"
+ }
+ ],
+ "support": {
+ "issues": "https://github.com/WHAnonymous/Chat-API/issues",
+ "wiki": "https://github.com/WHAnonymous/Chat-API/wiki",
+ "source": "https://github.com/WHAnonymous/Chat-API"
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "require": {
+ "php": ">=5.3",
+ "ext-curl": "*",
+ "ext-gd" : "*",
+ "ext-mcrypt": "*",
+ "ext-openssl" : "*",
+ "ext-pdo" : "*",
+ "ext-sockets" : "*",
+ "ext-sqlite3" : "*"
+ }
+}
diff --git a/examples/Simple CLI client.php b/examples/Simple CLI client.php
index e3b18df0..bc5fd03a 100755
--- a/examples/Simple CLI client.php
+++ b/examples/Simple CLI client.php
@@ -16,14 +16,13 @@
////////////////CONFIGURATION///////////////////////
////////////////////////////////////////////////////
-$username = "";
+$username = "";
$password = "";
-$identity = "";
-$nickname = "";
-$debug = false;
+$nickname = "";
+$debug = false;
/////////////////////////////////////////////////////
if ($_SERVER['argv'][1] == null) {
- echo "USO: php ".$_SERVER['argv'][0]." \n\nEj: php cliente.php 34123456789\n\n";
+ echo "USAGE: php ".$_SERVER['argv'][0]." \n\nEj: php client.php 34123456789\n\n";
exit(1);
}
$target = $_SERVER['argv'][1];
@@ -41,19 +40,23 @@ function fgets_u($pStdn)
return null;
}
-function onPresenceReceived($username, $from, $type)
+function onPresenceAvailable($username, $from)
{
- $dFrom = str_replace(array("@s.whatsapp.net","@g.us"), "", $from);
- if($type == "available")
- echo "<$dFrom is online>\n\n";
- else
- echo "<$dFrom is offline>\n\n";
+ $dFrom = str_replace(array("@s.whatsapp.net","@g.us"), "", $from);
+ echo "<$dFrom is online>\n\n";
+}
+
+function onPresenceUnavailable($username, $from, $last)
+{
+ $dFrom = str_replace(array("@s.whatsapp.net","@g.us"), "", $from);
+ echo "<$dFrom is offline>\n\n";
}
echo "[] logging in as '$nickname' ($username)\n";
-$w = new WhatsProt($username, $identity, $nickname, false);
+$w = new WhatsProt($username, $nickname, $debug);
-$w->eventManager()->bind("onPresence", "onPresenceReceived");
+$w->eventManager()->bind("onPresenceAvailable", "onPresenceAvailable");
+$w->eventManager()->bind("onPresenceUnavailable", "onPresenceUnavailable");
$w->connect(); // Nos conectamos a la red de WhatsApp
$w->loginWithPassword($password); // Iniciamos sesion con nuestra contraseña
@@ -68,34 +71,34 @@ function onPresenceReceived($username, $from, $type)
$pn = new ProcessNode($w, $target);
$w->setNewMessageBind($pn);
- while (1) {
+while (1) {
$w->pollMessage();
$msgs = $w->getMessages();
foreach ($msgs as $m) {
# process inbound messages
//print($m->NodeString("") . "\n");
}
- $line = fgets_u(STDIN);
- if ($line != "") {
- if (strrchr($line, " ")) {
- $command = trim(strstr($line, ' ', TRUE));
- } else {
- $command = $line;
- }
- switch ($command) {
- case "/query":
- $dst = trim(strstr($line, ' ', FALSE));
- echo "[] Interactive conversation with $contact:\n";
- break;
- case "/lastseen":
- echo "[] Last seen $target: ";
- $w->sendGetRequestLastSeen($target);
- break;
- default:
- $w->sendMessage($target , $line);
- break;
- }
+ $line = fgets_u(STDIN);
+ if ($line != "") {
+ if (strrchr($line, " ")) {
+ $command = trim(strstr($line, ' ', TRUE));
+ } else {
+ $command = $line;
+ }
+ switch ($command) {
+ case "/query":
+ $dst = trim(strstr($line, ' ', FALSE));
+ echo "[] Interactive conversation with $contact:\n";
+ break;
+ case "/lastseen":
+ echo "[] Last seen $target: ";
+ $w->sendGetRequestLastSeen($target);
+ break;
+ default:
+ $w->sendMessage($target , $line);
+ break;
}
+ }
}
class ProcessNode
@@ -115,7 +118,7 @@ public function process($node)
$text = $text->getData();
$notify = $node->getAttribute("notify");
- echo "\n- ".$notify.": ".$text." ".date('H:i')."\n";
+ echo "\n- ".$notify.": ".$text." ".date('H:i')."\n";
- }
-}
+ }
+}
diff --git a/examples/WA CLI Client/client.php b/examples/WA CLI Client/client.php
new file mode 100755
index 00000000..c595c7fb
--- /dev/null
+++ b/examples/WA CLI Client/client.php
@@ -0,0 +1,731 @@
+ PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
+ $db->exec('CREATE TABLE data (`username` TEXT, `password` TEXT, `nickname` TEXT, `login` TEXT)');
+ $sql = 'INSERT INTO data (`username`, `password`, `nickname`, `login`) VALUES (:username, :password, :nickname, :login)';
+ $query = $db->prepare($sql);
+
+ $query->execute(
+ array(
+ ':username' => $argv[1],
+ ':password' => $argv[2],
+ ':nickname' => $argv[3],
+ ':login' => '1'
+ )
+ );
+ }
+}
+
+if ((!file_exists($fileName)))
+{
+
+ echo "Welcome to CLI WA Client\n";
+ echo "========================\n\n\n";
+ echo "Your number > ";
+ $number = trim(fgets(STDIN));
+ $w = new WhatsProt($number, $nickname, $debug);
+
+ try
+ {
+ $result = $w->codeRequest('sms');
+ } catch (Exception $e)
+ {
+ echo "there is an error" . $e;
+ }
+ echo "\nEnter sms code you have received > ";
+ $code = trim(str_replace("-", "", fgets(STDIN)));
+ try
+ {
+ $result = $w->codeRegister($code);
+ } catch (Exception $e)
+ {
+ echo "there is an error";
+ }
+
+ echo "\nYour nickname > ";
+ $nickname = trim(fgets(STDIN));
+ do
+ {
+ echo "Is '$nickname' right?\n";
+ echo "yes/no > ";
+ $right = trim(fgets(STDIN));
+ if ($right != 'yes')
+ {
+ echo "\nYour nickname > ";
+ $nickname = trim(fgets(STDIN));
+ }
+
+ } while ($right != 'yes');
+
+ $db = new \PDO("sqlite:" . $fileName, null, null, array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
+ $db->exec('CREATE TABLE data (`username` TEXT, `password` TEXT, `nickname` TEXT, `login` TEXT)');
+
+ $sql = 'INSERT INTO data (`username`, `password`, `nickname`, `login`) VALUES (:username, :password, :nickname, :login)';
+ $query = $db->prepare($sql);
+
+ $query->execute(
+ array(
+ ':username' => $number,
+ ':password' => $result->pw,
+ ':nickname' => $nickname,
+ ':login' => '1'
+ )
+ );
+}
+
+$db = new \PDO("sqlite:" . $fileName, null, null, array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
+$sql = 'SELECT username, password, nickname, login FROM data';
+$row = $db->query($sql);
+$result = $row->fetchAll();
+$username = $result[0]['username'];
+$password = $result[0]['password'];
+$nickname = $result[0]['nickname'];
+$login = $result[0]['login'];
+
+$w = new WhatsProt($username, $nickname, $debug);
+$GLOBALS["wa"] = $w;
+$w->setMessageStore(new SqliteMessageStore($username));
+$events = new MyEvents($w);
+$w->eventManager()->bind('onGetSyncResult', 'onSyncResult');
+$w->eventManager()->bind('onGetRequestLastSeen', 'onGetRequestLastSeen');
+$w->eventManager()->bind('onPresenceAvailable', 'onPresenceAvailable');
+$w->eventManager()->bind('onPresenceUnavailable', 'onPresenceUnavailable');
+$w->eventManager()->bind('onGetImage', 'onGetImage');
+$w->eventManager()->bind('onGetVideo', 'onGetVideo');
+$w->eventManager()->bind('onGetAudio', 'onGetAudio');
+
+$w->connect();
+try
+{
+ $w->loginWithPassword($password);
+}
+catch (Exception $e)
+{
+ echo "Error: $e";
+ exit();
+}
+echo "\nConnected to WA\n\n";
+if ($login == '1')
+{
+ $w->sendGetClientConfig();
+ $w->sendGetServerProperties();
+ $w->sendGetGroups();
+ $w->sendGetBroadcastLists();
+
+ $sql = "UPDATE data SET login=?";
+ $query = $db->prepare($sql);
+ $query->execute(array('0'));
+}
+$w->sendGetPrivacyBlockedList();
+$w->sendAvailableForChat($nickname);
+$show = true;
+global $onlineContacts;
+$GLOBALS["online_contacts"] = array();
+$GLOBALS["current_contact"];
+$poll = 0;
+do
+{
+ if ($show)
+ {
+ showContacts();
+ $show = false;
+ }
+ $poll++;
+ if ($poll == 10)
+ {
+ $w->pollMessage();
+ $poll = 0;
+ }
+ $mainCmd = fgets_u(STDIN);
+ switch ($mainCmd) {
+ case '/add':
+ echo "\nEnter the number you want to add > ";
+ $numberToAdd = trim(fgets(STDIN));
+ do {
+ echo "\nIs it right yes/no > ";
+ $check = trim(fgets(STDIN));
+ if ($check != 'yes')
+ {
+ echo "\nEnter the number you want to add > ";
+ $numberToAdd = trim(fgets(STDIN));
+ }
+ } while ($check != 'yes');
+ echo "\nEnter the nickname/name > ";
+ $nickname = trim(fgets(STDIN));
+ $w->sendSync(array($numberToAdd), null, 3);
+ if ($existUser)
+ {
+ $w->sendPresenceSubscription($numberToAdd);
+ addContact($numberToAdd, $nickname);
+ }
+ break;
+ case '/delete':
+ echo "\nEnter the nickname you want to remove > ";
+ $nickname = trim(fgets(STDIN));
+ do {
+ echo "\nIs it right yes/no > ";
+ $check = trim(fgets(STDIN));
+ if ($check != 'yes')
+ {
+ echo "\nEnter the nickname you want to remove > ";
+ $nickname = trim(fgets(STDIN));
+ }
+ } while ($check != 'yes');
+ $numberToRemove = findPhoneByNickname($nickname);
+ $w->sendSync(array(), array($numberToRemove), 3);
+ $w->sendPresenceUnsubscription($numberToRemove);
+ $contactsDB = __DIR__ . DIRECTORY_SEPARATOR . 'contacts.db';
+ $cDB = new \PDO("sqlite:" . $contactsDB, null, null, array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
+ $sql = "DELETE FROM contacts WHERE nickname = :nickname";
+ $query = $cDB->prepare($sql);
+ $query->execute(array(':nickname' => $nickname));
+ break;
+ case '/contacts':
+ $show = true;
+ break;
+ case '/status':
+ echo "\nEnter your status > ";
+ $status = trim(fgets(STDIN));
+ do {
+ echo "\nIs it right yes/no > ";
+ $check = trim(fgets(STDIN));
+ if ($check != 'yes')
+ {
+ echo "\nEnter your status > ";
+ $status = trim(fgets(STDIN));
+ }
+ } while ($check != 'yes');
+ $w->sendStatusUpdate($status);
+ break;
+ case '/profile':
+ echo "\nEnter your profile picture URL > ";
+ $profile = trim(fgets(STDIN));
+ do {
+ echo "\nIs it right yes/no > ";
+ $check = trim(fgets(STDIN));
+ if ($check != 'yes')
+ {
+ echo "\nEnter your profile picture URL > ";
+ $profile = trim(fgets(STDIN));
+ }
+ } while ($check != 'yes');
+ if (!filter_var($profile, FILTER_VALIDATE_URL) === false) {
+ if(@getimagesize($profile) !== false) {
+ $w->sendSetProfilePicture($profile);
+ }
+ }
+ else {
+ echo "$profile is NOT a valid URL\n\n";
+ }
+ break;
+ case '/credits':
+ echo "\nSpecials thanks to 0xtryCatch :D\n";
+ break;
+ case '/secret';
+ echo "If you are a spammer or bulk sender, this is your lucky day! Follow the link:\n";
+ echo "http://bit.ly/1dOj8e0\n\n";
+ echo ":)\n\n";
+ exit();
+ break;
+ case '/chat':
+ echo "\nEnter the name of the contact > ";
+ $nickname = trim(fgets(STDIN));
+ do {
+ echo "\nIs it right yes/no > ";
+ $check = trim(fgets(STDIN));
+ if ($check != 'yes')
+ {
+ echo "\nEnter the number you want to add > ";
+ $nickname = trim(fgets(STDIN));
+ }
+ } while ($check != 'yes');
+
+ echo "\n\n";
+ echo "You are chatting with $nickname\n";
+ echo "=================================\n\n";
+ $contact = findPhoneByNickname($nickname);
+ $latestMsgs = getLatestMessages($contact);
+ $GLOBALS["current_contact"] = $contact;
+ foreach ($latestMsgs as $msg)
+ {
+ echo "\n- ".$nickname.": ".$msg['message']." ".date('t/m/Y h:i:s A', $msg['t'])."\n";
+ }
+ $pn = new ProcessNode($w, $contact);
+ $w->setNewMessageBind($pn);
+ $chatting = true;
+ $compose = true;
+ $lastSeen = true;
+ while ($chatting) {
+ $w->pollMessage();
+ $msgs = $w->getMessages();
+ foreach ($msgs as $m) {
+ # process inbound messages
+ //print($m->NodeString("") . "\n");
+ }
+ if ($compose)
+ {
+ $w->sendMessageComposing($contact);
+ $compose = false;
+ }
+ if ($lastSeen)
+ {
+ if (!in_array($contact, $GLOBALS["online_contacts"]))
+ $w->sendGetRequestLastSeen($contact);
+ $lastSeen = false;
+ }
+ $line = fgets_u(STDIN);
+ /*
+ $typing = true;
+ while (($c = fread(STDIN, 1)) && ($w->pollMessage()))
+ {
+ if ($typing)
+ $w->sendMessageComposing($contact);
+ switch (ord($c)) {
+ case 8:
+ // Backspace
+ $text = substr($line, 0, -1);
+ break;
+ case 10:
+ // Newline
+ $line = $text;
+ $text = "";
+ $w->sendMessagePaused($contact);
+ break 2;
+ default:
+ $text .= $c;
+ break;
+ }
+ $typing = false;
+ }
+ */
+ if ($line != "") {
+ if (strrchr($line, " ")) {
+ $command = trim(strstr($line, ' ', TRUE));
+ } else {
+ $command = $line;
+ }
+ switch ($command) {
+ case "/current":
+ $nickname = findNicknameByPhone($contact);
+ echo "[] Interactive conversation with $nickname:\n";
+ break;
+ case "/lastseen":
+ echo "[] Last seen $contact: ";
+ $w->sendMessagePaused($contact);
+ $compose = true;
+ $w->sendGetRequestLastSeen($contact);
+ break;
+ case "/block":
+ echo "< User is now blocked >\n";
+ $w->sendMessagePaused($contact);
+ $compose = true;
+ $blocked = privacySettings($contact, 'block');
+ $w->sendSetPrivacyBlockedList($blocked);
+ break;
+ case "/unblock":
+ echo "< User is now unblocked >\n";
+ $w->sendMessagePaused($contact);
+ $compose = true;
+ $blocked = privacySettings($contact, 'unblock');
+ $w->sendSetPrivacyBlockedList($blocked);
+ break;
+ case "/back":
+ echo "\nYou are now in the main menu\n";
+ echo "================================\n\n";
+ $chatting = false;
+ break;
+ case '/time':
+ echo date("l jS \of F Y h:i:s A") . "\n\n";
+ break;
+ case '/help':
+ echo "Available commands\n";
+ echo "==================\n\n";
+ echo "/query - Shows the number you are chatting with\n";
+ echo "/lastseen - Last seen of the user\n";
+ echo "/block - Blocks the user\n";
+ echo "/unblock - Unblocks user\n";
+ echo "/time - Current time\n";
+ echo "/back - Return to main menu\n\n";
+ break;
+ default:
+ $w->sendMessagePaused($contact);
+ if (!filter_var($line, FILTER_VALIDATE_URL) === false) {
+ if(@getimagesize($line) !== false) {
+ $w->sendMessageImage($contact, $line);
+ }
+ }
+ else
+ $w->sendMessage($contact , $line);
+ $compose = true;
+ break;
+ }
+ }
+ }
+ break;
+ case '/time':
+ echo date("l jS \of F Y h:i:s A") . "\n\n";
+ break;
+ case '/help':
+ echo "Available commands\n";
+ echo "==================\n\n";
+ echo "/add - Adds a contact\n";
+ echo "/delete - Removes a contact\n";
+ echo "/chat - Starts a conversation\n";
+ echo "/contacts - Shows all contacts\n";
+ echo "/status - Change status\n";
+ echo "/profile - Change profile image\n";
+ echo "/time - Current time\n";
+ echo "/credits - Credits & special thanks\n";
+ echo "/exit - Close WA CLI Client\n\n";
+ break;
+ default:
+ //code
+ break;
+ }
+} while (($mainCmd != '/exit'));
+
+$w->disconnect();
+echo "Disconnected. Bye! :D\n";
+
+function showContacts()
+{
+ $contactsDB = __DIR__ . DIRECTORY_SEPARATOR . 'contacts.db';
+ if (file_exists($contactsDB))
+ {
+
+ $cDB = new \PDO("sqlite:" . $contactsDB, null, null, array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
+ $sql = 'SELECT nickname FROM contacts';
+ $row = $cDB->query($sql);
+ $contacts = $row->fetchAll();
+ echo "\n Contacts\n";
+ echo "==================\n\n";
+ foreach ($contacts as $contact)
+ {
+ echo "- " . $contact['nickname'] . "\n";
+ }
+ echo "\n\n";
+ }
+}
+
+function fgets_u($pStdn)
+{
+ $pArr = array($pStdn);
+
+ if (false === ($num_changed_streams = stream_select($pArr, $write = NULL, $except = NULL, 0))) {
+ print("\$ 001 Socket Error : UNABLE TO WATCH STDIN.\n");
+
+ return FALSE;
+ } elseif ($num_changed_streams > 0) {
+ return trim(fgets($pStdn, 1024));
+ }
+ return null;
+}
+
+function addContact($number, $name)
+{
+ $contactsDB = __DIR__ . DIRECTORY_SEPARATOR . 'contacts.db';
+ if (!file_exists($contactsDB))
+ {
+ $db = new \PDO("sqlite:" . $contactsDB, null, null, array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
+ $db->exec('CREATE TABLE contacts (`phone` TEXT, `nickname` TEXT)');
+ }
+ else {
+ $db = new \PDO("sqlite:" . $contactsDB, null, null, array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
+ }
+ $sql = 'INSERT INTO contacts (`phone`, `nickname`) VALUES (:phone, :nickname)';
+ $query = $db->prepare($sql);
+
+ $query->execute(
+ array(
+ ':phone' => $number,
+ ':nickname' => $name
+ )
+ );
+
+}
+
+function findPhoneByNickname($contact)
+{
+ $contactsDB = __DIR__ . DIRECTORY_SEPARATOR . 'contacts.db';
+ if (file_exists($contactsDB))
+ {
+ $cDB = new \PDO("sqlite:" . $contactsDB, null, null, array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
+ $sql = 'SELECT phone FROM contacts WHERE nickname = :nickname';
+ $query = $cDB->prepare($sql);
+ $query->execute(array(':nickname' => $contact));
+ $contact = $query->fetchAll();
+ $contact = $contact[0]['phone'];
+
+ return $contact;
+ }
+}
+
+function findNicknameByPhone($phone)
+{
+ $contactsDB = __DIR__ . DIRECTORY_SEPARATOR . 'contacts.db';
+ if (file_exists($contactsDB))
+ {
+ $cDB = new \PDO("sqlite:" . $contactsDB, null, null, array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
+ $sql = 'SELECT nickname FROM contacts WHERE phone = :phone';
+ $query = $cDB->prepare($sql);
+ $query->execute(array(':phone' => $phone));
+ $contact = $query->fetchAll();
+ $contact = $contact[0]['nickname'];
+
+ return $contact;
+ }
+}
+
+function getLatestMessages($phone)
+{
+ $msgDB = $GLOBALS["msg_db"];
+ if (file_exists($msgDB))
+ {
+ $cDB = new \PDO("sqlite:" . $msgDB, null, null, array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
+ $sql = 'SELECT message, t FROM messages WHERE `from` = :phone LIMIT 20';
+ $query = $cDB->prepare($sql);
+ $query->execute(array(':phone' => $phone));
+ $messages = $query->fetchAll();
+
+ return $messages;
+ }
+}
+
+function privacySettings($number, $option)
+{
+ if ($option == 'block')
+ {
+ $privacyDB = __DIR__ . DIRECTORY_SEPARATOR . 'privacy.db';
+ if (!file_exists($privacyDB))
+ {
+ $pDB = new \PDO("sqlite:" . $privacyDB, null, null, array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
+ $pDB->exec('CREATE TABLE blocked (`phone` TEXT)');
+ }
+ else {
+ $pDB = new \PDO("sqlite:" . $privacyDB, null, null, array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
+ }
+ $sql = 'INSERT INTO blocked (`phone`) VALUES (:phone)';
+ $query = $pDB->prepare($sql);
+
+ $query->execute(
+ array(
+ ':phone' => $number
+ )
+ );
+
+ $sql = 'SELECT phone FROM blocked';
+ $query = $pDB->prepare($sql);
+ $query->execute();
+ $blocked = $query->fetchAll();
+ $i = 0;
+ for ($i; $i < count($blocked); $i++)
+ {
+ $blockedList[] = $blocked[$i]['phone'];
+ }
+
+ return $blockedList;
+ }
+ else {
+ $privacyDB = __DIR__ . DIRECTORY_SEPARATOR . 'privacy.db';
+ if (file_exists($privacyDB))
+ {
+ $pDB = new \PDO("sqlite:" . $privacyDB, null, null, array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
+ $sql = "DELETE FROM blocked WHERE phone = :phone";
+ $query = $pDB->prepare($sql);
+ $query->execute(array(':phone' => $number));
+
+ $sql = 'SELECT phone FROM blocked';
+ $query = $pDB->prepare($sql);
+ $query->execute();
+ $blocked = $query->fetchAll();
+ $i = 0;
+ for ($i; $i < count($blocked); $i++)
+ {
+ $blockedList[] = $blocked[$i]['phone'];
+ }
+
+ return $blockedList;
+ }
+ }
+
+}
+
+function onSyncResult($result)
+{
+ foreach ($result->existing as $number) {
+ global $existUser;
+ $existUser = true;
+ }
+}
+
+function onGetRequestLastSeen( $mynumber, $from, $id, $seconds )
+{
+ $nickname = findNicknameByPhone(ExtractNumber($from));
+ if (($seconds != "") || ($seconds != null))
+ echo "$nickname last seen: " . gmdate('l jS \of F Y h:i:s A', intval($seconds)). "\n";
+}
+
+function onPresenceAvailable($mynumber, $from)
+{
+ $number = ExtractNumber($from);
+ if (!in_array($number, $GLOBALS["online_contacts"]))
+ array_push($GLOBALS["online_contacts"], $number);
+ $nickname = findNicknameByPhone($number);
+ echo " < $nickname is now online >\n";
+}
+
+function onPresenceUnavailable($mynumber, $from, $last)
+{
+ $number = ExtractNumber($from);
+ if(($key = array_search($number, $GLOBALS["online_contacts"])) !== false) {
+ unset($GLOBALS["online_contacts"][$key]);
+ }
+ $nickname = findNicknameByPhone($number);
+ echo " < $nickname is now offline >\n";
+}
+
+function onGetMessage($mynumber, $from, $id, $type, $time, $name, $body)
+{
+ $number = ExtractNumber($from);
+ if ($number != $GLOBALS["current_contact"])
+ {
+ $nickname = findNicknameByPhone($number);
+ if (($nickname != "") || ($nickname != null))
+ echo " < New message from $nickname >";
+ else{
+ echo " < New message from $name ($number) >";
+ do{
+ echo "\nDo you want to add $name ($number)? add/block/nothing\n";
+ echo "> ";
+ $option = trim(fgets(STDIN));
+ } while(($option != 'add') || ($option != 'block') || ($option != 'nothing'));
+
+ switch ($option)
+ {
+ case 'add':
+ echo "\nEnter the nickname/name > ";
+ $nickname = trim(fgets(STDIN));
+ addContact($number, $nickname);
+ $GLOBALS['wa']->sendSync(array($number), null, 3);
+ $GLOBALS['wa']->sendPresenceSubscription($number);
+ break;
+ case 'block':
+ $blockedContacts = privacySettings($number, 'block');
+ $GLOBALS['wa']->sendSetPrivacyBlockedList($blockedContacts);
+ echo "$name ($number) is now blocked\n";
+ break;
+ }
+
+ }
+ }
+}
+
+function onGetImage($mynumber, $from, $id, $type, $time, $name, $size, $url, $file, $mimeType, $fileHash, $width, $height, $preview, $caption)
+{
+ $number = ExtractNumber($from);
+ $nickname = findNicknameByPhone($number);
+ $path = __DIR__ . DIRECTORY_SEPARATOR . "data/media/$nickname/";
+ if (!file_exists($path))
+ mkdir($path);
+ $filename = $path . time() . ".jpg";
+ $data = file_get_contents($url);
+ $fp = @fopen($filename, "w");
+ if ($fp) {
+ fwrite($fp, $data);
+ fclose($fp);
+ }
+ echo " < Received image from $nickname >\n";
+}
+
+function onGetVideo($mynumber, $from, $id, $type, $time, $name, $url, $file, $size, $mimeType, $fileHash, $duration, $vcodec, $acodec, $preview, $caption)
+{
+ $number = ExtractNumber($from);
+ $nickname = findNicknameByPhone($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 video from $nickname >\n";
+}
+
+function onGetAudio($mynumber, $from, $id, $type, $time, $name, $size, $url, $file, $mimeType, $fileHash, $duration, $acodec, $fromJID_ifGroup = null)
+{
+ $number = ExtractNumber($from);
+ $nickname = findNicknameByPhone($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";
+}
+
+class ProcessNode
+{
+ protected $wp = false;
+ protected $target = false;
+
+ public function __construct($wp, $target)
+ {
+ $this->wp = $wp;
+ $this->target = $target;
+ }
+
+ 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";
+ }
+
+ }
+}
diff --git a/examples/WA CLI Client/data/media/.gitkeep b/examples/WA CLI Client/data/media/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/ajaxDemo/socket.php b/examples/ajaxDemo/socket.php
index 08046217..bb19da94 100755
--- a/examples/ajaxDemo/socket.php
+++ b/examples/ajaxDemo/socket.php
@@ -75,7 +75,7 @@ function onGetImage($mynumber, $from, $id, $type, $t, $name, $size, $url, $file,
session_write_close();
}
-require_once '../whatsprot.class.php';
+require_once '../../src/whatsprot.class.php';
$target = @$_POST["target"];
$username = "************";
$password = "******************************";
diff --git a/examples/blockChecker.php b/examples/blockChecker.php
new file mode 100644
index 00000000..f27715df
--- /dev/null
+++ b/examples/blockChecker.php
@@ -0,0 +1,36 @@
+eventManager()->bind("onCredentialsBad", "onCredentialsBad");
+$w->eventManager()->bind("onCredentialsGood", "onCredentialsGood");
+
+
+$w->checkCredentials();
+
+?>
diff --git a/examples/bulk.php b/examples/bulk.php
deleted file mode 100755
index 1f37438f..00000000
--- a/examples/bulk.php
+++ /dev/null
@@ -1,156 +0,0 @@
-Login();
- * $wbs->SyncContacts($contacts);
- * $wbs->SendBulk($contacts, "bulk message");
- * or
- * $wbs->SendBroadcast($contacts, "Broadcast Message");
- */
-
-class WaBulkSender
-{
- protected $username;
- protected $password;
- protected $nickname;
- /** @var @var $wa WhatsProt */
- protected $wa;
- protected static $sendLock = false;
-
- /**
- * @param string $username
- * @param string $password
- * @param string $nickname
- * @param bool $debug
- */
- public function __construct($username, $password, $nickname = "WhatsApp", $debug = false)
- {
- $this->username = $username;
- $this->password = $password;
- $this->wa = new WhatsProt($username, null, $nickname, $debug);
- $this->bindEvents();
- }
-
- protected function bindEvents()
- {
- $this->wa->eventManager()->bind("onLoginFailed", "WaBulkSender::event_onLoginFailed");
- $this->wa->eventManager()->bind("onLogin", "WaBulkSender::event_onLogin");
- $this->wa->eventManager()->bind("onConnect", "WaBulkSender::event_onConnect");
- $this->wa->eventManager()->bind("onMessageReceivedServer", "WaBulkSender::event_onMessageReceivedServer");
- }
-
- public function Login()
- {
- echo "Connecting... ";
- $this->wa->connect();
- echo "Logging in... ";
- $this->wa->loginWithPassword($this->password);
- $this->wa->sendClientConfig();
- $this->wa->sendGetServerProperties();
- while($this->wa->pollMessage());
- echo "Ready for work!
";
- }
-
- /**
- * @param string $number
- * @param string $reason
- */
- public static function event_onLoginFailed($number, $reason)
- {
- die("login failed for $number; Reason: $reason");
- }
-
- /**
- * @param string $number
- */
- public static function event_onLogin($number)
- {
- echo "logged in as $number
";
- }
-
- /**
- * @param string $number
- * @param $socket
- */
- public static function event_onConnect($number, $socket)
- {
- echo "connected to WhatsApp
";
- }
-
- public static function event_onMessageReceivedServer($mynumber, $from, $id, $type)
- {
- if($from != "broadcast")
- {
- //unlock
- echo "$type with id $id from $mynumber to $from received by server
";
- static::$sendLock = false;
- }
- }
-
- /**
- * @param string[] $contacts
- */
- public function SyncContacts($contacts)
- {
- $this->wa->sendSync($contacts);
- echo "Synced " . count($contacts) . " contacts
";
- }
-
- /**
- * @param string[] $targets
- * @param string $message
- */
- public function SendBroadcast($targets, $message)
- {
- if(count($targets) > 25)
- {
- echo "Error: too many broadcast targets (" . count($targets) . ")
";
- return;
- }
- echo "Sending broadcast... ";
- $this->wa->sendBroadcastMessage($targets, $message);
- $this->wa->pollMessage();
- echo "done!
";
- }
-
- /**
- * @param string[] $targets
- * @param $message
- */
- public function SendBulk($targets, $message)
- {
- echo "Sending " . count($targets) . " bulk messages...
";
- foreach($targets as $target)
- {
- $this->wa->sendPresenceSubscription($target);
- $this->wa->pollMessage();
- $this->wa->sendMessageComposing($target);
- sleep(1);
- $this->wa->pollMessage();
- $this->wa->sendMessagePaused($target);
- static::$sendLock = true;
- echo "Sending message from " . $this->username . " to $target... ";
- $this->wa->sendMessage($target, $message);
- while(static::$sendLock)
- {
- //wait for server receipt
- sleep(1);
- }
- }
- echo "Finished sending bulk
";
- }
-}
diff --git a/examples/contactsync.php b/examples/contactsync.php
index 1f3851fe..5786ee39 100755
--- a/examples/contactsync.php
+++ b/examples/contactsync.php
@@ -5,16 +5,15 @@
$username = $_GET["phone"];
$password = $_GET["pass"];
-$u = $_GET["u"];
-if(!is_array($u))
-{
+$u = $_GET["u"];
+
+if (!is_array($u)) {
$u = array($u);
}
+
$numbers = array();
-foreach($u as $number)
-{
- if(substr($number, 0, 1) != "+")
- {
+foreach ($u as $number) {
+ if ($number[0] != "+") {
//add leading +
$number = "+$number";
}
@@ -27,19 +26,16 @@
*/
function onSyncResult($result)
{
- foreach($result->existing as $number)
- {
+ foreach ($result->existing as $number) {
echo "$number exists
";
}
- foreach($result->nonExisting as $number)
- {
+ foreach ($result->nonExisting as $number) {
echo "$number does not exist
";
}
- die();//to break out of the while(true) loop
+ die(); //to break out of the while(true) loop
}
-
-$wa = new WhatsProt($username, "", "WhatsApp", false);
+$wa = new WhatsProt($username, "WhatsApp", false);
//bind event handler
$wa->eventManager()->bind('onGetSyncResult', 'onSyncResult');
@@ -51,7 +47,6 @@ function onSyncResult($result)
$wa->sendSync($numbers);
//wait for response
-while(true)
-{
+while (true) {
$wa->pollMessage();
-}
\ No newline at end of file
+}
diff --git a/examples/exampleFunctional.php b/examples/exampleFunctional.php
index bcecdf4f..01534e0a 100755
--- a/examples/exampleFunctional.php
+++ b/examples/exampleFunctional.php
@@ -4,17 +4,11 @@
//Change to your time zone
date_default_timezone_set('Europe/Madrid');
-
-// phone number, deviceIdentity, and name.
-$options = getopt("d::", array("debug::"));
-$debug = (array_key_exists("debug", $options) || array_key_exists("d", $options)) ? true : false;
-
########## DO NOT COMMIT THIS FILE WITH YOUR CREDENTIALS ###########
///////////////////////CONFIGURATION///////////////////////
//////////////////////////////////////////////////////////
$username = "**your phone number**"; // Telephone number including the country code without '+' or '00'.
-$password = "**server generated whatsapp password**"; // A server generated Password you received from WhatsApp. This can NOT be manually created
-$identity = "**unique ID generated by WhatsApp client**"; // Obtained during registration with this API or using MissVenom (https://github.com/shirioko/MissVenom) to sniff from your phone.
+$password = "**server generated whatsapp password**"; // Use registerTool.php or exampleRegister.php to obtain your password
$nickname = "**your nickname**"; // This is the username (or nickname) displayed by WhatsApp clients.
$target = "**contact's phone number**"; // Destination telephone number including the country code without '+' or '00'.
$debug = false; // Set this to true, to see debug mode.
@@ -42,35 +36,34 @@ function onGetProfilePicture($from, $target, $type, $data)
} else {
$filename = $target . ".jpg";
}
- $filename = WhatsProt::PICTURES_FOLDER."/" . $filename;
- $fp = @fopen($filename, "w");
- if ($fp) {
- fwrite($fp, $data);
- fclose($fp);
- }
- echo "- Profile picture saved in /".WhatsProt::PICTURES_FOLDER."\n";
+ $filename = Constants::PICTURES_FOLDER. "/" . $filename;
+
+ file_put_contents($filename, $data);
+
+ echo "- Profile picture saved in " . Constants::PICTURES_FOLDER. "/" . $filename . "\n";
+}
+
+function onPresenceAvailable($username, $from)
+{
+ $dFrom = str_replace(array("@s.whatsapp.net","@g.us"), "", $from);
+ echo "<$dFrom is online>\n\n";
}
-function onPresenceReceived($username, $from, $type)
+function onPresenceUnavailable($username, $from, $last)
{
- $dFrom = str_replace(array("@s.whatsapp.net","@g.us"), "", $from);
- if($type == "available")
- echo "<$dFrom is online>\n\n";
- else
- echo "<$dFrom is offline>\n\n";
+ $dFrom = str_replace(array("@s.whatsapp.net","@g.us"), "", $from);
+ echo "<$dFrom is offline>\n\n";
}
echo "[] Logging in as '$nickname' ($username)\n";
//Create the whatsapp object and setup a connection.
-$w = new WhatsProt($username, $identity, $nickname, $debug);
+$w = new WhatsProt($username, $nickname, $debug);
$w->connect();
// Now loginWithPassword function sends Nickname and (Available) Presence
$w->loginWithPassword($password);
-echo "[*] Connected to WhatsApp\n\n";
-
//Retrieve large profile picture. Output is in /src/php/pictures/ (you need to bind a function
//to the event onProfilePicture so the script knows what to do.
$w->eventManager()->bind("onGetProfilePicture", "onGetProfilePicture");
@@ -78,8 +71,10 @@ function onPresenceReceived($username, $from, $type)
//Print when the user goes online/offline (you need to bind a function to the event onPressence
//so the script knows what to do)
-$w->eventManager()->bind("onPresence", "onPresenceReceived");
+$w->eventManager()->bind("onPresenceAvailable", "onPresenceAvailable");
+$w->eventManager()->bind("onPresenceUnavailable", "onPresenceUnavailable");
+echo "[*] Connected to WhatsApp\n\n";
//update your profile picture
$w->sendSetProfilePicture("demo/venom.jpg");
@@ -96,8 +91,6 @@ function onPresenceReceived($username, $from, $type)
//send Location
//$w->sendLocation($target, '4.948568', '52.352957');
-
-
// Implemented out queue messages and auto msgid
$w->sendMessage($target, "Guess the number :)");
$w->sendMessage($target, "Sent from WhatsApi at " . date('H:i'));
@@ -166,21 +159,23 @@ public function process($node)
{
// Example of process function, you have to guess a number (psss it's 5)
// If you guess it right you get a gift
- $text = $node->getChild('body');
- $text = $text->getData();
- if ($text && ($text == "5" || trim($text) == "5")) {
- $this->wp->sendMessageImage($this->target, "https://s3.amazonaws.com/f.cl.ly/items/2F3U0A1K2o051q1q1e1G/baby-nailed-it.jpg");
- $this->wp->sendMessage($this->target, "Congratulations you guessed the right number!");
- }
- elseif (ctype_digit($text)) {
- if( (int)$text != "5")
- $this->wp->sendMessage($this->target, "I'm sorry, try again!");
+ if ($node->getAttribute("type") == 'text')
+ {
+ $text = $node->getChild('body');
+ $text = $text->getData();
+ if ($text && ($text == "5" || trim($text) == "5")) {
+ $this->wp->sendMessageImage($this->target, "https://s3.amazonaws.com/f.cl.ly/items/2F3U0A1K2o051q1q1e1G/baby-nailed-it.jpg");
+ $this->wp->sendMessage($this->target, "Congratulations you guessed the right number!");
+ }
+ elseif (ctype_digit($text)) {
+ if ((int)$text != "5")
+ $this->wp->sendMessage($this->target, "I'm sorry, try again!");
+ }
+ $text = $node->getChild('body');
+ $text = $text->getData();
+ $notify = $node->getAttribute("notify");
+
+ echo "\n- ".$notify.": ".$text." ".date('H:i')."\n";
}
- $text = $node->getChild('body');
- $text = $text->getData();
- $notify = $node->getAttribute("notify");
-
- echo "\n- ".$notify.": ".$text." ".date('H:i')."\n";
-
}
}
diff --git a/examples/exampleRegister.php b/examples/exampleRegister.php
index f7122800..9a4d9c97 100755
--- a/examples/exampleRegister.php
+++ b/examples/exampleRegister.php
@@ -8,7 +8,6 @@
*
*/
-
require_once('../src/whatsprot.class.php');
/**
@@ -17,12 +16,10 @@
$debug = true;
$username = '34666554433'; // Telephone number including the country code without '+' or '00'.
-$identity = 'e807f1fcf82d132f9bb018ca6738a19f'; // Obtained during registration with this API or using MissVenom (https://github.com/shirioko/MissVenom) to sniff from your phone.
$nickname = 'John Doe'; // This is the username displayed by WhatsApp clients.
-
-// Create a instance of WhastPort.
-$w = new WhatsProt($username, $identity, $nickname, $debug);
+// Create an instance of WhatsProt.
+$w = new WhatsProt($username, $nickname, $debug);
// How to create an account __ONLY__ if you do not have a associated to our phone number.
// You can test your credentials with: $w->checkCredentials() (BUT ONLY ONLY IF YOU HAVE THE IDENTITY);
@@ -47,7 +44,6 @@
*/
$w->codeRequest('sms');
-
// You must wait until you receive a code from WhatsApp, either to your phone via sms
// or phonecall depending on what you selected above.
diff --git a/examples/registerTool.php b/examples/registerTool.php
new file mode 100644
index 00000000..e0be6770
--- /dev/null
+++ b/examples/registerTool.php
@@ -0,0 +1,54 @@
+codeRequest(trim($option));
+ } catch(Exception $e) {
+ echo $e->getMessage() . "\n";
+ exit(0);
+ }
+
+ echo "\n\nEnter the received code: ";
+ $code = str_replace("-", "", fgets(STDIN));
+
+ try {
+ $result = $w->codeRegister(trim($code));
+ echo "\nYour username is: ".$result->login."\n";
+ echo "Your password is: ".$result->pw."\n";
+ } catch(Exception $e) {
+ echo $e->getMessage() . "\n";
+ exit(0);
+ }
+} else {
+ try {
+ $result = $w->checkCredentials();
+ } catch (Exception $e) {
+ echo $e->getMessage() . "\n";
+ exit(0);
+ }
+}
diff --git a/examples/resetAccount.php b/examples/resetAccount.php
new file mode 100644
index 00000000..8ba128af
--- /dev/null
+++ b/examples/resetAccount.php
@@ -0,0 +1,45 @@
+connect();
+try
+{
+ $w->loginWithPassword($pw);
+}
+catch (Exception $e)
+{
+ echo "Failed to login, make sure your account is not blocked (use blockChecker.php) or check if your password is right\n\n";
+ exit(0);
+}
+
+$w->sendRemoveAccount();
+
+$i = 0;
+for ($i; $i < 5; $i++)
+{
+ $w->pollMessage();
+}
+
+$w->disconnect();
+
+unlink("../src/wadata/id.$username.dat");
+unlink("../src/wadata/nextChallenge.$username.dat");
+
+echo "\n\n OK! Now use registerTool.php \n\n";
diff --git a/examples/whatsapp.php b/examples/whatsapp.php
index 18210521..bfbe2327 100755
--- a/examples/whatsapp.php
+++ b/examples/whatsapp.php
@@ -37,7 +37,6 @@
//Config Template
//$config['YourFirstName'] = array(
-// 'id' => '',
// 'fromNumber' => '',
// 'nick' => '',
// 'waPassword' => ""
@@ -51,7 +50,6 @@
//
//Example
//$config['Jonathan'] = array(
-// 'id' => 'e807f1fcf82d132f9bb018ca6738a19f',
// 'fromNumber' => '441234567890',
// 'nick' => "Jonathan's iPhone",
// 'waPassword' => "EsdfsawS+/ffdskjsdhwebdgxbs=",
@@ -60,7 +58,6 @@
//);
$config['YOURNAME'] = array(
- 'id' => 'e807f1fcf82d132f9bb018ca6738a19f',
'fromNumber' => '441234567890',
'nick' => "YOURNICKNAME",
'waPassword' => "EsdfsawS+/ffdskjsdhwebdgxbs=",
@@ -325,7 +322,7 @@ public function __construct(array $config)
$this->nick = $this->config[$this->from]['nick'];
$this->password = $this->config[$this->from]['waPassword'];
- $this->wa = new WhatsProt($this->number, $this->id, $this->nick, false);
+ $this->wa = new WhatsProt($this->number, $this->nick, false);
$this->wa->eventManager()->bind('onGetMessage', array($this, 'processReceivedMessage'));
$this->wa->eventManager()->bind('onConnect', array($this, 'connected'));
$this->wa->eventManager()->bind('onGetGroups', array($this, 'processGroupArray'));
@@ -722,77 +719,91 @@ private function webLogout()
/**
* Show Web app Login page
*
- * @return void
+ * @return string
*/
- public function showWebLoginForm($error = null)
+ public function showWebLoginForm()
{
ob_start();
?>
-
-
- Personal Whatsapp Login
-
-
-
-
-
-
-
-
-
-
-
-
-
-
";
- }
- ?>
-
-
-
-
-
-
-
+
+
+
+ Personal Whatsapp Login
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Whatsapp! Login
+
+
+
+
+
+
+
-
-
-
- Personal Whatsapp
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ Personal Whatsapp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Whatsapp! Messenger
+
+
+
+
+
+
+
+
key = null;
+ }
+
+ public function setKey($key)
+ {
+ $this->key = $key;
+ }
+
+ public function nextTree($input = null)
+ {
+ if ($input != null) {
+ $this->input = $input;
+ }
+ $firstByte = $this->peekInt8();
+ $stanzaFlag = ($firstByte & 0xF0) >> 4;
+ $stanzaSize = $this->peekInt16(1) | (($firstByte & 0x0F) << 16);
+ if ($stanzaSize > strlen($this->input)) {
+ throw new Exception("Incomplete message $stanzaSize != " . strlen($this->input));
+ }
+ $this->readInt24();
+ if ($stanzaFlag & 8) {
+ if (isset($this->key)) {
+ $realSize = $stanzaSize - 4;
+ $this->input = $this->key->DecodeMessage($this->input, $realSize, 0, $realSize);// . $remainingData;
+ } else {
+ throw new Exception("Encountered encrypted message, missing key");
+ }
+ }
+ if ($stanzaSize > 0) {
+ return $this->nextTreeInternal();
+ }
+
+ return null;
+ }
+
+ protected function readNibble() {
+ $byte = $this->readInt8();
+
+ $ignoreLastNibble = (bool) ($byte & 0x80);
+ $size = ($byte & 0x7f);
+ $nrOfNibbles = $size * 2 - (int) $ignoreLastNibble;
+
+ $data = $this->fillArray($size);
+ $string = '';
+
+ for ($i = 0; $i < $nrOfNibbles; $i++) {
+ $byte = $data[(int) floor($i / 2)];
+ $ord = ord($byte);
+
+ $shift = 4 * (1 - $i % 2);
+ $decimal = ($ord & (15 << $shift)) >> $shift;
+
+ switch ($decimal) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ $string .= $decimal;
+ break;
+ case 10:
+ case 11:
+ $string .= chr($decimal - 10 + 45);
+ break;
+ default:
+ throw new Exception("Bad nibble: $decimal");
+ }
+ }
+
+ return $string;
+ }
+
+ protected function getToken($token)
+ {
+ $ret = "";
+ $subdict = false;
+ TokenMap::GetToken($token, $subdict, $ret);
+ if (!$ret) {
+ $token = $this->readInt8();
+ TokenMap::GetToken($token, $subdict, $ret);
+ if (!$ret) {
+ throw new Exception("BinTreeNodeReader->getToken: Invalid token $token");
+ }
+ }
+
+ return $ret;
+ }
+
+ protected function readString($token)
+ {
+ $ret = "";
+
+ if ($token == -1) {
+ throw new Exception("BinTreeNodeReader->readString: Invalid token $token");
+ }
+
+ if (($token > 2) && ($token < 0xf5)) {
+ $ret = $this->getToken($token);
+ } elseif ($token == 0) {
+ $ret = "";
+ } elseif ($token == 0xfc) {
+ $size = $this->readInt8();
+ $ret = $this->fillArray($size);
+ } elseif ($token == 0xfd) {
+ $size = $this->readInt24();
+ $ret = $this->fillArray($size);
+ } elseif ($token == 0xfa) {
+ $user = $this->readString($this->readInt8());
+ $server = $this->readString($this->readInt8());
+ if ((strlen($user) > 0) && (strlen($server) > 0)) {
+ $ret = $user . "@" . $server;
+ } elseif (strlen($server) > 0) {
+ $ret = $server;
+ }
+ } elseif ($token == 0xff) {
+ $ret = $this->readNibble();
+ }
+
+ return $ret;
+ }
+
+ protected function readAttributes($size)
+ {
+ $attributes = array();
+ $attribCount = ($size - 2 + $size % 2) / 2;
+
+ for ($i = 0; $i < $attribCount; $i++) {
+ $key = $this->readString($this->readInt8());
+ $value = $this->readString($this->readInt8());
+ $attributes[$key] = $value;
+ }
+
+ return $attributes;
+ }
+
+ protected function nextTreeInternal()
+ {
+ $token = $this->readInt8();
+ $size = $this->readListSize($token);
+ $token = $this->readInt8();
+ if ($token == 1) {
+ $attributes = $this->readAttributes($size);
+
+ return new ProtocolNode("start", $attributes, null, "");
+ } elseif ($token == 2) {
+ return null;
+ }
+ $tag = $this->readString($token);
+ $attributes = $this->readAttributes($size);
+ if (($size % 2) == 1) {
+ return new ProtocolNode($tag, $attributes, null, "");
+ }
+ $token = $this->readInt8();
+ if ($this->isListTag($token)) {
+ return new ProtocolNode($tag, $attributes, $this->readList($token), "");
+ }
+
+ return new ProtocolNode($tag, $attributes, null, $this->readString($token));
+ }
+
+ protected function isListTag($token)
+ {
+ return ($token == 248 || $token == 0 || $token == 249);
+ }
+
+ protected function readList($token)
+ {
+ $size = $this->readListSize($token);
+ $ret = array();
+ for ($i = 0; $i < $size; $i++) {
+ array_push($ret, $this->nextTreeInternal());
+ }
+
+ return $ret;
+ }
+
+ protected function readListSize($token)
+ {
+ if ($token == 0xf8) {
+ return $this->readInt8();
+ } elseif ($token == 0xf9) {
+ return $this->readInt16();
+ }
+
+ throw new Exception("BinTreeNodeReader->readListSize: Invalid token $token");
+ }
+
+ protected function peekInt24($offset = 0)
+ {
+ $ret = 0;
+ if (strlen($this->input) >= (3 + $offset)) {
+ $ret = ord(substr($this->input, $offset, 1)) << 16;
+ $ret |= ord(substr($this->input, $offset + 1, 1)) << 8;
+ $ret |= ord(substr($this->input, $offset + 2, 1)) << 0;
+ }
+
+ return $ret;
+ }
+
+ protected function readInt24()
+ {
+ $ret = $this->peekInt24();
+ if (strlen($this->input) >= 3) {
+ $this->input = substr($this->input, 3);
+ }
+
+ return $ret;
+ }
+
+ protected function peekInt16($offset = 0)
+ {
+ $ret = 0;
+ if (strlen($this->input) >= (2 + $offset)) {
+ $ret = ord(substr($this->input, $offset, 1)) << 8;
+ $ret |= ord(substr($this->input, $offset + 1, 1)) << 0;
+ }
+
+ return $ret;
+ }
+
+ protected function readInt16()
+ {
+ $ret = $this->peekInt16();
+ if ($ret > 0) {
+ $this->input = substr($this->input, 2);
+ }
+
+ return $ret;
+ }
+
+ protected function peekInt8($offset = 0)
+ {
+ $ret = 0;
+ if (strlen($this->input) >= (1 + $offset)) {
+ $sbstr = substr($this->input, $offset, 1);
+ $ret = ord($sbstr);
+ }
+
+ return $ret;
+ }
+
+ protected function readInt8()
+ {
+ $ret = $this->peekInt8();
+ if (strlen($this->input) >= 1) {
+ $this->input = substr($this->input, 1);
+ }
+
+ return $ret;
+ }
+
+ protected function fillArray($len)
+ {
+ $ret = "";
+ if (strlen($this->input) >= $len) {
+ $ret = substr($this->input, 0, $len);
+ $this->input = substr($this->input, $len);
+ }
+
+ return $ret;
+ }
+}
diff --git a/src/BinTreeNodeWriter.php b/src/BinTreeNodeWriter.php
new file mode 100644
index 00000000..a6f8c7fa
--- /dev/null
+++ b/src/BinTreeNodeWriter.php
@@ -0,0 +1,213 @@
+key = null;
+ }
+
+ public function setKey($key)
+ {
+ $this->key = $key;
+ }
+
+ public function StartStream($domain, $resource)
+ {
+ $attributes = array();
+
+ $attributes["to"] = $domain;
+ $attributes["resource"] = $resource;
+ $this->writeListStart(count($attributes) * 2 + 1);
+
+ $this->output .= "\x01";
+ $this->writeAttributes($attributes);
+
+ return "WA" . $this->writeInt8(1) . $this->writeInt8(5) . $this->flushBuffer();
+ }
+
+ /**
+ * @param ProtocolNode $node
+ * @param bool $encrypt
+ *
+ * @return string
+ */
+ public function write($node, $encrypt = true)
+ {
+ if ($node == null) {
+ $this->output .= "\x00";
+ } else {
+ $this->writeInternal($node);
+ }
+
+ return $this->flushBuffer($encrypt);
+ }
+
+ /**
+ * @param ProtocolNode $node
+ */
+ protected function writeInternal($node)
+ {
+ $len = 1;
+ if ($node->getAttributes() != null) {
+ $len += count($node->getAttributes()) * 2;
+ }
+ if (count($node->getChildren()) > 0) {
+ $len += 1;
+ }
+ if (strlen($node->getData()) > 0) {
+ $len += 1;
+ }
+ $this->writeListStart($len);
+ $this->writeString($node->getTag());
+ $this->writeAttributes($node->getAttributes());
+ if (strlen($node->getData()) > 0) {
+ $this->writeBytes($node->getData());
+ }
+ if ($node->getChildren()) {
+ $this->writeListStart(count($node->getChildren()));
+ foreach ($node->getChildren() as $child) {
+ $this->writeInternal($child);
+ }
+ }
+ }
+
+ protected function parseInt24($data)
+ {
+ $ret = ord(substr($data, 0, 1)) << 16;
+ $ret |= ord(substr($data, 1, 1)) << 8;
+ $ret |= ord(substr($data, 2, 1)) << 0;
+ return $ret;
+ }
+
+ protected function flushBuffer($encrypt = true)
+ {
+ $size = strlen($this->output);
+ $data = $this->output;
+ if ($this->key != null && $encrypt) {
+ $bsize = $this->getInt24($size);
+ //encrypt
+ $data = $this->key->EncodeMessage($data, $size, 0, $size);
+ $len = strlen($data);
+ $bsize[0] = chr((8 << 4) | (($len & 16711680) >> 16));
+ $bsize[1] = chr(($len & 65280) >> 8);
+ $bsize[2] = chr($len & 255);
+ $size = $this->parseInt24($bsize);
+ }
+ $ret = $this->writeInt24($size) . $data;
+ $this->output = '';
+ return $ret;
+ }
+
+ protected function getInt24($length)
+ {
+ $ret = '';
+ $ret .= chr((($length & 0xf0000) >> 16));
+ $ret .= chr((($length & 0xff00) >> 8));
+ $ret .= chr(($length & 0xff));
+ return $ret;
+ }
+
+ protected function writeToken($token)
+ {
+ if ($token < 0xf5) {
+ $this->output .= chr($token);
+ } elseif ($token <= 0x1f4) {
+ $this->output .= "\xfe" . chr($token - 0xf5);
+ }
+ }
+
+ protected function writeJid($user, $server)
+ {
+ $this->output .= "\xfa";
+ if (strlen($user) > 0) {
+ $this->writeString($user);
+ } else {
+ $this->writeToken(0);
+ }
+ $this->writeString($server);
+ }
+
+ protected function writeInt8($v)
+ {
+ $ret = chr($v & 0xff);
+
+ return $ret;
+ }
+
+ protected function writeInt16($v)
+ {
+ $ret = chr(($v & 0xff00) >> 8);
+ $ret .= chr(($v & 0x00ff) >> 0);
+
+ return $ret;
+ }
+
+ protected function writeInt24($v)
+ {
+ $ret = chr(($v & 0xff0000) >> 16);
+ $ret .= chr(($v & 0x00ff00) >> 8);
+ $ret .= chr(($v & 0x0000ff) >> 0);
+
+ return $ret;
+ }
+
+ protected function writeBytes($bytes)
+ {
+ $len = strlen($bytes);
+ if ($len >= 0x100) {
+ $this->output .= "\xfd";
+ $this->output .= $this->writeInt24($len);
+ } else {
+ $this->output .= "\xfc";
+ $this->output .= $this->writeInt8($len);
+ }
+ $this->output .= $bytes;
+ }
+
+ protected function writeString($tag)
+ {
+ $intVal = -1;
+ $subdict = false;
+ if (TokenMap::TryGetToken($tag, $subdict, $intVal)) {
+ if ($subdict) {
+ $this->writeToken(236);
+ }
+ $this->writeToken($intVal);
+ return;
+ }
+ $index = strpos($tag, '@');
+ if ($index) {
+ $server = substr($tag, $index + 1);
+ $user = substr($tag, 0, $index);
+ $this->writeJid($user, $server);
+ } else {
+ $this->writeBytes($tag);
+ }
+ }
+
+ protected function writeAttributes($attributes)
+ {
+ if ($attributes) {
+ foreach ($attributes as $key => $value) {
+ $this->writeString($key);
+ $this->writeString($value);
+ }
+ }
+ }
+
+ protected function writeListStart($len)
+ {
+ if ($len == 0) {
+ $this->output .= "\x00";
+ } elseif ($len < 256) {
+ $this->output .= "\xf8" . chr($len);
+ } else {
+ $this->output .= "\xf9" . $this->writeInt16($len);
+ }
+ }
+}
diff --git a/src/Constants.php b/src/Constants.php
new file mode 100644
index 00000000..f5438559
--- /dev/null
+++ b/src/Constants.php
@@ -0,0 +1,26 @@
+db = new \PDO("sqlite:" . $fileName, null, null, array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
+ if ($createTable)
+ {
+ $this->db->exec('CREATE TABLE messages (`from` TEXT, `to` TEXT, message TEXT, id TEXT, t TEXT)');
+ }
+ }
+
+ public function saveMessage($from, $to, $txt, $id, $t)
+ {
+ $sql = 'INSERT INTO messages (`from`, `to`, message, id, t) VALUES (:from, :to, :message, :messageId, :t)';
+ $query = $this->db->prepare($sql);
+
+ $query->execute(
+ array(
+ ':from' => $from,
+ ':to' => $to,
+ ':message' => $txt,
+ ':messageId' => $id,
+ ':t' => $t
+ )
+ );
+ }
+}
diff --git a/src/WhatsAppEvent.php b/src/WhatsAppEvent.php
deleted file mode 100755
index b291ca6e..00000000
--- a/src/WhatsAppEvent.php
+++ /dev/null
@@ -1,804 +0,0 @@
-addEventListener(new WhatsAppEventListenerLegacyAdapter($event,$listener));
- }
-
- /**
- * Executes all the binded callbacks when the event is fired. Don't this method,
- * this is included for backwards compatibility only.
- *
- * @param string $event
- * Name of the event.
- * @param array $arguments
- * The arguments to pass to each callback.
- *
- * @deprecated Fire events specifically by name.
- */
- public function fire($event, $arguments = array())
- {
- // For backwards compatibility only.
- foreach( self::$event_listeners as $event_listener ) {
- call_user_func_array(array($event_listener, $event), $arguments);
- }
- }
-
- /**
- * Fires the callback for each listener.
- *
- * @param function $callbackEvent
- */
- private function fireCallback($callbackEvent) {
- array_map($callbackEvent, self::$event_listeners);
- }
-
- // The supported events:
- function fireClose(
- $phone,
- $error
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $error) {
- $listener->onClose($phone, $error);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireCodeRegister(
- $phone,
- $login,
- $pw,
- $type,
- $expiration,
- $kind,
- $price,
- $cost,
- $currency,
- $price_expiration
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $login, $pw, $type, $expiration, $kind, $price, $cost, $currency, $price_expiration) {
- $listener->onCodeRegister($phone, $login, $pw, $type, $expiration, $kind, $price, $cost, $currency, $price_expiration);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireCodeRegisterFailed(
- $phone,
- $status,
- $reason,
- $retry_after
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $status, $reason, $retry_after) {
- $listener->onCodeRegisterFailed($phone, $status, $reason, $retry_after);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireCodeRequest(
- $phone,
- $method,
- $length
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $method, $length) {
- $listener->onCodeRequest($phone, $method, $length);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireCodeRequestFailed(
- $phone,
- $method,
- $reason,
- $value
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $method, $reason, $value) {
- $listener->onCodeRequestFailed($phone, $method, $reason, $value);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireCodeRequestFailedTooRecent(
- $phone,
- $method,
- $reason,
- $retry_after
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $method, $reason, $retry_after){
- $listener->onCodeRequestFailedTooRecent($phone, $method, $reason, $retry_after);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireConnect(
- $phone,
- $socket
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $socket) {
- $listener->onConnect($phone, $socket);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireConnectError(
- $phone,
- $socket
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $socket){
- $listener->onConnectError($phone, $socket);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireCredentialsBad(
- $phone,
- $status,
- $reason
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $status, $reason) {
- $listener->onCredentialsBad($phone, $status, $reason);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireCredentialsGood(
- $phone,
- $login,
- $pw,
- $type,
- $expiration,
- $kind,
- $price,
- $cost,
- $currency,
- $price_expiration
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $login, $pw, $type, $expiration, $kind, $price, $cost, $currency, $price_expiration) {
- $listener->onCredentialsGood($phone, $login, $pw, $type, $expiration, $kind, $price, $cost, $currency, $price_expiration);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireDisconnect(
- $phone,
- $socket
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $socket) {
- $listener->onDisconnect($phone, $socket);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireDissectPhone(
- $phone,
- $country,
- $cc,
- $mcc,
- $lc,
- $lg
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $country, $cc, $mcc, $lc, $lg) {
- $listener->onDissectPhone($phone, $country, $cc, $mcc, $lc, $lg);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireDissectPhoneFailed(
- $phone
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone) {
- $listener->onDissectPhoneFailed($phone);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGetAudio(
- $phone,
- $from,
- $msgid,
- $type,
- $time,
- $name,
- $size,
- $url,
- $file,
- $mimetype,
- $filehash,
- $duration,
- $acodec
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $from, $msgid, $type, $time, $name, $size, $url, $file, $mimetype, $filehash, $duration, $acodec) {
- $listener->onGetAudio($phone, $from, $msgid, $type, $time, $name, $size, $url, $file, $mimetype, $filehash, $duration, $acodec);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGetError(
- $phone,
- $id,
- $error
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $id, $error) {
- $listener->onGetError($phone, $id, $error);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGetGroups(
- $phone,
- $groupList
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $groupList) {
- $listener->onGetGroups($phone, $groupList);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGetGroupsInfo(
- $phone,
- $groupList
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $groupList) {
- $listener->onGetGroupsInfo($phone, $groupList);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGetGroupsSubject(
- $phone,
- $gId,
- $time,
- $author,
- $participant,
- $name,
- $subject
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $gId, $time, $author, $participant, $name, $subject) {
- $listener->onGetGroupsSubject($phone, $gId, $time, $author, $participant, $name, $subject);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGetImage(
- $phone,
- $from,
- $msgid,
- $type,
- $time,
- $name,
- $size,
- $url,
- $file,
- $mimetype,
- $filehash,
- $width,
- $height,
- $thumbnail
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $from, $msgid, $type, $time, $name, $size, $url, $file, $mimetype, $filehash, $width, $height, $thumbnail) {
- $listener->onGetImage($phone, $from, $msgid, $type, $time, $name, $size, $url, $file, $mimetype, $filehash, $width, $height, $thumbnail);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGetLocation(
- $phone,
- $from,
- $msgid,
- $type,
- $time,
- $name,
- $place_name,
- $longitude,
- $latitude,
- $url,
- $thumbnail
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $from, $msgid, $type, $time, $name, $place_name, $longitude, $latitude, $url, $thumbnail) {
- $listener->onGetLocation($phone, $from, $msgid, $type, $time, $name, $place_name, $longitude, $latitude, $url, $thumbnail);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGetMessage(
- $phone,
- $from,
- $msgid,
- $type,
- $time,
- $name,
- $message
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $from, $msgid, $type, $time, $name, $message) {
- $listener->onGetMessage($phone, $from, $msgid, $type, $time, $name, $message);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGetGroupMessage(
- $phone,
- $from,
- $author,
- $msgid,
- $type,
- $time,
- $name,
- $message
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $from, $author, $msgid, $type, $time, $name, $message) {
- $listener->onGetGroupMessage($phone, $from, $author, $msgid, $type, $time, $name, $message);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGetGroupParticipants(
- $phone,
- $groupId,
- $groupList
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $groupId, $groupList) {
- $listener->onGetGroupParticipants($phone, $groupId, $groupList);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGetPrivacyBlockedList(
- $phone,
- $children
- /*
- $data,
- $onGetProfilePicture,
- $phone,
- $from,
- $type,
- $thumbnail
- */
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $children) {
- $listener->onGetPrivacyBlockedList($phone, $children);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGetProfilePicture(
- $phone,
- $from,
- $type,
- $thumbnail
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $from, $type, $thumbnail) {
- $listener->onGetProfilePicture($phone, $from, $type, $thumbnail);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGetRequestLastSeen(
- $phone,
- $from,
- $msgid,
- $sec
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $from, $msgid, $sec) {
- $listener->onGetRequestLastSeen($phone, $from, $msgid, $sec);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGetServerProperties(
- $phone,
- $version,
- $properties
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $version, $properties) {
- $listener->onGetServerProperties($phone, $version, $properties);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGetStatus(
- $phone,
- $from,
- $type,
- $id,
- $t,
- $status
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $from, $type, $id, $t, $status) {
- $listener->onGetStatus($phone, $from, $type, $id, $t, $status);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGetvCard(
- $phone,
- $from,
- $msgid,
- $type,
- $time,
- $name,
- $contact,
- $vcard
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $from, $msgid, $type, $time, $name, $contact, $vcard){
- $listener->onGetvCard($phone, $from, $msgid, $type, $time, $name, $contact, $vcard);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGetVideo(
- $phone,
- $from,
- $msgid,
- $type,
- $time,
- $name,
- $url,
- $file,
- $size,
- $mimetype,
- $filehash,
- $duration,
- $vcodec,
- $acodec,
- $thumbnail
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $from, $msgid, $type, $time, $name, $url, $file, $size, $mimetype, $filehash, $duration, $vcodec, $acodec, $thumbnail){
- $listener->onGetVideo($phone, $from, $msgid, $type, $time, $name, $url, $file, $size, $mimetype, $filehash, $duration, $vcodec, $acodec, $thumbnail);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGroupsChatCreate(
- $phone,
- $gId
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $gId) {
- $listener->onGroupsChatCreate($phone, $gId);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGroupsChatEnd(
- $phone,
- $gId
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $gId) {
- $listener->onGroupsChatEnd($phone, $gId);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGroupsParticipantsAdd(
- $phone,
- $groupId,
- $participant
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $groupId, $participant) {
- $listener->onGroupsParticipantsAdd($phone, $groupId, $participant);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGroupsParticipantsRemove(
- $phone,
- $groupId,
- $participant,
- $author
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $groupId, $participant, $author) {
- $listener->onGroupsParticipantsRemove($phone, $groupId, $participant, $author);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireLogin(
- $phone
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone) {
- $listener->onLogin($phone);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireLoginFailed(
- $phone,
- $tag
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $tag) {
- $listener->onLoginFailed($phone, $tag);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireMessageComposing(
- $phone,
- $from,
- $msgid,
- $type,
- $time
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $from, $msgid, $type, $time) {
- $listener->onMessageComposing($phone, $from, $msgid, $type, $time);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireMediaMessageSent(
- $phone,
- $to,
- $id,
- $filetype,
- $url,
- $filename,
- $filesize,
- $filehash,
- $icon
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $to, $id, $filetype, $url, $filename, $filesize, $filehash, $icon) {
- $listener->onMediaMessageSent($phone, $to, $id, $filetype, $url, $filename, $filesize, $filehash, $icon);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireMediaUploadFailed(
- $phone,
- $id,
- $node,
- $messageNode,
- $reason
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $id, $node, $messageNode, $reason) {
- $listener->onMediaUploadFailed($phone, $id, $node, $messageNode, $reason);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireMessagePaused(
- $phone,
- $from,
- $msgid,
- $type,
- $time
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $from, $msgid, $type, $time) {
- $listener->onMessagePaused($phone, $from, $msgid, $type, $time);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireMessageReceivedClient(
- $phone,
- $from,
- $msgid,
- $type,
- $time
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $from, $msgid, $type, $time) {
- $listener->onMessageReceivedClient($phone, $from, $msgid, $type, $time);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireMessageReceivedServer(
- $phone,
- $from,
- $msgid,
- $type
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $from, $msgid, $type) {
- $listener->onMessageReceivedServer($phone, $from, $msgid, $type);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function firePing(
- $phone,
- $msgid
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $msgid) {
- $listener->onPing($phone, $msgid);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function firePresence(
- $phone,
- $from,
- $type
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $from, $type) {
- $listener->onPresence($phone, $from, $type);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireProfilePictureChanged(
- $phone,
- $from,
- $id,
- $t
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $from, $id, $t) {
- $listener->onProfilePictureChanged($phone, $from, $id, $t);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireProfilePictureDeleted(
- $phone,
- $from,
- $id,
- $t
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $from, $id, $t) {
- $listener->onProfilePictureDeleted($phone, $from, $id, $t);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireSendMessageReceived(
- $phone,
- $id,
- $from,
- $type
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $id, $from, $type) {
- $listener->onSendMessageReceived($phone, $id, $from, $type);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireSendPong(
- $phone,
- $msgid
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $msgid) {
- $listener->onSendPong($phone, $msgid);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireSendMessage(
- $phone,
- $targets,
- $id,
- $node
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $targets, $id, $node) {
- $listener->onSendMessage($phone, $targets, $id, $node);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireSendPresence(
- $phone,
- $type,
- $name
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $type, $name) {
- $listener->onSendPresence($phone, $type, $name);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireSendStatusUpdate(
- $phone,
- $msg
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $msg) {
- $listener->onSendStatusUpdate($phone, $msg);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireUploadFile(
- $phone,
- $name,
- $url
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $name, $url) {
- $listener->onUploadFile($phone, $name, $url);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireUploadFileFailed(
- $phone,
- $name
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($phone, $name) {
- $listener->onUploadFileFailed($phone, $name);
- };
- $this->fireCallback($callbackEvent);
- }
-
- /**
- * @param $result SyncResult
- */
- function fireGetSyncResult(
- $result
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($result) {
- $listener->onGetSyncResult($result);
- };
- $this->fireCallback($callbackEvent);
- }
-
- function fireGetReceipt(
- $from,
- $id,
- $offline,
- $retry
- ) {
- $callbackEvent = function(WhatsAppEventListener $listener) use ($from, $id, $offline, $retry) {
- $listener->onGetReceipt($from, $id, $offline, $retry);
- };
- $this->fireCallback($callbackEvent);
- }
-
-}
diff --git a/src/countries.csv b/src/countries.csv
index a3d79597..a9e318bb 100755
--- a/src/countries.csv
+++ b/src/countries.csv
@@ -1,254 +1,254 @@
-"Afghanistan",93,412,"AF","ps"
-"Albania",355,276,"AL","sq"
-"Alberta",1403,302,"CA","en"
-"Alberta",1780,302,"CA","en"
-"Algeria",213,603,"DZ","ar"
-"Andorra",376,213,"AD","ca"
-"Angola",244,631,"AO","pt"
-"Anguilla",1264,"365","AI","en"
-"Antarctica (Australian bases)",6721,232,"AQ","en"
-"Antigua and Barbuda",1268,"344","AG","en"
-"Argentina",54,722,"AR","es"
-"Armenia",374,283,"AM","hy"
-"Aruba",297,363,"AW","nl"
-"Ascension",247,658,"AC","en"
-"Australia",61,505,"AU","en"
-"Austria",43,232,"AT","de"
-"Azerbaijan",994,400,"AZ","az"
-"Bahamas",1242,"364","BS","en"
-"Bahrain",973,426,"BH","ar"
-"Bangladesh",880,470,"BD","bn"
-"Barbados",1246,"342","BB","en"
-"Belarus",375,257,"BY","be"
-"Belgium",32,206,"BE","nl"
-"Belize",501,702,"BZ","es"
-"Benin",229,616,"BJ","fr"
-"Bermuda",1441,"350","BM","en"
-"Bhutan",975,402,"BT","dz"
-"Bolivia",591,736,"BO","es"
-"Bosnia and Herzegovina",387,218,"BA","bs"
-"Botswana",267,652,"BW","en"
-"Brazil",55,724,"BR","pt"
-"British Columbia", 1250,302,"CA","en"
-"British Columbia", 1604,302,"CA","en"
-"British Columbia", 1778,302,"CA","en"
-"British Indian Ocean Territory",246,348,"IO","en"
-"British Virgin Islands",1284,"348","GB","en"
-"Brunei",673,528,"BN","ms"
-"Bulgaria",359,284,"BG","bg"
-"Burkina Faso",226,613,"BF","fr"
-"Burundi",257,642,"BI","rn"
-"Cambodia",855,456,"KH","km"
-"Cameroon",237,624,"CM","fr"
-"Cape Verde",238,625,"CV","pt"
-"Cayman Islands",1345,"346","GB","en"
-"Central African Republic",236,623,"CF","sg"
-"Chad",235,622,"TD","fr"
-"Chile",56,730,"CL","es"
-"China",86,"460|461","CN","en"
-"Colombia",57,732,"CO","es"
-"Comoros",269,654,"KM","fr"
-"Democratic Republic of the Congo",243,630,"CD","fr"
-"Republic of the Congo",242,629,"CG","fr"
-"Cook Islands",682,548,"CK","en"
-"Costa Rica",506,658,"CR","es"
-"Cote d'Ivoire",712,"612","CI","fr"
-"Croatia",385,219,"HR","hr"
-"Cuba",53,368,"CU","es"
-"Cyprus",357,280,"CY","el"
-"Czech Republic",420,230,"CZ","cs"
-"Denmark",45,238,"DK","da"
-"Djibouti",253,638,"DJ","fr"
-"Dominica",1767,"366","DM","en"
-"Dominican Republic",1809,"370","DO","es"
-"Dominican Republic",1829,"370","DO","en"
-"East Timor",670,514,"TL","pt"
-"Ecuador",593,740,"EC","es"
-"Egypt",20,602,"EG","ar"
-"El Salvador",503,706,"SV","es"
-"Equatorial Guinea",240,627,"GQ","es"
-"Eritrea",291,657,"ER","ti"
-"Estonia",372,248,"EE","et"
-"Ethiopia",251,636,"ET","am"
-"Falkland Islands",500,750,"FK","en"
-"Faroe Islands",298,288,"FO","fo"
-"Fiji",679,542,"FJ","en"
-"Finland",358,244,"FI","fi"
-"France",33,208,"FR","fr"
-"French Guiana",594,742,"GF","fr"
-"French Polynesia",689,547,"PF","fr"
-"Gabon",241,628,"GA","fr"
-"Gambia",220,607,"GM","en"
-"Gaza Strip",970,0,"PS","ar"
-"Georgia",995,282,"GE","ka"
-"Germany",49,262,"DE","de"
-"Ghana",233,620,"GH","ak"
-"Gibraltar",350,266,"GI","en"
-"Greece",30,202,"GR","el"
-"Greenland",299,290,"GL","kl"
-"Grenada",1473,"352","GD","en"
-"Guadeloupe",590,340,"GP","fr"
-"Guam",1671,"535","GU","en"
-"Guatemala",502,704,"GT","es"
-"Guinea",224,611,"GN","fr"
-"Guinea-Bissau",245,632,"GW","pt"
-"Guyana",592,738,"GY","pt"
-"Haiti",509,372,"HT","fr"
-"Honduras",504,708,"HN","es"
-"Hong Kong",852,454,"HK","zh"
-"Hungary",36,216,"HU","hu"
-"Iceland",354,274,"IS","is"
-"India",91,"404|405|406","IN","hi"
-"Indonesia",62,510,"ID","id"
-"Iraq",964,418,"IQ","ar"
-"Iran",98,432,"IR","fa"
-"Ireland (Eire)",353,272,"IE","en"
-"Israel",972,425,"IL","he"
-"Italy",39,222,"IT","it"
-"Jamaica",1876,"338","JM","en"
-"Japan",81,"440|441","JP","ja"
-"Jordan",962,416,"JO","ar"
-"Kazakhstan",7,401,"KZ","kk"
-"Kenya",254,639,"KE","sw"
-"Kiribati",686,545,"KI","en"
-"Kuwait",965,419,"KW","ar"
-"Kyrgyzstan",996,437,"KG","ky"
-"Laos",856,457,"LA","lo"
-"Latvia",371,247,"LV","lv"
-"Lebanon",961,415,"LB","ar"
-"Lesotho",266,651,"LS","st"
-"Liberia",231,618,"LR","en"
-"Libya",218,606,"LY","ar"
-"Liechtenstein",423,295,"LI","de"
-"Lithuania",370,246,"LT","lt"
-"Luxembourg",352,270,"LU","fr"
-"Macau",853,455,"MO","pt"
-"Republic of Macedonia",389,294,"MK","mk"
-"Madagascar",261,646,"MG","mg"
-"Malawi",265,650,"MW","ny"
-"Malaysia",60,502,"MY","en"
-"Maldives",960,472,"MV","dv"
-"Mali",223,610,"ML","fr"
-"Malta",356,278,"MT","mt"
-"Manitoba",1204,302,"CA","en"
-"Marshall Islands",692,551,"MH","mh"
-"Martinique",596,340,"MQ","fr"
-"Mauritania",222,609,"MR","ar"
-"Mauritius",230,617,"MU","en"
-"Mayotte",262,654,"YT","fr"
-"Mexico",52,334,"MX","es"
-"Federated States of Micronesia",691,550,"FM","en"
-"Moldova",373,259,"MD","ru"
-"Monaco",377,212,"MC","fr"
-"Mongolia",976,428,"MN","mn"
-"Montenegro",382,297,"ME","sr"
-"Montserrat",1664,"354",MS,"en"
-"Morocco",212,"604","MA","ar"
-"Mozambique",258,643,"MZ","pt"
-"Myanmar",95,414,"MM","my"
-"Namibia",264,649,"NA","en"
-"Nauru",674,536,"NR","na"
-"Netherlands",31,204,"NL","nl"
-"Netherlands Antilles",599,362,"AN","nl"
-"Nepal",977,429,"NP","ne"
-"New Brunswick",1506,302,"CA","en"
-"New Caledonia",687,546,"NC","fr"
-"New Zealand",64,530,"NZ","en"
-"Newfoundland",1709,302,"CA","en"
-"Nicaragua",505,710,"NI","es"
-"Niger",227,614,"NE","fr"
-"Nigeria",234,621,"NG","ha"
-"Niue",683,555,"NU","en"
-"Norfolk Island",6723,505,"NF","en"
-"North Korea",850,467,"KP","ko"
-"Northern Mariana Islands",1670,"534","MP","en"
-"Northwest Territories",1867,302,"CA","en"
-"Norway",47,242,"NO","nb"
-"Nova Scotia",1902,302,"CA","en"
-"Oman",968,422,"OM","ar"
-"Ontario",1416,302,"CA","en"
-"Ontario",1519,302,"CA","en"
-"Ontario",1613,302,"CA","en"
-"Ontario",1647,302,"CA","en"
-"Ontario",1705,302,"CA","en"
-"Ontario",1807,302,"CA","en"
-"Ontario",1905,302,"CA","en"
-"Pakistan",92,410,"PK","en"
-"Palau",680,552,"PW","en"
-"Palestine",970,425,"PS","ar"
-"Panama",507,714,"PA","es"
-"Papua New Guinea",675,537,"PG","ho"
-"Paraguay",595,744,"PY","es"
-"Peru",51,716,"PE","es"
-"Philippines",63,515,"PH","fil"
-"Poland",48,260,"PL","pl"
-"Portugal",351,268,"PT","pt"
-"Qatar",974,427,"QA","ar"
-"Quebec",1418,302,"CA","en"
-"Quebec",1450,302,"CA","en"
-"Quebec",1514,302,"CA","en"
-"Quebec",1819,302,"CA","en"
-"Reunion",262,647,"RE","fr"
-"Romania",40,226,"RO","ro"
-"Russia",7,250,"RU","ru"
-"Rwanda",250,635,"RW","rw"
-"Saint-Barthelemy",590,340,"BL","fr"
-"Saint Helena",290,658,"SH","en"
-"Saint Kitts and Nevis",1869,"356","KN","en"
-"Saint Lucia",1758,"358","LC","en"
-"Saint Martin (French side)",590,340, "MF","fr"
-"Saint Pierre and Miquelon",508,308,"PM","fr"
-"Saint Vincent and the Grenadines",1670,"360","VC","en"
-"Samoa",685,549,"WS","sm"
-"Sao Tome and Principe",239,626,"ST","pt"
-"Saskatchewan",1306,302,"CA","en"
-"Saudi Arabia",966,420,"SA","ar"
-"Senegal",221,608,"SN","wo"
-"Serbia",381,220,"RS","sr"
-"Seychelles",248,633,"SC","fr"
-"Sierra Leone",232,619,"SL","en"
-"Singapore",65,525,"SG","en"
-"Slovakia",421,231,"SK","sk"
-"Slovenia",386,293,"SI","sl"
-"Solomon Islands",677,540,"SB","en"
-"Somalia",252,637,"SO","so"
-"South Africa",27,655,"ZA","xh"
-"South Korea",82,450,"KR","ko"
-"South Sudan",211,659,"SS","en"
-"Spain",34,214,"es","es"
-"Sri Lanka",94,413,"LK","si"
-"Sudan",249,634,"SD","ar"
-"Suriname",597,746,"SR","nl"
-"Swaziland",268,653,"SZ","ss"
-"Sweden",46,240,"SE","sv"
-"Switzerland",41,228,"CH","de"
-"Syria",963,417,"SY","ar"
-"Taiwan",886,466,"TW","cmn"
-"Tajikistan",992,436,"TJ","tg"
-"Tanzania",255,640,"TZ","sw"
-"Thailand",66,520,"TH","th"
-"Togo",228,615,"TG","fr"
-"Tokelau",690,690,"TK","tkl"
-"Tonga",676,539,"TO","to"
-"Trinidad and Tobago",1868,"374","TT","en"
-"Tunisia",216,605,"TN","ar"
-"Turkey",90,286,"TR","tr"
-"Turkmenistan",993,438,"TM","tk"
-"Turks and Caicos Islands",1649,"376","TC","en"
-"Tuvalu",688,553,"TV","tvl"
-"Uganda",256,641,"UG","sw"
-"Ukraine",380,255,"UA","uk"
-"United Arab Emirates",971,"424|430|431","AE","ar"
-"United Kingdom",44,"234|235","GB","en"
-"United States of America",1,"310|311|312|313|314|315|316","en","en"
-"Uruguay",598,748,"UY","es"
-"Uzbekistan",998,434,"UZ","uz"
-"Vanuatu",678,541,"VU","bi"
-"Venezuela",58,734,"VE","es"
-"Vietnam",84,452,"VN","vi"
-"U.S. Virgin Islands",1340,"332","VI","en"
-"Wallis and Futuna",681,543,"WF","fr"
-"West Bank",970,0,"PS","ar"
-"Yemen",967,421,"YE","ar"
-"Zambia",260,645,"ZM","en"
-"Zimbabwe",263,648,"ZW","en"
+"Afghanistan",93,412,"AF","ps","040"
+"Albania",355,276,"AL","sq","002"
+"Alberta",1403,302,"CA","en","720"
+"Alberta",1780,302,"CA","en","720"
+"Algeria",213,603,"DZ","ar","001"
+"Andorra",376,213,"AD","ca","003"
+"Angola",244,631,"AO","pt","002"
+"Anguilla",1264,"365","AI","en","840"
+"Antarctica (Australian bases)",6721,901,"AQ","en","001"
+"Antigua and Barbuda",1268,"344","AG","en","050"
+"Argentina",54,722,"AR","es","010"
+"Armenia",374,283,"AM","hy","010"
+"Aruba",297,363,"AW","nl","001"
+"Ascension",247,658,"AC","en","001"
+"Australia",61,505,"AU","en","003"
+"Austria",43,232,"AT","de","001"
+"Azerbaijan",994,400,"AZ","az","001"
+"Bahamas",1242,"364","BS","en","039"
+"Bahrain",973,426,"BH","ar","001"
+"Bangladesh",880,470,"BD","bn","001"
+"Barbados",1246,"342","BB","en","750"
+"Belarus",375,257,"BY","be","001"
+"Belgium",32,206,"BE","nl","001"
+"Belize",501,702,"BZ","es","067"
+"Benin",229,616,"BJ","fr","003"
+"Bermuda",1441,350,"BM","en","001"
+"Bhutan",975,402,"BT","dz","011"
+"Bolivia",591,736,"BO","es","002"
+"Bosnia and Herzegovina",387,218,"BA","bs","003"
+"Botswana",267,652,"BW","en","002"
+"Brazil",55,724,"BR","pt","002"
+"British Columbia", 1250,302,"CA","en","370"
+"British Columbia", 1604,302,"CA","en","370"
+"British Columbia", 1778,302,"CA","en","370"
+"British Indian Ocean Territory",246,348,"IO","en","170"
+"British Virgin Islands",1284,"348","GB","en","170"
+"Brunei",673,528,"BN","ms","011"
+"Bulgaria",359,284,"BG","bg","003"
+"Burkina Faso",226,613,"BF","fr","002"
+"Burundi",257,642,"BI","rn","001"
+"Cambodia",855,456,"KH","km","002"
+"Cameroon",237,624,"CM","fr","001"
+"Cape Verde",238,625,"CV","pt","001"
+"Cayman Islands",1345,"346","GB","en","050"
+"Central African Republic",236,623,"CF","sg","001"
+"Chad",235,622,"TD","fr","003"
+"Chile",56,730,"CL","es","002"
+"China",86,"460|461","CN","en","001"
+"Colombia",57,732,"CO","es","102"
+"Comoros",269,654,"KM","fr","001"
+"Democratic Republic of the Congo",243,630,"CD","fr","086"
+"Republic of the Congo",242,629,"CG","fr","001"
+"Cook Islands",682,548,"CK","en","001"
+"Costa Rica",506,712,"CR","es","004"
+"Cote d'Ivoire",712,"612","CI","fr","002"
+"Croatia",385,219,"HR","hr","001"
+"Cuba",53,368,"CU","es","001"
+"Cyprus",357,280,"CY","el","001"
+"Czech Republic",420,230,"CZ","cs","001"
+"Denmark",45,238,"DK","da","001"
+"Djibouti",253,638,"DJ","fr","001"
+"Dominica",1767,"366","DM","en","020"
+"Dominican Republic",1809,"370","DO","es","001"
+"Dominican Republic",1829,"370","DO","en","001"
+"East Timor",670,514,"TL","pt","001"
+"Ecuador",593,740,"EC","es","001"
+"Egypt",20,602,"EG","ar","002"
+"El Salvador",503,706,"SV","es","001"
+"Equatorial Guinea",240,627,"GQ","es","003"
+"Eritrea",291,657,"ER","ti","001"
+"Estonia",372,248,"EE","et","001"
+"Ethiopia",251,636,"ET","am","001"
+"Falkland Islands",500,750,"FK","en","001"
+"Faroe Islands",298,288,"FO","fo","001"
+"Fiji",679,542,"FJ","en","001"
+"Finland",358,244,"FI","fi","005"
+"France",33,208,"FR","fr","001"
+"French Guiana",594,340,"GF","fr","011"
+"French Polynesia",689,547,"PF","fr","015"
+"Gabon",241,628,"GA","fr","001"
+"Gambia",220,607,"GM","en","001"
+"Gaza Strip",970,0,"PS","ar","001"
+"Georgia",995,282,"GE","ka","001"
+"Germany",49,262,"DE","de","001"
+"Ghana",233,620,"GH","ak","001"
+"Gibraltar",350,266,"GI","en","001"
+"Greece",30,202,"GR","el","001"
+"Greenland",299,290,"GL","kl","001"
+"Grenada",1473,"352","GD","en","030"
+"Guadeloupe",590,340,"GP","fr","002"
+"Guam",1671,"310","GU","en","032"
+"Guatemala",502,704,"GT","es","001"
+"Guinea",224,611,"GN","fr","001"
+"Guinea-Bissau",245,632,"GW","pt","001"
+"Guyana",592,738,"GY","pt","001"
+"Haiti",509,372,"HT","fr","001"
+"Honduras",504,708,"HN","es","001"
+"Hong Kong",852,454,"HK","zh","006"
+"Hungary",36,216,"HU","hu","001"
+"Iceland",354,274,"IS","is","001"
+"India",91,"404|405|406","IN","hi","011"
+"Indonesia",62,510,"ID","id","001"
+"Iraq",964,418,"IQ","ar","020"
+"Iran",98,432,"IR","fa","011"
+"Ireland (Eire)",353,272,"IE","en","001"
+"Israel",972,425,"IL","he","001"
+"Italy",39,222,"IT","it","001"
+"Jamaica",1876,"338","JM","en","050"
+"Japan",81,"440|441","JP","ja","001"
+"Jordan",962,416,"JO","ar","001"
+"Kazakhstan",77,401,"KZ","kk","001"
+"Kenya",254,639,"KE","sw","002"
+"Kiribati",686,545,"KI","en","009"
+"Kuwait",965,419,"KW","ar","003"
+"Kyrgyzstan",996,437,"KG","ky","001"
+"Laos",856,457,"LA","lo","001"
+"Latvia",371,247,"LV","lv","001"
+"Lebanon",961,415,"LB","ar","003"
+"Lesotho",266,651,"LS","st","001"
+"Liberia",231,618,"LR","en","007"
+"Libya",218,606,"LY","ar","003"
+"Liechtenstein",423,295,"LI","de","001"
+"Lithuania",370,246,"LT","lt","001"
+"Luxembourg",352,270,"LU","fr","001"
+"Macau",853,455,"MO","pt","004"
+"Republic of Macedonia",389,294,"MK","mk","001"
+"Madagascar",261,646,"MG","mg","001"
+"Malawi",265,650,"MW","ny","001"
+"Malaysia",60,502,"MY","en","013"
+"Maldives",960,472,"MV","dv","001"
+"Mali",223,610,"ML","fr","001"
+"Malta",356,278,"MT","mt","001"
+"Manitoba",1204,302,"CA","en","370"
+"Marshall Islands",692,551,"MH","mh","001"
+"Martinique",596,340,"MQ","fr","001"
+"Mauritania",222,609,"MR","ar","002"
+"Mauritius",230,617,"MU","en","001"
+"Mayotte",262,654,"YT","fr","001"
+"Mexico",52,334,"MX","es","030"
+"Federated States of Micronesia",691,550,"FM","en","001"
+"Moldova",373,259,"MD","ru","001"
+"Monaco",377,212,"MC","fr","001"
+"Mongolia",976,428,"MN","mn","088"
+"Montenegro",382,297,"ME","sr","001"
+"Montserrat",1664,"354",MS,"en","860"
+"Morocco",212,"604","MA","ar","001"
+"Mozambique",258,643,"MZ","pt","001"
+"Myanmar",95,414,"MM","my","005"
+"Namibia",264,649,"NA","en","001"
+"Nauru",674,536,"NR","na","002"
+"Netherlands",31,204,"NL","nl","004"
+"Netherlands Antilles",599,362,"AN","nl","069"
+"Nepal",977,429,"NP","ne","001"
+"New Brunswick",1506,302,"CA","en","370"
+"New Caledonia",687,546,"NC","fr","001"
+"New Zealand",64,530,"NZ","en","001"
+"Newfoundland",1709,302,"CA","en","370"
+"Nicaragua",505,710,"NI","es","030"
+"Niger",227,614,"NE","fr","004"
+"Nigeria",234,621,"NG","ha","020"
+"Niue",683,555,"NU","en","001"
+"Norfolk Island",6723,505,"NF","en","010"
+"North Korea",850,467,"KP","ko","005"
+"Northern Mariana Islands",1670,"534","MP","en","001"
+"Northwest Territories",1867,302,"CA","en","370"
+"Norway",47,242,"NO","nb","001"
+"Nova Scotia",1902,302,"CA","en","370"
+"Oman",968,422,"OM","ar","002"
+"Ontario",1416,302,"CA","en","370"
+"Ontario",1519,302,"CA","en","370"
+"Ontario",1613,302,"CA","en","370"
+"Ontario",1647,302,"CA","en","370"
+"Ontario",1705,302,"CA","en","370"
+"Ontario",1807,302,"CA","en","370"
+"Ontario",1905,302,"CA","en","370"
+"Pakistan",92,410,"PK","en","001"
+"Palau",680,552,"PW","en","001"
+"Palestine",970,425,"PS","ar","006"
+"Panama",507,714,"PA","es","002"
+"Papua New Guinea",675,537,"PG","ho","001"
+"Paraguay",595,744,"PY","es","001"
+"Peru",51,716,"PE","es","006"
+"Philippines",63,515,"PH","fil","002"
+"Poland",48,260,"PL","pl","001"
+"Portugal",351,268,"PT","pt","001"
+"Qatar",974,427,"QA","ar","001"
+"Quebec",1418,302,"CA","en","370"
+"Quebec",1450,302,"CA","en","370"
+"Quebec",1514,302,"CA","en","370"
+"Quebec",1819,302,"CA","en","370"
+"Reunion",262,647,"RE","fr","002"
+"Romania",40,226,"RO","ro","001"
+"Russia",79,250,"RU","ru","001"
+"Rwanda",250,635,"RW","rw","013"
+"Saint-Barthelemy",590,340,"BL","fr","001"
+"Saint Helena",290,658,"SH","en","000"
+"Saint Kitts and Nevis",1869,"356","KN","en","050"
+"Saint Lucia",1758,"358","LC","en","050"
+"Saint Martin (French side)",590,340, "MF","fr","001"
+"Saint Pierre and Miquelon",508,308,"PM","fr","001"
+"Saint Vincent and the Grenadines",1670,"360","VC","en","070"
+"Samoa",685,549,"WS","sm","001"
+"Sao Tome and Principe",239,626,"ST","pt","001"
+"Saskatchewan",1306,302,"CA","en","370"
+"Saudi Arabia",966,420,"SA","ar","001"
+"Senegal",221,608,"SN","wo","001"
+"Serbia",381,220,"RS","sr","001"
+"Seychelles",248,633,"SC","fr","001"
+"Sierra Leone",232,619,"SL","en","001"
+"Singapore",65,525,"SG","en","001"
+"Slovakia",421,231,"SK","sk","001"
+"Slovenia",386,293,"SI","sl","031"
+"Solomon Islands",677,540,"SB","en","001"
+"Somalia",252,637,"SO","so","001"
+"South Africa",27,655,"ZA","xh","001"
+"South Korea",82,450,"KR","ko","005"
+"South Sudan",211,659,"SS","en","002"
+"Spain",34,214,"ES","es","007"
+"Sri Lanka",94,413,"LK","si","001"
+"Sudan",249,634,"SD","ar","001"
+"Suriname",597,746,"SR","nl","002"
+"Swaziland",268,653,"SZ","ss","010"
+"Sweden",46,240,"SE","sv","001"
+"Switzerland",41,228,"CH","de","001"
+"Syria",963,417,"SY","ar","001"
+"Taiwan",886,466,"TW","cmn","001"
+"Tajikistan",992,436,"TJ","tg","001"
+"Tanzania",255,640,"TZ","sw","002"
+"Thailand",66,520,"TH","th","018"
+"Togo",228,615,"TG","fr","001"
+"Tokelau",690,690,"TK","tkl","001"
+"Tonga",676,539,"TO","to","001"
+"Trinidad and Tobago",1868,"374","TT","en","012"
+"Tunisia",216,605,"TN","ar","001"
+"Turkey",90,286,"TR","tr","001"
+"Turkmenistan",993,438,"TM","tk","001"
+"Turks and Caicos Islands",1649,"376","TC","en","350"
+"Tuvalu",688,553,"TV","tvl","001"
+"Uganda",256,641,"UG","sw","001"
+"Ukraine",380,255,"UA","uk","001"
+"United Arab Emirates",971,"424|430|431","AE","ar","003"
+"United Kingdom",44,"234|235","GB","en","002"
+"United States of America",1,"310|311|312|313|314|315|316","US","en","150"
+"Uruguay",598,748,"UY","es","007"
+"Uzbekistan",998,434,"UZ","uz","001"
+"Vanuatu",678,541,"VU","bi","005"
+"Venezuela",58,734,"VE","es","004"
+"Vietnam",84,452,"VN","vi","001"
+"U.S. Virgin Islands",1340,"332","VI","en","001"
+"Wallis and Futuna",681,543,"WF","fr","001"
+"West Bank",970,425,"PS","ar","001"
+"Yemen",967,421,"YE","ar","001"
+"Zambia",260,645,"ZM","en","001"
+"Zimbabwe",263,648,"ZW","en","001"
diff --git a/src/decode.php b/src/decode.php
deleted file mode 100755
index 04c710e1..00000000
--- a/src/decode.php
+++ /dev/null
@@ -1,42 +0,0 @@
- $v) {
- $str .= " " . getToken(hexdec($v));
- }
-
- return $str;
-}
-
-function str2hex($string)
-{
- $hexstr = unpack('H*', $string);
-
- return array_shift($hexstr);
-}
-
-function hex2str($hexstr)
-{
- $hexstr = str_replace(' ', '', $hexstr);
- $hexstr = str_replace('\x', '', $hexstr);
- $retstr = pack('H*', $hexstr);
-
- return $retstr;
-}
-
-function printhexstr($data, $name)
-{
- $data = str2hex($data);
- $len = strlen($data);
- print("Len: $len - $name\n");
- for ($i = 0; $i < $len; $i += 2) {
- if ((($i - 1) % 32) == 31) {
- print("\n");
- }
- printf(" %s%s", $data[$i], $data[$i + 1]);
- }
-
- print("\n");
-}
diff --git a/src/events/AllEvents.php b/src/events/AllEvents.php
new file mode 100644
index 00000000..660d6dd5
--- /dev/null
+++ b/src/events/AllEvents.php
@@ -0,0 +1,116 @@
+whatsProt = $whatsProt;
+ return $this;
+ }
+
+ /**
+ * Register the events you want to listen for.
+ *
+ * @param array $eventList
+ * @return AllEvents
+ */
+ public function setEventsToListenFor(array $eventList)
+ {
+ $this->eventsToListenFor = $eventList;
+ return $this->startListening();
+ }
+
+ /**
+ * Binds the requested events to the event manager.
+ * @return $this
+ */
+ protected function startListening()
+ {
+ foreach ($this->eventsToListenFor as $event) {
+ if (is_callable(array($this, $event))) {
+ $this->whatsProt->eventManager()->bind($event, array($this, $event));
+ }
+ }
+ return $this;
+ }
+
+ //Adding to this list? Please put them in alphabetical order!
+ public function onCallReceived($mynumber, $from, $id, $notify, $time, $callId) {}
+ public function onClose($mynumber, $error) {}
+ public function onCodeRegister($mynumber, $login, $password, $type, $expiration, $kind, $price, $cost, $currency, $price_expiration) {}
+ public function onCodeRegisterFailed($mynumber, $status, $reason, $retry_after) {}
+ public function onCodeRequest($mynumber, $method, $length) {}
+ public function onCodeRequestFailed($mynumber, $method, $reason, $param) {}
+ public function onCodeRequestFailedTooRecent($mynumber, $method, $reason, $retry_after) {}
+ public function onCodeRequestFailedTooManyGuesses($mynumber, $method, $reason, $retry_after) {}
+ public function onConnect($mynumber, $socket) {}
+ public function onConnectError($mynumber, $socket) {}
+ public function onCredentialsBad($mynumber, $status, $reason) {}
+ public function onCredentialsGood($mynumber, $login, $password, $type, $expiration, $kind, $price, $cost, $currency, $price_expiration) {}
+ public function onDisconnect($mynumber, $socket) {}
+ public function onDissectPhone($mynumber, $phonecountry, $phonecc, $phone, $phonemcc, $phoneISO3166, $phoneISO639, $phonemnc) {}
+ public function onDissectPhoneFailed($mynumber) {}
+ public function onGetAudio($mynumber, $from, $id, $type, $time, $name, $size, $url, $file, $mimeType, $fileHash, $duration, $acodec, $fromJID_ifGroup = null) {}
+ public function onGetBroadcastLists($mynumber, $broadcastLists){}
+ public function onGetError($mynumber, $from, $id, $data, $errorType = null) {}
+ public function onGetExtendAccount($mynumber, $kind, $status, $creation, $expiration) {}
+ public function onGetFeature($mynumber, $from, $encrypt) {}
+ public function onGetGroupMessage($mynumber, $from_group_jid, $from_user_jid, $id, $type, $time, $name, $body) {}
+ public function onGetGroups($mynumber, $groupList) {}
+ public function onGetGroupV2Info( $mynumber, $group_id, $creator, $creation, $subject, $participants, $admins, $fromGetGroup ){}
+ public function onGetGroupsSubject($mynumber, $group_jid, $time, $author, $name, $subject) {}
+ public function onGetImage($mynumber, $from, $id, $type, $time, $name, $size, $url, $file, $mimeType, $fileHash, $width, $height, $preview, $caption) {}
+ public function onGetGroupImage($mynumber, $from_group_jid, $from_user_jid, $id, $type, $time, $name, $size, $url, $file, $mimeType, $fileHash, $width, $height, $preview, $caption) {}
+ public function onGetGroupVideo($mynumber, $from_group_jid, $from_user_jid, $id, $type, $time, $name, $url, $file, $size, $mimeType, $fileHash, $duration, $vcodec, $acodec, $preview, $caption) {}
+ public function onGetKeysLeft($mynumber, $keysLeft) {}
+ public function onGetLocation($mynumber, $from, $id, $type, $time, $name, $author, $longitude, $latitude, $url, $preview, $fromJID_ifGroup = null) {}
+ public function onGetMessage($mynumber, $from, $id, $type, $time, $name, $body) {}
+ public function onGetNormalizedJid($mynumber, $data) {}
+ public function onGetPrivacyBlockedList($mynumber, $data) {}
+ public function onGetProfilePicture($mynumber, $from, $type, $data) {}
+ public function onGetReceipt($from, $id, $offline, $retry) {}
+ public function onGetRequestLastSeen($mynumber, $from, $id, $seconds) {}
+ public function onGetServerProperties($mynumber, $version, $props) {}
+ public function onGetServicePricing($mynumber, $price, $cost, $currency, $expiration) {}
+ public function onGetStatus($mynumber, $from, $requested, $id, $time, $data) {}
+ public function onGetSyncResult($result) {}
+ public function onGetVideo($mynumber, $from, $id, $type, $time, $name, $url, $file, $size, $mimeType, $fileHash, $duration, $vcodec, $acodec, $preview, $caption) {}
+ public function onGetvCard($mynumber, $from, $id, $type, $time, $name, $vcardname, $vcard, $fromJID_ifGroup = null) {}
+ public function onGroupCreate($mynumber, $groupId) {}
+ public function onGroupisCreated($mynumber, $creator, $gid, $subject, $admin, $creation, $members = array()) {}
+ public function onGroupsChatCreate($mynumber, $gid) {}
+ public function onGroupsChatEnd($mynumber, $gid) {}
+ public function onGroupsParticipantsAdd($mynumber, $groupId, $jid) {}
+ public function onGroupsParticipantChangedNumber($mynumber, $groupId, $time, $oldNumber, $notify, $newNumber) {}
+ public function onGroupsParticipantsPromote($myNumber, $groupJID, $time, $issuerJID, $issuerName, $promotedJIDs = array()) {}
+ public function onGroupsParticipantsRemove($mynumber, $groupId, $jid) {}
+ public function onLogin($mynumber) {}
+ public function onLoginFailed($mynumber, $data) {}
+ public function onLoginSuccess($mynumber, $kind, $status, $creation, $expiration) {}
+ public function onAccountExpired($mynumber, $kind, $status, $creation, $expiration ){}
+ public function onMediaMessageSent($mynumber, $to, $id, $filetype, $url, $filename, $filesize, $filehash, $caption, $icon) {}
+ public function onMediaUploadFailed($mynumber, $id, $node, $messageNode, $statusMessage) {}
+ public function onMessageComposing($mynumber, $from, $id, $type, $time) {}
+ public function onMessagePaused($mynumber, $from, $id, $type, $time) {}
+ public function onMessageReceivedClient($mynumber, $from, $id, $type, $time, $participant) {}
+ public function onMessageReceivedServer($mynumber, $from, $id, $type, $time) {}
+ public function onNumberWasAdded($mynumber, $jid) {}
+ public function onNumberWasRemoved($mynumber, $jid) {}
+ public function onNumberWasUpdated($mynumber, $jid) {}
+ public function onPaidAccount($mynumber, $author, $kind, $status, $creation, $expiration) {}
+ public function onPaymentRecieved($mynumber, $kind, $status, $creation, $expiration) {}
+ public function onPing($mynumber, $id) {}
+ public function onPresenceAvailable($mynumber, $from) {}
+ public function onPresenceUnavailable($mynumber, $from, $last) {}
+ public function onProfilePictureChanged($mynumber, $from, $id, $time) {}
+ public function onProfilePictureDeleted($mynumber, $from, $id, $time) {}
+ public function onSendMessage($mynumber, $target, $messageId, $node) {}
+ public function onSendMessageReceived($mynumber, $id, $from, $type) {}
+ public function onSendPong($mynumber, $msgid) {}
+ public function onSendPresence($mynumber, $type, $name ) {}
+ public function onSendStatusUpdate($mynumber, $txt ) {}
+ public function onStreamError($data) {}
+ public function onWebSync($mynumber, $from, $id, $syncData, $code, $name) {}
+}
diff --git a/src/events/MyEvents.php b/src/events/MyEvents.php
new file mode 100644
index 00000000..457022bd
--- /dev/null
+++ b/src/events/MyEvents.php
@@ -0,0 +1,89 @@
+WooHoo!, Phone number $mynumber connected successfully!
";
+ }
+
+ public function onDisconnect($mynumber, $socket)
+ {
+ echo "Booo!, Phone number $mynumber is disconnected!
";
+ }
+}
diff --git a/src/events/WhatsApiEventsManager.php b/src/events/WhatsApiEventsManager.php
new file mode 100644
index 00000000..ad785661
--- /dev/null
+++ b/src/events/WhatsApiEventsManager.php
@@ -0,0 +1,19 @@
+listeners[$event][] = $callback;
+ }
+
+ public function fire($event, array $parameters)
+ {
+ if (!empty($this->listeners[$event])) {
+ foreach ($this->listeners[$event] as $listener) {
+ call_user_func_array($listener, $parameters);
+ }
+ }
+ }
+}
diff --git a/src/events/WhatsAppEventListener.php b/src/events/WhatsAppEventListener.php
deleted file mode 100755
index 41b73dc2..00000000
--- a/src/events/WhatsAppEventListener.php
+++ /dev/null
@@ -1,435 +0,0 @@
-eventName = $eventName;
- $this->callback = $callback;
- }
-
- protected function handleEvent($eventName, array $arguments)
- {
- if( $this->eventName === $eventName ) {
- call_user_func_array( $this->callback, $arguments );
- }
- }
-
-}
-
diff --git a/src/events/WhatsAppEventListenerProxy.php b/src/events/WhatsAppEventListenerProxy.php
deleted file mode 100755
index 710331e7..00000000
--- a/src/events/WhatsAppEventListenerProxy.php
+++ /dev/null
@@ -1,556 +0,0 @@
-handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onCodeRegister(
- $phone,
- $login,
- $pw,
- $type,
- $expiration,
- $kind,
- $price,
- $cost,
- $currency,
- $price_expiration
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onCodeRegisterFailed(
- $phone,
- $status,
- $reason,
- $retry_after
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onCodeRequest(
- $phone,
- $method,
- $length
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onCodeRequestFailed(
- $phone,
- $method,
- $reason,
- $value
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onCodeRequestFailedTooRecent(
- $phone,
- $method,
- $reason,
- $retry_after
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onConnect(
- $phone,
- $socket
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onCredentialsBad(
- $phone,
- $status,
- $reason
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onCredentialsGood(
- $phone,
- $login,
- $pw,
- $type,
- $expiration,
- $kind,
- $price,
- $cost,
- $currency,
- $price_expiration
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onDisconnect(
- $phone,
- $socket
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onDissectPhone(
- $phone,
- $country,
- $cc,
- $mcc,
- $lc,
- $lg
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onDissectPhoneFailed(
- $phone
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onGetAudio(
- $phone,
- $from,
- $msgid,
- $type,
- $time,
- $name,
- $size,
- $url,
- $file,
- $mimetype,
- $filehash,
- $duration,
- $acodec
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onGetError(
- $phone,
- $id,
- $error
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onGetGroups(
- $phone,
- $groupList
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onGetGroupsInfo(
- $phone,
- $groupList
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onGetGroupsSubject(
- $phone,
- $gId,
- $time,
- $author,
- $participant,
- $name,
- $subject
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onGetImage(
- $phone,
- $from,
- $msgid,
- $type,
- $time,
- $name,
- $size,
- $url,
- $file,
- $mimetype,
- $filehash,
- $width,
- $height,
- $thumbnail
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onGetLocation(
- $phone,
- $from,
- $msgid,
- $type,
- $time,
- $name,
- $place_name,
- $longitude,
- $latitude,
- $url,
- $thumbnail
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onGetMessage(
- $phone,
- $from,
- $msgid,
- $type,
- $time,
- $name,
- $message
- ) {
- $func = __FUNCTION__;
- $args = func_get_args();
- $this->handleEvent($func, $args);
- }
-
- function onGetGroupMessage(
- $phone,
- $from,
- $author,
- $msgid,
- $type,
- $time,
- $name,
- $message
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onGetPrivacyBlockedList(
- $phone,
- $children
- /*
- $data,
- $onGetProfilePicture,
- $phone,
- $from,
- $type,
- $thumbnail
- */
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onGetProfilePicture(
- $phone,
- $from,
- $type,
- $thumbnail
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onGetRequestLastSeen(
- $phone,
- $from,
- $msgid,
- $sec
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onGetServerProperties(
- $phone,
- $version,
- $properties
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onGetvCard(
- $phone,
- $from,
- $msgid,
- $type,
- $time,
- $name,
- $contact,
- $vcard
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onGetVideo(
- $phone,
- $from,
- $msgid,
- $type,
- $time,
- $name,
- $url,
- $file,
- $size,
- $mimetype,
- $filehash,
- $duration,
- $vcodec,
- $acodec,
- $thumbnail
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onGroupsChatCreate(
- $phone,
- $gId
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onGroupsChatEnd(
- $phone,
- $gId
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onGroupsParticipantsAdd(
- $phone,
- $groupId,
- $participant
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onGroupsParticipantsRemove(
- $phone,
- $groupId,
- $participant,
- $author
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onLogin(
- $phone
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onMessageComposing(
- $phone,
- $from,
- $msgid,
- $type,
- $time
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onMessagePaused(
- $phone,
- $from,
- $msgid,
- $type,
- $time
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onMessageReceivedClient(
- $phone,
- $from,
- $msgid,
- $type,
- $time
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onMessageReceivedServer(
- $phone,
- $from,
- $msgid,
- $type
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onPing(
- $phone,
- $msgid
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onPresence(
- $phone,
- $from,
- $type
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onSendMessageReceived(
- $phone,
- $id,
- $from,
- $type
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onSendPong(
- $phone,
- $msgid
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onSendPresence(
- $phone,
- $type,
- $name
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onSendStatusUpdate(
- $phone,
- $msg
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onUploadFile(
- $phone,
- $name,
- $url
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- function onUploadFileFailed(
- $phone,
- $name
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- public function onConnectError(
- $phone,
- $socket
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- public function onGetGroupParticipants(
- $phone,
- $groupId,
- $groupList
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- public function onGetStatus(
- $phone,
- $from,
- $type,
- $id,
- $t,
- $status
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- public function onLoginFailed(
- $phone,
- $tag
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- public function onMediaMessageSent(
- $phone,
- $to,
- $id,
- $filetype,
- $url,
- $filename,
- $filesize,
- $filehash,
- $icon
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- public function onMediaUploadFailed(
- $phone,
- $id,
- $node,
- $messageNode,
- $reason
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- public function onProfilePictureChanged(
- $phone,
- $from,
- $id,
- $t
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- public function onProfilePictureDeleted(
- $phone,
- $from,
- $id,
- $t
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- public function onSendMessage(
- $phone,
- $targets,
- $id,
- $node
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- /**
- * @param SyncResult $result
- * @return mixed|void
- */
- public function onGetSyncResult(
- $result
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
- public function onGetReceipt(
- $from,
- $id,
- $offline,
- $retry
- ) {
- $this->handleEvent(__FUNCTION__, func_get_args());
- }
-
-}
diff --git a/src/exception.php b/src/exception.php
index 0436c5b4..387ef571 100755
--- a/src/exception.php
+++ b/src/exception.php
@@ -3,14 +3,20 @@ interface IException
{
/* Protected methods inherited from Exception class */
public function getMessage(); // Exception message
+
public function getCode(); // User-defined Exception code
+
public function getFile(); // Source filename
+
public function getLine(); // Source line
+
public function getTrace(); // An array of the backtrace()
+
public function getTraceAsString(); // Formated string of trace
/* Overrideable methods inherited from Exception class */
public function __toString(); // formated string for display
+
public function __construct($message = null, $code = 0);
}
@@ -20,12 +26,10 @@ abstract class CustomException extends Exception implements IException
protected $code = 0; // User-defined exception code
protected $file; // Source filename of exception
protected $line; // Source line of exception
- private $string; // Unknown
- private $trace; // Unknown
public function __construct($message = null, $code = 0)
{
- if (!$message) {
+ if ( ! $message) {
throw new $this('Unknown ' . get_class($this));
}
parent::__construct($message, $code);
@@ -34,7 +38,14 @@ public function __construct($message = null, $code = 0)
public function __toString()
{
return get_class($this) . " '{$this->message}' in {$this->file}({$this->line})\n"
- . "{$this->getTraceAsString()}";
+ . "{$this->getTraceAsString()}";
}
-
}
+
+/*
+ * Exception occurs when we have no active socket
+ * connection to whatsapp
+ */
+class ConnectionException extends Exception{}
+
+class LoginFailureException extends Exception{}
\ No newline at end of file
diff --git a/src/func.php b/src/func.php
index f62c346b..0d41b788 100755
--- a/src/func.php
+++ b/src/func.php
@@ -1,30 +1,5 @@
- * this will show the smiling face, require emojisprite.css and emojisprite.png
- *
- * Otherwise, if it is "false", return an id. Example, ##1F604##.
- *
- * @return string
- */
-function ParseMessageInboundForEmojis($txt, $span = true) {
- $Emojis = ArrayEmojis();
- foreach ($Emojis as $Emoji) {
- $txt = str_replace(
- array($Emoji['iOS2'], $Emoji['iOS5'], $Emoji['iOS7']),
- (($span == true) ? '##'.$Emoji['Hex'].'##' : '##'.$Emoji['Hex'].'##'),
- $txt
- );
- }
- return $txt;
-}
+
/**
* This function extracts the phone number.
*
@@ -34,14 +9,16 @@ function ParseMessageInboundForEmojis($txt, $span = true) {
* @return string
* Returns the number of phone cleanly.
*
-**/
-function ExtractNumber($from){
- return str_replace(array("@s.whatsapp.net","@g.us"), "", $from);
+ **/
+function ExtractNumber($from)
+{
+ return str_replace(array("@s.whatsapp.net", "@g.us"), "", $from);
}
+
function wa_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
$algorithm = strtolower($algorithm);
- if (!in_array($algorithm, hash_algos(), true)) {
+ if ( ! in_array($algorithm, hash_algos(), true)) {
die('PBKDF2 ERROR: Invalid hash algorithm.');
}
if ($count <= 0 || $key_length <= 0) {
@@ -71,85 +48,96 @@ function wa_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_outpu
function preprocessProfilePicture($path)
{
list($width, $height) = getimagesize($path);
- if ($width != $height) {
- throw new Exception("Profile picture needs to be square (image is $width x $height)");
- }
- if ($width > 640) {
- throw new Exception("Profile picture maximum size of 640 x 640 (image is $width x $height)");
+ if ($width > $height) {
+ $y = 0;
+ $x = ($width - $height) / 2;
+ $smallestSide = $height;
+ } else {
+ $x = 0;
+ $y = ($height - $width) / 2;
+ $smallestSide = $width;
}
- $img = imagecreatefromjpeg($path);
- unlink($path);
- imagejpeg($img, $path, 50);
+
+ $size = 639;
+ $image = imagecreatetruecolor($size, $size);
+ $img = imagecreatefromstring(file_get_contents($path));
+
+ imagecopyresampled($image, $img, 0, 0, $x, $y, $size, $size, $smallestSide, $smallestSide);
+ ob_start();
+ imagejpeg($image);
+ $i = ob_get_contents();
+ ob_end_clean();
+
+ imagedestroy($image);
imagedestroy($img);
+
+ return $i;
}
function createIcon($file)
{
- if ((extension_loaded('gd')) && (file_exists($file))){
+ if ((extension_loaded('gd')) && (file_exists($file))) {
return createIconGD($file);
} else {
- return giftThumbnail();
+ return base64_decode(giftThumbnail());
}
}
-function createIconGD($file, $size = 100, $raw = false)
+function createIconGD($file, $size = 100, $raw = true)
{
list($width, $height) = getimagesize($file);
if ($width > $height) {
- //landscape
- $nheight = ($height / $width) * $size;
- $nwidth = $size;
+ $y = 0;
+ $x = ($width - $height) / 2;
+ $smallestSide = $height;
} else {
- $nwidth = ($width / $height) * $size;
- $nheight = $size;
+ $x = 0;
+ $y = ($height - $width) / 2;
+ $smallestSide = $width;
}
- $image_p = imagecreatetruecolor($nwidth, $nheight);
- $image = imagecreatefromjpeg($file);
- imagecopyresampled($image_p, $image, 0, 0, 0, 0, $nwidth, $nheight, $width, $height);
+
+ $image_p = imagecreatetruecolor($size, $size);
+ $image = imagecreatefromstring(file_get_contents($file));
+
+ imagecopyresampled($image_p, $image, 0, 0, $x, $y, $size, $size, $smallestSide, $smallestSide);
ob_start();
imagejpeg($image_p);
$i = ob_get_contents();
ob_end_clean();
- if ($raw) {
- return $i;
- } else {
- return base64_encode($i);
- }
+
+ imagedestroy($image);
+ imagedestroy($image_p);
+
+ return $i;
}
function createVideoIcon($file)
{
- // @todo: Add support for video thumbnail create.
- // @see: http://stackoverflow.com/questions/14662027/generate-thumbnail-for-a-bunch-of-mp4-video-in-a-folder
- //return giftThumbnail();
-
/* should install ffmpeg for the method to work successfully */
if (checkFFMPEG()) {
//generate thumbnail
- $preview = sys_get_temp_dir().'/'.md5($file).'.jpg';
+ $preview = sys_get_temp_dir() . '/' . md5($file) . '.jpg';
@unlink($preview);
//capture video preview
- $command = "ffmpeg -i \"" . $file . "\" -f image2 -vframes 1 \"" . $preview . "\"";
+ $command = "ffmpeg -i \"" . $file . "\" -f mjpeg -ss 00:00:01 -vframes 1 \"" . $preview . "\"";
exec($command);
- // Parsear la imagen
- //TODO: Make it work using libGD (see createIcon())
return createIconGD($preview);
} else {
- //fallback
- return giftThumbnail();
+ return base64_decode(videoThumbnail());
}
}
function checkFFMPEG()
{
- //check if ffmpeg is intalled
- $cmd = "ffmpeg -version";
- $res = exec($cmd, $output, $returnvalue);
- if($returnvalue == 0)
- return true;
- return false;
+ //check if ffmpeg is installed.
+ $output = array();
+ $returnvalue = false;
+
+ exec('ffmpeg -version', $output, $returnvalue);
+
+ return ($returnvalue === 0);
}
function giftThumbnail()
@@ -161,31 +149,94 @@ function videoThumbnail()
{
return '/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAABQAAD/4QMpaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjAtYzA2MCA2MS4xMzQ3NzcsIDIwMTAvMDIvMTItMTc6MzI6MDAgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzUgV2luZG93cyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2MTQyRUVCOEI3MDgxMUUyQjNGQkY1OEU5M0U2MDE1MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2MTQyRUVCOUI3MDgxMUUyQjNGQkY1OEU5M0U2MDE1MyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjYxNDJFRUI2QjcwODExRTJCM0ZCRjU4RTkzRTYwMTUzIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjYxNDJFRUI3QjcwODExRTJCM0ZCRjU4RTkzRTYwMTUzIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+/+4ADkFkb2JlAGTAAAAAAf/bAIQAAgICAgICAgICAgMCAgIDBAMCAgMEBQQEBAQEBQYFBQUFBQUGBgcHCAcHBgkJCgoJCQwMDAwMDAwMDAwMDAwMDAEDAwMFBAUJBgYJDQsJCw0PDg4ODg8PDAwMDAwPDwwMDAwMDA8MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM/8AAEQgAZABkAwERAAIRAQMRAf/EALUAAAEEAwEBAQAAAAAAAAAAAAAGBwgJBAUKAwECAQEAAQUBAQAAAAAAAAAAAAAAAwECBAYHBQgQAAAFBAADBAUFDQkBAAAAAAECAwQFABEGByESCDFRIhNBYTIUCYEz07QVcZGhUmKCkqIjZWZ2OHKzJHSEJZUWNhcRAAIBAQMHCAgEBwEAAAAAAAABAgMRBAUhMdGScwYWQVGRseFSJAdhcaEiQrLSNcESE1PwgTJyI2M0F//aAAwDAQACEQMRAD8Av8oAoAoAoAoAoAoAoAoAoAoAoAoAoAoAoAoDHcPGjQomdOkWxShzGMqcpAAA9NzCHCgEe/2draKAxpPYONx4E4n95lWaVv01QoDBLtvXKxUDsspbSyTohTtV4wiz9NUpvZEijVNUpr+oaAZCa64umuEUXRPnLqScN1DJKt2ENKLmBQhuUxBN7qBQEBCw3GgHTf7gTaRDibb6+y5+ybslXwnK0aoiZJFIVjAQF3SYiblDgFqAhEx+KDrybyTHsbhNaThFckkmka0fzMnFx6CZ3ipUiHV8tdyYCgJuPC9APDvbZG/sS1lnubYkrBMZPDY5zJfYibhJ44VTbiAqcoHYn4JkAxh8Ijw7KAxOgHqKyfqK1FMTObyjWZy7GZ5aPkZVmVJNFw3WSI5anKmig2KSxTiQfBx5b3G9ATpoBP5ZKuYLFslm2ZElHkPFPHzVNfm8oyjdA6pAU5fFyiJQvbjagKXMj+IbmSbhw1hMhlpVqkPISTJFRkcCwhwMciShHByFEfZAxxNbt40A9nSx1JOd2LZxCZdO5QTI4b3eQimycr7sVePU/ZKmKDYEuKatuYA9BgoBout3Mdga5m8SnsYeShsNyVmZgsR1MSSoN5RpcxiiALAWyyRgMHrKNALPoj21D7HwvIseyLGYJ7m2GSBnKzpw3FZRzHPx5kVv2hhNdJQDJiN/xe+gIpddkJlWv9poZTDqlY4ns1sLxqmi2TBNvJNClSeNwMJRHxF5VShfsEe6gJndDu8pLYmm20JIySSeU6oXJCv1OVFJRSPOAqR7oR5QvcvMmI/jF9dAVz9Yelswg93zymDNJjJsWz9McgjEIQyz4GbhwYSvWhyNRP5YlWATkAQDwm4dlAWrdLWV7AyjS2HG2PjWTQ2bY0QYOXbSsRKCo/TZABWz0pfdzc5VkeUDflANAVi7o+Hdvd3t3OSaj1s7kNfyz77WxiROu0jwalff4g7YAdrInAWyphAvh7LekKAuT1hi24VNc4c32fg5i5w2iUY7MkE3sc4bO1UieQdUDlciBgXTABOAh2iIcaAxujTpaddMEZtSOPKouonOcpPLYxDpAJlI2NIUwIN11fZOoHOIDyXKBSl4iIjYCaVAJ/LW6jvFcmaIoi5VdRL1JJuWwioY6BygUL8OIjbjQHNDvpzgLrNwVxpvjaavlOCz44CVVvBmODo/uQpJugMAOAa8oORSAExU4l9NALHo/SScdQOF/wDX2kuko2RfLZA597S8r7MBAQcFUICPEDmEgBx9q1AT363ZbA4zQj1DIIl9MO5ObjkcbYqO00hF4mcVTrAYiQGAE0SnvbtvagIh9CCMPNbinZGGxd7AxUFjDks/IN5AwmU98VIm2bjzJiA3OUTcezlvQD+9cWdYtrHFMCbQ7B87yzIZpd21I+dIuwRYtERKuqBHCKpScx1CFASlAaAxOhrYGU7RPsKeyf7RJjGNFYxkGZsug1N9pK86y3lmbt0hHlR5QG4j20BreuHqayjUeT4FhuucyyuHlV4tzL5QmjLAYARXUKmyKYFEj2MPlnMFrcKAdfoo2LsbZ2scgzrZGW5fKISOQKMsSVPMGTN7sySKRyYPKTTKJRWMIAIgPZQETes/q22DgG7HuC6y2TmcLH4zDsksiQSl01SBJuAM4UDmWRUMAkSOmBgva/ooCc3SXkme5fojDMt2ZluZS+S5eo7k2LlSYMkqMcusJWRTFSTTLxIXmCxb2MF6AVXSXuyT2hvDqsxhtk8pPYHryWhWGJtZRYjszZcEnLeQMg55QUMmou3EQKcTWELltegJ+0B+FEyKpnSULzJqFEpyj6QELCFAcpmzcWLj21NhYtDFI3gYDIpaPiET3OqRu0dKJpFOe4cwgUoAI241tW5+B0MYvkqFdyUVBy92xO1NLlTyZTwN48Xnhd2jVgk25KOX1N/gLHTW0Mv0hKzs3isXByknPs02C7mXRWVFBBNTzRKj5Sqduc1uYRv2BXRX5a4Z+5V6Y/SaZHf28csI9D0mXunbuwd8BjieXkiYxpjHvBmDGIRVSSOq55QOqqCqqgiYClAoWtYKs/8AN8N/cq9MfpL+O7xyQj0PSZ+lty51omMnozD4bHX45I7SdycjLILquB8hMU0kiiksmAELcRtbtEatflxhv7lXpj9Jct+rx3I9D0iX3FlWZ75ydjlOZqsmTqMjiRkdHRaZ02qKJTmUMYpVTqG5lDmuYb+gKjfl3hq+Or0x+kuW/F47kPbpHa07u3YmlsLQwXD4bGl4sj1zIuH0g2cKO3Dl0ICc6p01yFHlApSlsHAAqN+X2HL46vTH6S5b7XjuQ9ukZzaMLO7lzqc2Fl0mCM7PAgRVuwTAjVuk2SBFJFAignMBSlC/ER4iI1G9wcOXx1elaCq30vHch7dJJvXG/dl6wwrFcBxeExT7AxBoVnHe9M3B1lQA4qHVXMVwUDHUOYTGEACrHuHh/fqdK0F63zvHch7dJEjLtPq5/k+T5dkeQPHE3mEi5k5pZMCFKZZ0fmOUlwEQKUPCUL8AAKs4Fw/v1OlaCvGVfuR9ukmybqd3BiuHCyg4bEI5li8IRjBpkZOQBuk1QBBAS3c25iAACH5VY1+3LuNC7VKsZVPzRg5LKrLUrcuTMTXTe6tWr06bhGyUkuXlfrHm+D7jccjqHaOaqpnWyrI8yOxmpVQ5jCsiybpuEgEo8AHzXixjD6eb1BXL07Ub+85bzVSgUBy9boNy7v2uP8Xz311auheWn3Kpsn8yNK39VtwhtF1MQAKca7W2cnUT1BSo2yVRMlI17CI2KHC49ny1FJkiiKBsTsrHlIlUTfN06x5SJFE3aCXZwqGUi9I3DdsJrcKjbK22G5TbJpJmVVMVNMgXOc3AAqiI2xu84fKPoKVTQAyTFJHmsPAVBAweI3q7gqDE4fluNfn/AE59Rl4XLxtHaR6yxj4RX9Pmcfz8++pM6+e45kdxlnZaxVxQKA5dN3m5d27WH+MZ764tXQfLX7lU2T+ZGm79K24w2i6mNuCldpbOVqJ7EPcQCopMlUR9tIIIOMgmEHCCblBSKEFEVSFOQweaXtKYBCvGxebVOLTsy/gZ9yinJ28w9EnqjGJLmVYFVgnJuIC28SN/WibgH5ohXlQxKrDP7y9OkzJ3OEs2QQkhrDJ4q526BJpsXj5zP27B3pG8QfJesuGIU558j9Okxp3WcfSadszOU4pqkMmoUbGSOUSmAfWUbCFZDdpjt2G4FRuyKHmjzqdpUC+0P3e75aKLZE5GrcC5fmAVfCkXimgX2Q9Y94+upoxUSNyE9lbLy8Wnj29loYf1i1h4q/A19nPqZl4VLxtDaR6ywf4RP9Pmcfz8++pM6+eY5kd2lnZaxVxQKA5b96m5d1bVH+Mp364tW/8Alv8AcqmyfzI0/fdW3GG0XUxrQU9ddnbOYqJmtjcxr1FJkiiSG0V/6WV9cWP96WvFxd/416zOuUfefqJYtyXtWutnpG2TOikUTmOFicREPR90ewKtsbLWxD5PleErFO2fkRmXIBy+W0KB1Sj/AJgtgL9+s+7XWussfdXp0GFXr0fiyv8AjlGUO1ZqulVGDZVq1ON0kFlfOUL/AGj2C/3q9yP5kvedr6DyJyTeTIjZosOAcPRVbSFyNJm7Ly8Myc9rcrA4/rFrBxR+Cr7Ofysy8KfjaG0j1k2/hE/0+Zx/Pz76kyr58jmR3yWdlrFXFAoDlp34Ntz7UH+M5364vW/+XH3Kpsn8yNS30/4obRdTGhFyQnaYa7KzmkUZjV+QtgAhjD8gVFJEqiPnp/J46DmpV/MOk41mMaKaahwMcx1BUKIEKUoCIjYOyvLxGhKrBKKtdpkXecYSbb5B2pHcwKCKWPxh1Q7CvX48pfulRIN/0hrDpYTyzf8AJaRUvy+FdIkHU/Pz5ry0ms4SEfC1IPlol9QJksH3716FO706X9K0nn1a8p52bNg1AAAAKAB6ACr2YzkKto1CxfDUbI2xRt2YCHs1Y2WNie2E0AmBZce1uWNUG/5xawsTfg6+zn8rM3Cn46htI9ZLL4RP9Pecfz6++pMq+f45kd/lnZaxVxQKA5Y+oI/LuTahr8BzSd4/6xet+8ufuM9k+tGqb4q25x/vXUxjTKCYbBXZWznKjYbmORuICPbVrRbKQuY9H2eAVY0Y8pC4YI+yNqjaIZSFqxR7OFWNETkLJikHDhUTI3IVzJELF7KjZY2KlogA24VEylpoNmNuXXOaGt2RSo/hLWDiL8JX2c/lZm4U/G0NpDrRIj4RSyJen7OEhUKCn/fnvgvx8TFmIcPkGuBRzI+g5Z2Wu1cWn4Nfhb0CFwoDnh3j0ub5ktr7HeMtWT8zGSOSychGycc3Mugqi6drLJKJqkAwCBk1AuFrgPAbCFejheK3jDK3613aUrLMqtTT5LDEvtxpXyn+nVVqtt5sozanSv1AIcf/AIrmNg9P2esIfgSrYuPsV70NRaTyHurcXyS1uw8y9OXUMh83pjLwt+7V/oacfYr3oai0kb3Rw98ktbsPcmiepdH5rTWXcO+NW+hpx9ivehqLSWvc7DnyT1noMkun+qlH5rTWWcP3Wr9DVOPcU56eotJbwZhvNPWegyC6z6ukfmtN5X8sUp9DVOPMU56ep2lvBWG809d6D2Lg/WWl81pnKf8AiT/Q1TjrE+enqdpTgjDOaeu9B7FxrraS+b0zlFg7P9oN9DVOOcS/16naU4Hwzmnr9hkkiOuwogCOlsnMPoD7HH6Gqcb4l/r1O0pwPhnNPX7DEnsM698mhn0G70llAMpFPy3HLGeWIlvew2IQRD1XrHvO92IXilKlJwSkrHZGx2PPltdlpPddz8Ou1WNWMZOUXarZNq1ZnZZyE0/h6aF6idVsX5c3xh1iOPykoZ8ES/EpHJjAkRIyp0wMPLzCXgA8eF61k2guO5T+Ty38XLb5aA9qALUB8sHdQBYO4KALB3BQBYO4KALB3BQBYO4KALB3BQBYO6gCwd1AfaAKAKAKAKAKAKAKAKAKAKAKAKAKAKA//9k=';
}
+function updateData($nameFile, $WAver, $WAToken = null)
+{
+ $file = __DIR__ . "/" . $nameFile;
+ $open = fopen($file, 'r+');
+ $content = fread($open, filesize($file));
+ fclose($open);
+
+ $content = explode("\n", $content);
+
+ if ($file == __DIR__ . '/token.php') {
+ $content[22] = ' $releaseTime = \'' . $WAToken .'\';';
+ } else {
+ if ($file == __DIR__ . '/Constants.php') {
+ $content[21] = ' const WHATSAPP_VER = \'' . trim($WAver) . '\'; // The WhatsApp version.';
+ $content[22] = ' const WHATSAPP_USER_AGENT = \'WhatsApp/' . trim($WAver) . ' S40Version/14.26 Device/Nokia302\'; // User agent used in request/registration code.';
+ }
+ }
+
+ $content = implode("\n", $content);
-//Generate Array of Emojis iOS2, iOS5 and iOS7
-function ArrayEmojis(){
- return array(array('iOS2' => '
','iOS5' => '😄','iOS7' => '','Hex' => '1F604'), array('iOS2' => '
','iOS5' => '😃','iOS7' => '','Hex' => '1F603'), array('iOS2' => '','iOS5' => '😀','iOS7' => '','Hex' => '1F600'), array('iOS2' => '
','iOS5' => '😊','iOS7' => '','Hex' => '1F60A'), array('iOS2' => '
','iOS5' => '☺','iOS7' => '☺️','Hex' => '263A'), array('iOS2' => '
','iOS5' => '😉','iOS7' => '','Hex' => '1F609'), array('iOS2' => '
','iOS5' => '😍','iOS7' => '','Hex' => '1F60D'), array('iOS2' => '
','iOS5' => '😘','iOS7' => '','Hex' => '1F618'), array('iOS2' => '
','iOS5' => '😚','iOS7' => '','Hex' => '1F61A'), array('iOS2' => '','iOS5' => '😗','iOS7' => '','Hex' => '1F617'), array('iOS2' => '','iOS5' => '😙','iOS7' => '','Hex' => '1F619'), array('iOS2' => '
','iOS5' => '😜','iOS7' => '','Hex' => '1F61C'), array('iOS2' => '
','iOS5' => '😝','iOS7' => '','Hex' => '1F61D'), array('iOS2' => '','iOS5' => '😛','iOS7' => '','Hex' => '1F61B'), array('iOS2' => '
','iOS5' => '😳','iOS7' => '','Hex' => '1F633'), array('iOS2' => '
','iOS5' => '😁','iOS7' => '','Hex' => '1F601'), array('iOS2' => '
','iOS5' => '😔','iOS7' => '','Hex' => '1F614'), array('iOS2' => '
','iOS5' => '😌','iOS7' => '','Hex' => '1F60C'), array('iOS2' => '
','iOS5' => '😒','iOS7' => '','Hex' => '1F612'), array('iOS2' => '
','iOS5' => '😞','iOS7' => '','Hex' => '1F61E'), array('iOS2' => '
','iOS5' => '😣','iOS7' => '','Hex' => '1F623'), array('iOS2' => '
','iOS5' => '😢','iOS7' => '','Hex' => '1F622'), array('iOS2' => '
','iOS5' => '😂','iOS7' => '','Hex' => '1F602'), array('iOS2' => '
','iOS5' => '😭','iOS7' => '','Hex' => '1F62D'), array('iOS2' => '
','iOS5' => '😪','iOS7' => '','Hex' => '1F62A'), array('iOS2' => '
','iOS5' => '😥','iOS7' => '','Hex' => '1F625'), array('iOS2' => '
','iOS5' => '😰','iOS7' => '','Hex' => '1F630'), array('iOS2' => '','iOS5' => '😅','iOS7' => '','Hex' => '1F605'), array('iOS2' => '
','iOS5' => '😓','iOS7' => '','Hex' => '1F613'), array('iOS2' => '','iOS5' => '😩','iOS7' => '','Hex' => '1F629'), array('iOS2' => '','iOS5' => '😫','iOS7' => '','Hex' => '1F62B'), array('iOS2' => '
','iOS5' => '😨','iOS7' => '','Hex' => '1F628'), array('iOS2' => '
','iOS5' => '😱','iOS7' => '','Hex' => '1F631'), array('iOS2' => '
','iOS5' => '😠','iOS7' => '','Hex' => '1F620'), array('iOS2' => '
','iOS5' => '😡','iOS7' => '','Hex' => '1F621'), array('iOS2' => '','iOS5' => '😤','iOS7' => '','Hex' => '1F624'), array('iOS2' => '
','iOS5' => '😖','iOS7' => '','Hex' => '1F616'), array('iOS2' => '','iOS5' => '😆','iOS7' => '','Hex' => '1F606'), array('iOS2' => '','iOS5' => '😋','iOS7' => '','Hex' => '1F60B'), array('iOS2' => '
','iOS5' => '😷','iOS7' => '','Hex' => '1F637'), array('iOS2' => '','iOS5' => '😎','iOS7' => '','Hex' => '1F60E'), array('iOS2' => '','iOS5' => '😴','iOS7' => '','Hex' => '1F634'), array('iOS2' => '','iOS5' => '😵','iOS7' => '','Hex' => '1F635'), array('iOS2' => '
','iOS5' => '😲','iOS7' => '','Hex' => '1F632'), array('iOS2' => '','iOS5' => '😟','iOS7' => '','Hex' => '1F61F'), array('iOS2' => '','iOS5' => '😦','iOS7' => '','Hex' => '1F626'), array('iOS2' => '','iOS5' => '😧','iOS7' => '','Hex' => '1F627'), array('iOS2' => '','iOS5' => '😈','iOS7' => '','Hex' => '1F608'), array('iOS2' => '
','iOS5' => '👿','iOS7' => '','Hex' => '1F47F'), array('iOS2' => '','iOS5' => '😮','iOS7' => '','Hex' => '1F62E'), array('iOS2' => '','iOS5' => '😬','iOS7' => '','Hex' => '1F62C'), array('iOS2' => '','iOS5' => '😐','iOS7' => '','Hex' => '1F610'), array('iOS2' => '','iOS5' => '😕','iOS7' => '','Hex' => '1F615'), array('iOS2' => '','iOS5' => '😯','iOS7' => '','Hex' => '1F62F'), array('iOS2' => '','iOS5' => '😶','iOS7' => '','Hex' => '1F636'), array('iOS2' => '','iOS5' => '😇','iOS7' => '','Hex' => '1F607'), array('iOS2' => '
','iOS5' => '😏','iOS7' => '','Hex' => '1F60F'), array('iOS2' => '','iOS5' => '😑','iOS7' => '','Hex' => '1F611'), array('iOS2' => '
','iOS5' => '👲','iOS7' => '','Hex' => '1F472'), array('iOS2' => '
','iOS5' => '👳','iOS7' => '','Hex' => '1F473'), array('iOS2' => '
','iOS5' => '👮','iOS7' => '','Hex' => '1F46E'), array('iOS2' => '
','iOS5' => '👷','iOS7' => '','Hex' => '1F477'), array('iOS2' => '
','iOS5' => '💂','iOS7' => '','Hex' => '1F482'), array('iOS2' => '
','iOS5' => '👶','iOS7' => '','Hex' => '1F476'), array('iOS2' => '
','iOS5' => '👦','iOS7' => '','Hex' => '1F466'), array('iOS2' => '
','iOS5' => '👧','iOS7' => '','Hex' => '1F467'), array('iOS2' => '
','iOS5' => '👨','iOS7' => '','Hex' => '1F468'), array('iOS2' => '
','iOS5' => '👩','iOS7' => '','Hex' => '1F469'), array('iOS2' => '
','iOS5' => '👴','iOS7' => '','Hex' => '1F474'), array('iOS2' => '
','iOS5' => '👵','iOS7' => '','Hex' => '1F475'), array('iOS2' => '
','iOS5' => '👱','iOS7' => '','Hex' => '1F471'), array('iOS2' => '
','iOS5' => '👼','iOS7' => '','Hex' => '1F47C'), array('iOS2' => '
','iOS5' => '👸','iOS7' => '','Hex' => '1F478'), array('iOS2' => '','iOS5' => '😺','iOS7' => '','Hex' => '1F63A'), array('iOS2' => '','iOS5' => '😸','iOS7' => '','Hex' => '1F638'), array('iOS2' => '','iOS5' => '😻','iOS7' => '','Hex' => '1F63B'), array('iOS2' => '','iOS5' => '😽','iOS7' => '','Hex' => '1F63D'), array('iOS2' => '','iOS5' => '😼','iOS7' => '','Hex' => '1F63C'), array('iOS2' => '','iOS5' => '🙀','iOS7' => '','Hex' => '1F640'), array('iOS2' => '','iOS5' => '😿','iOS7' => '','Hex' => '1F63F'), array('iOS2' => '','iOS5' => '😹','iOS7' => '','Hex' => '1F639'), array('iOS2' => '','iOS5' => '😾','iOS7' => '','Hex' => '1F63E'), array('iOS2' => '','iOS5' => '👹','iOS7' => '','Hex' => '1F479'), array('iOS2' => '','iOS5' => '👺','iOS7' => '','Hex' => '1F47A'), array('iOS2' => '','iOS5' => '🙈','iOS7' => '','Hex' => '1F648'), array('iOS2' => '','iOS5' => '🙉','iOS7' => '','Hex' => '1F649'), array('iOS2' => '','iOS5' => '🙊','iOS7' => '','Hex' => '1F64A'), array('iOS2' => '
','iOS5' => '💀','iOS7' => '','Hex' => '1F480'), array('iOS2' => '
','iOS5' => '👽','iOS7' => '','Hex' => '1F47D'), array('iOS2' => '
','iOS5' => '💩','iOS7' => '','Hex' => '1F4A9'), array('iOS2' => '
','iOS5' => '🔥','iOS7' => '','Hex' => '1F525'), array('iOS2' => '
','iOS5' => '✨','iOS7' => '','Hex' => '2728'), array('iOS2' => '
','iOS5' => '🌟','iOS7' => '','Hex' => '1F31F'), array('iOS2' => '','iOS5' => '💫','iOS7' => '','Hex' => '1F4AB'), array('iOS2' => '','iOS5' => '💥','iOS7' => '','Hex' => '1F4A5'), array('iOS2' => '
','iOS5' => '💢','iOS7' => '','Hex' => '1F4A2'), array('iOS2' => '
','iOS5' => '💦','iOS7' => '','Hex' => '1F4A6'), array('iOS2' => '','iOS5' => '💧','iOS7' => '','Hex' => '1F4A7'), array('iOS2' => '
','iOS5' => '💤','iOS7' => '','Hex' => '1F4A4'), array('iOS2' => '
','iOS5' => '💨','iOS7' => '','Hex' => '1F4A8'), array('iOS2' => '
','iOS5' => '👂','iOS7' => '','Hex' => '1F442'), array('iOS2' => '
','iOS5' => '👀','iOS7' => '','Hex' => '1F440'), array('iOS2' => '
','iOS5' => '👃','iOS7' => '','Hex' => '1F443'), array('iOS2' => '','iOS5' => '👅','iOS7' => '','Hex' => '1F445'), array('iOS2' => '
','iOS5' => '👄','iOS7' => '','Hex' => '1F444'), array('iOS2' => '
','iOS5' => '👍','iOS7' => '','Hex' => '1F44D'), array('iOS2' => '
','iOS5' => '👎','iOS7' => '','Hex' => '1F44E'), array('iOS2' => '
','iOS5' => '👌','iOS7' => '','Hex' => '1F44C'), array('iOS2' => '
','iOS5' => '👊','iOS7' => '','Hex' => '1F44A'), array('iOS2' => '
','iOS5' => '✊','iOS7' => '','Hex' => '270A'), array('iOS2' => '
','iOS5' => '✌','iOS7' => '✌️','Hex' => '270C'), array('iOS2' => '
','iOS5' => '👋','iOS7' => '','Hex' => '1F44B'), array('iOS2' => '
','iOS5' => '✋','iOS7' => '','Hex' => '270B'), array('iOS2' => '
','iOS5' => '👐','iOS7' => '','Hex' => '1F450'), array('iOS2' => '
','iOS5' => '👆','iOS7' => '','Hex' => '1F446'), array('iOS2' => '
','iOS5' => '👇','iOS7' => '','Hex' => '1F447'), array('iOS2' => '
','iOS5' => '👉','iOS7' => '','Hex' => '1F449'), array('iOS2' => '
','iOS5' => '👈','iOS7' => '','Hex' => '1F448'), array('iOS2' => '
','iOS5' => '🙌','iOS7' => '','Hex' => '1F64C'), array('iOS2' => '
','iOS5' => '🙏','iOS7' => '','Hex' => '1F64F'), array('iOS2' => '
','iOS5' => '☝','iOS7' => '☝️','Hex' => '261D'), array('iOS2' => '
','iOS5' => '👏','iOS7' => '','Hex' => '1F44F'), array('iOS2' => '
','iOS5' => '💪','iOS7' => '','Hex' => '1F4AA'), array('iOS2' => '
','iOS5' => '🚶','iOS7' => '','Hex' => '1F6B6'), array('iOS2' => '
','iOS5' => '🏃','iOS7' => '','Hex' => '1F3C3'), array('iOS2' => '
','iOS5' => '💃','iOS7' => '','Hex' => '1F483'), array('iOS2' => '
','iOS5' => '👫','iOS7' => '','Hex' => '1F46B'), array('iOS2' => '','iOS5' => '👪','iOS7' => '','Hex' => '1F46A'), array('iOS2' => '','iOS5' => '👬','iOS7' => '','Hex' => '1F46C'), array('iOS2' => '','iOS5' => '👭','iOS7' => '','Hex' => '1F46D'), array('iOS2' => '
','iOS5' => '💏','iOS7' => '','Hex' => '1F48F'), array('iOS2' => '
','iOS5' => '💑','iOS7' => '','Hex' => '1F491'), array('iOS2' => '
','iOS5' => '👯','iOS7' => '','Hex' => '1F46F'), array('iOS2' => '
','iOS5' => '🙆','iOS7' => '','Hex' => '1F646'), array('iOS2' => '
','iOS5' => '🙅','iOS7' => '','Hex' => '1F645'), array('iOS2' => '
','iOS5' => '💁','iOS7' => '','Hex' => '1F481'), array('iOS2' => '','iOS5' => '🙋','iOS7' => '','Hex' => '1F64B'), array('iOS2' => '
','iOS5' => '💆','iOS7' => '','Hex' => '1F486'), array('iOS2' => '
','iOS5' => '💇','iOS7' => '','Hex' => '1F487'), array('iOS2' => '
','iOS5' => '💅','iOS7' => '','Hex' => '1F485'), array('iOS2' => '','iOS5' => '👰','iOS7' => '','Hex' => '1F470'), array('iOS2' => '','iOS5' => '🙎','iOS7' => '','Hex' => '1F64E'), array('iOS2' => '','iOS5' => '🙍','iOS7' => '','Hex' => '1F64D'), array('iOS2' => '
','iOS5' => '🙇','iOS7' => '','Hex' => '1F647'), array('iOS2' => '
','iOS5' => '🎩','iOS7' => '','Hex' => '1F3A9'), array('iOS2' => '
','iOS5' => '👑','iOS7' => '','Hex' => '1F451'), array('iOS2' => '
','iOS5' => '👒','iOS7' => '','Hex' => '1F452'), array('iOS2' => '
','iOS5' => '👟','iOS7' => '','Hex' => '1F45F'), array('iOS2' => '','iOS5' => '👞','iOS7' => '','Hex' => '1F45E'), array('iOS2' => '
','iOS5' => '👡','iOS7' => '','Hex' => '1F461'), array('iOS2' => '
','iOS5' => '👠','iOS7' => '','Hex' => '1F460'), array('iOS2' => '
','iOS5' => '👢','iOS7' => '','Hex' => '1F462'), array('iOS2' => '
','iOS5' => '👕','iOS7' => '','Hex' => '1F455'), array('iOS2' => '
','iOS5' => '👔','iOS7' => '','Hex' => '1F454'), array('iOS2' => '','iOS5' => '👚','iOS7' => '','Hex' => '1F45A'), array('iOS2' => '
','iOS5' => '👗','iOS7' => '','Hex' => '1F457'), array('iOS2' => '','iOS5' => '🎽','iOS7' => '','Hex' => '1F3BD'), array('iOS2' => '','iOS5' => '👖','iOS7' => '','Hex' => '1F456'), array('iOS2' => '
','iOS5' => '👘','iOS7' => '','Hex' => '1F458'), array('iOS2' => '
','iOS5' => '👙','iOS7' => '','Hex' => '1F459'), array('iOS2' => '
','iOS5' => '💼','iOS7' => '','Hex' => '1F4BC'), array('iOS2' => '
','iOS5' => '👜','iOS7' => '','Hex' => '1F45C'), array('iOS2' => '','iOS5' => '👝','iOS7' => '','Hex' => '1F45D'), array('iOS2' => '','iOS5' => '👛','iOS7' => '','Hex' => '1F45B'), array('iOS2' => '','iOS5' => '👓','iOS7' => '','Hex' => '1F453'), array('iOS2' => '
','iOS5' => '🎀','iOS7' => '','Hex' => '1F380'), array('iOS2' => '
','iOS5' => '🌂','iOS7' => '','Hex' => '1F302'), array('iOS2' => '
','iOS5' => '💄','iOS7' => '','Hex' => '1F484'), array('iOS2' => '
','iOS5' => '💛','iOS7' => '','Hex' => '1F49B'), array('iOS2' => '
','iOS5' => '💙','iOS7' => '','Hex' => '1F499'), array('iOS2' => '
','iOS5' => '💜','iOS7' => '','Hex' => '1F49C'), array('iOS2' => '
','iOS5' => '💚','iOS7' => '','Hex' => '1F49A'), array('iOS2' => '
','iOS5' => '❤','iOS7' => '❤️','Hex' => '2764'), array('iOS2' => '
','iOS5' => '💔','iOS7' => '','Hex' => '1F494'), array('iOS2' => '
','iOS5' => '💗','iOS7' => '','Hex' => '1F497'), array('iOS2' => '
','iOS5' => '💓','iOS7' => '','Hex' => '1F493'), array('iOS2' => '','iOS5' => '💕','iOS7' => '','Hex' => '1F495'), array('iOS2' => '','iOS5' => '💖','iOS7' => '','Hex' => '1F496'), array('iOS2' => '','iOS5' => '💞','iOS7' => '','Hex' => '1F49E'), array('iOS2' => '
','iOS5' => '💘','iOS7' => '','Hex' => '1F498'), array('iOS2' => '','iOS5' => '💌','iOS7' => '','Hex' => '1F48C'), array('iOS2' => '
','iOS5' => '💋','iOS7' => '','Hex' => '1F48B'), array('iOS2' => '
','iOS5' => '💍','iOS7' => '','Hex' => '1F48D'), array('iOS2' => '
','iOS5' => '💎','iOS7' => '','Hex' => '1F48E'), array('iOS2' => '','iOS5' => '👤','iOS7' => '','Hex' => '1F464'), array('iOS2' => '','iOS5' => '👥','iOS7' => '','Hex' => '1F465'), array('iOS2' => '','iOS5' => '💬','iOS7' => '','Hex' => '1F4AC'), array('iOS2' => '
','iOS5' => '👣','iOS7' => '','Hex' => '1F463'), array('iOS2' => '','iOS5' => '💭','iOS7' => '','Hex' => '1F4AD'), array('iOS2' => '
','iOS5' => '🏠','iOS7' => '','Hex' => '1F3E0'), array('iOS2' => '','iOS5' => '🏡','iOS7' => '','Hex' => '1F3E1'), array('iOS2' => '
','iOS5' => '🏫','iOS7' => '','Hex' => '1F3EB'), array('iOS2' => '
','iOS5' => '🏢','iOS7' => '','Hex' => '1F3E2'), array('iOS2' => '
','iOS5' => '🏣','iOS7' => '','Hex' => '1F3E3'), array('iOS2' => '
','iOS5' => '🏥','iOS7' => '','Hex' => '1F3E5'), array('iOS2' => '
','iOS5' => '🏦','iOS7' => '','Hex' => '1F3E6'), array('iOS2' => '
','iOS5' => '🏪','iOS7' => '','Hex' => '1F3EA'), array('iOS2' => '
','iOS5' => '🏩','iOS7' => '','Hex' => '1F3E9'), array('iOS2' => '
','iOS5' => '🏨','iOS7' => '','Hex' => '1F3E8'), array('iOS2' => '
','iOS5' => '💒','iOS7' => '','Hex' => '1F492'), array('iOS2' => '
','iOS5' => '⛪','iOS7' => '⛪️','Hex' => '26EA'), array('iOS2' => '
','iOS5' => '🏬','iOS7' => '','Hex' => '1F3EC'), array('iOS2' => '','iOS5' => '🏤','iOS7' => '','Hex' => '1F3E4'), array('iOS2' => '
','iOS5' => '🌇','iOS7' => '','Hex' => '1F307'), array('iOS2' => '
','iOS5' => '🌆','iOS7' => '','Hex' => '1F306'), array('iOS2' => '
','iOS5' => '🏯','iOS7' => '','Hex' => '1F3EF'), array('iOS2' => '
','iOS5' => '🏰','iOS7' => '','Hex' => '1F3F0'), array('iOS2' => '
','iOS5' => '⛺','iOS7' => '⛺️','Hex' => '26FA'), array('iOS2' => '
','iOS5' => '🏭','iOS7' => '','Hex' => '1F3ED'), array('iOS2' => '
','iOS5' => '🗼','iOS7' => '','Hex' => '1F5FC'), array('iOS2' => '','iOS5' => '🗾','iOS7' => '','Hex' => '1F5FE'), array('iOS2' => '
','iOS5' => '🗻','iOS7' => '','Hex' => '1F5FB'), array('iOS2' => '
','iOS5' => '🌄','iOS7' => '','Hex' => '1F304'), array('iOS2' => '
','iOS5' => '🌅','iOS7' => '','Hex' => '1F305'), array('iOS2' => '
','iOS5' => '🌃','iOS7' => '','Hex' => '1F303'), array('iOS2' => '
','iOS5' => '🗽','iOS7' => '','Hex' => '1F5FD'), array('iOS2' => '','iOS5' => '🌉','iOS7' => '','Hex' => '1F309'), array('iOS2' => '','iOS5' => '🎠','iOS7' => '','Hex' => '1F3A0'), array('iOS2' => '
','iOS5' => '🎡','iOS7' => '','Hex' => '1F3A1'), array('iOS2' => '
','iOS5' => '⛲','iOS7' => '⛲️','Hex' => '26F2'), array('iOS2' => '
','iOS5' => '🎢','iOS7' => '','Hex' => '1F3A2'), array('iOS2' => '
','iOS5' => '🚢','iOS7' => '','Hex' => '1F6A2'), array('iOS2' => '
','iOS5' => '⛵','iOS7' => '⛵️','Hex' => '26F5'), array('iOS2' => '
','iOS5' => '🚤','iOS7' => '','Hex' => '1F6A4'), array('iOS2' => '','iOS5' => '🚣','iOS7' => '','Hex' => '1F6A3'), array('iOS2' => '','iOS5' => '⚓','iOS7' => '⚓️','Hex' => '2693'), array('iOS2' => '
','iOS5' => '🚀','iOS7' => '','Hex' => '1F680'), array('iOS2' => '
','iOS5' => '✈','iOS7' => '✈️','Hex' => '2708'), array('iOS2' => '
','iOS5' => '💺','iOS7' => '','Hex' => '1F4BA'), array('iOS2' => '','iOS5' => '🚁','iOS7' => '','Hex' => '1F681'), array('iOS2' => '','iOS5' => '🚂','iOS7' => '','Hex' => '1F682'), array('iOS2' => '','iOS5' => '🚊','iOS7' => '','Hex' => '1F68A'), array('iOS2' => '
','iOS5' => '🚉','iOS7' => '','Hex' => '1F689'), array('iOS2' => '','iOS5' => '🚞','iOS7' => '','Hex' => '1F69E'), array('iOS2' => '','iOS5' => '🚆','iOS7' => '','Hex' => '1F686'), array('iOS2' => '
','iOS5' => '🚄','iOS7' => '','Hex' => '1F684'), array('iOS2' => '
','iOS5' => '🚅','iOS7' => '','Hex' => '1F685'), array('iOS2' => '','iOS5' => '🚈','iOS7' => '','Hex' => '1F688'), array('iOS2' => '
','iOS5' => '🚇','iOS7' => '','Hex' => '1F687'), array('iOS2' => '','iOS5' => '🚝','iOS7' => '','Hex' => '1F69D'), array('iOS2' => '
','iOS5' => '🚋','iOS7' => '','Hex' => '1F68B'), array('iOS2' => '','iOS5' => '🚃','iOS7' => '','Hex' => '1F683'), array('iOS2' => '','iOS5' => '🚎','iOS7' => '','Hex' => '1F68E'), array('iOS2' => '
','iOS5' => '🚌','iOS7' => '','Hex' => '1F68C'), array('iOS2' => '','iOS5' => '🚍','iOS7' => '','Hex' => '1F68D'), array('iOS2' => '
','iOS5' => '🚙','iOS7' => '','Hex' => '1F699'), array('iOS2' => '','iOS5' => '🚘','iOS7' => '','Hex' => '1F698'), array('iOS2' => '
','iOS5' => '🚗','iOS7' => '','Hex' => '1F697'), array('iOS2' => '
','iOS5' => '🚕','iOS7' => '','Hex' => '1F695'), array('iOS2' => '','iOS5' => '🚖','iOS7' => '','Hex' => '1F696'), array('iOS2' => '','iOS5' => '🚛','iOS7' => '','Hex' => '1F69B'), array('iOS2' => '
','iOS5' => '🚚','iOS7' => '','Hex' => '1F69A'), array('iOS2' => '','iOS5' => '🚨','iOS7' => '','Hex' => '1F6A8'), array('iOS2' => '
','iOS5' => '🚓','iOS7' => '','Hex' => '1F693'), array('iOS2' => '','iOS5' => '🚔','iOS7' => '','Hex' => '1F694'), array('iOS2' => '
','iOS5' => '🚒','iOS7' => '','Hex' => '1F692'), array('iOS2' => '
','iOS5' => '🚑','iOS7' => '','Hex' => '1F691'), array('iOS2' => '','iOS5' => '🚐','iOS7' => '','Hex' => '1F690'), array('iOS2' => '
','iOS5' => '🚲','iOS7' => '','Hex' => '1F6B2'), array('iOS2' => '','iOS5' => '🚡','iOS7' => '','Hex' => '1F6A1'), array('iOS2' => '','iOS5' => '🚟','iOS7' => '','Hex' => '1F69F'), array('iOS2' => '','iOS5' => '🚠','iOS7' => '','Hex' => '1F6A0'), array('iOS2' => '','iOS5' => '🚜','iOS7' => '','Hex' => '1F69C'), array('iOS2' => '
','iOS5' => '💈','iOS7' => '','Hex' => '1F488'), array('iOS2' => '
','iOS5' => '🚏','iOS7' => '','Hex' => '1F68F'), array('iOS2' => '
','iOS5' => '🎫','iOS7' => '','Hex' => '1F3AB'), array('iOS2' => '','iOS5' => '🚦','iOS7' => '','Hex' => '1F6A6'), array('iOS2' => '
','iOS5' => '🚥','iOS7' => '','Hex' => '1F6A5'), array('iOS2' => '
','iOS5' => '⚠','iOS7' => '⚠️','Hex' => '26A0'), array('iOS2' => '
','iOS5' => '🚧','iOS7' => '','Hex' => '1F6A7'), array('iOS2' => '
','iOS5' => '🔰','iOS7' => '','Hex' => '1F530'), array('iOS2' => '
','iOS5' => '⛽','iOS7' => '⛽️','Hex' => '26FD'), array('iOS2' => '','iOS5' => '🏮','iOS7' => '','Hex' => '1F3EE'), array('iOS2' => '
','iOS5' => '🎰','iOS7' => '','Hex' => '1F3B0'), array('iOS2' => '
','iOS5' => '♨','iOS7' => '♨️','Hex' => '2668'), array('iOS2' => '','iOS5' => '🗿','iOS7' => '','Hex' => '1F5FF'), array('iOS2' => '','iOS5' => '🎪','iOS7' => '','Hex' => '1F3AA'), array('iOS2' => '','iOS5' => '🎭','iOS7' => '','Hex' => '1F3AD'), array('iOS2' => '','iOS5' => '📍','iOS7' => '','Hex' => '1F4CD'), array('iOS2' => '','iOS5' => '🚩','iOS7' => '','Hex' => '1F6A9'), array('iOS2' => '
','iOS5' => '🇯🇵','iOS7' => '','Hex' => '1F1EF_1F1F5'), array('iOS2' => '
','iOS5' => '🇰🇷','iOS7' => '','Hex' => '1F1F0_1F1F7'), array('iOS2' => '
','iOS5' => '🇩🇪','iOS7' => '','Hex' => '1F1E9_1F1EA'), array('iOS2' => '
','iOS5' => '🇨🇳','iOS7' => '','Hex' => '1F1E8_1F1F3'), array('iOS2' => '
','iOS5' => '🇺🇸','iOS7' => '','Hex' => '1F1FA_1F1F8'), array('iOS2' => '
','iOS5' => '🇫🇷','iOS7' => '','Hex' => '1F1EB_1F1F7'), array('iOS2' => '
','iOS5' => '🇪🇸','iOS7' => '','Hex' => '1F1EA_1F1F8'), array('iOS2' => '
','iOS5' => '🇮🇹','iOS7' => '','Hex' => '1F1EE_1F1F9'), array('iOS2' => '
','iOS5' => '🇷🇺','iOS7' => '','Hex' => '1F1F7_1F1FA'), array('iOS2' => '
','iOS5' => '🇬🇧','iOS7' => '','Hex' => '1F1EC_1F1E7'), array('iOS2' => '
','iOS5' => '
','iOS7' => '','Hex' => '
'), array('iOS2' => '
','iOS5' => '🐶','iOS7' => '','Hex' => '1F436'), array('iOS2' => '
','iOS5' => '🐺','iOS7' => '','Hex' => '1F43A'), array('iOS2' => '
','iOS5' => '🐱','iOS7' => '','Hex' => '1F431'), array('iOS2' => '
','iOS5' => '🐭','iOS7' => '','Hex' => '1F42D'), array('iOS2' => '
','iOS5' => '🐹','iOS7' => '','Hex' => '1F439'), array('iOS2' => '
','iOS5' => '🐰','iOS7' => '','Hex' => '1F430'), array('iOS2' => '
','iOS5' => '🐸','iOS7' => '','Hex' => '1F438'), array('iOS2' => '
','iOS5' => '🐯','iOS7' => '','Hex' => '1F42F'), array('iOS2' => '
','iOS5' => '🐨','iOS7' => '','Hex' => '1F428'), array('iOS2' => '
','iOS5' => '🐻','iOS7' => '','Hex' => '1F43B'), array('iOS2' => '
','iOS5' => '🐷','iOS7' => '','Hex' => '1F437'), array('iOS2' => '','iOS5' => '🐽','iOS7' => '','Hex' => '1F43D'), array('iOS2' => '
','iOS5' => '🐮','iOS7' => '','Hex' => '1F42E'), array('iOS2' => '
','iOS5' => '🐗','iOS7' => '','Hex' => '1F417'), array('iOS2' => '
','iOS5' => '🐵','iOS7' => '','Hex' => '1F435'), array('iOS2' => '
','iOS5' => '🐒','iOS7' => '','Hex' => '1F412'), array('iOS2' => '
','iOS5' => '🐴','iOS7' => '','Hex' => '1F434'), array('iOS2' => '
','iOS5' => '🐑','iOS7' => '','Hex' => '1F411'), array('iOS2' => '
','iOS5' => '🐘','iOS7' => '','Hex' => '1F418'), array('iOS2' => '','iOS5' => '🐼','iOS7' => '','Hex' => '1F43C'), array('iOS2' => '
','iOS5' => '🐧','iOS7' => '','Hex' => '1F427'), array('iOS2' => '
','iOS5' => '🐦','iOS7' => '','Hex' => '1F426'), array('iOS2' => '
','iOS5' => '🐤','iOS7' => '','Hex' => '1F424'), array('iOS2' => '','iOS5' => '🐥','iOS7' => '','Hex' => '1F425'), array('iOS2' => '','iOS5' => '🐣','iOS7' => '','Hex' => '1F423'), array('iOS2' => '
','iOS5' => '🐔','iOS7' => '','Hex' => '1F414'), array('iOS2' => '
','iOS5' => '🐍','iOS7' => '','Hex' => '1F40D'), array('iOS2' => '','iOS5' => '🐢','iOS7' => '','Hex' => '1F422'), array('iOS2' => '
','iOS5' => '🐛','iOS7' => '','Hex' => '1F41B'), array('iOS2' => '','iOS5' => '🐝','iOS7' => '','Hex' => '1F41D'), array('iOS2' => '','iOS5' => '🐜','iOS7' => '','Hex' => '1F41C'), array('iOS2' => '','iOS5' => '🐞','iOS7' => '','Hex' => '1F41E'), array('iOS2' => '','iOS5' => '🐌','iOS7' => '','Hex' => '1F40C'), array('iOS2' => '
','iOS5' => '🐙','iOS7' => '','Hex' => '1F419'), array('iOS2' => '
','iOS5' => '🐚','iOS7' => '','Hex' => '1F41A'), array('iOS2' => '
','iOS5' => '🐠','iOS7' => '','Hex' => '1F420'), array('iOS2' => '
','iOS5' => '🐟','iOS7' => '','Hex' => '1F41F'), array('iOS2' => '
','iOS5' => '🐬','iOS7' => '','Hex' => '1F42C'), array('iOS2' => '
','iOS5' => '🐳','iOS7' => '','Hex' => '1F433'), array('iOS2' => '','iOS5' => '🐋','iOS7' => '','Hex' => '1F40B'), array('iOS2' => '','iOS5' => '🐄','iOS7' => '','Hex' => '1F404'), array('iOS2' => '','iOS5' => '🐏','iOS7' => '','Hex' => '1F40F'), array('iOS2' => '','iOS5' => '🐀','iOS7' => '','Hex' => '1F400'), array('iOS2' => '','iOS5' => '🐃','iOS7' => '','Hex' => '1F403'), array('iOS2' => '','iOS5' => '🐅','iOS7' => '','Hex' => '1F405'), array('iOS2' => '','iOS5' => '🐇','iOS7' => '','Hex' => '1F407'), array('iOS2' => '','iOS5' => '🐉','iOS7' => '','Hex' => '1F409'), array('iOS2' => '
','iOS5' => '🐎','iOS7' => '','Hex' => '1F40E'), array('iOS2' => '','iOS5' => '🐐','iOS7' => '','Hex' => '1F410'), array('iOS2' => '','iOS5' => '🐓','iOS7' => '','Hex' => '1F413'), array('iOS2' => '','iOS5' => '🐕','iOS7' => '','Hex' => '1F415'), array('iOS2' => '','iOS5' => '🐖','iOS7' => '','Hex' => '1F416'), array('iOS2' => '','iOS5' => '🐁','iOS7' => '','Hex' => '1F401'), array('iOS2' => '','iOS5' => '🐂','iOS7' => '','Hex' => '1F402'), array('iOS2' => '','iOS5' => '🐲','iOS7' => '','Hex' => '1F432'), array('iOS2' => '','iOS5' => '🐡','iOS7' => '','Hex' => '1F421'), array('iOS2' => '','iOS5' => '🐊','iOS7' => '','Hex' => '1F40A'), array('iOS2' => '
','iOS5' => '🐫','iOS7' => '','Hex' => '1F42B'), array('iOS2' => '','iOS5' => '🐪','iOS7' => '','Hex' => '1F42A'), array('iOS2' => '','iOS5' => '🐆','iOS7' => '','Hex' => '1F406'), array('iOS2' => '','iOS5' => '🐈','iOS7' => '','Hex' => '1F408'), array('iOS2' => '','iOS5' => '🐩','iOS7' => '','Hex' => '1F429'), array('iOS2' => '','iOS5' => '🐾','iOS7' => '','Hex' => '1F43E'), array('iOS2' => '
','iOS5' => '💐','iOS7' => '','Hex' => '1F490'), array('iOS2' => '
','iOS5' => '🌸','iOS7' => '','Hex' => '1F338'), array('iOS2' => '
','iOS5' => '🌷','iOS7' => '','Hex' => '1F337'), array('iOS2' => '
','iOS5' => '🍀','iOS7' => '','Hex' => '1F340'), array('iOS2' => '
','iOS5' => '🌹','iOS7' => '','Hex' => '1F339'), array('iOS2' => '
','iOS5' => '🌻','iOS7' => '','Hex' => '1F33B'), array('iOS2' => '
','iOS5' => '🌺','iOS7' => '','Hex' => '1F33A'), array('iOS2' => '
','iOS5' => '🍁','iOS7' => '','Hex' => '1F341'), array('iOS2' => '
','iOS5' => '🍃','iOS7' => '','Hex' => '1F343'), array('iOS2' => '
','iOS5' => '🍂','iOS7' => '','Hex' => '1F342'), array('iOS2' => '','iOS5' => '🌿','iOS7' => '','Hex' => '1F33F'), array('iOS2' => '
','iOS5' => '🌾','iOS7' => '','Hex' => '1F33E'), array('iOS2' => '','iOS5' => '🍄','iOS7' => '','Hex' => '1F344'), array('iOS2' => '
','iOS5' => '🌵','iOS7' => '','Hex' => '1F335'), array('iOS2' => '
','iOS5' => '🌴','iOS7' => '','Hex' => '1F334'), array('iOS2' => '','iOS5' => '🌲','iOS7' => '','Hex' => '1F332'), array('iOS2' => '','iOS5' => '🌳','iOS7' => '','Hex' => '1F333'), array('iOS2' => '','iOS5' => '🌰','iOS7' => '','Hex' => '1F330'), array('iOS2' => '','iOS5' => '🌱','iOS7' => '','Hex' => '1F331'), array('iOS2' => '','iOS5' => '🌼','iOS7' => '','Hex' => '1F33C'), array('iOS2' => '','iOS5' => '🌐','iOS7' => '','Hex' => '1F310'), array('iOS2' => '','iOS5' => '🌞','iOS7' => '','Hex' => '1F31E'), array('iOS2' => '','iOS5' => '🌝','iOS7' => '','Hex' => '1F31D'), array('iOS2' => '','iOS5' => '🌚','iOS7' => '','Hex' => '1F31A'), array('iOS2' => '','iOS5' => '🌑','iOS7' => '','Hex' => '1F311'), array('iOS2' => '','iOS5' => '🌒','iOS7' => '','Hex' => '1F312'), array('iOS2' => '','iOS5' => '🌓','iOS7' => '','Hex' => '1F313'), array('iOS2' => '','iOS5' => '🌔','iOS7' => '','Hex' => '1F314'), array('iOS2' => '','iOS5' => '🌕','iOS7' => '','Hex' => '1F315'), array('iOS2' => '','iOS5' => '🌖','iOS7' => '','Hex' => '1F316'), array('iOS2' => '','iOS5' => '🌗','iOS7' => '','Hex' => '1F317'), array('iOS2' => '','iOS5' => '🌘','iOS7' => '','Hex' => '1F318'), array('iOS2' => '','iOS5' => '🌜','iOS7' => '','Hex' => '1F31C'), array('iOS2' => '','iOS5' => '🌛','iOS7' => '','Hex' => '1F31B'), array('iOS2' => '
','iOS5' => '🌙','iOS7' => '','Hex' => '1F319'), array('iOS2' => '','iOS5' => '🌍','iOS7' => '','Hex' => '1F30D'), array('iOS2' => '','iOS5' => '🌎','iOS7' => '','Hex' => '1F30E'), array('iOS2' => '','iOS5' => '🌏','iOS7' => '','Hex' => '1F30F'), array('iOS2' => '','iOS5' => '🌋','iOS7' => '','Hex' => '1F30B'), array('iOS2' => '','iOS5' => '🌌','iOS7' => '','Hex' => '1F30C'), array('iOS2' => '','iOS5' => '🌠','iOS7' => '','Hex' => '1F320'), array('iOS2' => '','iOS5' => '⭐','iOS7' => '⭐️','Hex' => '2B50'), array('iOS2' => '
','iOS5' => '☀','iOS7' => '☀️','Hex' => '2600'), array('iOS2' => '','iOS5' => '⛅','iOS7' => '⛅️','Hex' => '26C5'), array('iOS2' => '
','iOS5' => '☁','iOS7' => '☁️','Hex' => '2601'), array('iOS2' => '
','iOS5' => '⚡','iOS7' => '⚡️','Hex' => '26A1'), array('iOS2' => '
','iOS5' => '☔','iOS7' => '☔️','Hex' => '2614'), array('iOS2' => '','iOS5' => '❄','iOS7' => '❄️','Hex' => '2744'), array('iOS2' => '
','iOS5' => '⛄','iOS7' => '⛄️','Hex' => '26C4'), array('iOS2' => '
','iOS5' => '🌀','iOS7' => '🌀','Hex' => '1F300'), array('iOS2' => '','iOS5' => '🌁','iOS7' => '','Hex' => '1F301'), array('iOS2' => '
','iOS5' => '🌈','iOS7' => '','Hex' => '1F308'), array('iOS2' => '
','iOS5' => '🌊','iOS7' => '','Hex' => '1F30A'), array('iOS2' => '
','iOS5' => '🎍','iOS7' => '','Hex' => '1F38D'), array('iOS2' => '
','iOS5' => '💝','iOS7' => '','Hex' => '1F49D'), array('iOS2' => '
','iOS5' => '🎎','iOS7' => '','Hex' => '1F38E'), array('iOS2' => '
','iOS5' => '🎒','iOS7' => '','Hex' => '1F392'), array('iOS2' => '
','iOS5' => '🎓','iOS7' => '','Hex' => '1F393'), array('iOS2' => '
','iOS5' => '🎏','iOS7' => '','Hex' => '1F38F'), array('iOS2' => '
','iOS5' => '🎆','iOS7' => '','Hex' => '1F386'), array('iOS2' => '
','iOS5' => '🎇','iOS7' => '','Hex' => '1F387'), array('iOS2' => '
','iOS5' => '🎐','iOS7' => '','Hex' => '1F390'), array('iOS2' => '
','iOS5' => '🎑','iOS7' => '','Hex' => '1F391'), array('iOS2' => '
','iOS5' => '🎃','iOS7' => '','Hex' => '1F383'), array('iOS2' => '
','iOS5' => '👻','iOS7' => '','Hex' => '1F47B'), array('iOS2' => '
','iOS5' => '🎅','iOS7' => '','Hex' => '1F385'), array('iOS2' => '
','iOS5' => '🎄','iOS7' => '','Hex' => '1F384'), array('iOS2' => '
','iOS5' => '🎁','iOS7' => '','Hex' => '1F381'), array('iOS2' => '','iOS5' => '🎋','iOS7' => '','Hex' => '1F38B'), array('iOS2' => '
','iOS5' => '🎉','iOS7' => '','Hex' => '1F389'), array('iOS2' => '','iOS5' => '🎊','iOS7' => '','Hex' => '1F38A'), array('iOS2' => '
','iOS5' => '🎈','iOS7' => '','Hex' => '1F388'), array('iOS2' => '
','iOS5' => '🎌','iOS7' => '','Hex' => '1F38C'), array('iOS2' => '','iOS5' => '🔮','iOS7' => '','Hex' => '1F52E'), array('iOS2' => '
','iOS5' => '🎥','iOS7' => '','Hex' => '1F3A5'), array('iOS2' => '
','iOS5' => '📷','iOS7' => '','Hex' => '1F4F7'), array('iOS2' => '','iOS5' => '📹','iOS7' => '','Hex' => '1F4F9'), array('iOS2' => '
','iOS5' => '📼','iOS7' => '','Hex' => '1F4FC'), array('iOS2' => '
','iOS5' => '💿','iOS7' => '','Hex' => '1F4BF'), array('iOS2' => '
','iOS5' => '📀','iOS7' => '','Hex' => '1F4C0'), array('iOS2' => '
','iOS5' => '💽','iOS7' => '','Hex' => '1F4BD'), array('iOS2' => '','iOS5' => '💾','iOS7' => '','Hex' => '1F4BE'), array('iOS2' => '
','iOS5' => '💻','iOS7' => '','Hex' => '1F4BB'), array('iOS2' => '
','iOS5' => '📱','iOS7' => '','Hex' => '1F4F1'), array('iOS2' => '
','iOS5' => '☎','iOS7' => '☎️','Hex' => '260E'), array('iOS2' => '','iOS5' => '📞','iOS7' => '','Hex' => '1F4DE'), array('iOS2' => '','iOS5' => '📟','iOS7' => '','Hex' => '1F4DF'), array('iOS2' => '
','iOS5' => '📠','iOS7' => '','Hex' => '1F4E0'), array('iOS2' => '
','iOS5' => '📡','iOS7' => '','Hex' => '1F4E1'), array('iOS2' => '
','iOS5' => '📺','iOS7' => '','Hex' => '1F4FA'), array('iOS2' => '
','iOS5' => '📻','iOS7' => '','Hex' => '1F4FB'), array('iOS2' => '','iOS5' => '🔊','iOS7' => '','Hex' => '1F50A'), array('iOS2' => '','iOS5' => '🔉','iOS7' => '','Hex' => '1F509'), array('iOS2' => '
','iOS5' => '🔈','iOS7' => '','Hex' => '1F508'), array('iOS2' => '','iOS5' => '🔇','iOS7' => '','Hex' => '1F507'), array('iOS2' => '
','iOS5' => '🔔','iOS7' => '','Hex' => '1F514'), array('iOS2' => '','iOS5' => '🔕','iOS7' => '','Hex' => '1F515'), array('iOS2' => '
','iOS5' => '📢','iOS7' => '','Hex' => '1F4E2'), array('iOS2' => '
','iOS5' => '📣','iOS7' => '','Hex' => '1F4E3'), array('iOS2' => '','iOS5' => '⏳','iOS7' => '','Hex' => '23F3'), array('iOS2' => '','iOS5' => '⌛','iOS7' => '⌛️','Hex' => '231B'), array('iOS2' => '','iOS5' => '⏰','iOS7' => '⏰','Hex' => '23F0'), array('iOS2' => '','iOS5' => '⌚','iOS7' => '⌚️','Hex' => '231A'), array('iOS2' => '
','iOS5' => '🔓','iOS7' => '','Hex' => '1F513'), array('iOS2' => '
','iOS5' => '🔒','iOS7' => '','Hex' => '1F512'), array('iOS2' => '','iOS5' => '🔏','iOS7' => '','Hex' => '1F50F'), array('iOS2' => '','iOS5' => '🔐','iOS7' => '','Hex' => '1F510'), array('iOS2' => '
','iOS5' => '🔑','iOS7' => '','Hex' => '1F511'), array('iOS2' => '','iOS5' => '🔎','iOS7' => '','Hex' => '1F50E'), array('iOS2' => '
','iOS5' => '💡','iOS7' => '','Hex' => '1F4A1'), array('iOS2' => '','iOS5' => '🔦','iOS7' => '','Hex' => '1F526'), array('iOS2' => '','iOS5' => '🔆','iOS7' => '','Hex' => '1F506'), array('iOS2' => '','iOS5' => '🔅','iOS7' => '','Hex' => '1F505'), array('iOS2' => '','iOS5' => '🔌','iOS7' => '','Hex' => '1F50C'), array('iOS2' => '','iOS5' => '🔋','iOS7' => '','Hex' => '1F50B'), array('iOS2' => '
','iOS5' => '🔍','iOS7' => '','Hex' => '1F50D'), array('iOS2' => '','iOS5' => '🛁','iOS7' => '','Hex' => '1F6C1'), array('iOS2' => '
','iOS5' => '🛀','iOS7' => '','Hex' => '1F6C0'), array('iOS2' => '','iOS5' => '🚿','iOS7' => '','Hex' => '1F6BF'), array('iOS2' => '
','iOS5' => '🚽','iOS7' => '','Hex' => '1F6BD'), array('iOS2' => '','iOS5' => '🔧','iOS7' => '','Hex' => '1F527'), array('iOS2' => '','iOS5' => '🔩','iOS7' => '','Hex' => '1F529'), array('iOS2' => '
','iOS5' => '🔨','iOS7' => '','Hex' => '1F528'), array('iOS2' => '','iOS5' => '🚪','iOS7' => '','Hex' => '1F6AA'), array('iOS2' => '
','iOS5' => '🚬','iOS7' => '','Hex' => '1F6AC'), array('iOS2' => '
','iOS5' => '💣','iOS7' => '','Hex' => '1F4A3'), array('iOS2' => '
','iOS5' => '🔫','iOS7' => '','Hex' => '1F52B'), array('iOS2' => '','iOS5' => '🔪','iOS7' => '','Hex' => '1F52A'), array('iOS2' => '
','iOS5' => '💊','iOS7' => '','Hex' => '1F48A'), array('iOS2' => '
','iOS5' => '💉','iOS7' => '','Hex' => '1F489'), array('iOS2' => '
','iOS5' => '💰','iOS7' => '','Hex' => '1F4B0'), array('iOS2' => '','iOS5' => '💴','iOS7' => '','Hex' => '1F4B4'), array('iOS2' => '','iOS5' => '💵','iOS7' => '','Hex' => '1F4B5'), array('iOS2' => '','iOS5' => '💷','iOS7' => '','Hex' => '1F4B7'), array('iOS2' => '','iOS5' => '💶','iOS7' => '','Hex' => '1F4B6'), array('iOS2' => '','iOS5' => '💳','iOS7' => '','Hex' => '1F4B3'), array('iOS2' => '','iOS5' => '💸','iOS7' => '','Hex' => '1F4B8'), array('iOS2' => '
','iOS5' => '📲','iOS7' => '','Hex' => '1F4F2'), array('iOS2' => '','iOS5' => '📧','iOS7' => '','Hex' => '1F4E7'), array('iOS2' => '','iOS5' => '📥','iOS7' => '','Hex' => '1F4E5'), array('iOS2' => '','iOS5' => '📤','iOS7' => '','Hex' => '1F4E4'), array('iOS2' => '','iOS5' => '✉','iOS7' => '✉️','Hex' => '2709'), array('iOS2' => '
','iOS5' => '📩','iOS7' => '','Hex' => '1F4E9'), array('iOS2' => '','iOS5' => '📨','iOS7' => '','Hex' => '1F4E8'), array('iOS2' => '','iOS5' => '📯','iOS7' => '','Hex' => '1F4EF'), array('iOS2' => '
','iOS5' => '📫','iOS7' => '','Hex' => '1F4EB'), array('iOS2' => '','iOS5' => '📪','iOS7' => '','Hex' => '1F4EA'), array('iOS2' => '','iOS5' => '📬','iOS7' => '','Hex' => '1F4EC'), array('iOS2' => '','iOS5' => '📭','iOS7' => '','Hex' => '1F4ED'), array('iOS2' => '
','iOS5' => '📮','iOS7' => '','Hex' => '1F4EE'), array('iOS2' => '','iOS5' => '📦','iOS7' => '','Hex' => '1F4E6'), array('iOS2' => '
','iOS5' => '📝','iOS7' => '','Hex' => '1F4DD'), array('iOS2' => '','iOS5' => '📄','iOS7' => '','Hex' => '1F4C4'), array('iOS2' => '','iOS5' => '📃','iOS7' => '','Hex' => '1F4C3'), array('iOS2' => '','iOS5' => '📑','iOS7' => '','Hex' => '1F4D1'), array('iOS2' => '','iOS5' => '📊','iOS7' => '','Hex' => '1F4CA'), array('iOS2' => '','iOS5' => '📈','iOS7' => '','Hex' => '1F4C8'), array('iOS2' => '','iOS5' => '📉','iOS7' => '','Hex' => '1F4C9'), array('iOS2' => '','iOS5' => '📜','iOS7' => '','Hex' => '1F4DC'), array('iOS2' => '','iOS5' => '📋','iOS7' => '','Hex' => '1F4CB'), array('iOS2' => '','iOS5' => '📅','iOS7' => '','Hex' => '1F4C5'), array('iOS2' => '','iOS5' => '📆','iOS7' => '','Hex' => '1F4C6'), array('iOS2' => '','iOS5' => '📇','iOS7' => '','Hex' => '1F4C7'), array('iOS2' => '','iOS5' => '📁','iOS7' => '','Hex' => '1F4C1'), array('iOS2' => '','iOS5' => '📂','iOS7' => '','Hex' => '1F4C2'), array('iOS2' => '
','iOS5' => '✂','iOS7' => '✂️','Hex' => '2702'), array('iOS2' => '','iOS5' => '📌','iOS7' => '','Hex' => '1F4CC'), array('iOS2' => '','iOS5' => '📎','iOS7' => '','Hex' => '1F4CE'), array('iOS2' => '','iOS5' => '✒','iOS7' => '✒️','Hex' => '2712'), array('iOS2' => '','iOS5' => '✏','iOS7' => '✏️','Hex' => '270F'), array('iOS2' => '','iOS5' => '📏','iOS7' => '','Hex' => '1F4CF'), array('iOS2' => '','iOS5' => '📐','iOS7' => '','Hex' => '1F4D0'), array('iOS2' => '','iOS5' => '📕','iOS7' => '','Hex' => '1F4D5'), array('iOS2' => '','iOS5' => '📗','iOS7' => '','Hex' => '1F4D7'), array('iOS2' => '','iOS5' => '📘','iOS7' => '','Hex' => '1F4D8'), array('iOS2' => '','iOS5' => '📙','iOS7' => '','Hex' => '1F4D9'), array('iOS2' => '','iOS5' => '📓','iOS7' => '','Hex' => '1F4D3'), array('iOS2' => '','iOS5' => '📔','iOS7' => '','Hex' => '1F4D4'), array('iOS2' => '','iOS5' => '📒','iOS7' => '','Hex' => '1F4D2'), array('iOS2' => '','iOS5' => '📚','iOS7' => '','Hex' => '1F4DA'), array('iOS2' => '
','iOS5' => '📖','iOS7' => '','Hex' => '1F4D6'), array('iOS2' => '','iOS5' => '🔖','iOS7' => '','Hex' => '1F516'), array('iOS2' => '','iOS5' => '📛','iOS7' => '','Hex' => '1F4DB'), array('iOS2' => '','iOS5' => '🔬','iOS7' => '','Hex' => '1F52C'), array('iOS2' => '','iOS5' => '🔭','iOS7' => '','Hex' => '1F52D'), array('iOS2' => '','iOS5' => '📰','iOS7' => '','Hex' => '1F4F0'), array('iOS2' => '
','iOS5' => '🎨','iOS7' => '','Hex' => '1F3A8'), array('iOS2' => '
','iOS5' => '🎬','iOS7' => '','Hex' => '1F3AC'), array('iOS2' => '
','iOS5' => '🎤','iOS7' => '','Hex' => '1F3A4'), array('iOS2' => '
','iOS5' => '🎧','iOS7' => '','Hex' => '1F3A7'), array('iOS2' => '','iOS5' => '🎼','iOS7' => '','Hex' => '1F3BC'), array('iOS2' => '
','iOS5' => '🎵','iOS7' => '','Hex' => '1F3B5'), array('iOS2' => '
','iOS5' => '🎶','iOS7' => '','Hex' => '1F3B6'), array('iOS2' => '','iOS5' => '🎹','iOS7' => '','Hex' => '1F3B9'), array('iOS2' => '','iOS5' => '🎻','iOS7' => '','Hex' => '1F3BB'), array('iOS2' => '
','iOS5' => '🎺','iOS7' => '','Hex' => '1F3BA'), array('iOS2' => '
','iOS5' => '🎷','iOS7' => '','Hex' => '1F3B7'), array('iOS2' => '
','iOS5' => '🎸','iOS7' => '','Hex' => '1F3B8'), array('iOS2' => '
','iOS5' => '👾','iOS7' => '','Hex' => '1F47E'), array('iOS2' => '','iOS5' => '🎮','iOS7' => '','Hex' => '1F3AE'), array('iOS2' => '','iOS5' => '🃏','iOS7' => '','Hex' => '1F0CF'), array('iOS2' => '','iOS5' => '🎴','iOS7' => '','Hex' => '1F3B4'), array('iOS2' => '
','iOS5' => '🀄','iOS7' => '🀄️','Hex' => '1F004'), array('iOS2' => '','iOS5' => '🎲','iOS7' => '','Hex' => '1F3B2'), array('iOS2' => '
','iOS5' => '🎯','iOS7' => '','Hex' => '1F3AF'), array('iOS2' => '
','iOS5' => '🏈','iOS7' => '','Hex' => '1F3C8'), array('iOS2' => '
','iOS5' => '🏀','iOS7' => '','Hex' => '1F3C0'), array('iOS2' => '
','iOS5' => '⚽','iOS7' => '⚽️','Hex' => '26BD'), array('iOS2' => '
','iOS5' => '⚾','iOS7' => '⚾️','Hex' => '26BE'), array('iOS2' => '
','iOS5' => '🎾','iOS7' => '','Hex' => '1F3BE'), array('iOS2' => '
','iOS5' => '🎱','iOS7' => '','Hex' => '1F3B1'), array('iOS2' => '','iOS5' => '🏉','iOS7' => '','Hex' => '1F3C9'), array('iOS2' => '','iOS5' => '🎳','iOS7' => '','Hex' => '1F3B3'), array('iOS2' => '
','iOS5' => '⛳','iOS7' => '⛳️','Hex' => '26F3'), array('iOS2' => '','iOS5' => '🚵','iOS7' => '','Hex' => '1F6B5'), array('iOS2' => '','iOS5' => '🚴','iOS7' => '','Hex' => '1F6B4'), array('iOS2' => '
','iOS5' => '🏁','iOS7' => '','Hex' => '1F3C1'), array('iOS2' => '','iOS5' => '🏇','iOS7' => '','Hex' => '1F3C7'), array('iOS2' => '
','iOS5' => '🏆','iOS7' => '','Hex' => '1F3C6'), array('iOS2' => '
','iOS5' => '🎿','iOS7' => '','Hex' => '1F3BF'), array('iOS2' => '','iOS5' => '🏂','iOS7' => '','Hex' => '1F3C2'), array('iOS2' => '
','iOS5' => '🏊','iOS7' => '','Hex' => '1F3CA'), array('iOS2' => '
','iOS5' => '🏄','iOS7' => '','Hex' => '1F3C4'), array('iOS2' => '','iOS5' => '🎣','iOS7' => '','Hex' => '1F3A3'), array('iOS2' => '
','iOS5' => '☕','iOS7' => '☕️','Hex' => '2615'), array('iOS2' => '
','iOS5' => '🍵','iOS7' => '','Hex' => '1F375'), array('iOS2' => '
','iOS5' => '🍶','iOS7' => '','Hex' => '1F376'), array('iOS2' => '','iOS5' => '🍼','iOS7' => '','Hex' => '1F37C'), array('iOS2' => '
','iOS5' => '🍺','iOS7' => '','Hex' => '1F37A'), array('iOS2' => '
','iOS5' => '🍻','iOS7' => '','Hex' => '1F37B'), array('iOS2' => '
','iOS5' => '🍸','iOS7' => '','Hex' => '1F378'), array('iOS2' => '','iOS5' => '🍹','iOS7' => '','Hex' => '1F379'), array('iOS2' => '','iOS5' => '🍷','iOS7' => '','Hex' => '1F377'), array('iOS2' => '
','iOS5' => '🍴','iOS7' => '','Hex' => '1F374'), array('iOS2' => '','iOS5' => '🍕','iOS7' => '','Hex' => '1F355'), array('iOS2' => '
','iOS5' => '🍔','iOS7' => '','Hex' => '1F354'), array('iOS2' => '
','iOS5' => '🍟','iOS7' => '','Hex' => '1F35F'), array('iOS2' => '','iOS5' => '🍗','iOS7' => '','Hex' => '1F357'), array('iOS2' => '','iOS5' => '🍖','iOS7' => '','Hex' => '1F356'), array('iOS2' => '
','iOS5' => '🍝','iOS7' => '','Hex' => '1F35D'), array('iOS2' => '
','iOS5' => '🍛','iOS7' => '','Hex' => '1F35B'), array('iOS2' => '','iOS5' => '🍤','iOS7' => '','Hex' => '1F364'), array('iOS2' => '
','iOS5' => '🍱','iOS7' => '','Hex' => '1F371'), array('iOS2' => '
','iOS5' => '🍣','iOS7' => '','Hex' => '1F363'), array('iOS2' => '','iOS5' => '🍥','iOS7' => '','Hex' => '1F365'), array('iOS2' => '
','iOS5' => '🍙','iOS7' => '','Hex' => '1F359'), array('iOS2' => '
','iOS5' => '🍘','iOS7' => '','Hex' => '1F358'), array('iOS2' => '
','iOS5' => '🍚','iOS7' => '','Hex' => '1F35A'), array('iOS2' => '
','iOS5' => '🍜','iOS7' => '','Hex' => '1F35C'), array('iOS2' => '
','iOS5' => '🍲','iOS7' => '','Hex' => '1F372'), array('iOS2' => '
','iOS5' => '🍢','iOS7' => '','Hex' => '1F362'), array('iOS2' => '
','iOS5' => '🍡','iOS7' => '','Hex' => '1F361'), array('iOS2' => '
','iOS5' => '🍳','iOS7' => '','Hex' => '1F373'), array('iOS2' => '
','iOS5' => '🍞','iOS7' => '','Hex' => '1F35E'), array('iOS2' => '','iOS5' => '🍩','iOS7' => '','Hex' => '1F369'), array('iOS2' => '','iOS5' => '🍮','iOS7' => '','Hex' => '1F36E'), array('iOS2' => '
','iOS5' => '🍦','iOS7' => '','Hex' => '1F366'), array('iOS2' => '','iOS5' => '🍨','iOS7' => '','Hex' => '1F368'), array('iOS2' => '
','iOS5' => '🍧','iOS7' => '','Hex' => '1F367'), array('iOS2' => '
','iOS5' => '🎂','iOS7' => '','Hex' => '1F382'), array('iOS2' => '
','iOS5' => '🍰','iOS7' => '','Hex' => '1F370'), array('iOS2' => '','iOS5' => '🍪','iOS7' => '','Hex' => '1F36A'), array('iOS2' => '','iOS5' => '🍫','iOS7' => '','Hex' => '1F36B'), array('iOS2' => '','iOS5' => '🍬','iOS7' => '','Hex' => '1F36C'), array('iOS2' => '','iOS5' => '🍭','iOS7' => '','Hex' => '1F36D'), array('iOS2' => '','iOS5' => '🍯','iOS7' => '','Hex' => '1F36F'), array('iOS2' => '
','iOS5' => '🍎','iOS7' => '','Hex' => '1F34E'), array('iOS2' => '','iOS5' => '🍏','iOS7' => '','Hex' => '1F34F'), array('iOS2' => '
','iOS5' => '🍊','iOS7' => '','Hex' => '1F34A'), array('iOS2' => '','iOS5' => '🍋','iOS7' => '','Hex' => '1F34B'), array('iOS2' => '','iOS5' => '🍒','iOS7' => '','Hex' => '1F352'), array('iOS2' => '','iOS5' => '🍇','iOS7' => '','Hex' => '1F347'), array('iOS2' => '
','iOS5' => '🍉','iOS7' => '','Hex' => '1F349'), array('iOS2' => '
','iOS5' => '🍓','iOS7' => '','Hex' => '1F353'), array('iOS2' => '','iOS5' => '🍑','iOS7' => '','Hex' => '1F351'), array('iOS2' => '','iOS5' => '🍈','iOS7' => '','Hex' => '1F348'), array('iOS2' => '','iOS5' => '🍌','iOS7' => '','Hex' => '1F34C'), array('iOS2' => '','iOS5' => '🍐','iOS7' => '','Hex' => '1F350'), array('iOS2' => '','iOS5' => '🍍','iOS7' => '','Hex' => '1F34D'), array('iOS2' => '','iOS5' => '🍠','iOS7' => '','Hex' => '1F360'), array('iOS2' => '
','iOS5' => '🍆','iOS7' => '','Hex' => '1F346'), array('iOS2' => '
','iOS5' => '🍅','iOS7' => '','Hex' => '1F345'), array('iOS2' => '','iOS5' => '🌽','iOS7' => '','Hex' => '1F33D'), array('iOS2' => '
','iOS5' => '1⃣','iOS7' => '','Hex' => '0031_20E3'), array('iOS2' => '
','iOS5' => '2⃣','iOS7' => '','Hex' => '0032_20E3'), array('iOS2' => '
','iOS5' => '3⃣','iOS7' => '','Hex' => '0033_20E3'), array('iOS2' => '
','iOS5' => '4⃣','iOS7' => '','Hex' => '0034_20E3'), array('iOS2' => '
','iOS5' => '2⃣','iOS7' => '','Hex' => '0032_20E3'), array('iOS2' => '
','iOS5' => '0⃣','iOS7' => '','Hex' => '0030_20E3'), array('iOS2' => '
','iOS5' => '5⃣','iOS7' => '','Hex' => '0035_20E3'), array('iOS2' => '
','iOS5' => '6⃣','iOS7' => '','Hex' => '0036_20E3'), array('iOS2' => '
','iOS5' => '7⃣','iOS7' => '','Hex' => '0037_20E3'), array('iOS2' => '
','iOS5' => '8⃣','iOS7' => '','Hex' => '0038_20E3'), array('iOS2' => '
','iOS5' => '9⃣','iOS7' => '','Hex' => '0039_20E3'), array('iOS2' => '','iOS5' => '🔟','iOS7' => '','Hex' => '1F51F'), array('iOS2' => '','iOS5' => '🔢','iOS7' => '','Hex' => '1F522'), array('iOS2' => '
','iOS5' => '#⃣','iOS7' => '','Hex' => '0023_20E3'), array('iOS2' => '','iOS5' => '🔣','iOS7' => '','Hex' => '1F523'), array('iOS2' => '
','iOS5' => '⬆','iOS7' => '⬆️','Hex' => '2B06'), array('iOS2' => '
','iOS5' => '⬇','iOS7' => '⬇️','Hex' => '2B07'), array('iOS2' => '
','iOS5' => '⬅','iOS7' => '⬅️','Hex' => '2B05'), array('iOS2' => '
','iOS5' => '➡','iOS7' => '➡️','Hex' => '27A1'), array('iOS2' => '','iOS5' => '🔠','iOS7' => '','Hex' => '1F520'), array('iOS2' => '','iOS5' => '🔡','iOS7' => '','Hex' => '1F521'), array('iOS2' => '','iOS5' => '🔤','iOS7' => '','Hex' => '1F524'), array('iOS2' => '
','iOS5' => '↗','iOS7' => '↗️','Hex' => '2197'), array('iOS2' => '
','iOS5' => '↖','iOS7' => '↖️','Hex' => '2196'), array('iOS2' => '
','iOS5' => '↘','iOS7' => '↘️','Hex' => '2198'), array('iOS2' => '
','iOS5' => '↙','iOS7' => '↙️','Hex' => '2199'), array('iOS2' => '','iOS5' => '↔','iOS7' => '↔️','Hex' => '2194'), array('iOS2' => '','iOS5' => '↕','iOS7' => '↕️','Hex' => '2195'), array('iOS2' => '','iOS5' => '🔄','iOS7' => '','Hex' => '1F504'), array('iOS2' => '
','iOS5' => '◀','iOS7' => '◀️','Hex' => '25C0'), array('iOS2' => '
','iOS5' => '▶','iOS7' => '▶️','Hex' => '25B6'), array('iOS2' => '','iOS5' => '🔼','iOS7' => '','Hex' => '1F53C'), array('iOS2' => '','iOS5' => '🔽','iOS7' => '','Hex' => '1F53D'), array('iOS2' => '','iOS5' => '↩','iOS7' => '↩️','Hex' => '21A9'), array('iOS2' => '','iOS5' => '↪','iOS7' => '↪️','Hex' => '21AA'), array('iOS2' => '','iOS5' => 'ℹ','iOS7' => 'ℹ️','Hex' => '2139'), array('iOS2' => '
','iOS5' => '⏪','iOS7' => '','Hex' => '23EA'), array('iOS2' => '
','iOS5' => '⏩','iOS7' => '','Hex' => '23E9'), array('iOS2' => '','iOS5' => '⏫','iOS7' => '','Hex' => '23EB'), array('iOS2' => '','iOS5' => '⏬','iOS7' => '','Hex' => '23EC'), array('iOS2' => '','iOS5' => '⤵','iOS7' => '⤵️','Hex' => '2935'), array('iOS2' => '','iOS5' => '⤴','iOS7' => '⤴️','Hex' => '2934'), array('iOS2' => '
','iOS5' => '🆗','iOS7' => '','Hex' => '1F197'), array('iOS2' => '','iOS5' => '🔀','iOS7' => '','Hex' => '1F500'), array('iOS2' => '','iOS5' => '🔁','iOS7' => '','Hex' => '1F501'), array('iOS2' => '','iOS5' => '🔂','iOS7' => '','Hex' => '1F502'), array('iOS2' => '
','iOS5' => '🆕','iOS7' => '','Hex' => '1F195'), array('iOS2' => '
','iOS5' => '🆙','iOS7' => '','Hex' => '1F199'), array('iOS2' => '
','iOS5' => '🆒','iOS7' => '','Hex' => '1F192'), array('iOS2' => '','iOS5' => '🆓','iOS7' => '','Hex' => '1F193'), array('iOS2' => '','iOS5' => '🆖','iOS7' => '','Hex' => '1F196'), array('iOS2' => '
','iOS5' => '📶','iOS7' => '','Hex' => '1F4F6'), array('iOS2' => '
','iOS5' => '🎦','iOS7' => '','Hex' => '1F3A6'), array('iOS2' => '
','iOS5' => '🈁','iOS7' => '','Hex' => '1F201'), array('iOS2' => '
','iOS5' => '🈯','iOS7' => '🈯️','Hex' => '1F22F'), array('iOS2' => '
','iOS5' => '🈳','iOS7' => '','Hex' => '1F233'), array('iOS2' => '
','iOS5' => '🈵','iOS7' => '','Hex' => '1F235'), array('iOS2' => '','iOS5' => '🈴','iOS7' => '','Hex' => '1F234'), array('iOS2' => '','iOS5' => '🈲','iOS7' => '','Hex' => '1F232'), array('iOS2' => '
','iOS5' => '🉐','iOS7' => '','Hex' => '1F250'), array('iOS2' => '
','iOS5' => '🈹','iOS7' => '','Hex' => '1F239'), array('iOS2' => '
','iOS5' => '🈺','iOS7' => '','Hex' => '1F23A'), array('iOS2' => '
','iOS5' => '🈶','iOS7' => '','Hex' => '1F236'), array('iOS2' => '
','iOS5' => '🈚','iOS7' => '🈚️','Hex' => '1F21A'), array('iOS2' => '
','iOS5' => '🚻','iOS7' => '','Hex' => '1F6BB'), array('iOS2' => '
','iOS5' => '🚹','iOS7' => '','Hex' => '1F6B9'), array('iOS2' => '
','iOS5' => '🚺','iOS7' => '','Hex' => '1F6BA'), array('iOS2' => '
','iOS5' => '🚼','iOS7' => '','Hex' => '1F6BC'), array('iOS2' => '
','iOS5' => '🚾','iOS7' => '','Hex' => '1F6BE'), array('iOS2' => '','iOS5' => '🚰','iOS7' => '','Hex' => '1F6B0'), array('iOS2' => '','iOS5' => '🚮','iOS7' => '','Hex' => '1F6AE'), array('iOS2' => '
','iOS5' => '🅿','iOS7' => '🅿️','Hex' => '1F17F'), array('iOS2' => '
','iOS5' => '♿','iOS7' => '♿️','Hex' => '267F'), array('iOS2' => '
','iOS5' => '🚭','iOS7' => '','Hex' => '1F6AD'), array('iOS2' => '
','iOS5' => '🈷','iOS7' => '','Hex' => '1F237'), array('iOS2' => '
','iOS5' => '🈸','iOS7' => '','Hex' => '1F238'), array('iOS2' => '
','iOS5' => '🈂','iOS7' => '','Hex' => '1F202'), array('iOS2' => '','iOS5' => 'Ⓜ','iOS7' => 'Ⓜ️','Hex' => '24C2'), array('iOS2' => '','iOS5' => '🛂','iOS7' => '','Hex' => '1F6C2'), array('iOS2' => '','iOS5' => '🛄','iOS7' => '','Hex' => '1F6C4'), array('iOS2' => '','iOS5' => '🛅','iOS7' => '','Hex' => '1F6C5'), array('iOS2' => '','iOS5' => '🛃','iOS7' => '','Hex' => '1F6C3'), array('iOS2' => '','iOS5' => '🉑','iOS7' => '','Hex' => '1F251'), array('iOS2' => '
','iOS5' => '㊙','iOS7' => '㊙️','Hex' => '3299'), array('iOS2' => '
','iOS5' => '㊗','iOS7' => '㊗️','Hex' => '3297'), array('iOS2' => '','iOS5' => '🆑','iOS7' => '','Hex' => '1F191'), array('iOS2' => '','iOS5' => '🆘','iOS7' => '','Hex' => '1F198'), array('iOS2' => '
','iOS5' => '🆔','iOS7' => '','Hex' => '1F194'), array('iOS2' => '','iOS5' => '🚫','iOS7' => '','Hex' => '1F6AB'), array('iOS2' => '
','iOS5' => '🔞','iOS7' => '','Hex' => '1F51E'), array('iOS2' => '','iOS5' => '📵','iOS7' => '','Hex' => '1F4F5'), array('iOS2' => '','iOS5' => '🚯','iOS7' => '','Hex' => '1F6AF'), array('iOS2' => '','iOS5' => '🚱','iOS7' => '','Hex' => '1F6B1'), array('iOS2' => '','iOS5' => '🚳','iOS7' => '','Hex' => '1F6B3'), array('iOS2' => '','iOS5' => '🚷','iOS7' => '','Hex' => '1F6B7'), array('iOS2' => '','iOS5' => '🚸','iOS7' => '','Hex' => '1F6B8'), array('iOS2' => '','iOS5' => '⛔','iOS7' => '⛔️','Hex' => '26D4'), array('iOS2' => '
','iOS5' => '✳','iOS7' => '✳️','Hex' => '2733'), array('iOS2' => '','iOS5' => '❇','iOS7' => '❇️','Hex' => '2747'), array('iOS2' => '','iOS5' => '❎','iOS7' => '','Hex' => '274E'), array('iOS2' => '','iOS5' => '✅','iOS7' => '','Hex' => '2705'), array('iOS2' => '
','iOS5' => '✴','iOS7' => '✴️','Hex' => '2734'), array('iOS2' => '
','iOS5' => '💟','iOS7' => '','Hex' => '1F49F'), array('iOS2' => '
','iOS5' => '🆚','iOS7' => '','Hex' => '1F19A'), array('iOS2' => '
','iOS5' => '📳','iOS7' => '','Hex' => '1F4F3'), array('iOS2' => '
','iOS5' => '📴','iOS7' => '','Hex' => '1F4F4'), array('iOS2' => '
','iOS5' => '🅰','iOS7' => '','Hex' => '1F170'), array('iOS2' => '
','iOS5' => '🅱','iOS7' => '','Hex' => '1F171'), array('iOS2' => '
','iOS5' => '🆎','iOS7' => '','Hex' => '1F18E'), array('iOS2' => '
','iOS5' => '🅾','iOS7' => '','Hex' => '1F17E'), array('iOS2' => '','iOS5' => '💠','iOS7' => '','Hex' => '1F4A0'), array('iOS2' => '
','iOS5' => '➿','iOS7' => '','Hex' => '27BF'), array('iOS2' => '','iOS5' => '♻','iOS7' => '♻️','Hex' => '267B'), array('iOS2' => '
','iOS5' => '♈','iOS7' => '♈️','Hex' => '2648'), array('iOS2' => '
','iOS5' => '♉','iOS7' => '♉️','Hex' => '2649'), array('iOS2' => '
','iOS5' => '♊','iOS7' => '♊️','Hex' => '264A'), array('iOS2' => '
','iOS5' => '♋','iOS7' => '♋️','Hex' => '264B'), array('iOS2' => '
','iOS5' => '♌','iOS7' => '♌️','Hex' => '264C'), array('iOS2' => '
','iOS5' => '♍','iOS7' => '♍️','Hex' => '264D'), array('iOS2' => '
','iOS5' => '♎','iOS7' => '♎️','Hex' => '264E'), array('iOS2' => '
','iOS5' => '♏','iOS7' => '♏️','Hex' => '264F'), array('iOS2' => '
','iOS5' => '♐','iOS7' => '♐️','Hex' => '2650'), array('iOS2' => '
','iOS5' => '♑','iOS7' => '♑️','Hex' => '2651'), array('iOS2' => '
','iOS5' => '♒','iOS7' => '♒️','Hex' => '2652'), array('iOS2' => '
','iOS5' => '♓','iOS7' => '♓️','Hex' => '2653'), array('iOS2' => '
','iOS5' => '⛎','iOS7' => '','Hex' => '26CE'), array('iOS2' => '
','iOS5' => '🔯','iOS7' => '','Hex' => '1F52F'), array('iOS2' => '
','iOS5' => '🏧','iOS7' => '','Hex' => '1F3E7'), array('iOS2' => '
','iOS5' => '💹','iOS7' => '','Hex' => '1F4B9'), array('iOS2' => '','iOS5' => '💲','iOS7' => '','Hex' => '1F4B2'), array('iOS2' => '
','iOS5' => '💱','iOS7' => '','Hex' => '1F4B1'), array('iOS2' => '©','iOS5' => '©','iOS7' => '','Hex' => '00A9'), array('iOS2' => '®','iOS5' => '®','iOS7' => '','Hex' => '00AE'), array('iOS2' => '™','iOS5' => '™','iOS7' => '','Hex' => '2122'), array('iOS2' => '
','iOS5' => '〽','iOS7' => '〽️','Hex' => '303D'), array('iOS2' => '','iOS5' => '〰','iOS7' => '','Hex' => '3030'), array('iOS2' => '
','iOS5' => '🔝','iOS7' => '','Hex' => '1F51D'), array('iOS2' => '','iOS5' => '🔚','iOS7' => '','Hex' => '1F51A'), array('iOS2' => '','iOS5' => '🔙','iOS7' => '','Hex' => '1F519'), array('iOS2' => '','iOS5' => '🔛','iOS7' => '','Hex' => '1F51B'), array('iOS2' => '','iOS5' => '🔜','iOS7' => '','Hex' => '1F51C'), array('iOS2' => '
','iOS5' => '❌','iOS7' => '','Hex' => '274C'), array('iOS2' => '
','iOS5' => '⭕','iOS7' => '⭕️','Hex' => '2B55'), array('iOS2' => '','iOS5' => '❗','iOS7' => '❗️','Hex' => '2757'), array('iOS2' => '','iOS5' => '❓','iOS7' => '','Hex' => '2753'), array('iOS2' => '
','iOS5' => '❕','iOS7' => '','Hex' => '2755'), array('iOS2' => '
','iOS5' => '❔','iOS7' => '','Hex' => '2754'), array('iOS2' => '','iOS5' => '🔃','iOS7' => '','Hex' => '1F503'), array('iOS2' => '
','iOS5' => '🕛','iOS7' => '','Hex' => '1F55B'), array('iOS2' => '','iOS5' => '🕧','iOS7' => '','Hex' => '1F567'), array('iOS2' => '
','iOS5' => '🕐','iOS7' => '','Hex' => '1F550'), array('iOS2' => '','iOS5' => '🕜','iOS7' => '','Hex' => '1F55C'), array('iOS2' => '
','iOS5' => '🕑','iOS7' => '','Hex' => '1F551'), array('iOS2' => '','iOS5' => '🕝','iOS7' => '','Hex' => '1F55D'), array('iOS2' => '
','iOS5' => '🕒','iOS7' => '','Hex' => '1F552'), array('iOS2' => '','iOS5' => '🕞','iOS7' => '','Hex' => '1F55E'), array('iOS2' => '
','iOS5' => '🕓','iOS7' => '','Hex' => '1F553'), array('iOS2' => '','iOS5' => '🕟','iOS7' => '','Hex' => '1F55F'), array('iOS2' => '
','iOS5' => '🕔','iOS7' => '','Hex' => '1F554'), array('iOS2' => '','iOS5' => '🕠','iOS7' => '','Hex' => '1F560'), array('iOS2' => '
','iOS5' => '🕕','iOS7' => '','Hex' => '1F555'), array('iOS2' => '
','iOS5' => '🕖','iOS7' => '','Hex' => '1F556'), array('iOS2' => '
','iOS5' => '🕗','iOS7' => '','Hex' => '1F557'), array('iOS2' => '
','iOS5' => '🕘','iOS7' => '','Hex' => '1F558'), array('iOS2' => '
','iOS5' => '🕙','iOS7' => '','Hex' => '1F559'), array('iOS2' => '
','iOS5' => '🕚','iOS7' => '','Hex' => '1F55A'), array('iOS2' => '','iOS5' => '🕡','iOS7' => '','Hex' => '1F561'), array('iOS2' => '','iOS5' => '🕢','iOS7' => '','Hex' => '1F562'), array('iOS2' => '','iOS5' => '🕣','iOS7' => '','Hex' => '1F563'), array('iOS2' => '','iOS5' => '🕤','iOS7' => '','Hex' => '1F564'), array('iOS2' => '','iOS5' => '🕥','iOS7' => '','Hex' => '1F565'), array('iOS2' => '','iOS5' => '🕦','iOS7' => '','Hex' => '1F566'), array('iOS2' => '','iOS5' => '✖','iOS7' => '✖️','Hex' => '2716'), array('iOS2' => '','iOS5' => '➕','iOS7' => '','Hex' => '2795'), array('iOS2' => '','iOS5' => '➖','iOS7' => '','Hex' => '2796'), array('iOS2' => '','iOS5' => '➗','iOS7' => '','Hex' => '2797'), array('iOS2' => '
','iOS5' => '♠','iOS7' => '♠️','Hex' => '2660'), array('iOS2' => '
','iOS5' => '♥','iOS7' => '♥️','Hex' => '2665'), array('iOS2' => '
','iOS5' => '♣','iOS7' => '♣️','Hex' => '2663'), array('iOS2' => '
','iOS5' => '♦','iOS7' => '♦️','Hex' => '2666'), array('iOS2' => '','iOS5' => '💮','iOS7' => '','Hex' => '1F4AE'), array('iOS2' => '','iOS5' => '💯','iOS7' => '','Hex' => '1F4AF'), array('iOS2' => '','iOS5' => '✔','iOS7' => '✔️','Hex' => '2714'), array('iOS2' => '','iOS5' => '☑','iOS7' => '☑️','Hex' => '2611'), array('iOS2' => '','iOS5' => '🔘','iOS7' => '','Hex' => '1F518'), array('iOS2' => '','iOS5' => '🔗','iOS7' => '','Hex' => '1F517'), array('iOS2' => '','iOS5' => '➰','iOS7' => '','Hex' => '27B0'), array('iOS2' => '
','iOS5' => '🔱','iOS7' => '','Hex' => '1F531'), array('iOS2' => '
','iOS5' => '🔲','iOS7' => '','Hex' => '1F532'), array('iOS2' => '
','iOS5' => '🔳','iOS7' => '','Hex' => '1F533'), array('iOS2' => '','iOS5' => '◼','iOS7' => '◼️','Hex' => '25FC'), array('iOS2' => '','iOS5' => '◻','iOS7' => '◻️','Hex' => '25FB'), array('iOS2' => '','iOS5' => '◾','iOS7' => '◾️','Hex' => '25FE'), array('iOS2' => '','iOS5' => '◽','iOS7' => '◽️','Hex' => '25FD'), array('iOS2' => '','iOS5' => '▪','iOS7' => '▪️','Hex' => '25AA'), array('iOS2' => '','iOS5' => '▫','iOS7' => '▫️','Hex' => '25AB'), array('iOS2' => '','iOS5' => '🔺','iOS7' => '','Hex' => '1F53A'), array('iOS2' => '','iOS5' => '⬜','iOS7' => '⬜️','Hex' => '2B1C'), array('iOS2' => '','iOS5' => '⬛','iOS7' => '⬛️','Hex' => '2B1B'), array('iOS2' => '','iOS5' => '⚫','iOS7' => '⚫️','Hex' => '26AB'), array('iOS2' => '','iOS5' => '⚪','iOS7' => '⚪️','Hex' => '26AA'), array('iOS2' => '
','iOS5' => '🔴','iOS7' => '','Hex' => '1F534'), array('iOS2' => '','iOS5' => '🔵','iOS7' => '','Hex' => '1F535'), array('iOS2' => '','iOS5' => '🔻','iOS7' => '','Hex' => '1F53B'), array('iOS2' => '','iOS5' => '🔶','iOS7' => '','Hex' => '1F536'), array('iOS2' => '','iOS5' => '🔷','iOS7' => '','Hex' => '1F537'), array('iOS2' => '','iOS5' => '🔸','iOS7' => '','Hex' => '1F538'), array('iOS2' => '','iOS5' => '🔹','iOS7' => '','Hex' => '1F539'), array('iOS2' => '','iOS5' => '⁉','iOS7' => '⁉️','Hex' => '2049'), array('iOS2' => '','iOS5' => '‼','iOS7' => '‼️','Hex' => '203C'));
+ file_put_contents($file, $content);
}
-function updateData($nameFile, $WAver, $classesMD5 = "")
+/**
+ * This function generates a paymentLink where you can extend the account-expiration.
+ *
+ * @param string $number Your number with international code, e.g. 49123456789
+ * @param int $sku The Time in years (1, 3 or 5) you want to extend the account-expiration.
+ *
+ * @return string Returns the link.
+ **/
+function generatePaymentLink($number, $sku)
{
- $file = $nameFile;
- $open = fopen($file, 'r+');
- $content = fread($open,filesize($file));
- fclose($open);
+ return sprintf("https://www.whatsapp.com/payments/cksum_pay.php?phone=%s&cksum=%s&sku=%d",
+ $number,
+ md5($number."abc"),
+ $sku,
+ ($sku != 1 && $sku != 3 && $sku != 5) ? 1 : $sku);
+}
- $content = explode("\n",$content);
+// Gets mime type of a file using various methods
+function get_mime($file)
+{
+ if (function_exists("finfo_file")) {
+ $finfo = finfo_open(FILEINFO_MIME_TYPE);
+ $mime = finfo_file($finfo, $file);
+ finfo_close($finfo);
+ return $mime;
+ }
- if ($file == 'token.php')
- $content[5] = ' $classesMd5 = '."\"".$classesMD5."\"; // $WAver";
- else if ($file == 'whatsprot.class.php'){
- $content[48] = ' const WHATSAPP_VER = \''.$WAver. '\'; // The WhatsApp version.';
- $content[49] = ' const WHATSAPP_USER_AGENT = \'WhatsApp/'.$WAver.' Android/4.3 Device/GalaxyS3\'; // User agent used in request/registration code.';
- }
+ if (function_exists("mime_content_type")) {
+ return mime_content_type($file);
+ }
- $content = implode("\n",$content);
+ if (!strncasecmp(PHP_OS, 'WIN', 3) == 0 && !stristr(ini_get("disable_functions"), "shell_exec")) {
+ $file = escapeshellarg($file);
+ $mime = shell_exec("file -b --mime-type " . $file);
+ return trim($mime);
+ }
- $open = fopen($file,'w');
- fwrite($open,$content);
- fclose($open);
+ return false;
+}
+
+function getExtensionFromMime($mime)
+{
+ $extensions = array(
+ 'audio/3gpp' => '3gp',
+ 'audio/x-caf' => 'caf',
+ 'audio/wav' => 'wav',
+ 'audio/mpeg' => 'mp3',
+ 'audio/mpeg3' => 'mp3',
+ 'audio/x-mpeg-3' => 'mp3',
+ 'audio/x-ms-wma' => 'wma',
+ 'audio/ogg' => 'ogg',
+ 'audio/aiff' => 'aif',
+ 'audio/x-aiff' => 'aif',
+ 'audio/aac' => 'aac',
+ 'audio/mp4' => 'm4a',
+ 'image/jpeg' => 'jpg',
+ 'image/gif' => 'gif',
+ 'image/png' => 'png',
+ 'video/3gpp' => '3gp',
+ 'video/mp4' => 'mp4',
+ 'video/quicktime' => 'mov',
+ 'video/avi' => 'avi',
+ 'video/msvideo' => 'avi',
+ 'video/x-msvideo' => 'avi'
+ );
+
+ return $extensions[$mime];
}
diff --git a/src/keystream.class.php b/src/keystream.class.php
index 97914672..4caed2f3 100755
--- a/src/keystream.class.php
+++ b/src/keystream.class.php
@@ -9,7 +9,9 @@
require_once("rc4.php");
require_once("func.php");
-class KeyStream {
+class KeyStream
+{
+
public static $AuthMethod = "WAUTH-2";
const DROP = 768;
private $rc4;
@@ -18,25 +20,24 @@ class KeyStream {
public function __construct($key, $macKey)
{
- $this->rc4 = new rc4($key, self::DROP);
+ $this->rc4 = new rc4($key, self::DROP);
$this->macKey = $macKey;
}
public static function GenerateKeys($password, $nonce)
{
- $array = array(
- "key",//placeholders
+ $array = array(
+ "key", //placeholders
"key",
"key",
"key"
);
$array2 = array(1, 2, 3, 4);
$nonce .= '0';
- for($j = 0; $j < count($array); $j++)
- {
+ for ($j = 0; $j < count($array); $j++) {
$nonce[(strlen($nonce) - 1)] = chr($array2[$j]);
- $foo = wa_pbkdf2("sha1", $password, $nonce, 2, 20, true);
- $array[$j] = $foo;
+ $foo = wa_pbkdf2("sha1", $password, $nonce, 2, 20, true);
+ $array[$j] = $foo;
}
return $array;
}
@@ -45,12 +46,10 @@ public function DecodeMessage($buffer, $macOffset, $offset, $length)
{
$mac = $this->computeMac($buffer, $offset, $length);
//validate mac
- for($i = 0; $i < 4; $i++)
- {
+ for ($i = 0; $i < 4; $i++) {
$foo = ord($buffer[$macOffset + $i]);
$bar = ord($mac[$i]);
- if($foo !== $bar)
- {
+ if ($foo !== $bar) {
throw new Exception("MAC mismatch: $foo != $bar");
}
}
@@ -60,8 +59,8 @@ public function DecodeMessage($buffer, $macOffset, $offset, $length)
public function EncodeMessage($buffer, $macOffset, $offset, $length)
{
$data = $this->rc4->cipher($buffer, $offset, $length);
- $mac = $this->computeMac($data, $offset, $length);
- return substr($data, 0, $macOffset).substr($mac, 0, 4).substr($data, $macOffset + 4);
+ $mac = $this->computeMac($data, $offset, $length);
+ return substr($data, 0, $macOffset) . substr($mac, 0, 4) . substr($data, $macOffset + 4);
}
private function computeMac($buffer, $offset, $length)
@@ -76,4 +75,4 @@ private function computeMac($buffer, $offset, $length)
$this->seq++;
return hash_final($hmac, true);
}
-}
\ No newline at end of file
+}
diff --git a/src/mediauploader.php b/src/mediauploader.php
index 36b20ab7..1f1426b6 100755
--- a/src/mediauploader.php
+++ b/src/mediauploader.php
@@ -1,5 +1,4 @@
getChild("media")->getAttribute("url");
+ $url = $uploadResponseNode->getChild("media")->getAttribute("url");
$filepath = $messageContainer["filePath"];
- $to = $messageContainer["to"];
+ $to = $messageContainer["to"];
return self::getPostString($filepath, $url, $mediafile, $to, $selfJID);
}
@@ -57,11 +56,10 @@ protected static function getPostString($filepath, $url, $mediafile, $to, $from)
$host = parse_url($url, PHP_URL_HOST);
//filename to md5 digest
- $cryptoname = md5($filepath) . "." . $mediafile['fileextension'];
- $boundary = "zzXXzzYYzzXXzzQQ";
- $contentlength = 0;
+ $cryptoname = md5($filepath) . "." . $mediafile['fileextension'];
+ $boundary = "zzXXzzYYzzXXzzQQ";
- if(is_array($to)) {
+ if (is_array($to)) {
$to = implode(',', $to);
}
@@ -77,19 +75,14 @@ protected static function getPostString($filepath, $url, $mediafile, $to, $from)
$fBAOS = "\r\n--" . $boundary . "--\r\n";
- $contentlength += strlen($hBAOS);
- $contentlength += strlen($fBAOS);
- $contentlength += $mediafile['filesize'];
+ $contentlength = strlen($hBAOS) + strlen($fBAOS) + $mediafile['filesize'];
$POST = "POST " . $url . "\r\n";
$POST .= "Content-Type: multipart/form-data; boundary=" . $boundary . "\r\n";
$POST .= "Host: " . $host . "\r\n";
- $POST .= "User-Agent: " . WhatsProt::WHATSAPP_USER_AGENT . "\r\n";
+ $POST .= "User-Agent: " . Constants::WHATSAPP_USER_AGENT . "\r\n";
$POST .= "Content-Length: " . $contentlength . "\r\n\r\n";
return self::sendData($host, $POST, $hBAOS, $filepath, $mediafile, $fBAOS);
}
-
}
-
-?>
diff --git a/src/networkinfo.csv b/src/networkinfo.csv
new file mode 100644
index 00000000..75072977
--- /dev/null
+++ b/src/networkinfo.csv
@@ -0,0 +1,1703 @@
+289,649,088,2191,ab,Abkhazia,7,A-Mobile
+289,649,068,1679,ab,Abkhazia,7,A-Mobile2
+289,649,067,1663,ab,Abkhazia,7,Aquafon
+412,1042,088,2191,af,Afghanistan,93,Afghan Telecom Corp. (AT)
+412,1042,080,2063,af,Afghanistan,93,Afghan Telecom Corp. (AT)2
+412,1042,001,31,af,Afghanistan,93,Afghan Wireless/AWCC
+412,1042,040,1039,af,Afghanistan,93,Areeba
+412,1042,050,1295,af,Afghanistan,93,Etisalat
+412,1042,020,527,af,Afghanistan,93,Roshan
+276,630,001,31,al,Albania,355,AMC Mobil
+276,630,003,63,al,Albania,355,Eagle Mobile
+276,630,004,79,al,Albania,355,PLUS Communication Sh.a
+276,630,002,47,al,Albania,355,Vodafone
+603,1539,001,31,dz,Algeria,213,ATM Mobils
+603,1539,002,47,dz,Algeria,213,Orascom / DJEZZY
+603,1539,003,63,dz,Algeria,213,Wataniya / Nedjma
+544,1348,011,287,as,American Samoa,684,Blue Sky Communications
+213,531,003,63,ad,Andorra,376,Mobiland
+631,1585,004,79,ao,Angola,244,MoviCel
+631,1585,002,47,ao,Angola,244,Unitel
+365,869,840,2112,ai,Anguilla,1264,Cable and Wireless
+365,869,010,16,ai,Anguilla,1264,Digicell / Wireless Vent. Ltd
+344,836,030,48,ag,Antigua and Barbuda,1268,APUA PCS
+344,836,920,2336,ag,Antigua and Barbuda,1268,C & W
+344,836,930,2352,ag,Antigua and Barbuda,1268,Cing. Wirel./DigiCel
+722,1826,310,784,ar,Argentina Republic,54,Claro/ CTI/AMX
+722,1826,330,816,ar,Argentina Republic,54,Claro/ CTI/AMX2
+722,1826,320,800,ar,Argentina Republic,54,Claro/ CTI/AMX3
+722,1826,010,16,ar,Argentina Republic,54,Compania De Radiocomunicaciones Moviles SA
+722,1826,070,112,ar,Argentina Republic,54,Movistar/Telefonica
+722,1826,020,32,ar,Argentina Republic,54,Nextel
+722,1826,341,833,ar,Argentina Republic,54,Telecom Personal S.A.
+283,643,001,31,am,Armenia,374,ArmenTel/Beeline
+283,643,004,4,am,Armenia,374,Karabakh Telecom
+283,643,010,271,am,Armenia,374,Orange
+283,643,005,95,am,Armenia,374,Vivacell
+363,867,020,527,aw,Aruba,297,Digicel
+363,867,001,31,aw,Aruba,297,Setar GSM
+505,1285,014,335,au,Australia,61,AAPT Ltd.
+505,1285,024,591,au,Australia,61,Advanced Comm Tech Pty.
+505,1285,009,159,au,Australia,61,Airnet Commercial Australia Ltd.
+505,1285,004,79,au,Australia,61,Department of Defense
+505,1285,026,623,au,Australia,61,Dialogue Communications Pty Ltd
+505,1285,012,303,au,Australia,61,H3G Ltd.
+505,1285,006,111,au,Australia,61,H3G Ltd.
+505,1285,088,2191,au,Australia,61,Localstar Holding Pty. Ltd
+505,1285,019,415,au,Australia,61,Lycamobile Pty Ltd
+505,1285,008,143,au,Australia,61,Railcorp/Vodafone
+505,1285,099,2463,au,Australia,61,Railcorp/Vodafone2
+505,1285,013,319,au,Australia,61,Railcorp/Vodafone3
+505,1285,090,2319,au,Australia,61,Singtel Optus
+505,1285,002,47,au,Australia,61,Singtel Optus2
+505,1285,001,31,au,Australia,61,Telstra Corp. Ltd.
+505,1285,011,287,au,Australia,61,Telstra Corp. Ltd.2
+505,1285,071,1823,au,Australia,61,Telstra Corp. Ltd.3
+505,1285,072,1839,au,Australia,61,Telstra Corp. Ltd.4
+505,1285,005,95,au,Australia,61,The Ozitel Network Pty.
+505,1285,016,367,au,Australia,61,Victorian Rail Track Corp. (VicTrack)
+505,1285,007,127,au,Australia,61,Vodafone
+505,1285,003,63,au,Australia,61,Vodafone2
+232,562,002,47,at,Austria,43,A1 MobilKom
+232,562,011,287,at,Austria,43,A1 MobilKom2
+232,562,009,159,at,Austria,43,A1 MobilKom3
+232,562,001,31,at,Austria,43,A1 MobilKom4
+232,562,015,351,at,Austria,43,T-Mobile/Telering
+232,562,000,15,at,Austria,43,Fix Line
+232,562,010,271,at,Austria,43,H3G
+232,562,014,335,at,Austria,43,H3G2
+232,562,012,303,at,Austria,43,Orange/One Connect
+232,562,006,111,at,Austria,43,Orange/One Connect2
+232,562,005,95,at,Austria,43,Orange/One Connect
+232,562,004,79,at,Austria,43,T-Mobile/Telering
+232,562,003,63,at,Austria,43,T-Mobile/Telering2
+232,562,007,127,at,Austria,43,T-Mobile/Telering3
+232,562,008,143,at,Austria,43,Telefonica
+400,1024,001,31,az,Azerbaijan,994,Azercell Telekom B.M.
+400,1024,004,79,az,Azerbaijan,994,Azerfon.
+400,1024,003,63,az,Azerbaijan,994,Caspian American Telecommunications LLC (CATEL)
+400,1024,002,47,az,Azerbaijan,994,J.V. Bakcell GSM 2000
+364,868,030,783,bs,Bahamas,1242,Bahamas Telco. Comp.
+364,868,390,912,bs,Bahamas,1242,Bahamas Telco. Comp.2
+364,868,039,927,bs,Bahamas,1242,Bahamas Telco. Comp.3
+364,868,003,3,bs,Bahamas,1242,Smart Communications
+426,1062,001,31,bh,Bahrain,973,Batelco
+426,1062,002,47,bh,Bahrain,973,MTC Vodafone
+426,1062,004,79,bh,Bahrain,973,VIVA
+470,1136,002,47,bd,Bangladesh,880,Robi/Aktel
+470,1136,005,95,bd,Bangladesh,880,Citycell
+470,1136,006,111,bd,Bangladesh,880,Citycell2
+470,1136,001,31,bd,Bangladesh,880,GrameenPhone
+470,1136,003,63,bd,Bangladesh,880,Orascom
+470,1136,004,79,bd,Bangladesh,880,TeleTalk
+470,1136,007,127,bd,Bangladesh,880,Airtel/Warid
+342,834,600,1536,bb,Barbados,1246,C & W BET Ltd.
+342,834,810,2064,bb,Barbados,1246,Cingular Wireless
+342,834,750,1872,bb,Barbados,1246,Digicel
+342,834,050,80,bb,Barbados,1246,Digicel2
+342,834,820,2080,bb,Barbados,1246,Sunbeach
+257,599,003,63,by,Belarus,375,BelCel JV
+257,599,004,79,by,Belarus,375,BeST
+257,599,001,31,by,Belarus,375,Mobile Digital Communications
+257,599,002,47,by,Belarus,375,MTS
+206,518,020,527,be,Belgium,32,Base/KPN
+206,518,001,31,be,Belgium,32,Belgacom/Proximus
+206,518,010,271,be,Belgium,32,Mobistar/Orange
+206,518,002,47,be,Belgium,32,SNCT/NMBS
+206,518,005,95,be,Belgium,32,Telenet BidCo NV
+702,1794,067,1663,bz,Belize,501,DigiCell
+702,1794,068,1679,bz,Belize,501,International Telco (INTELCO)
+616,1558,004,79,bj,Benin,229,Bell Benin/BBCOM
+616,1558,002,47,bj,Benin,229,Etisalat/MOOV
+616,1558,005,5,bj,Benin,229,GloMobile
+616,1558,001,31,bj,Benin,229,Libercom
+616,1558,003,63,bj,Benin,229,MTN/Spacetel
+350,848,000,0,bm,Bermuda,1441,Bermuda Digital Communications Ltd (BDC)
+350,848,099,2463,bm,Bermuda,1441,CellOne Ltd
+350,848,010,271,bm,Bermuda,1441,DigiCel / Cingular
+350,848,002,47,bm,Bermuda,1441,M3 Wireless Ltd
+350,848,001,31,bm,Bermuda,1441,Telecommunications (Bermuda & West Indies) Ltd (Digicel Bermuda)
+402,1026,011,287,bt,Bhutan,975,B-Mobile
+402,1026,017,383,bt,Bhutan,975,Bhutan Telecom Ltd (BTL)
+402,1026,077,1919,bt,Bhutan,975,TashiCell
+736,1846,002,47,bo,Bolivia,591,Entel Pcs
+736,1846,001,31,bo,Bolivia,591,Nuevatel
+736,1846,003,63,bo,Bolivia,591,TELECEL BOLIVIA
+362,866,091,2335,bq,Bonaire Sint Eustatius and Saba,,United Telecommunications Services NV (UTS)
+218,536,090,2319,ba,Bosnia & Herzegov.,387,BH Mobile
+218,536,003,63,ba,Bosnia & Herzegov.,387,Eronet Mobile
+218,536,005,95,ba,Bosnia & Herzegov.,387,M-Tel
+652,1618,004,79,bw,Botswana,267,beMOBILE
+652,1618,001,31,bw,Botswana,267,Mascom Wireless (Pty) Ltd.
+652,1618,002,47,bw,Botswana,267,Orange
+724,1828,012,303,br,Brazil,55,Claro/Albra/America Movil
+724,1828,038,911,br,Brazil,55,Claro/Albra/America Movil2
+724,1828,005,95,br,Brazil,55,Claro/Albra/America Movil3
+724,1828,001,1,br,Brazil,55,Vivo S.A./Telemig
+724,1828,033,831,br,Brazil,55,CTBC Celular SA (CTBC)
+724,1828,032,815,br,Brazil,55,CTBC Celular SA (CTBC)2
+724,1828,034,847,br,Brazil,55,CTBC Celular SA (CTBC)3
+724,1828,008,8,br,Brazil,55,TIM
+724,1828,000,15,br,Brazil,55,Nextel (Telet)
+724,1828,039,927,br,Brazil,55,Nextel (Telet)2
+724,1828,030,783,br,Brazil,55,Oi (TNL PCS / Oi)
+724,1828,031,799,br,Brazil,55,Oi (TNL PCS / Oi)2
+724,1828,024,591,br,Brazil,55,Amazonia Celular S/A
+724,1828,016,367,br,Brazil,55,Brazil Telcom
+724,1828,015,351,br,Brazil,55,Sercontel Cel
+724,1828,07,7,br,Brazil,55,CTBC/Triangulo
+724,1828,019,415,br,Brazil,55,Vivo S.A./Telemig
+724,1828,003,63,br,Brazil,55,TIM
+724,1828,002,47,br,Brazil,55,TIM2
+724,1828,004,79,br,Brazil,55,TIM3
+724,1828,037,895,br,Brazil,55,Unicel do Brasil Telecomunicacoes Ltda
+724,1828,006,111,br,Brazil,55,Vivo S.A./Telemig
+724,1828,023,575,br,Brazil,55,Vivo S.A./Telemig2
+724,1828,011,287,br,Brazil,55,Vivo S.A./Telemig3
+724,1828,010,271,br,Brazil,55,Vivo S.A./Telemig4
+348,840,570,1392,vg,British Virgin Islands,284,Caribbean Cellular
+348,840,770,1904,vg,British Virgin Islands,284,Digicel
+348,840,170,368,vg,British Virgin Islands,284,LIME
+528,1320,002,47,bn,Brunei Darussalam,673,b-mobile
+528,1320,011,287,bn,Brunei Darussalam,673,Datastream (DTSCom)
+528,1320,001,31,bn,Brunei Darussalam,673,Telekom Brunei Bhd (TelBru)
+284,644,006,111,bg,Bulgaria,359,BTC Mobile EOOD (vivatel)
+284,644,003,63,bg,Bulgaria,359,BTC Mobile EOOD (vivatel)2
+284,644,005,95,bg,Bulgaria,359,Cosmo Mobile EAD/Globul
+284,644,001,31,bg,Bulgaria,359,MobilTel AD
+613,1555,003,63,bf,Burkina Faso,226,TeleCel
+613,1555,001,31,bf,Burkina Faso,226,TeleMob-OnaTel
+613,1555,002,47,bf,Burkina Faso,226,AirTel/ZAIN/CelTel
+414,1044,001,31,mm,Burma/Myanmar,95,Myanmar Post & Teleco.
+642,1602,002,47,bi,Burundi,257,Africel / Safaris
+642,1602,008,143,bi,Burundi,257,HiTs Telecom
+642,1602,003,63,bi,Burundi,257,Onatel / Telecel
+642,1602,007,127,bi,Burundi,257,Smart Mobile / LACELL
+642,1602,001,31,bi,Burundi,257,Spacetel / Econet
+642,1602,082,2095,bi,Burundi,257,U-COM
+456,1110,004,79,kh,Cambodia,855,Cambodia Advance Communications Co. Ltd (CADCOMMS)
+456,1110,002,47,kh,Cambodia,855,Hello/Malaysia Telcom
+456,1110,008,143,kh,Cambodia,855,Metfone
+456,1110,018,399,kh,Cambodia,855,MFone/Camshin
+456,1110,001,31,kh,Cambodia,855,Mobitel/Cam GSM
+456,1110,003,63,kh,Cambodia,855,QB/Cambodia Adv. Comms.
+456,1110,005,95,kh,Cambodia,855,Smart Mobile
+456,1110,006,111,kh,Cambodia,855,Smart Mobile2
+456,1110,009,159,kh,Cambodia,855,Sotelco Ltd (Beeline Cambodia)
+624,1572,001,31,cm,Cameroon,237,MTN
+624,1572,002,47,cm,Cameroon,237,Orange
+302,770,652,1618,ca,Canada,1,BC Tel Mobility
+302,770,630,1584,ca,Canada,1,Bell Aliant
+302,770,651,1617,ca,Canada,1,Bell Mobility
+302,770,610,1552,ca,Canada,1,Bell Mobility2
+302,770,670,1648,ca,Canada,1,CityWest Mobility
+302,770,360,864,ca,Canada,1,Clearnet
+302,770,361,865,ca,Canada,1,Clearnet2
+302,770,380,896,ca,Canada,1,DMTS Mobility
+302,770,710,1808,ca,Canada,1,Globalstar Canada
+302,770,640,1600,ca,Canada,1,Latitude Wireless
+302,770,370,880,ca,Canada,1,FIDO (Rogers AT&T/ Microcell)
+302,770,320,800,ca,Canada,1,mobilicity
+302,770,702,1794,ca,Canada,1,MT&T Mobility
+302,770,655,1621,ca,Canada,1,MTS Mobility
+302,770,660,1632,ca,Canada,1,MTS Mobility2
+302,770,701,1793,ca,Canada,1,NB Tel Mobility
+302,770,703,1795,ca,Canada,1,New Tel Mobility
+302,770,760,1888,ca,Canada,1,Public Mobile
+302,770,657,1623,ca,Canada,1,Quebectel Mobility
+302,770,720,1824,ca,Canada,1,Rogers AT&T Wireless
+302,770,654,1620,ca,Canada,1,Sask Tel Mobility
+302,770,680,1664,ca,Canada,1,Sask Tel Mobility2
+302,770,656,1622,ca,Canada,1,Tbay Mobility
+302,770,220,544,ca,Canada,1,Telus Mobility
+302,770,653,1619,ca,Canada,1,Telus Mobility2
+302,770,500,1280,ca,Canada,1,Videotron
+302,770,490,1168,ca,Canada,1,WIND
+625,1573,001,31,cv,Cape Verde,238,CV Movel
+625,1573,002,47,cv,Cape Verde,238,T+ Telecom
+346,838,050,80,ky,Cayman Islands,1345,Digicel Cayman Ltd
+346,838,006,6,ky,Cayman Islands,1345,Digicel Ltd.
+346,838,140,320,ky,Cayman Islands,1345,LIME / Cable & Wirel.
+623,1571,001,31,cf,Central African Rep.,236,Centrafr. Telecom+
+623,1571,004,79,cf,Central African Rep.,236,Nationlink
+623,1571,003,63,cf,Central African Rep.,236,Orange/Celca
+623,1571,002,47,cf,Central African Rep.,236,Telecel Centraf.
+622,1570,004,79,td,Chad,235,Salam/Sotel
+622,1570,002,47,td,Chad,235,Tchad Mobile
+622,1570,003,63,td,Chad,235,Tigo/Milicom/Tchad Mobile
+622,1570,001,31,td,Chad,235,Zain/Airtel/Celtel
+730,1840,006,111,cl,Chile,56,Blue Two Chile SA
+730,1840,011,287,cl,Chile,56,Celupago SA
+730,1840,015,351,cl,Chile,56,Cibeles Telecom SA
+730,1840,003,63,cl,Chile,56,Claro
+730,1840,010,271,cl,Chile,56,Entel PCS
+730,1840,001,1,cl,Chile,56,Entel Telefonia Mov
+730,1840,014,335,cl,Chile,56,Netline Telefonica Movil Ltda
+730,1840,009,159,cl,Chile,56,Nextel SA
+730,1840,005,95,cl,Chile,56,Nextel SA2
+730,1840,004,79,cl,Chile,56,Nextel SA3
+730,1840,002,47,cl,Chile,56,TELEFONICA
+730,1840,007,127,cl,Chile,56,TELEFONICA2
+730,1840,012,303,cl,Chile,56,Telestar Movil SA
+730,1840,000,15,cl,Chile,56,TESAM SA
+730,1840,013,319,cl,Chile,56,Tribe Mobile SPA
+730,1840,008,143,cl,Chile,56,VTR Banda Ancha SA
+460,1120,000,15,cn,China,86,China Mobile GSM
+460,1120,007,127,cn,China,86,China Mobile GSM2
+460,1120,002,47,cn,China,86,China Mobile GSM3
+460,1120,004,79,cn,China,86,China Space Mobile Satellite Telecommunications Co. Ltd (China Spacecom)
+460,1120,005,5,cn,China,86,China Telecom
+460,1120,003,63,cn,China,86,China Telecom2
+460,1120,006,6,cn,China,86,China Unicom
+460,1120,001,31,cn,China,86,China Unicom2
+732,1842,130,304,co,Colombia,57,Avantel SAS
+732,1842,102,258,co,Colombia,57,Movistar
+732,1842,103,259,co,Colombia,57,TIGO/Colombia Movil
+732,1842,001,1,co,Colombia,57,TIGO/Colombia Movil
+732,1842,101,257,co,Colombia,57,Comcel S.A. Occel S.A./Celcaribe
+732,1842,002,2,co,Colombia,57,Edatel S.A.
+732,1842,123,291,co,Colombia,57,Movistar
+732,1842,111,273,co,Colombia,57,TIGO/Colombia Movil
+732,1842,142,322,co,Colombia,57,UNE EPM Telecomunicaciones SA ESP
+732,1842,020,32,co,Colombia,57,UNE EPM Telecomunicaciones SA ESP2
+732,1842,154,340,co,Colombia,57,Virgin Mobile Colombia SAS
+654,1620,001,31,km,Comoros,269,HURI - SNPT
+630,1584,086,2159,cd,Congo Dem. Rep.,243,Orange RDC sarl
+630,1584,005,95,cd,Congo Dem. Rep.,243,SuperCell
+630,1584,089,2207,cd,Congo Dem. Rep.,243,TIGO/Oasis
+630,1584,001,31,cd,Congo Dem. Rep.,243,Vodacom
+630,1584,088,2191,cd,Congo Dem. Rep.,243,Yozma Timeturns sprl (YTT)
+630,1584,002,47,cd,Congo Dem. Rep.,243,ZAIN CelTel
+629,1577,001,31,cg,Congo Republic,242,Airtel Congo SA
+629,1577,002,2,cg,Congo Republic,242,Zain/Celtel
+629,1577,010,271,cg,Congo Republic,242,MTN/Libertis
+629,1577,007,127,cg,Congo Republic,242,Warid
+548,1352,001,31,ck,Cook Islands,682,Telecom Cook Islands
+712,1810,003,63,cr,Costa Rica,506,Claro
+712,1810,002,47,cr,Costa Rica,506,ICE
+712,1810,001,31,cr,Costa Rica,506,ICE2
+712,1810,004,79,cr,Costa Rica,506,Movistar
+219,537,001,31,hr,Croatia,385,T-Mobile/Cronet
+219,537,002,47,hr,Croatia,385,Tele2
+219,537,010,271,hr,Croatia,385,VIPnet d.o.o.
+368,872,001,31,cu,Cuba,53,C-COM
+362,866,095,2399,cw,Curacao,,EOCG Wireless NV
+362,866,069,1695,cw,Curacao,,Polycom N.V./ Curacao Telecom d.b.a. Digicel
+280,640,010,271,cy,Cyprus,357,MTN/Areeba
+280,640,020,527,cy,Cyprus,357,PrimeTel PLC
+280,640,001,31,cy,Cyprus,357,Vodafone/CyTa
+230,560,008,143,cz,Czech Rep.,420,Compatel s.r.o.
+230,560,002,47,cz,Czech Rep.,420,O2
+230,560,001,31,cz,Czech Rep.,420,T-Mobile / RadioMobil
+230,560,005,95,cz,Czech Rep.,420,Travel Telekommunikation s.r.o.
+230,560,004,79,cz,Czech Rep.,420,Ufone
+230,560,099,2463,cz,Czech Rep.,420,Vodafone
+230,560,003,63,cz,Czech Rep.,420,Vodafone
+238,568,005,5,dk,Denmark,45,ApS KBUS
+238,568,023,575,dk,Denmark,45,Banedanmark
+238,568,028,655,dk,Denmark,45,CoolTEL ApS
+238,568,006,111,dk,Denmark,45,Hi3G
+238,568,012,303,dk,Denmark,45,Lycamobile Ltd
+238,568,003,63,dk,Denmark,45,Mach Connectivity ApS
+238,568,007,127,dk,Denmark,45,
+238,568,004,79,dk,Denmark,45,NextGen Mobile Ltd (CardBoardFish)
+238,568,010,271,dk,Denmark,45,TDC Denmark
+238,568,001,31,dk,Denmark,45,TDC Denmark2
+238,568,002,47,dk,Denmark,45,Telenor/Sonofon
+238,568,077,1919,dk,Denmark,45,Telenor/Sonofon
+238,568,020,527,dk,Denmark,45,Telia
+238,568,030,783,dk,Denmark,45,Telia2
+638,1592,001,31,dj,Djibouti,253,Djibouti Telecom SA (Evatis)
+366,870,110,272,dm,Dominica,1767,C & W
+366,870,020,32,dm,Dominica,1767,Cingular Wireless/Digicel
+366,870,050,80,dm,Dominica,1767,Wireless Ventures (Dominica) Ltd (Digicel Dominica)
+370,880,002,47,do,Dominican Republic,1809,Claro
+370,880,001,31,do,Dominican Republic,1809,Orange
+370,880,003,63,do,Dominican Republic,1809,TRIcom
+370,880,004,79,do,Dominican Republic,1809,Trilogy Dominicana S. A.
+740,1856,002,47,ec,Ecuador,593,Alegro/Telcsa
+740,1856,000,15,ec,Ecuador,593,MOVISTAR/OteCel
+740,1856,001,31,ec,Ecuador,593,Porta/Conecel
+602,1538,001,31,eg,Egypt,20,EMS - Mobinil
+602,1538,003,63,eg,Egypt,20,ETISALAT
+602,1538,002,47,eg,Egypt,20,Vodafone (Misrfone Telecom)
+706,1798,001,31,sv,El Salvador,503,CLARO/CTE
+706,1798,002,47,sv,El Salvador,503,Digicel
+706,1798,005,95,sv,El Salvador,503,INTELFON SA de CV
+706,1798,004,79,sv,El Salvador,503,Telefonica
+706,1798,003,63,sv,El Salvador,503,Telemovil
+627,1575,003,63,gq,Equatorial Guinea,240,HiTs-GE
+627,1575,001,31,gq,Equatorial Guinea,240,ORANGE/GETESA
+657,1623,000,0,er,Eritrea,291,EriTel
+657,1623,001,31,er,Eritrea,291,Eritel
+248,584,001,31,ee,Estonia,372,EMT GSM
+248,584,002,47,ee,Estonia,372,Radiolinja Eesti
+248,584,003,63,ee,Estonia,372,Tele2 Eesti AS
+248,584,004,79,ee,Estonia,372,Top Connect OU
+636,1590,001,31,et,Ethiopia,251,ETH/MTN
+750,1872,001,1,fk,Falkland Islands (Malvinas),,Cable and Wireless South Atlantic Ltd (Falkland Islands
+288,648,003,63,fo,Faroe Islands,298,Edge Mobile Sp/F
+288,648,001,31,fo,Faroe Islands,298,Faroese Telecom
+288,648,002,47,fo,Faroe Islands,298,Kall GSM
+542,1346,002,47,fj,Fiji,679,DigiCell
+542,1346,001,31,fj,Fiji,679,Vodafone
+244,580,014,335,fi,Finland,358,Alands
+244,580,026,623,fi,Finland,358,Compatel Ltd
+244,580,013,319,fi,Finland,358,DNA/Finnet
+244,580,012,303,fi,Finland,358,DNA/Finnet2
+244,580,004,79,fi,Finland,358,DNA/Finnet
+244,580,003,63,fi,Finland,358,DNA/Finnet2
+244,580,021,543,fi,Finland,358,Elisa/Saunalahti
+244,580,005,95,fi,Finland,358,Elisa/Saunalahti2
+244,580,082,2095,fi,Finland,358,ID-Mobile
+244,580,011,287,fi,Finland,358,Mundio Mobile (Finland) Ltd
+244,580,009,159,fi,Finland,358,Nokia Oyj
+244,580,010,271,fi,Finland,358,TDC Oy Finland
+244,580,091,2335,fi,Finland,358,TeliaSonera
+208,520,027,639,fr,France,33,AFONE SA
+208,520,092,2351,fr,France,33,Association Plate-forme Telecom
+208,520,028,655,fr,France,33,Astrium
+208,520,088,2191,fr,France,33,Bouygues Telecom
+208,520,021,543,fr,France,33,Bouygues Telecom2
+208,520,020,527,fr,France,33,Bouygues Telecom3
+208,520,014,335,fr,France,33,Lliad/FREE Mobile
+208,520,005,95,fr,France,33,GlobalStar
+208,520,007,127,fr,France,33,GlobalStar2
+208,520,006,111,fr,France,33,GlobalStar3
+208,520,029,671,fr,France,33,Orange
+208,520,016,367,fr,France,33,Lliad/FREE Mobile
+208,520,015,351,fr,France,33,Lliad/FREE Mobile2
+208,520,025,607,fr,France,33,Lycamobile SARL
+208,520,024,591,fr,France,33,MobiquiThings
+208,520,003,63,fr,France,33,MobiquiThings2
+208,520,031,799,fr,France,33,Mundio Mobile (France) Ltd
+208,520,026,623,fr,France,33,NRJ
+208,520,089,2207,fr,France,33,Omer/Virgin Mobile
+208,520,023,575,fr,France,33,Omer/Virgin Mobile2
+208,520,002,47,fr,France,33,Orange
+208,520,001,31,fr,France,33,Orange2
+208,520,091,2335,fr,France,33,Orange3
+208,520,013,319,fr,France,33,S.F.R.
+208,520,011,287,fr,France,33,S.F.R.2
+208,520,010,271,fr,France,33,S.F.R.3
+208,520,009,159,fr,France,33,S.F.R.4
+208,520,004,79,fr,France,33,SISTEER
+208,520,000,15,fr,France,33,Tel/Tel
+208,520,022,559,fr,France,33,Transatel SA
+340,832,020,527,gf,French Guiana,594,Bouygues/DigiCel
+340,832,008,143,gf,French Guiana,594,AMIGO/Dauphin
+340,832,001,31,gf,French Guiana,594,Orange Caribe
+340,832,002,47,gf,French Guiana,594,Outremer Telecom
+340,832,011,287,gf,French Guiana,594,TelCell GSM
+340,832,003,63,gf,French Guiana,594,TelCell GSM2
+547,1351,015,351,pf,French Polynesia,689,Pacific Mobile Telecom (PMT)
+547,1351,020,527,pf,French Polynesia,689,Tikiphone
+628,1576,004,79,ga,Gabon,241,Azur/Usan S.A.
+628,1576,001,31,ga,Gabon,241,Libertis S.A.
+628,1576,002,47,ga,Gabon,241,MOOV/Telecel
+628,1576,003,63,ga,Gabon,241,ZAIN/Celtel Gabon S.A.
+607,1543,002,47,gm,Gambia,220,Africel
+607,1543,003,63,gm,Gambia,220,Comium
+607,1543,001,31,gm,Gambia,220,Gamcel
+607,1543,004,79,gm,Gambia,220,Q-Cell
+282,642,001,31,ge,Georgia,995,Geocell Ltd.
+282,642,003,3,ge,Georgia,995,Iberiatel Ltd.
+282,642,002,47,ge,Georgia,995,Magti GSM Ltd.
+282,642,004,79,ge,Georgia,995,MobiTel/Beeline
+282,642,000,0,ge,Georgia,995,Silknet
+262,610,017,383,de,Germany,49,E-Plus
+262,610,003,63,de,Germany,49,E-Plus2
+262,610,005,95,de,Germany,49,E-Plus3
+262,610,077,1919,de,Germany,49,E-Plus4
+262,610,014,335,de,Germany,49,Group 3G UMTS
+262,610,043,1087,de,Germany,49,Lycamobile
+262,610,013,319,de,Germany,49,Mobilcom
+262,610,007,127,de,Germany,49,O2
+262,610,011,287,de,Germany,49,O2-2
+262,610,008,143,de,Germany,49,O2-3
+262,610,010,271,de,Germany,49,O2-4
+262,610,012,303,de,Germany,49,O2-5
+262,610,006,111,de,Germany,49,Telekom/T-mobile
+262,610,001,31,de,Germany,49,Telekom/T-mobile2
+262,610,016,367,de,Germany,49,Telogic/ViStream
+262,610,004,79,de,Germany,49,Vodafone D2
+262,610,002,47,de,Germany,49,Vodafone D2-2
+262,610,009,159,de,Germany,49,Vodafone D2-3
+620,1568,004,79,gh,Ghana,233,Expresso Ghana Ltd
+620,1568,007,127,gh,Ghana,233,GloMobile
+620,1568,003,63,gh,Ghana,233,Milicom/Tigo
+620,1568,001,31,gh,Ghana,233,MTN
+620,1568,002,47,gh,Ghana,233,Vodafone
+620,1568,006,111,gh,Ghana,233,ZAIN
+266,614,006,111,gi,Gibraltar,350,CTS Mobile
+266,614,009,159,gi,Gibraltar,350,eazi telecom
+266,614,001,31,gi,Gibraltar,350,Gibtel GSM
+202,514,007,127,gr,Greece,30,AMD Telecom SA
+202,514,002,47,gr,Greece,30,Cosmote
+202,514,001,31,gr,Greece,30,Cosmote2
+202,514,004,79,gr,Greece,30,Organismos Sidirodromon Ellados (OSE)
+202,514,003,63,gr,Greece,30,OTE Hellenic Telecommunications Organization SA
+202,514,010,271,gr,Greece,30,Tim/Wind
+202,514,009,159,gr,Greece,30,Tim/Wind2
+202,514,005,95,gr,Greece,30,Vodafone
+290,656,001,31,gl,Greenland,299,Tele Greenland
+352,850,110,272,gd,Grenada,1473,Cable & Wireless
+352,850,030,48,gd,Grenada,1473,Digicel
+352,850,050,80,gd,Grenada,1473,Digicel2
+340,832,008,143,gp,Guadeloupe,590,Dauphin Telecom SU (Guadeloupe Telecom) (Guadeloupe)
+340,832,020,527,gp,Guadeloupe,590,Digicel Antilles Francaises Guyane SA (Guadeloupe)
+340,832,001,31,gp,Guadeloupe,590,Orange Caribe
+340,832,002,47,gp,Guadeloupe,590,Outremer Telecom Guadeloupe (only) (Guadeloupe)
+340,832,010,271,gp,Guadeloupe,590,United Telecommunications Services Caraibe SARL (UTS Caraibe Guadeloupe Telephone Mobile) (Guadeloupe)
+340,832,003,63,gp,Guadeloupe,590,United Telecommunications Services Caraibe SARL (UTS Caraibe Guadeloupe Telephone Mobile) (Guadeloupe)2
+310,784,480,1152,gu,Guam,1671,Choice Phone LLC
+310,784,370,880,gu,Guam,1671,Docomo
+310,784,470,1136,gu,Guam,1671,Docomo2
+310,784,140,320,gu,Guam,1671,GTA Wireless
+310,784,033,51,gu,Guam,1671,Guam Teleph. Auth.
+310,784,032,50,gu,Guam,1671,IT&E OverSeas
+311,785,250,592,gu,Guam,1671,Wave Runner LLC
+704,1796,001,31,gt,Guatemala,502,SERCOM
+704,1796,003,63,gt,Guatemala,502,Telefonica
+704,1796,002,47,gt,Guatemala,502,TIGO/COMCEL
+611,1553,004,4,gn,Guinea,224,Areeba - MTN
+611,1553,005,95,gn,Guinea,224,Celcom
+611,1553,003,63,gn,Guinea,224,Intercel
+611,1553,001,31,gn,Guinea,224,Orange/Spacetel
+611,1553,002,47,gn,Guinea,224,SotelGui
+632,1586,000,0,gw,Guinea-Bissau,245,GuineTel
+632,1586,001,31,gw,Guinea-Bissau,245,GuineTel2
+632,1586,003,63,gw,Guinea-Bissau,245,Orange
+632,1586,002,47,gw,Guinea-Bissau,245,SpaceTel
+738,1848,002,47,gy,Guyana,592,Cellink Plus
+738,1848,001,31,gy,Guyana,592,DigiCel
+372,882,001,31,ht,Haiti,509,Comcel
+372,882,002,47,ht,Haiti,509,Digicel
+372,882,003,63,ht,Haiti,509,National Telecom SA (NatCom)
+708,1800,040,64,hn,Honduras,504,Digicel
+708,1800,030,48,hn,Honduras,504,HonduTel
+708,1800,001,1,hn,Honduras,504,SERCOM/CLARO
+708,1800,002,2,hn,Honduras,504,Telefonica/CELTEL
+454,1108,013,319,hk,Hongkong China,852,China Mobile/Peoples
+454,1108,012,303,hk,Hongkong China,852,China Mobile/Peoples2
+454,1108,009,159,hk,Hongkong China,852,China Motion
+454,1108,007,127,hk,Hongkong China,852,China Unicom Ltd
+454,1108,011,287,hk,Hongkong China,852,China-HongKong Telecom Ltd (CHKTL)
+454,1108,001,31,hk,Hongkong China,852,Citic Telecom Ltd.
+454,1108,018,399,hk,Hongkong China,852,CSL Ltd.
+454,1108,002,47,hk,Hongkong China,852,CSL Ltd.2
+454,1108,000,15,hk,Hongkong China,852,CSL Ltd.3
+454,1108,010,271,hk,Hongkong China,852,CSL/New World PCS Ltd.
+454,1108,014,335,hk,Hongkong China,852,H3G/Hutchinson
+454,1108,005,95,hk,Hongkong China,852,H3G/Hutchinson2
+454,1108,004,79,hk,Hongkong China,852,H3G/Hutchinson3
+454,1108,003,63,hk,Hongkong China,852,H3G/Hutchinson4
+454,1108,016,367,hk,Hongkong China,852,HKT/PCCW
+454,1108,019,415,hk,Hongkong China,852,HKT/PCCW2
+454,1108,020,527,hk,Hongkong China,852,HKT/PCCW3
+454,1108,029,671,hk,Hongkong China,852,HKT/PCCW4
+454,1108,047,1151,hk,Hongkong China,852,shared by private TETRA systems
+454,1108,040,1039,hk,Hongkong China,852,shared by private TETRA systems2
+454,1108,008,143,hk,Hongkong China,852,Trident Telecom Ventures Ltd.
+454,1108,017,383,hk,Hongkong China,852,Vodafone/SmarTone
+454,1108,015,351,hk,Hongkong China,852,Vodafone/SmarTone2
+454,1108,006,111,hk,Hongkong China,852,Vodafone/SmarTone3
+216,534,001,31,hu,Hungary,36,Pannon/Telenor
+216,534,030,783,hu,Hungary,36,T-mobile/Magyar
+216,534,071,1823,hu,Hungary,36,UPC Magyarorszag Kft.
+216,534,070,1807,hu,Hungary,36,Vodafone
+274,628,009,159,is,Iceland,354,Amitelo
+274,628,007,127,is,Iceland,354,IceCell
+274,628,008,143,is,Iceland,354,Landssiminn
+274,628,001,31,is,Iceland,354,Landssiminn2
+274,628,011,287,is,Iceland,354,NOVA
+274,628,004,79,is,Iceland,354,VIKING/IMC
+274,628,003,63,is,Iceland,354,Vodafone/Tal hf
+274,628,005,95,is,Iceland,354,Vodafone/Tal hf2
+274,628,002,47,is,Iceland,354,Vodafone/Tal hf3
+404,1028,029,671,in,India,91,Aircel
+404,1028,028,655,in,India,91,Aircel2
+404,1028,025,607,in,India,91,Aircel3
+404,1028,017,383,in,India,91,Aircel4
+404,1028,042,1071,in,India,91,Aircel5
+404,1028,033,831,in,India,91,Aircel6
+404,1028,001,1,in,India,91,Aircel Digilink India
+404,1028,015,351,in,India,91,Aircel Digilink India-2
+404,1028,060,1551,in,India,91,Aircel Digilink India-3
+404,1028,049,000,in,India,91,AirTel
+405,1029,055,1375,in,India,91,AirTel2
+405,1029,054,1375,in,India,91,AirTel3
+405,1029,053,1343,in,India,91,AirTel4
+405,1029,051,1311,in,India,91,AirTel5
+405,1029,056,1391,in,India,91,Airtel (Bharati Mobile) - Assam
+404,1028,086,2159,in,India,91,Barakhamba Sales & Serv.
+404,1028,013,319,in,India,91,Vodafone
+404,1028,011,000,in,India,91,Vodafone2
+404,1028,058,1423,in,India,91,BSNL
+404,1028,081,2079,in,India,91,BSNL2
+404,1028,074,1871,in,India,91,BSNL3
+404,1028,038,911,in,India,91,BSNL4
+404,1028,057,1407,in,India,91,BSNL5
+404,1028,080,2063,in,India,91,BSNL6
+404,1028,073,1855,in,India,91,BSNL7
+404,1028,034,847,in,India,91,BSNL8
+404,1028,066,1647,in,India,91,BSNL9
+404,1028,055,1375,in,India,91,BSNL10
+404,1028,072,1839,in,India,91,BSNL11
+404,1028,077,1919,in,India,91,BSNL12
+404,1028,064,1615,in,India,91,BSNL13
+404,1028,054,1359,in,India,91,BSNL14
+404,1028,071,1823,in,India,91,BSNL15
+404,1028,076,1903,in,India,91,BSNL16
+404,1028,053,1343,in,India,91,BSNL17
+404,1028,062,1583,in,India,91,BSNL18
+404,1028,059,1439,in,India,91,BSNL19
+404,1028,075,1887,in,India,91,BSNL20
+404,1028,051,1311,in,India,91,BSNL21
+405,1028,001,000,in,INDIA,91,Reliance
+405,1028,025,000,in,India,91,TATA DOCOMO
+405,1028,036,000,in,India,91,TATA DOCOMO2
+405,1029,010,271,in,India,91,Bharti Airtel Limited (Delhi)
+404,1028,079,1951,in,India,91,CellOne A&N
+404,1028,089,2207,in,India,91,Escorts Telecom Ltd.
+404,1028,088,2191,in,India,91,Escorts Telecom Ltd.2
+404,1028,087,2175,in,India,91,Escorts Telecom Ltd.3
+404,1028,082,2095,in,India,91,Escorts Telecom Ltd.4
+404,1028,012,303,in,India,91,Escotel Mobile Communications
+404,1028,019,415,in,India,91,Escotel Mobile Communications2
+404,1028,056,1391,in,India,91,Escotel Mobile Communications3
+405,1029,005,5,in,India,91,Fascel Limited
+404,1028,005,5,in,India,91,Fascel
+404,1028,070,1807,in,India,91,Hexacom India
+404,1028,016,367,in,India,91,Hexcom India2
+404,1028,004,4,in,India,91,Idea Cellular Ltd.
+404,1028,024,591,in,India,91,Idea Cellular Ltd.
+404,1028,022,559,in,India,91,Idea Cellular Ltd.2
+404,1028,078,1935,in,India,91,Idea Cellular Ltd.3
+404,1028,007,7,in,India,91,Idea Cellular Ltd.4
+404,1028,069,1695,in,India,91,Mahanagar Telephone Nigam
+404,1028,068,1679,in,India,91,Mahanagar Telephone Nigam2
+404,1028,083,2111,in,India,91,Reliable Internet Services
+405,1029,009,9,in,India,91,RELIANCE TELECOM
+404,1028,036,879,in,India,91,Reliance Telecom Private
+404,1028,052,1327,in,India,91,Reliance Telecom Private2
+404,1028,050,1295,in,India,91,Reliance Telecom Private3
+404,1028,067,1663,in,India,91,Reliance Telecom Private4
+404,1028,018,399,in,India,91,Reliance Telecom Private5
+404,1028,085,2143,in,India,91,Reliance Telecom Private6
+404,1028,009,9,in,India,91,Reliance Telecom Private7
+404,1028,041,1055,in,India,91,RPG Cellular
+404,1028,014,335,in,India,91,Spice
+404,1028,044,1103,in,India,91,Spice2
+404,1028,011,287,in,India,91,Sterling Cellular Ltd.
+404,1028,030,783,in,India,91,Usha Martin Telecom
+510,1296,008,143,id,Indonesia,62,Axis/Natrindo
+510,1296,089,2207,id,Indonesia,62,H3G CP
+510,1296,021,543,id,Indonesia,62,Indosat/Satelindo/M3
+510,1296,001,31,id,Indonesia,62,Indosat/Satelindo/M3
+510,1296,000,0,id,Indonesia,62,PT Pasifik Satelit Nusantara (PSN)
+510,1296,000,15,id,Indonesia,62,PT Pasifik Satelit Nusantara (PSN)2
+510,1296,027,639,id,Indonesia,62,PT Sampoerna Telekomunikasi Indonesia (STI)
+510,1296,009,159,id,Indonesia,62,PT Smartfren Telecom Tbk
+510,1296,028,655,id,Indonesia,62,PT Smartfren Telecom Tbk2
+510,1296,011,287,id,Indonesia,62,PT. Excelcom
+510,1296,010,271,id,Indonesia,62,Telkomsel
+901,2305,013,319,n/a,International Networks,882,Antarctica
+432,1074,019,415,ir,Iran,98,Mobile Telecommunications Company of Esfahan JV-PJS (MTCE)
+432,1074,070,1807,ir,Iran,98,MTCE
+432,1074,035,863,ir,Iran,98,MTN/IranCell
+432,1074,032,815,ir,Iran,98,Taliya
+432,1074,011,287,ir,Iran,98,TCI / MCI
+432,1074,014,335,ir,Iran,98,TKC/KFZO
+418,1048,005,95,iq,Iraq,964,Asia Cell
+418,1048,092,2351,iq,Iraq,964,Itisaluna and Kalemat
+418,1048,082,2095,iq,Iraq,964,Korek
+418,1048,040,1039,iq,Iraq,964,Korek2
+418,1048,045,1119,iq,Iraq,964,Mobitel (Iraq-Kurdistan) and Moutiny
+418,1048,030,783,iq,Iraq,964,Orascom Telecom
+418,1048,020,527,iq,Iraq,964,ZAIN/Atheer
+418,1048,008,8,iq,Iraq,964,Sanatel
+272,626,004,79,ie,Ireland,353,Access Telecom Ltd.
+272,626,009,159,ie,Ireland,353,Clever Communications Ltd
+272,626,007,127,ie,Ireland,353,eircom Ltd
+272,626,005,95,ie,Ireland,353,H3G
+272,626,011,287,ie,Ireland,353,Liffey Telecom
+272,626,013,319,ie,Ireland,353,Lycamobile
+272,626,003,63,ie,Ireland,353,Meteor Mobile Ltd.
+272,626,002,47,ie,Ireland,353,O2/Digifone
+272,626,001,31,ie,Ireland,353,Vodafone Eircell
+425,1061,014,335,il,Israel,972,Alon Cellular Ltd
+425,1061,002,47,il,Israel,972,Cellcom ltd.
+425,1061,008,143,il,Israel,972,Golan Telekom
+425,1061,015,351,il,Israel,972,Home Cellular Ltd
+425,1061,077,1919,il,Israel,972,Hot Mobile/Mirs
+425,1061,007,127,il,Israel,972,Hot Mobile/Mirs2
+425,1061,001,31,il,Israel,972,Orange/Partner Co. Ltd.
+425,1061,003,63,il,Israel,972,Pelephone
+425,1061,016,367,il,Israel,972,Rami Levy Hashikma Marketing Communications Ltd
+222,546,034,847,it,Italy,39,BT Italia SpA
+222,546,002,47,it,Italy,39,Elsacom
+222,546,099,2463,it,Italy,39,Hi3G
+222,546,033,831,it,Italy,39,Hi3G-2
+222,546,077,1919,it,Italy,39,IPSE 2000
+222,546,035,863,it,Italy,39,Lycamobile Srl
+222,546,007,127,it,Italy,39,Noverca Italia Srl
+222,546,030,783,it,Italy,39,RFI Rete Ferroviaria Italiana SpA
+222,546,048,1167,it,Italy,39,Telecom Italia Mobile SpA
+222,546,043,1087,it,Italy,39,Telecom Italia Mobile SpA2
+222,546,001,31,it,Italy,39,TIM
+222,546,010,271,it,Italy,39,Vodafone
+222,546,006,111,it,Italy,39,Vodafone
+222,546,044,1103,it,Italy,39,WIND (Blu)
+222,546,088,2191,it,Italy,39,WIND (Blu)2
+612,1554,007,127,ci,Ivory Coast,225,Aircomm SA
+612,1554,002,47,ci,Ivory Coast,225,Atlantik Tel./Moov
+612,1554,004,79,ci,Ivory Coast,225,Comium
+612,1554,001,1,ci,Ivory Coast,225,Comstar
+612,1554,005,95,ci,Ivory Coast,225,MTN
+612,1554,003,63,ci,Ivory Coast,225,Orange
+612,1554,006,111,ci,Ivory Coast,225,OriCell
+612,1554,000,0,ci,Ivory Coast,225,Warid
+338,824,110,272,jm,Jamaica,1876,Cable & Wireless
+338,824,020,32,jm,Jamaica,1876,Cable & Wireless-2
+338,824,180,384,jm,Jamaica,1876,Cable & Wireless-3
+338,824,050,80,jm,Jamaica,1876,DIGICEL/Mossel
+440,1088,000,0,jp,Japan,81,eMobile
+440,1088,074,1871,jp,Japan,81,KDDI Corporation
+440,1088,070,1807,jp,Japan,81,KDDI Corporation2
+440,1088,089,2207,jp,Japan,81,KDDI Corporation3
+440,1088,051,1311,jp,Japan,81,KDDI Corporation4
+440,1088,075,1887,jp,Japan,81,KDDI Corporation5
+440,1088,056,1391,jp,Japan,81,KDDI Corporation6
+441,1089,070,1807,jp,Japan,81,KDDI Corporation7
+440,1088,052,1327,jp,Japan,81,KDDI Corporation8
+440,1088,076,1903,jp,Japan,81,KDDI Corporation9
+440,1088,071,1823,jp,Japan,81,KDDI Corporation10
+440,1088,053,1343,jp,Japan,81,KDDI Corporation11
+440,1088,077,1919,jp,Japan,81,KDDI Corporation12
+440,1088,008,143,jp,Japan,81,KDDI Corporation13
+440,1088,072,1839,jp,Japan,81,KDDI Corporation14
+440,1088,054,1359,jp,Japan,81,KDDI Corporation15
+440,1088,079,1951,jp,Japan,81,KDDI Corporation16
+440,1088,007,127,jp,Japan,81,KDDI Corporation17
+440,1088,073,1855,jp,Japan,81,KDDI Corporation18
+440,1088,055,1375,jp,Japan,81,KDDI Corporation19
+440,1088,088,2191,jp,Japan,81,KDDI Corporation20
+440,1088,050,1295,jp,Japan,81,KDDI Corporation21
+440,1088,021,543,jp,Japan,81,NTT Docomo
+441,1089,044,1103,jp,Japan,81,NTT Docomo2
+440,1088,013,319,jp,Japan,81,NTT Docomo3
+440,1088,023,575,jp,Japan,81,NTT Docomo4
+440,1088,016,367,jp,Japan,81,NTT Docomo5
+441,1089,099,2463,jp,Japan,81,NTT Docomo6
+440,1088,034,847,jp,Japan,81,NTT Docomo7
+440,1088,069,1695,jp,Japan,81,NTT Docomo8
+440,1088,064,1615,jp,Japan,81,NTT Docomo9
+440,1088,037,895,jp,Japan,81,NTT Docomo10
+440,1088,025,607,jp,Japan,81,NTT Docomo11
+440,1088,022,559,jp,Japan,81,NTT Docomo12
+441,1089,043,1087,jp,Japan,81,NTT Docomo13
+440,1088,027,639,jp,Japan,81,NTT Docomo14
+440,1088,002,47,jp,Japan,81,NTT Docomo15
+440,1088,017,383,jp,Japan,81,NTT Docomo16
+440,1088,031,799,jp,Japan,81,NTT Docomo17
+440,1088,087,2175,jp,Japan,81,NTT Docomo18
+440,1088,065,1631,jp,Japan,81,NTT Docomo19
+440,1088,036,879,jp,Japan,81,NTT Docomo20
+441,1089,092,2351,jp,Japan,81,NTT Docomo21
+440,1088,012,303,jp,Japan,81,NTT Docomo22
+440,1088,058,1423,jp,Japan,81,NTT Docomo23
+440,1088,028,655,jp,Japan,81,NTT Docomo24
+440,1088,003,63,jp,Japan,81,NTT Docomo25
+440,1088,018,399,jp,Japan,81,NTT Docomo26
+441,1089,091,2335,jp,Japan,81,NTT Docomo27
+440,1088,032,815,jp,Japan,81,NTT Docomo28
+440,1088,061,1567,jp,Japan,81,NTT Docomo29
+440,1088,066,1647,jp,Japan,81,NTT Docomo30
+440,1088,035,863,jp,Japan,81,NTT Docomo31
+441,1089,093,2367,jp,Japan,81,NTT Docomo32
+441,1089,040,1039,jp,Japan,81,NTT Docomo33
+440,1088,049,1183,jp,Japan,81,NTT Docomo34
+440,1088,029,671,jp,Japan,81,NTT Docomo35
+440,1088,009,159,jp,Japan,81,NTT Docomo36
+440,1088,019,415,jp,Japan,81,NTT Docomo37
+441,1089,090,2319,jp,Japan,81,NTT Docomo38
+440,1088,033,831,jp,Japan,81,NTT Docomo39
+440,1088,060,1551,jp,Japan,81,NTT Docomo40
+440,1088,014,335,jp,Japan,81,NTT Docomo41
+441,1089,094,2383,jp,Japan,81,NTT Docomo42
+441,1089,041,1055,jp,Japan,81,NTT Docomo43
+440,1088,067,1663,jp,Japan,81,NTT Docomo44
+440,1088,062,1583,jp,Japan,81,NTT Docomo45
+440,1088,001,31,jp,Japan,81,NTT Docomo46
+440,1088,039,927,jp,Japan,81,NTT Docomo47
+440,1088,030,783,jp,Japan,81,NTT Docomo48
+440,1088,010,271,jp,Japan,81,NTT Docomo49
+440,1088,020,527,jp,Japan,81,NTT Docomo50
+441,1089,045,1119,jp,Japan,81,NTT Docomo51
+440,1088,024,591,jp,Japan,81,NTT Docomo52
+440,1088,015,351,jp,Japan,81,NTT Docomo53
+441,1089,098,2447,jp,Japan,81,NTT Docomo54
+441,1089,042,1071,jp,Japan,81,NTT Docomo55
+440,1088,068,1679,jp,Japan,81,NTT Docomo56
+440,1088,063,1599,jp,Japan,81,NTT Docomo57
+440,1088,038,911,jp,Japan,81,NTT Docomo58
+440,1088,026,623,jp,Japan,81,NTT Docomo59
+440,1088,011,287,jp,Japan,81,NTT Docomo60
+440,1088,099,2463,jp,Japan,81,NTT Docomo61
+440,1088,078,1935,jp,Japan,81,Okinawa Cellular Telephone
+440,1088,047,1151,jp,Japan,81,SoftBank Mobile Corp
+440,1088,095,2399,jp,Japan,81,SoftBank Mobile Corp2
+440,1088,041,1055,jp,Japan,81,SoftBank Mobile Corp3
+441,1089,064,1615,jp,Japan,81,SoftBank Mobile Corp4
+440,1088,046,1135,jp,Japan,81,SoftBank Mobile Corp5
+440,1088,097,2431,jp,Japan,81,SoftBank Mobile Corp6
+440,1088,042,1071,jp,Japan,81,SoftBank Mobile Corp7
+441,1089,065,1631,jp,Japan,81,SoftBank Mobile Corp8
+440,1088,090,2319,jp,Japan,81,SoftBank Mobile Corp9
+440,1088,092,2351,jp,Japan,81,SoftBank Mobile Corp10
+440,1088,098,2447,jp,Japan,81,SoftBank Mobile Corp11
+440,1088,043,1087,jp,Japan,81,SoftBank Mobile Corp12
+440,1088,048,1167,jp,Japan,81,SoftBank Mobile Corp13
+440,1088,093,2367,jp,Japan,81,SoftBank Mobile Corp14
+440,1088,006,111,jp,Japan,81,SoftBank Mobile Corp15
+441,1089,061,1567,jp,Japan,81,SoftBank Mobile Corp16
+440,1088,044,1103,jp,Japan,81,SoftBank Mobile Corp17
+440,1088,004,79,jp,Japan,81,SoftBank Mobile Corp18
+440,1088,094,2383,jp,Japan,81,SoftBank Mobile Corp19
+441,1089,062,1583,jp,Japan,81,SoftBank Mobile Corp20
+440,1088,045,1119,jp,Japan,81,SoftBank Mobile Corp21
+440,1088,040,1039,jp,Japan,81,SoftBank Mobile Corp22
+440,1088,096,2415,jp,Japan,81,SoftBank Mobile Corp23
+441,1089,063,1599,jp,Japan,81,SoftBank Mobile Corp24
+440,1088,085,2143,jp,Japan,81,KDDI Corporation
+440,1088,083,2111,jp,Japan,81,KDDI Corporation2
+440,1088,081,2079,jp,Japan,81,KDDI Corporation3
+440,1088,080,2063,jp,Japan,81,KDDI Corporation4
+440,1088,086,2159,jp,Japan,81,KDDI Corporation5
+440,1088,084,2127,jp,Japan,81,KDDI Corporation6
+440,1088,082,2095,jp,Japan,81,KDDI Corporation7
+416,1046,077,1919,jo,Jordan,962,Orange/Petra
+416,1046,003,63,jo,Jordan,962,Umniah Mobile Co.
+416,1046,002,2,jo,Jordan,962,Xpress
+416,1046,001,31,jo,Jordan,962,ZAIN /J.M.T.S
+401,1025,001,31,kz,Kazakhstan,7,Beeline/KaR-Tel LLP
+401,1025,007,127,kz,Kazakhstan,7,Dalacom/Altel
+401,1025,002,47,kz,Kazakhstan,7,K-Cell
+401,1025,077,1919,kz,Kazakhstan,7,NEO/MTS
+639,1593,005,95,ke,Kenya,254,Econet Wireless
+639,1593,007,127,ke,Kenya,254,Orange
+639,1593,002,47,ke,Kenya,254,Safaricom Ltd.
+639,1593,003,63,ke,Kenya,254,Zain/Celtel Ltd.
+545,1349,009,9,ki,Kiribati,686,Kiribati Frigate
+467,1127,193,403,kp,Korea N. Dem. People's Rep.,850,Sun Net
+450,1104,002,47,kr,Korea S Republic of,82,KT Freetel Co. Ltd.
+450,1104,004,79,kr,Korea S Republic of,82,KT Freetel Co. Ltd.2
+450,1104,008,143,kr,Korea S Republic of,82,KT Freetel Co. Ltd.3
+450,1104,006,111,kr,Korea S Republic of,82,LG Telecom
+450,1104,003,63,kr,Korea S Republic of,82,SK Telecom
+450,1104,005,95,kr,Korea S Republic of,82,SK Telecom Co. Ltd
+419,1049,004,79,kw,Kuwait,965,Viva
+419,1049,003,63,kw,Kuwait,965,Wantaniya
+419,1049,002,47,kw,Kuwait,965,Zain
+437,1079,003,63,kg,Kyrgyzstan,996,AkTel LLC
+437,1079,001,31,kg,Kyrgyzstan,996,Beeline/Bitel
+437,1079,005,95,kg,Kyrgyzstan,996,MEGACOM
+437,1079,009,159,kg,Kyrgyzstan,996,O!/NUR Telecom
+457,1111,002,47,la,Laos P.D.R.,856,ETL Mobile
+457,1111,001,31,la,Laos P.D.R.,856,Lao Tel
+457,1111,008,143,la,Laos P.D.R.,856,Tigo/Millicom
+457,1111,003,63,la,Laos P.D.R.,856,UNITEL/LAT
+247,583,005,95,lv,Latvia,371,Bite Latvija
+247,583,001,31,lv,Latvia,371,Latvian Mobile Phone
+247,583,009,159,lv,Latvia,371,SIA Camel Mobile
+247,583,008,143,lv,Latvia,371,SIA IZZI
+247,583,007,127,lv,Latvia,371,SIA Master Telecom
+247,583,006,111,lv,Latvia,371,SIA Rigatta
+247,583,002,47,lv,Latvia,371,Tele2
+247,583,003,63,lv,Latvia,371,TRIATEL/Telekom Baltija
+415,1045,033,831,lb,Lebanon,961,Cellis
+415,1045,032,815,lb,Lebanon,961,Cellis2
+415,1045,035,863,lb,Lebanon,961,Cellis3
+415,1045,034,847,lb,Lebanon,961,FTML Cellis
+415,1045,039,927,lb,Lebanon,961,MIC2/LibanCell
+415,1045,038,911,lb,Lebanon,961,MIC2/LibanCell2
+415,1045,037,895,lb,Lebanon,961,MIC2/LibanCell3
+415,1045,001,31,lb,Lebanon,961,MIC1 (Alfa)
+415,1045,003,63,lb,Lebanon,961,Touch
+415,1045,036,879,lb,Lebanon,961,MIC2/LibanCell
+651,1617,002,47,ls,Lesotho,266,Econet/Ezi-cel
+651,1617,001,31,ls,Lesotho,266,Vodacom Lesotho
+618,1560,007,127,lr,Liberia,231,Celcom
+618,1560,003,63,lr,Liberia,231,Celcom2
+618,1560,004,79,lr,Liberia,231,Comium BVI
+618,1560,002,47,lr,Liberia,231,Libercell
+618,1560,020,527,lr,Liberia,231,LibTelco
+618,1560,001,31,lr,Liberia,231,Lonestar
+606,1542,002,47,ly,Libya,218,Al-Madar
+606,1542,001,31,ly,Libya,218,Al-Madar2
+606,1542,006,111,ly,Libya,218,Hatef
+606,1542,000,15,ly,Libya,218,Libyana
+606,1542,003,63,ly,Libya,218,Libyana2
+295,661,006,111,li,Liechtenstein,423,CUBIC (Liechtenstein
+295,661,007,127,li,Liechtenstein,423,First Mobile AG
+295,661,005,95,li,Liechtenstein,423,Mobilkom AG
+295,661,002,47,li,Liechtenstein,423,Orange
+295,661,001,31,li,Liechtenstein,423,Swisscom FL AG
+295,661,077,1919,li,Liechtenstein,423,Alpmobile/Tele2
+246,582,002,47,lt,Lithuania,370,Bite
+246,582,001,31,lt,Lithuania,370,Omnitel
+246,582,003,63,lt,Lithuania,370,Tele2
+270,624,077,1919,lu,Luxembourg,352,Millicom Tango GSM
+270,624,001,31,lu,Luxembourg,352,P+T LUXGSM
+270,624,099,2463,lu,Luxembourg,352,VOXmobile S.A.
+455,1109,004,79,mo,Macao China,853,C.T.M. TELEMOVEL+
+455,1109,001,31,mo,Macao China,853,C.T.M. TELEMOVEL+2
+455,1109,002,47,mo,Macao China,853,China Telecom
+455,1109,005,95,mo,Macao China,853,Hutchison Telephone (Macau) Company Ltd
+455,1109,003,63,mo,Macao China,853,Hutchison Telephone (Macau) Company Ltd-2
+455,1109,006,111,mo,Macao China,853,Smartone Mobile
+455,1109,000,15,mo,Macao China,853,Smartone Mobile-2
+294,660,075,1887,mk,Macedonia,389,MTS/Cosmofone
+294,660,002,47,mk,Macedonia,389,MTS/Cosmofone2
+294,660,001,31,mk,Macedonia,389,T-Mobile/Mobimak
+294,660,003,63,mk,Macedonia,389,VIP Mobile
+646,1606,001,31,mg,Madagascar,261,MADACOM
+646,1606,002,47,mg,Madagascar,261,Orange/Soci
+646,1606,003,63,mg,Madagascar,261,Sacel
+646,1606,004,79,mg,Madagascar,261,Telma
+650,1616,001,31,mw,Malawi,265,TNM/Telekom Network Ltd.
+650,1616,010,271,mw,Malawi,265,Zain/Celtel ltd.
+502,1282,001,31,my,Malaysia,60,Art900
+502,1282,151,337,my,Malaysia,60,Baraka Telecom Sdn Bhd
+502,1282,013,319,my,Malaysia,60,CelCom
+502,1282,019,415,my,Malaysia,60,CelCom2
+502,1282,016,367,my,Malaysia,60,Digi Telecommunications
+502,1282,010,271,my,Malaysia,60,Digi Telecommunications-2
+502,1282,020,527,my,Malaysia,60,Electcoms Wireless Sdn Bhd
+502,1282,012,303,my,Malaysia,60,Maxis
+502,1282,017,383,my,Malaysia,60,Maxis2
+502,1282,011,287,my,Malaysia,60,MTX Utara
+502,1282,153,339,my,Malaysia,60,Packet One Networks (Malaysia) Sdn Bhd
+502,1282,155,341,my,Malaysia,60,Samata Communications Sdn Bhd
+502,1282,154,340,my,Malaysia,60,Talk Focus Sdn Bhd
+502,1282,018,399,my,Malaysia,60,U Mobile
+502,1282,152,338,my,Malaysia,60,YES
+472,1138,001,31,mv,Maldives,960,Dhiraagu/C&W
+472,1138,002,47,mv,Maldives,960,Wataniya/WMOBILE
+610,1552,001,31,ml,Mali,223,Malitel
+610,1552,002,47,ml,Mali,223,Orange/IKATEL
+278,632,021,543,mt,Malta,356,GO/Mobisle
+278,632,077,1919,mt,Malta,356,Melita
+278,632,001,31,mt,Malta,356,Vodafone
+340,832,002,47,mq,Martinique (French Department of),,Outremer Telecom Martinique (only) (Martinique
+340,832,012,303,mq,Martinique (French Department of),,United Telecommunications Services Caraibe SARL (UTS Caraibe Martinique Telephone Mobile) (Martinique
+340,832,003,63,mq,Martinique (French Department of),,United Telecommunications Services Caraibe SARL (UTS Caraibe Martinique Telephone Mobile) (Martinique-2
+609,1545,002,47,mr,Mauritania,222,Chinguitel SA
+609,1545,001,31,mr,Mauritania,222,Mattel
+609,1545,010,271,mr,Mauritania,222,Mauritel
+617,1559,010,271,mu,Mauritius,230,Emtel Ltd
+617,1559,002,47,mu,Mauritius,230,Mahanagar Telephone
+617,1559,003,63,mu,Mauritius,230,Mahanagar Telephone2
+617,1559,001,31,mu,Mauritius,230,Orange/Cellplus
+334,820,000,15,mx,Mexico,52,Axtel
+334,820,050,1295,mx,Mexico,52,IUSACell/UneFon
+334,820,050,80,mx,Mexico,52,IUSACell/UneFon
+334,820,040,64,mx,Mexico,52,IUSACell/UneFon2
+334,820,004,79,mx,Mexico,52,IUSACell/UneFon3
+334,820,003,63,mx,Mexico,52,Movistar/Pegaso
+334,820,030,48,mx,Mexico,52,Movistar/Pegaso2
+334,820,001,31,mx,Mexico,52,NEXTEL
+334,820,090,144,mx,Mexico,52,NEXTEL2
+334,820,010,16,mx,Mexico,52,NEXTEL3
+334,820,080,128,mx,Mexico,52,Operadora Unefon SA de CV
+334,820,070,112,mx,Mexico,52,Operadora Unefon SA de CV2
+334,820,060,96,mx,Mexico,52,SAI PCS
+334,820,000,15,mx,Mexico,52,SAI PCS-2
+334,820,002,47,mx,Mexico,52,TelCel/America Movil
+334,820,020,32,mx,Mexico,52,TelCel/America Movil-2
+550,1360,001,31,fm,Micronesia,691,FSM Telecom
+259,601,004,79,md,Moldova,373,Eventis Mobile
+259,601,099,2463,md,Moldova,373,IDC/Unite
+259,601,005,95,md,Moldova,373,IDC/Unite2
+259,601,003,63,md,Moldova,373,IDC/Unite3
+259,601,002,47,md,Moldova,373,Moldcell
+259,601,001,31,md,Moldova,373,Orange/Voxtel
+212,530,001,31,mc,Monaco,377,Dardafone LLC
+212,530,010,271,mc,Monaco,377,Monaco Telecom
+212,530,001,31,mc,Monaco,377,Monaco Telecom2
+212,530,001,31,mc,Monaco,377,Post and Telecommunications of Kosovo JSC (PTK)
+428,1064,098,2447,mn,Mongolia,976,G-Mobile Corporation Ltd
+428,1064,099,2463,mn,Mongolia,976,Mobicom
+428,1064,000,15,mn,Mongolia,976,Skytel Co. Ltd
+428,1064,088,2191,mn,Mongolia,976,Unitel
+297,663,002,47,me,Montenegro,382,Monet/T-mobile
+297,663,003,63,me,Montenegro,382,Mtel
+297,663,001,31,me,Montenegro,382,Promonte GSM
+354,852,860,2144,ms,Montserrat,1664,Cable & Wireless
+604,1540,001,31,ma,Morocco,212,IAM/Itissallat
+604,1540,002,47,ma,Morocco,212,INWI/WANA
+604,1540,000,15,ma,Morocco,212,Medi Telecom
+643,1603,001,31,mz,Mozambique,258,mCel
+643,1603,003,63,mz,Mozambique,258,Movitel
+643,1603,004,79,mz,Mozambique,258,Vodacom Sarl
+649,1609,003,63,na,Namibia,264,Leo / Orascom
+649,1609,001,31,na,Namibia,264,MTC
+649,1609,002,47,na,Namibia,264,Switch/Nam. Telec.
+429,1065,002,47,np,Nepal,977,Ncell
+429,1065,001,31,np,Nepal,977,NT Mobile / Namaste
+429,1065,004,79,np,Nepal,977,Smart Cell
+204,516,014,335,nl,Netherlands,31,6GMOBILE BV
+204,516,023,575,nl,Netherlands,31,Aspider Solutions
+204,516,005,95,nl,Netherlands,31,Elephant Talk Communications Premium Rate Services Netherlands BV
+204,516,017,383,nl,Netherlands,31,Intercity Mobile Communications BV
+204,516,010,271,nl,Netherlands,31,KPN Telecom B.V.
+204,516,008,143,nl,Netherlands,31,KPN Telecom B.V.2
+204,516,069,1695,nl,Netherlands,31,KPN Telecom B.V.3
+204,516,012,303,nl,Netherlands,31,KPN/Telfort
+204,516,028,655,nl,Netherlands,31,Lancelot BV
+204,516,009,159,nl,Netherlands,31,Lycamobile Ltd
+204,516,006,111,nl,Netherlands,31,Mundio/Vectone Mobile
+204,516,021,543,nl,Netherlands,31,NS Railinfrabeheer B.V.
+204,516,024,591,nl,Netherlands,31,Private Mobility Nederland BV
+204,516,098,2447,nl,Netherlands,31,T-Mobile B.V.
+204,516,016,367,nl,Netherlands,31,T-Mobile B.V.2
+204,516,020,527,nl,Netherlands,31,Orange/T-mobile
+204,516,002,47,nl,Netherlands,31,Tele2
+204,516,007,127,nl,Netherlands,31,Teleena Holding BV
+204,516,068,1679,nl,Netherlands,31,Unify Mobile
+204,516,018,399,nl,Netherlands,31,UPC Nederland BV
+204,516,004,79,nl,Netherlands,31,Vodafone Libertel
+204,516,003,63,nl,Netherlands,31,Voiceworks Mobile BV
+204,516,015,351,nl,Netherlands,31,Ziggo BV
+362,866,630,1584,an,Netherlands Antilles,599,Cingular Wireless
+362,866,069,1695,an,Netherlands Antilles,599,DigiCell
+362,866,051,1311,an,Netherlands Antilles,599,TELCELL GSM
+362,866,091,2335,an,Netherlands Antilles,599,SETEL GSM
+362,866,951,2385,an,Netherlands Antilles,599,UTS Wireless
+546,1350,001,31,nc,New Caledonia,687,OPT Mobilis
+530,1328,028,655,nz,New Zealand,64,2degrees
+530,1328,005,95,nz,New Zealand,64,Telecom Mobile Ltd
+530,1328,002,2,nz,New Zealand,64,NZ Telecom CDMA
+530,1328,004,4,nz,New Zealand,64,Telstra
+530,1328,024,591,nz,New Zealand,64,Two Degrees Mobile Ltd
+530,1328,001,31,nz,New Zealand,64,Vodafone
+530,1328,003,3,nz,New Zealand,64,Walker Wireless Ltd.
+710,1808,021,543,ni,Nicaragua,505,Empresa Nicaraguense de Telecomunicaciones SA (ENITEL)
+710,1808,030,783,ni,Nicaragua,505,Movistar
+710,1808,073,1855,ni,Nicaragua,505,Claro
+614,1556,003,63,ne,Niger,227,Etisalat/TeleCel
+614,1556,004,79,ne,Niger,227,Orange/Sahelc.
+614,1556,001,31,ne,Niger,227,Orange/Sahelc.2
+614,1556,002,47,ne,Niger,227,Zain/CelTel
+621,1569,020,527,ng,Nigeria,234,Airtel/ZAIN/Econet
+621,1569,060,1551,ng,Nigeria,234,ETISALAT
+621,1569,050,1295,ng,Nigeria,234,Glo Mobile
+621,1569,040,1039,ng,Nigeria,234,M-Tel/Nigeria Telecom. Ltd.
+621,1569,030,783,ng,Nigeria,234,MTN
+621,1569,099,2463,ng,Nigeria,234,Starcomms
+621,1569,025,607,ng,Nigeria,234,Visafone
+621,1569,001,31,ng,Nigeria,234,Visafone2
+555,1365,001,31,nu,Niue,,Niue Telecom
+242,578,009,159,no,Norway,47,Com4 AS
+242,578,020,527,no,Norway,47,Jernbaneverket (GSM-R)
+242,578,021,543,no,Norway,47,Jernbaneverket (GSM-R)-2
+242,578,023,575,no,Norway,47,Lycamobile Ltd
+242,578,002,47,no,Norway,47,Netcom
+242,578,022,559,no,Norway,47,Network Norway AS
+242,578,005,95,no,Norway,47,Network Norway AS-2
+242,578,006,6,no,Norway,47,ICE Nordisk Mobiltelefon AS
+242,578,008,143,no,Norway,47,TDC Mobil A/S
+242,578,004,79,no,Norway,47,Tele2
+242,578,012,303,no,Norway,47,Telenor
+242,578,001,31,no,Norway,47,Telenor2
+242,578,003,63,no,Norway,47,Teletopia
+242,578,007,127,no,Norway,47,Ventelo AS
+422,1058,003,63,om,Oman,968,Nawras
+422,1058,002,47,om,Oman,968,Oman Mobile/GTO
+410,1040,008,143,pk,Pakistan,92,Instaphone
+410,1040,001,31,pk,Pakistan,92,Mobilink
+410,1040,006,111,pk,Pakistan,92,Telenor
+410,1040,003,63,pk,Pakistan,92,UFONE/PAKTel
+410,1040,007,127,pk,Pakistan,92,Warid Telecom
+410,1040,004,79,pk,Pakistan,92,ZONG/CMPak
+552,1362,080,2063,pw,Palau (Republic of),,Palau Mobile Corp. (PMC) (Palau
+552,1362,001,31,pw,Palau (Republic of),,Palau National Communications Corp. (PNCC) (Palau
+425,1061,005,95,ps,Palestinian Territory,970,Jawwal
+425,1061,006,111,ps,Palestinian Territory,970,Wataniya Mobile
+714,1812,001,31,pa,Panama,507,Cable & Wireless S.A.
+714,1812,003,63,pa,Panama,507,Claro
+714,1812,004,79,pa,Panama,507,Digicel
+714,1812,020,32,pa,Panama,507,Movistar
+714,1812,002,47,pa,Panama,507,Movistar2
+537,1335,003,63,pg,Papua New Guinea,675,Digicel
+537,1335,002,47,pg,Papua New Guinea,675,GreenCom PNG Ltd
+537,1335,001,31,pg,Papua New Guinea,675,Pacific Mobile
+744,1860,002,47,py,Paraguay,595,Claro/Hutchison
+744,1860,003,63,py,Paraguay,595,Compa
+744,1860,001,31,py,Paraguay,595,Hola/VOX
+744,1860,005,5,py,Paraguay,595,TIM/Nucleo/Personal
+744,1860,004,79,py,Paraguay,595,Tigo/Telecel
+716,1814,020,527,pe,Peru,51,Claro /Amer.Mov./TIM
+716,1814,010,271,pe,Peru,51,Claro /Amer.Mov./TIM-2
+716,1814,002,47,pe,Peru,51,GlobalStar
+716,1814,001,31,pe,Peru,51,GlobalStar2
+716,1814,006,111,pe,Peru,51,Movistar
+716,1814,007,127,pe,Peru,51,Nextel
+515,1301,000,15,ph,Philippines,63,Fix Line
+515,1301,001,31,ph,Philippines,63,Globe Telecom
+515,1301,002,47,ph,Philippines,63,Globe Telecom2
+515,1301,088,2191,ph,Philippines,63,Next Mobile
+515,1301,018,399,ph,Philippines,63,RED Mobile/Cure
+515,1301,003,63,ph,Philippines,63,Smart
+515,1301,005,95,ph,Philippines,63,SUN/Digitel
+260,608,017,383,pl,Poland,48,Aero2 SP.
+260,608,018,399,pl,Poland,48,AMD Telecom.
+260,608,038,911,pl,Poland,48,CallFreedom Sp. z o.o.
+260,608,012,303,pl,Poland,48,Cyfrowy POLSAT S.A.
+260,608,008,143,pl,Poland,48,e-Telko
+260,608,009,159,pl,Poland,48,Lycamobile
+260,608,016,367,pl,Poland,48,Mobyland
+260,608,036,879,pl,Poland,48,Mundio Mobile Sp. z o.o.
+260,608,007,127,pl,Poland,48,Play/P4
+260,608,011,287,pl,Poland,48,NORDISK Polska
+260,608,005,95,pl,Poland,48,Orange/IDEA/Centertel
+260,608,003,63,pl,Poland,48,Orange/IDEA/Centertel2
+260,608,035,863,pl,Poland,48,PKP Polskie Linie Kolejowe S.A.
+260,608,098,2447,pl,Poland,48,Play/P4
+260,608,006,111,pl,Poland,48,Play/P4-2
+260,608,001,31,pl,Poland,48,Polkomtel/Plus
+260,608,010,271,pl,Poland,48,Sferia
+260,608,014,335,pl,Poland,48,Sferia2
+260,608,013,319,pl,Poland,48,Sferia3
+260,608,034,847,pl,Poland,48,T-Mobile/ERA
+260,608,002,47,pl,Poland,48,T-Mobile/ERA-2
+260,608,015,351,pl,Poland,48,Tele2
+260,608,004,79,pl,Poland,48,Tele2
+268,616,004,79,pt,Portugal,351,CTT - Correios de Portugal SA
+268,616,003,63,pt,Portugal,351,Optimus
+268,616,007,127,pt,Portugal,351,Optimus2
+268,616,006,111,pt,Portugal,351,TMN
+268,616,001,31,pt,Portugal,351,Vodafone
+330,816,011,287,pr,Puerto Rico,,Puerto Rico Telephone Company Inc. (PRTC)
+427,1063,001,31,qa,Qatar,974,Qtel
+427,1063,002,47,qa,Qatar,974,Vodafone
+647,1607,000,15,re,Reunion,262,Orange
+647,1607,002,47,re,Reunion,262,Outremer Telecom
+647,1607,010,271,re,Reunion,262,SFR
+226,550,003,63,ro,Romania,40,Cosmote
+226,550,011,287,ro,Romania,40,Enigma Systems
+226,550,010,271,ro,Romania,40,Orange
+226,550,005,95,ro,Romania,40,RCS&RDS Digi Mobile
+226,550,002,47,ro,Romania,40,Romtelecom SA
+226,550,006,111,ro,Romania,40,Telemobil/Zapp
+226,550,001,31,ro,Romania,40,Vodafone
+226,550,004,79,ro,Romania,40,Telemobil/Zapp-2
+250,592,012,303,ru,Russian Federation,79,Baykal Westcom
+250,592,028,655,ru,Russian Federation,79,Bee Line GSM
+250,592,010,271,ru,Russian Federation,79,DTC/Don Telecom
+250,592,020,527,ru,Russian Federation,79,JSC Rostov Cellular Communications
+250,592,013,319,ru,Russian Federation,79,Kuban GSM
+250,592,035,863,ru,Russian Federation,79,LLC Ekaterinburg-2000
+250,592,020,527,ru,Russian Federation,79,LLC Personal Communication Systems in the Region
+250,592,002,47,ru,Russian Federation,79,Megafon
+250,592,001,31,ru,Russian Federation,79,MTS
+250,592,003,63,ru,Russian Federation,79,NCC
+250,592,016,367,ru,Russian Federation,79,NTC
+250,592,019,415,ru,Russian Federation,79,OJSC Altaysvyaz
+250,592,099,2463,ru,Russian Federation,79,OJSC Vimpel-Communications (VimpelCom)
+250,592,011,287,ru,Russian Federation,79,Orensot
+250,592,092,2351,ru,Russian Federation,79,Printelefone
+250,592,004,79,ru,Russian Federation,79,Sibchallenge
+250,592,044,1103,ru,Russian Federation,79,StavTelesot
+250,592,020,527,ru,Russian Federation,79,Tele2/ECC/Volgogr.
+250,592,093,2367,ru,Russian Federation,79,Telecom XXL
+250,592,039,927,ru,Russian Federation,79,U-Tel/Ermak RMS
+250,592,017,383,ru,Russian Federation,79,U-Tel/Ermak RMS-2
+250,592,039,927,ru,Russian Federation,79,UralTel
+250,592,017,383,ru,Russian Federation,79,UralTel2
+250,592,005,95,ru,Russian Federation,79,Yenisey Telecom
+250,592,015,351,ru,Russian Federation,79,ZAO SMARTS
+250,592,007,127,ru,Russian Federation,79,ZAO SMARTS-2
+635,1589,014,335,rw,Rwanda,250,Airtel Rwanda Ltd
+635,1589,010,271,rw,Rwanda,250,MTN/Rwandacell
+635,1589,013,319,rw,Rwanda,250,TIGO
+356,854,110,272,kn,Saint Kitts and Nevis,1869,Cable & Wireless
+356,854,050,1295,kn,Saint Kitts and Nevis,1869,Digicel
+356,854,070,1807,kn,Saint Kitts and Nevis,1869,UTS Cariglobe
+358,856,110,272,lc,Saint Lucia,1758,Cable & Wireless
+358,856,030,783,lc,Saint Lucia,1758,Cingular Wireless
+358,856,050,1295,lc,Saint Lucia,1758,Digicel (St Lucia) Limited
+549,1353,027,639,ws,Samoa,685,Samoatel Mobile
+549,1353,001,31,ws,Samoa,685,Telecom Samoa Cellular Ltd.
+292,658,001,31,sm,San Marino,378,Prima Telecom
+626,1574,001,31,st,Sao Tome & Principe,239,CSTmovel
+901,2305,014,335,n/a,Satellite Networks,870,AeroMobile
+901,2305,011,287,n/a,Satellite Networks,870,InMarSAT
+901,2305,012,303,n/a,Satellite Networks,870,Maritime Communications Partner AS
+901,2305,005,95,n/a,Satellite Networks,870,Thuraya Satellite
+420,1056,007,7,sa,Saudi Arabia,966,Zain
+420,1056,003,63,sa,Saudi Arabia,966,Etihad/Etisalat/Mobily
+420,1056,001,31,sa,Saudi Arabia,966,STC/Al Jawal
+420,1056,004,79,sa,Saudi Arabia,966,Zain
+608,1544,003,63,sn,Senegal,221,Expresso/Sudatel
+608,1544,001,31,sn,Senegal,221,Orange/Sonatel
+608,1544,002,47,sn,Senegal,221,Sentel GSM
+220,544,003,63,rs,Serbia,381,MTS/Telekom Srbija
+220,544,002,47,rs,Serbia,381,Telenor/Mobtel
+220,544,001,1,rs,Serbia,381,Telenor/Mobtel2
+220,544,005,95,rs,Serbia,381,VIP Mobile
+633,1587,010,271,sc,Seychelles,248,Airtel
+633,1587,001,31,sc,Seychelles,248,C&W
+633,1587,002,47,sc,Seychelles,248,Smartcom
+619,1561,003,3,sl,Sierra Leone,232,Africel
+619,1561,005,5,sl,Sierra Leone,232,Africel2
+619,1561,001,1,sl,Sierra Leone,232,Zain/Celtel
+619,1561,004,4,sl,Sierra Leone,232,Comium
+619,1561,002,2,sl,Sierra Leone,232,Tigo/Millicom
+619,1561,025,607,sl,Sierra Leone,232,Mobitel
+525,1317,012,303,sg,Singapore,65,GRID Communications Pte Ltd
+525,1317,003,63,sg,Singapore,65,MobileOne Ltd
+525,1317,001,31,sg,Singapore,65,Singtel
+525,1317,007,127,sg,Singapore,65,Singtel2
+525,1317,002,47,sg,Singapore,65,Singtel3
+525,1317,006,111,sg,Singapore,65,Starhub
+525,1317,005,95,sg,Singapore,65,Starhub2
+362,866,051,1311,sx,Sint Maarten (Dutch part),,TelCell NV (Sint Maarten
+362,866,091,2335,sx,Sint Maarten (Dutch part),,UTS St. Maarten (Sint Maarten
+231,561,006,111,sk,Slovakia,421,O2
+231,561,001,31,sk,Slovakia,421,Orange
+231,561,005,95,sk,Slovakia,421,Orange2
+231,561,015,351,sk,Slovakia,421,Orange
+231,561,002,47,sk,Slovakia,421,T-Mobile
+231,561,004,79,sk,Slovakia,421,T-Mobile3
+231,561,099,2463,sk,Slovakia,421,Zeleznice Slovenskej republiky (ZSR)
+293,659,041,1055,si,Slovenia,386,Dukagjini Telecommunications Sh.P.K.
+293,659,041,1055,si,Slovenia,386,Ipko Telecommunications d. o. o.
+293,659,041,1055,si,Slovenia,386,Mobitel
+293,659,040,1039,si,Slovenia,386,SI.Mobil
+293,659,010,271,si,Slovenia,386,Slovenske zeleznice d.o.o.
+293,659,064,1615,si,Slovenia,386,T-2 d.o.o.
+293,659,070,1807,si,Slovenia,386,TusMobil/VEGA
+540,1344,002,47,sb,Solomon Islands,677,bemobile
+540,1344,010,271,sb,Solomon Islands,677,BREEZE
+540,1344,001,31,sb,Solomon Islands,677,BREEZE2
+637,1591,030,783,so,Somalia,252,Golis
+637,1591,019,415,so,Somalia,252,HorTel
+637,1591,060,1551,so,Somalia,252,Nationlink
+637,1591,010,271,so,Somalia,252,Nationlink2
+637,1591,004,4,so,Somalia,252,Somafone
+637,1591,082,2095,so,Somalia,252,Telcom Mobile Somalia
+637,1591,001,31,so,Somalia,252,Telesom
+655,1621,002,47,za,South Africa,27,8.ta
+655,1621,021,543,za,South Africa,27,Cape Town Metropolitan
+655,1621,007,127,za,South Africa,27,Cell C
+655,1621,012,303,za,South Africa,27,MTN
+655,1621,010,271,za,South Africa,27,MTN2
+655,1621,006,111,za,South Africa,27,Sentech
+655,1621,001,31,za,South Africa,27,Vodacom
+655,1621,019,415,za,South Africa,27,Wireless Business Solutions (Pty) Ltd
+659,1625,003,63,ss,South Sudan (Republic of),,Gemtel Ltd (South Sudan
+659,1625,002,47,ss,South Sudan (Republic of),,MTN South Sudan (South Sudan
+659,1625,004,79,ss,South Sudan (Republic of),,Network of The World Ltd (NOW) (South Sudan
+659,1625,006,111,ss,South Sudan (Republic of),,Zain South Sudan (South Sudan
+214,532,023,575,es,Spain,34,Lycamobile SL
+214,532,022,559,es,Spain,34,Movistar2
+214,532,015,351,es,Spain,34,BT Espana Compania de Servicios Globales de Telecomunicaciones SAU
+214,532,018,399,es,Spain,34,Cableuropa SAU (ONO)
+214,532,008,143,es,Spain,34,Euskaltel SA
+214,532,020,527,es,Spain,34,fonYou Wireless SL
+214,532,021,543,es,Spain,34,Jazz Telecom SAU
+214,532,026,623,es,Spain,34,Lleida
+214,532,025,607,es,Spain,34,Lycamobile SL
+214,532,007,127,es,Spain,34,Movistar
+214,532,005,95,es,Spain,34,Movistar (resellers)
+214,532,009,159,es,Spain,34,Orange (resellers)
+214,532,003,63,es,Spain,34,Orange
+214,532,011,287,es,Spain,34,Orange2
+214,532,017,383,es,Spain,34,R Cable y Telecomunicaciones Galicia SA
+214,532,019,415,es,Spain,34,Simyo/KPN
+214,532,016,367,es,Spain,34,Telecable de Asturias SA
+214,532,027,639,es,Spain,34,Truphone
+214,532,001,31,es,Spain,34,Vodafone
+214,532,006,111,es,Spain,34,Vodafone Enabler Espana SL
+214,532,004,79,es,Spain,34,Yoigo
+413,1043,005,95,lk,Sri Lanka,94,Bharti Airtel
+413,1043,003,63,lk,Sri Lanka,94,Etisalat/Tigo
+413,1043,008,143,lk,Sri Lanka,94,H3G Hutchison
+413,1043,001,31,lk,Sri Lanka,94,Mobitel Ltd.
+413,1043,002,47,lk,Sri Lanka,94,MTN/Dialog
+308,776,001,31,pm,St. Pierre & Miquelon,508,Ameris
+360,864,110,272,vc,St. Vincent & Gren.,1784,C & W
+360,864,010,271,vc,St. Vincent & Gren.,1784,Cingular
+360,864,100,256,vc,St. Vincent & Gren.,1784,Cingular-2
+360,864,050,80,vc,St. Vincent & Gren.,1784,Digicel
+360,864,070,1807,vc,St. Vincent & Gren.,1784,Digicel
+634,1588,000,15,sd,Sudan,249,Canar Telecom
+634,1588,022,559,sd,Sudan,249,MTN
+634,1588,002,47,sd,Sudan,249,MTN
+634,1588,015,351,sd,Sudan,249,Sudani One
+634,1588,007,127,sd,Sudan,249,Sudani One-2
+634,1588,005,95,sd,Sudan,249,Vivacell
+634,1588,008,143,sd,Sudan,249,Vivacell
+634,1588,001,31,sd,Sudan,249,ZAIN/Mobitel
+634,1588,006,111,sd,Sudan,249,ZAIN/Mobitel-2
+746,1862,003,63,sr,Suriname,597,Digicel
+746,1862,002,47,sr,Suriname,597,Telecommunicatiebedrijf Suriname (TELESUR)
+746,1862,001,1,sr,Suriname,597,Telesur
+746,1862,004,79,sr,Suriname,597,UNIQA
+653,1619,010,271,sz,Swaziland,268,Swazi MTN
+653,1619,001,31,sz,Swaziland,268,SwaziTelecom
+240,576,035,863,se,Sweden,46,42 Telecom AB
+240,576,016,367,se,Sweden,46,42 Telecom AB-2
+240,576,026,623,se,Sweden,46,Beepsend
+240,576,000,15,se,Sweden,46,Compatel
+240,576,028,655,se,Sweden,46,CoolTEL Aps
+240,576,025,607,se,Sweden,46,Digitel Mobile Srl
+240,576,022,559,se,Sweden,46,Eu Tel AB
+240,576,027,639,se,Sweden,46,Fogg Mobile AB
+240,576,018,399,se,Sweden,46,Generic Mobile Systems Sweden AB
+240,576,017,383,se,Sweden,46,Gotalandsnatet AB
+240,576,002,47,se,Sweden,46,H3G Access AB
+240,576,004,79,se,Sweden,46,H3G Access AB-2
+240,576,036,879,se,Sweden,46,ID Mobile
+240,576,023,575,se,Sweden,46,Infobip Ltd.
+240,576,011,287,se,Sweden,46,Lindholmen Science Park AB
+240,576,012,303,se,Sweden,46,Lycamobile Ltd
+240,576,029,671,se,Sweden,46,Mercury International Carrier Services
+240,576,003,63,se,Sweden,46,Orange
+240,576,010,271,se,Sweden,46,Spring Mobil AB
+240,576,014,335,se,Sweden,46,TDC Sverige AB
+240,576,007,127,se,Sweden,46,Tele2 Sverige AB
+240,576,005,95,se,Sweden,46,Tele2 Sverige AB
+240,576,024,591,se,Sweden,46,Tele2 Sverige AB-2
+240,576,024,591,se,Sweden,46,Telenor (Vodafone)
+240,576,008,143,se,Sweden,46,Telenor (Vodafone)-2
+240,576,004,79,se,Sweden,46,Telenor (Vodafone)-3
+240,576,006,111,se,Sweden,46,Telenor (Vodafone)-4
+240,576,009,159,se,Sweden,46,Telenor Mobile Sverige AS
+240,576,005,95,se,Sweden,46,Telia Mobile
+240,576,001,31,se,Sweden,46,Telia Mobile-2
+240,576,000,15,se,Sweden,46,EUTel
+240,576,008,143,se,Sweden,46,Timepiece Servicos De Consultoria LDA (Universal Telecom)
+240,576,013,319,se,Sweden,46,Ventelo Sverige AB
+240,576,020,527,se,Sweden,46,Wireless Maingate AB
+240,576,015,351,se,Sweden,46,Wireless Maingate Nordic AB
+228,552,051,1311,ch,Switzerland,41,BebbiCell AG
+228,552,009,159,ch,Switzerland,41,Comfone AG
+228,552,005,95,ch,Switzerland,41,Comfone AG
+228,552,007,127,ch,Switzerland,41,TDC Sunrise
+228,552,054,1359,ch,Switzerland,41,Lycamobile AG
+228,552,052,1327,ch,Switzerland,41,Mundio Mobile AG
+228,552,003,63,ch,Switzerland,41,Orange
+228,552,001,31,ch,Switzerland,41,Swisscom
+228,552,012,303,ch,Switzerland,41,TDC Sunrise2
+228,552,002,47,ch,Switzerland,41,TDC Sunrise3
+228,552,008,143,ch,Switzerland,41,TDC Sunrise4
+228,552,053,1343,ch,Switzerland,41,upc cablecom GmbH
+417,1047,002,47,sy,Syrian Arab Republic,963,MTN/Spacetel
+417,1047,009,159,sy,Syrian Arab Republic,963,Syriatel Holdings
+417,1047,001,31,sy,Syrian Arab Republic,963,Syriatel Holdings2
+466,1126,068,1679,tw,Taiwan,886,ACeS Taiwan - ACeS Taiwan Telecommunications Co Ltd
+466,1126,005,95,tw,Taiwan,886,Asia Pacific Telecom Co. Ltd (APT)
+466,1126,011,287,tw,Taiwan,886,Chunghwa Telecom LDM
+466,1126,092,2351,tw,Taiwan,886,Chunghwa Telecom LDM-2
+466,1126,002,47,tw,Taiwan,886,Far EasTone
+466,1126,001,31,tw,Taiwan,886,Far EasTone2
+466,1126,007,127,tw,Taiwan,886,Far EasTone3
+466,1126,006,111,tw,Taiwan,886,Far EasTone4
+466,1126,003,63,tw,Taiwan,886,Far EasTone6
+466,1126,010,271,tw,Taiwan,886,Global Mobile Corp.
+466,1126,056,1391,tw,Taiwan,886,International Telecom Co. Ltd (FITEL)
+466,1126,088,2191,tw,Taiwan,886,KG Telecom
+466,1126,099,2463,tw,Taiwan,886,TransAsia
+466,1126,097,2431,tw,Taiwan,886,Taiwan Cellular
+466,1126,093,2367,tw,Taiwan,886,Mobitai
+466,1126,089,2207,tw,Taiwan,886,VIBO
+466,1126,009,159,tw,Taiwan,886,VMAX Telecom Co. Ltd
+436,1078,004,79,tj,Tajikistan,992,Babilon-M
+436,1078,005,95,tj,Tajikistan,992,Bee Line
+436,1078,002,47,tj,Tajikistan,992,CJSC Indigo Tajikistan
+436,1078,012,303,tj,Tajikistan,992,Tcell/JC Somoncom
+436,1078,003,63,tj,Tajikistan,992,MLT/TT mobile
+436,1078,001,31,tj,Tajikistan,992,Tcell/JC Somoncom
+640,1600,008,143,tz,Tanzania,255,Benson Informatics Ltd
+640,1600,006,111,tz,Tanzania,255,Dovetel (T) Ltd
+640,1600,009,159,tz,Tanzania,255,ExcellentCom (T) Ltd
+640,1600,011,287,tz,Tanzania,255,Smile Communications Tanzania Ltd
+640,1600,007,127,tz,Tanzania,255,Tanzania Telecommunications Company Ltd (TTCL)
+640,1600,002,47,tz,Tanzania,255,TIGO/MIC
+640,1600,001,1,tz,Tanzania,255,Tri Telecomm. Ltd.
+640,1600,004,79,tz,Tanzania,255,Vodacom Ltd
+640,1600,005,95,tz,Tanzania,255,ZAIN/Celtel
+640,1600,003,63,tz,Tanzania,255,Zantel/Zanzibar Telecom
+520,1312,020,527,th,Thailand,66,ACeS Thailand - ACeS Regional Services Co Ltd
+520,1312,015,351,th,Thailand,66,ACT Mobile
+520,1312,003,63,th,Thailand,66,Advanced Wireless Networks/AWN
+520,1312,001,31,th,Thailand,66,AIS/Advanced Info Service
+520,1312,023,575,th,Thailand,66,Digital Phone Co.
+520,1312,000,15,th,Thailand,66,Hutch/CAT CDMA
+520,1312,018,399,th,Thailand,66,Total Access (DTAC)
+520,1312,005,95,th,Thailand,66,Total Access (DTAC)
+520,1312,004,79,th,Thailand,66,True Move/Orange
+520,1312,099,2463,th,Thailand,66,True Move/Orange
+514,1300,001,31,tl,Timor-Leste,670,Telin/ Telkomcel
+514,1300,002,47,tl,Timor-Leste,670,Timor Telecom
+615,1557,002,2,tg,Togo,228,Telecel/MOOV
+615,1557,003,63,tg,Togo,228,Telecel/MOOV-2
+615,1557,001,31,tg,Togo,228,Togo Telecom/TogoCELL
+539,1337,043,1087,to,Tonga,676,Shoreline Communication
+539,1337,001,1,to,Tonga,676,Tonga Communications
+374,884,129,297,tt,Trinidad and Tobago,1868,Bmobile/TSTT
+374,884,130,304,tt,Trinidad and Tobago,1868,Digicel
+374,884,140,320,tt,Trinidad and Tobago,1868,LaqTel Ltd.
+605,1541,001,31,tn,Tunisia,216,Orange
+605,1541,003,63,tn,Tunisia,216,Orascom Telecom
+605,1541,002,47,tn,Tunisia,216,Tunisie Telecom
+286,646,004,79,tr,Turkey,90,AVEA/Aria
+286,646,003,63,tr,Turkey,90,AVEA/Aria-2
+286,646,001,31,tr,Turkey,90,Turkcell
+286,646,002,47,tr,Turkey,90,Vodafone-Telsim
+438,1080,001,31,tm,Turkmenistan,993,Barash Communication
+438,1080,002,47,tm,Turkmenistan,993,TM-Cell
+376,886,350,848,tc,Turks and Caicos Islands,,Cable & Wireless (TCI) Ltd
+376,886,050,80,tc,Turks and Caicos Islands,,Digicel TCI Ltd
+376,886,352,850,tc,Turks and Caicos Islands,,IslandCom Communications Ltd.
+553,1363,001,31,tv,Tuvalu,,Tuvalu Telecommunication Corporation (TTC)
+641,1601,001,31,ug,Uganda,256,Celtel
+641,1601,066,1647,ug,Uganda,256,i-Tel Ltd
+641,1601,030,783,ug,Uganda,256,K2 Telecom Ltd
+641,1601,010,271,ug,Uganda,256,MTN Ltd.
+641,1601,014,335,ug,Uganda,256,Orange
+641,1601,033,831,ug,Uganda,256,Smile Communications Uganda Ltd
+641,1601,018,399,ug,Uganda,256,Suretelecom Uganda Ltd
+641,1601,011,287,ug,Uganda,256,Uganda Telecom Ltd.
+641,1601,022,559,ug,Uganda,256,Airtel/Warid
+255,597,006,111,ua,Ukraine,380,Astelit/LIFE
+255,597,005,95,ua,Ukraine,380,Golden Telecom
+255,597,039,927,ua,Ukraine,380,Golden Telecom
+255,597,004,79,ua,Ukraine,380,Intertelecom Ltd (IT)
+255,597,067,1663,ua,Ukraine,380,KyivStar
+255,597,003,63,ua,Ukraine,380,KyivStar2
+255,597,021,543,ua,Ukraine,380,Telesystems Of Ukraine CJSC (TSU)
+255,597,007,127,ua,Ukraine,380,TriMob LLC
+255,597,050,1295,ua,Ukraine,380,UMC/MTS
+255,597,002,47,ua,Ukraine,380,Beeline
+255,597,001,31,ua,Ukraine,380,UMC/MTS
+255,597,068,1679,ua,Ukraine,380,Beeline
+424,1060,003,63,ae,United Arab Emirates,971,DU
+431,1073,002,47,ae,United Arab Emirates,971,Etisalat
+430,1072,002,47,ae,United Arab Emirates,971,Etisalat-2
+424,1060,002,47,ae,United Arab Emirates,971,Etisalat-3
+234,564,003,63,gb,United Kingdom,44,Airtel/Vodafone
+234,564,076,1903,gb,United Kingdom,44,BT Group
+234,564,077,1919,gb,United Kingdom,44,BT Group2
+234,564,007,127,gb,United Kingdom,44,Cable and Wireless
+234,564,092,2351,gb,United Kingdom,44,Cable and Wireless-2
+234,564,036,879,gb,United Kingdom,44,Calbe and Wireless Isle of Man
+234,564,018,399,gb,United Kingdom,44,Cloud9/wire9 Tel.
+235,565,002,47,gb,United Kingdom,44,Everyth. Ev.wh.
+234,564,017,383,gb,United Kingdom,44,FlexTel
+234,564,055,1375,gb,United Kingdom,44,Guernsey Telecoms
+234,564,014,335,gb,United Kingdom,44,HaySystems
+234,564,094,2383,gb,United Kingdom,44,Hutchinson 3G
+234,564,020,527,gb,United Kingdom,44,Hutchinson 3G-2
+234,564,075,1887,gb,United Kingdom,44,Inquam Telecom Ltd
+234,564,050,1295,gb,United Kingdom,44,Jersey Telecom
+234,564,035,863,gb,United Kingdom,44,JSC Ingenicum
+234,564,026,623,gb,United Kingdom,44,Lycamobile
+234,564,058,1423,gb,United Kingdom,44,Manx Telecom
+234,564,001,31,gb,United Kingdom,44,Mapesbury C. Ltd
+234,564,028,655,gb,United Kingdom,44,Marthon Telecom
+234,564,010,271,gb,United Kingdom,44,O2 Ltd.
+234,564,002,47,gb,United Kingdom,44,O2 Ltd.
+234,564,011,287,gb,United Kingdom,44,O2 Ltd.
+234,564,008,143,gb,United Kingdom,44,OnePhone
+234,564,016,367,gb,United Kingdom,44,Opal Telecom
+234,564,034,847,gb,United Kingdom,44,Everyth. Ev.wh./Orange
+234,564,033,831,gb,United Kingdom,44,Everyth. Ev.wh./Orange-2
+234,564,019,415,gb,United Kingdom,44,PMN/Teleware
+234,564,012,303,gb,United Kingdom,44,Railtrack Plc
+234,564,022,559,gb,United Kingdom,44,Routotelecom
+234,564,024,591,gb,United Kingdom,44,Stour Marine
+234,564,037,895,gb,United Kingdom,44,Synectiv Ltd.
+234,564,031,799,gb,United Kingdom,44,Everyth. Ev.wh./T-Mobile
+234,564,030,783,gb,United Kingdom,44,Everyth. Ev.wh./T-Mobile-2
+234,564,032,815,gb,United Kingdom,44,Everyth. Ev.wh./T-Mobile-3
+234,564,027,639,gb,United Kingdom,44,Vodafone
+234,564,009,159,gb,United Kingdom,44,Tismi
+234,564,025,607,gb,United Kingdom,44,Truphone
+234,564,051,1311,gb,United Kingdom,44,Jersey Telecom
+234,564,023,575,gb,United Kingdom,44,Vectofone Mobile Wifi
+234,564,015,351,gb,United Kingdom,44,Vodafone
+234,564,091,2335,gb,United Kingdom,44,Vodafone
+234,564,078,1935,gb,United Kingdom,44,Wave Telecom Ltd
+310,784,050,80,us,United States,1,
+310,784,880,2176,us,United States,1,
+310,784,850,2128,us,United States,1,Aeris Comm. Inc.
+310,784,640,1600,us,United States,1,
+310,784,510,1296,us,United States,1,Airtel Wireless LLC
+310,784,190,400,us,United States,1,Unknown
+312,786,090,144,us,United States,1,Allied Wireless Communications Corporation
+311,785,130,304,us,United States,1,
+311,785,030,48,us,United States,1,Americell PA3 LP
+310,784,710,1808,us,United States,1,Arctic Slope Telephone Association Cooperative Inc.
+310,784,380,896,us,United States,1,AT&T Wireless Inc.
+310,784,170,368,us,United States,1,AT&T Wireless Inc.2
+310,784,150,336,us,United States,1,AT&T Wireless Inc.3
+310,784,680,1664,us,United States,1,AT&T Wireless Inc.4
+310,784,070,112,us,United States,1,AT&T Wireless Inc.5
+310,784,560,1376,us,United States,1,AT&T Wireless Inc.6
+310,784,410,1040,us,United States,1,AT&T Wireless Inc.7
+310,784,980,2432,us,United States,1,AT&T Wireless Inc.8
+311,785,440,1088,us,United States,1,Bluegrass Wireless LLC
+311,785,810,2064,us,United States,1,Bluegrass Wireless LLC-2
+311,785,800,2048,us,United States,1,Bluegrass Wireless LLC-3
+310,784,900,2304,us,United States,1,Cable & Communications Corp.
+311,785,590,1424,us,United States,1,California RSA No. 3 Limited Partnership
+311,785,500,1280,us,United States,1,Cambridge Telephone Company Inc.
+310,784,830,2096,us,United States,1,Caprock Cellular Ltd.
+311,785,272,626,us,United States,1,Verizon Wireless
+311,785,288,648,us,United States,1,Verizon Wireless2
+311,785,277,631,us,United States,1,Verizon Wireless3
+311,785,482,1154,us,United States,1,Verizon Wireless4
+310,784,590,1424,us,United States,1,Verizon Wireless5
+311,785,282,642,us,United States,1,Verizon Wireless6
+311,785,487,1159,us,United States,1,Verizon Wireless7
+311,785,271,625,us,United States,1,Verizon Wireless8
+311,785,287,647,us,United States,1,Verizon Wireless9
+311,785,276,630,us,United States,1,Verizon Wireless10
+311,785,481,1153,us,United States,1,Verizon Wireless11
+310,784,013,19,us,United States,1,Verizon Wireless12
+311,785,281,641,us,United States,1,Verizon Wireless13
+311,785,486,1158,us,United States,1,Verizon Wireless14
+311,785,270,624,us,United States,1,Verizon Wireless15
+311,785,286,646,us,United States,1,Verizon Wireless16
+311,785,275,629,us,United States,1,Verizon Wireless17
+311,785,480,1152,us,United States,1,Verizon Wireless18
+310,784,012,18,us,United States,1,Verizon Wireless19
+311,785,280,640,us,United States,1,Verizon Wireless20
+311,785,485,1157,us,United States,1,Verizon Wireless21
+311,785,110,272,us,United States,1,Verizon Wireless22
+311,785,285,645,us,United States,1,Verizon Wireless23
+311,785,274,628,us,United States,1,Verizon Wireless24
+311,785,390,912,us,United States,1,Verizon Wireless25
+310,784,010,16,us,United States,1,Verizon Wireless26
+311,785,279,633,us,United States,1,Verizon Wireless27
+311,785,484,1156,us,United States,1,Verizon Wireless28
+310,784,910,2320,us,United States,1,Verizon Wireless29
+311,785,284,644,us,United States,1,Verizon Wireless30
+311,785,489,1161,us,United States,1,Verizon Wireless31
+311,785,273,627,us,United States,1,Verizon Wireless32
+311,785,289,649,us,United States,1,Verizon Wireless33
+310,784,004,4,us,United States,1,Verizon Wireless34
+311,785,278,632,us,United States,1,Verizon Wireless35
+311,785,483,1155,us,United States,1,Verizon Wireless36
+310,784,890,2192,us,United States,1,Verizon Wireless37
+311,785,283,643,us,United States,1,Verizon Wireless38
+311,785,488,1160,us,United States,1,Verizon Wireless39
+312,786,280,640,us,United States,1,Cellular Network Partnership LLC
+312,786,270,624,us,United States,1,Cellular Network Partnership LLC-2
+310,784,360,864,us,United States,1,Cellular Network Partnership LLC-3
+311,785,190,400,us,United States,1,
+310,784,230,560,us,United States,1,Cellular South Licenses Inc.
+310,784,030,48,us,United States,1,
+311,785,120,288,us,United States,1,Choice Phone LLC
+310,784,480,1152,us,United States,1,Choice Phone LLC-2
+310,784,630,1584,us,United States,1,
+310,784,420,1056,us,United States,1,Cincinnati Bell Wireless LLC
+310,784,180,384,us,United States,1,Cingular Wireless
+310,784,620,1568,us,United States,1,Coleman County Telco /Trans TX
+311,785,040,64,us,United States,1,
+310,784,006,6,us,United States,1,Consolidated Telcom
+310,784,060,1551,us,United States,1,Consolidated Telcom
+312,786,380,896,us,United States,1,
+310,784,930,2352,us,United States,1,
+311,785,240,576,us,United States,1,
+310,784,008,8,us,United States,1,Unknown
+310,784,080,128,us,United States,1,
+310,784,700,1792,us,United States,1,Cross Valliant Cellular Partnership
+311,785,140,320,us,United States,1,Cross Wireless Telephone Co.
+312,786,030,48,us,United States,1,Cross Wireless Telephone Co.2
+311,785,520,1312,us,United States,1,
+311,785,810,2064,us,United States,1,Cumberland Cellular Partnership
+311,785,800,2048,us,United States,1,Cumberland Cellular Partnership2
+311,785,440,1088,us,United States,1,Cumberland Cellular Partnership3
+312,786,040,64,us,United States,1,Custer Telephone Cooperative Inc.
+310,784,016,22,us,United States,1,Denali Spectrum License LLC
+310,784,440,1088,us,United States,1,Dobson Cellular Systems
+310,784,990,2448,us,United States,1,E.N.M.R. Telephone Coop.
+312,786,130,304,us,United States,1,East Kentucky Network LLC
+312,786,120,288,us,United States,1,East Kentucky Network LLC-2
+310,784,750,1872,us,United States,1,East Kentucky Network LLC-3
+310,784,009,9,us,United States,1,Edge Wireless LLC
+310,784,090,144,us,United States,1,Edge Wireless LLC
+310,784,610,1552,us,United States,1,Elkhart TelCo. / Epic Touch Co.
+311,785,210,528,us,United States,1,
+311,785,311,785,us,United States,1,Farmers
+311,785,460,1120,us,United States,1,Fisher Wireless Services Inc.
+311,785,370,880,us,United States,1,GCI Communication Corp.
+310,784,430,1072,us,United States,1,GCI Communication Corp.-2
+310,784,920,2336,us,United States,1,Get Mobile Inc.
+310,784,970,2416,us,United States,1,
+310,784,007,127,us,United States,1,Unknown
+311,785,250,592,us,United States,1,i CAN_GSM
+311,785,340,832,us,United States,1,Illinois Valley Cellular RSA 2 Partnership
+311,785,030,48,us,United States,1,
+312,786,170,368,us,United States,1,Iowa RSA No. 2 Limited Partnership
+311,785,410,1040,us,United States,1,Iowa RSA No. 2 Limited Partnership2
+310,784,770,1904,us,United States,1,Iowa Wireless Services LLC
+310,784,650,1616,us,United States,1,Jasper
+310,784,870,2160,us,United States,1,Kaplan Telephone Company Inc.
+311,785,810,2064,us,United States,1,Kentucky RSA #3 Cellular General Partnership
+311,785,800,2048,us,United States,1,Kentucky RSA #3 Cellular General Partnership2
+311,785,440,1088,us,United States,1,Kentucky RSA #3 Cellular General Partnership3
+311,785,440,1088,us,United States,1,Kentucky RSA #4 Cellular General Partnership4
+311,785,810,2064,us,United States,1,Kentucky RSA #4 Cellular General Partnership5
+311,785,800,2048,us,United States,1,Kentucky RSA #4 Cellular General Partnership6
+312,786,180,384,us,United States,1,Keystone Wireless LLC
+310,784,690,1680,us,United States,1,Keystone Wireless LLC-2
+311,785,310,784,us,United States,1,Lamar County Cellular
+310,784,016,22,us,United States,1,LCW Wireless Operations LLC
+310,784,016,22,us,United States,1,Leap Wireless International Inc.
+311,785,090,144,us,United States,1,
+310,784,040,64,us,United States,1,Matanuska Tel. Assn. Inc.
+310,784,780,1920,us,United States,1,Message Express Co. / Airlink PCS
+311,785,660,1632,us,United States,1,
+311,785,330,816,us,United States,1,Michigan Wireless LLC
+311,785,000,0,us,United States,1,
+311,785,390,912,us,United States,1,
+310,784,400,1024,us,United States,1,Minnesota South. Wirel. Co. / Hickory
+312,786,010,16,us,United States,1,Missouri RSA No 5 Partnership
+311,785,920,2336,us,United States,1,Missouri RSA No 5 Partnership2
+311,785,020,32,us,United States,1,Missouri RSA No 5 Partnership3
+311,785,010,16,us,United States,1,Missouri RSA No 5 Partnership4
+312,786,220,544,us,United States,1,Missouri RSA No 5 Partnership5
+310,784,350,848,us,United States,1,Mohave Cellular LP
+310,784,570,1392,us,United States,1,MTPCS LLC
+310,784,290,656,us,United States,1,NEP Cellcorp Inc.
+310,784,034,847,us,United States,1,Nevada Wireless LLC
+311,785,380,896,us,United States,1,
+310,784,600,1536,us,United States,1,New-Cell Inc.
+311,785,100,256,us,United States,1,
+311,785,300,768,us,United States,1,Nexus Communications Inc.
+310,784,130,304,us,United States,1,North Carolina RSA 3 Cellular Tel. Co.
+312,786,230,560,us,United States,1,North Dakota Network Company
+311,785,610,1552,us,United States,1,North Dakota Network Company2
+310,784,450,1104,us,United States,1,Northeast Colorado Cellular Inc.
+311,785,710,1808,us,United States,1,Northeast Wireless Networks LLC
+310,784,670,1648,us,United States,1,Northstar
+310,784,011,17,us,United States,1,Northstar
+311,785,420,1056,us,United States,1,Northwest Missouri Cellular Limited Partnership
+310,784,540,1344,us,United States,1,
+310,784,740,1856,us,United States,1,
+310,784,760,1888,us,United States,1,Panhandle Telephone Cooperative Inc.
+310,784,580,1408,us,United States,1,PCS ONE
+311,785,170,368,us,United States,1,PetroCom
+311,785,670,1648,us,United States,1,Pine Belt Cellular Inc.
+311,785,080,128,us,United States,1,
+310,784,790,1936,us,United States,1,
+310,784,100,256,us,United States,1,Plateau Telecommunications Inc.
+310,784,940,2368,us,United States,1,Poka Lambro Telco Ltd.
+311,785,540,1344,us,United States,1,
+311,785,730,1840,us,United States,1,
+310,784,500,1280,us,United States,1,Public Service Cellular Inc.
+312,786,160,352,us,United States,1,RSA 1 Limited Partnership
+311,785,430,1072,us,United States,1,RSA 1 Limited Partnership
+311,785,350,848,us,United States,1,Sagebrush Cellular Inc.
+311,785,030,48,us,United States,1,Sagir Inc.
+311,785,910,2320,us,United States,1,
+310,784,046,1135,us,United States,1,SIMMETRY
+311,785,260,608,us,United States,1,SLO Cellular Inc / Cellular One of San Luis
+310,784,320,800,us,United States,1,Smith Bagley Inc.
+310,784,015,351,us,United States,1,Unknown
+316,790,011,17,us,United States,1,Southern Communications Services Inc.
+310,784,002,2,us,United States,1,Sprint Spectrum
+312,786,190,400,us,United States,1,Sprint Spectrum
+311,785,880,2176,us,United States,1,Sprint Spectrum2
+311,785,870,2160,us,United States,1,Sprint Spectrum3
+311,785,490,1168,us,United States,1,Sprint Spectrum4
+310,784,120,288,us,United States,1,Sprint Spectrum5
+316,790,010,16,us,United States,1,Sprint Spectrum6
+310,784,031,799,us,United States,1,T-Mobile
+310,784,220,544,us,United States,1,T-Mobile2
+310,784,270,624,us,United States,1,T-Mobile3
+310,784,210,528,us,United States,1,T-Mobile4
+310,784,260,608,us,United States,1,T-Mobile5
+310,784,200,512,us,United States,1,T-Mobile6
+310,784,250,592,us,United States,1,T-Mobile7
+310,784,160,352,us,United States,1,T-Mobile8
+310,784,240,576,us,United States,1,T-Mobile9
+310,784,660,1632,us,United States,1,T-Mobile10
+310,784,230,560,us,United States,1,T-Mobile11
+310,784,300,768,us,United States,1,T-Mobile12
+310,784,280,640,us,United States,1,T-Mobile13
+310,784,330,816,us,United States,1,T-Mobile14
+310,784,800,2048,us,United States,1,T-Mobile15
+310,784,310,784,us,United States,1,T-Mobile16
+311,785,740,1856,us,United States,1,
+310,784,740,1856,us,United States,1,Telemetrix Inc.
+310,784,014,335,us,United States,1,Testing
+310,784,950,2384,us,United States,1,Unknown
+310,784,860,2144,us,United States,1,Texas RSA 15B2 Limited Partnership
+311,785,830,2096,us,United States,1,Thumb Cellular Limited Partnership
+311,785,050,80,us,United States,1,Thumb Cellular Limited Partnership2
+310,784,460,1120,us,United States,1,TMP Corporation
+310,784,490,1168,us,United States,1,Triton PCS
+312,786,290,656,us,United States,1,Uintah Basin Electronics Telecommunications Inc.
+311,785,860,2144,us,United States,1,Uintah Basin Electronics Telecommunications Inc.2
+310,784,960,2400,us,United States,1,Uintah Basin Electronics Telecommunications Inc.3
+310,784,020,32,us,United States,1,Union Telephone Co.
+311,785,220,544,us,United States,1,United States Cellular Corp.
+310,784,730,1840,us,United States,1,United States Cellular Corp.2
+311,785,650,1616,us,United States,1,United Wireless Communications Inc.
+310,784,038,911,us,United States,1,USA 3650 AT&T
+310,784,520,1312,us,United States,1,VeriSign
+310,784,003,3,us,United States,1,Unknown
+310,784,023,575,us,United States,1,Unknown2
+310,784,024,591,us,United States,1,Unknown3
+310,784,025,607,us,United States,1,Unknown4
+310,784,530,1328,us,United States,1,West Virginia Wireless
+310,784,026,623,us,United States,1,Unknown
+310,784,340,832,us,United States,1,Westlink Communications LLC
+311,785,150,336,us,United States,1,
+311,785,070,112,us,United States,1,Wisconsin RSA #7 Limited Partnership
+310,784,390,912,us,United States,1,Yorkville Telephone Cooperative
+748,1864,001,31,uy,Uruguay,598,Ancel/Antel
+748,1864,003,63,uy,Uruguay,598,Ancel/Antel
+748,1864,010,271,uy,Uruguay,598,Claro/AM Wireless
+748,1864,007,127,uy,Uruguay,598,MOVISTAR
+434,1076,004,79,uz,Uzbekistan,998,Bee Line/Unitel
+434,1076,001,1,uz,Uzbekistan,998,Buztel
+434,1076,007,127,uz,Uzbekistan,998,MTS/Uzdunrobita
+434,1076,005,95,uz,Uzbekistan,998,Ucell/Coscom
+434,1076,002,2,uz,Uzbekistan,998,Uzmacom
+541,1345,005,95,vu,Vanuatu,678,DigiCel
+541,1345,000,0,vu,Vanuatu,678,DigiCel2
+541,1345,001,31,vu,Vanuatu,678,SMILE
+734,1844,003,3,ve,Venezuela,58,DigiTel C.A.
+734,1844,002,47,ve,Venezuela,58,DigiTel C.A.2
+734,1844,001,1,ve,Venezuela,58,DigiTel C.A.3
+734,1844,006,111,ve,Venezuela,58,Movilnet C.A.
+734,1844,004,79,ve,Venezuela,58,Movistar/TelCel
+452,1106,007,127,vn,Viet Nam,84,Beeline
+452,1106,001,31,vn,Viet Nam,84,Mobifone
+452,1106,003,63,vn,Viet Nam,84,S-Fone/Telecom
+452,1106,005,95,vn,Viet Nam,84,VietnaMobile
+452,1106,008,143,vn,Viet Nam,84,Viettel Mobile
+452,1106,006,111,vn,Viet Nam,84,Viettel Mobile2
+452,1106,004,79,vn,Viet Nam,84,Viettel Mobile3
+452,1106,002,47,vn,Viet Nam,84,Vinaphone
+376,886,350,848,vi,Virgin Islands U.S.,1340,Cable & Wireless (Turks & Caicos)
+376,886,050,1295,vi,Virgin Islands U.S.,1340,Digicel
+376,886,352,850,vi,Virgin Islands U.S.,1340,IslandCom
+421,1057,004,79,ye,Yemen,967,HITS/Y Unitel
+421,1057,002,47,ye,Yemen,967,MTN/Spacetel
+421,1057,001,31,ye,Yemen,967,Sabaphone
+421,1057,003,63,ye,Yemen,967,Yemen Mob. CDMA
+645,1605,003,63,zm,Zambia,260,Cell Z/MTS
+645,1605,002,47,zm,Zambia,260,MTN/Telecel
+645,1605,001,31,zm,Zambia,260,Zain/Celtel
+648,1608,004,79,zw,Zimbabwe,263,Econet
+648,1608,001,31,zw,Zimbabwe,263,Net One
+648,1608,003,63,zw,Zimbabwe,263,Telecel
diff --git a/src/protocol.class.php b/src/protocol.class.php
index ed352483..3ed24ad6 100755
--- a/src/protocol.class.php
+++ b/src/protocol.class.php
@@ -1,5 +1,4 @@
input;
}
-
}
class ProtocolNode
@@ -37,15 +35,11 @@ class ProtocolNode
*/
private static function isCli()
{
- if(self::$cli === null)
- {
+ if (self::$cli === null) {
//initial setter
- if(php_sapi_name() == "cli")
- {
+ if (php_sapi_name() == "cli") {
self::$cli = true;
- }
- else
- {
+ } else {
self::$cli = false;
}
}
@@ -86,15 +80,15 @@ public function getChildren()
public function __construct($tag, $attributeHash, $children, $data)
{
- $this->tag = $tag;
+ $this->tag = $tag;
$this->attributeHash = $attributeHash;
- $this->children = $children;
- $this->data = $data;
+ $this->children = $children;
+ $this->data = $data;
}
/**
* @param string $indent
- * @param bool $isChild
+ * @param bool $isChild
* @return string
*/
public function nodeString($indent = "", $isChild = false)
@@ -103,11 +97,10 @@ public function nodeString($indent = "", $isChild = false)
$lt = "<";
$gt = ">";
$nl = "\n";
- if(!self::isCli())
- {
- $lt = "<";
- $gt = ">";
- $nl = "
";
+ if ( ! self::isCli()) {
+ $lt = "<";
+ $gt = ">";
+ $nl = "
";
$indent = str_replace(" ", " ", $indent);
}
@@ -136,13 +129,11 @@ public function nodeString($indent = "", $isChild = false)
$ret .= implode($nl, $foo);
$ret .= $nl . $indent;
}
- $ret .= $lt . "/" . $this->tag . $gt;
+ $ret .= $lt . "/" . $this->tag . $gt;
- if(!$isChild)
- {
+ if ( ! $isChild) {
$ret .= $nl;
- if(!self::isCli())
- {
+ if ( ! self::isCli()) {
$ret .= $nl;
}
}
@@ -182,14 +173,10 @@ public function getChild($tag)
{
$ret = null;
if ($this->children) {
- if(is_int($tag))
- {
- if(isset($this->children[$tag]))
- {
+ if (is_int($tag)) {
+ if (isset($this->children[$tag])) {
return $this->children[$tag];
- }
- else
- {
+ } else {
return null;
}
}
@@ -222,17 +209,16 @@ public function hasChild($tag)
public function refreshTimes($offset = 0)
{
if (isset($this->attributeHash['id'])) {
- $id = $this->attributeHash['id'];
- $parts = explode('-', $id);
- $parts[0] = time() + $offset;
+ $id = $this->attributeHash['id'];
+ $parts = explode('-', $id);
+ $parts[0] = time() + $offset;
$this->attributeHash['id'] = implode('-', $parts);
}
if (isset($this->attributeHash['t'])) {
$this->attributeHash['t'] = time();
}
}
-
-
+
/**
* Print human readable ProtocolNode object
*
@@ -247,462 +233,6 @@ public function __toString()
'data' => $this->data
);
- return print_r( $readableNode, true );
- }
-
-}
-
-class BinTreeNodeReader
-{
- private $input;
- /** @var $key KeyStream */
- private $key;
-
- public function resetKey()
- {
- $this->key = null;
- }
-
- public function setKey($key)
- {
- $this->key = $key;
- }
-
- public function nextTree($input = null)
- {
- if ($input != null) {
- $this->input = $input;
- }
- $firstByte = $this->peekInt8();
- $stanzaFlag = ($firstByte & 0xF0) >> 4;
- $stanzaSize = $this->peekInt16(1) | (($firstByte & 0x0F) << 16);
- if ($stanzaSize > strlen($this->input)) {
- throw new Exception("Incomplete message $stanzaSize != " . strlen($this->input));
- }
- $this->readInt24();
- if ($stanzaFlag & 8) {
- if (isset($this->key)) {
- $realSize = $stanzaSize - 4;
- $this->input = $this->key->DecodeMessage($this->input, $realSize, 0, $realSize);// . $remainingData;
- } else {
- throw new Exception("Encountered encrypted message, missing key");
- }
- }
- if ($stanzaSize > 0) {
- return $this->nextTreeInternal();
- }
-
- return null;
- }
-
- protected function getToken($token)
- {
- $ret = "";
- $subdict = false;
- TokenMap::GetToken($token, $subdict, $ret);
- if(!$ret)
- {
- $token = $this->readInt8();
- TokenMap::GetToken($token, $subdict, $ret);
- if(!$ret)
- {
- throw new Exception("BinTreeNodeReader->getToken: Invalid token $token");
- }
- }
- return $ret;
- }
-
- protected function readString($token)
- {
- $ret = "";
- if ($token == -1) {
- throw new Exception("BinTreeNodeReader->readString: Invalid token $token");
- }
- if (($token > 4) && ($token < 0xf5)) {
- $ret = $this->getToken($token);
- } elseif ($token == 0) {
- $ret = "";
- } elseif ($token == 0xfc) {
- $size = $this->readInt8();
- $ret = $this->fillArray($size);
- } elseif ($token == 0xfd) {
- $size = $this->readInt24();
- $ret = $this->fillArray($size);
- } elseif ($token == 0xfe) {
- $token = $this->readInt8();
- $ret = $this->getToken($token + 0xf5);
- } elseif ($token == 0xfa) {
- $user = $this->readString($this->readInt8());
- $server = $this->readString($this->readInt8());
- if ((strlen($user) > 0) && (strlen($server) > 0)) {
- $ret = $user . "@" . $server;
- } elseif (strlen($server) > 0) {
- $ret = $server;
- }
- }
-
- return $ret;
+ return print_r($readableNode, true);
}
-
- protected function readAttributes($size)
- {
- $attributes = array();
- $attribCount = ($size - 2 + $size % 2) / 2;
- for ($i = 0; $i < $attribCount; $i++) {
- $key = $this->readString($this->readInt8());
- $value = $this->readString($this->readInt8());
- $attributes[$key] = $value;
- }
-
- return $attributes;
- }
-
- protected function nextTreeInternal()
- {
- $token = $this->readInt8();
- $size = $this->readListSize($token);
- $token = $this->readInt8();
- if ($token == 1) {
- $attributes = $this->readAttributes($size);
-
- return new ProtocolNode("start", $attributes, null, "");
- } elseif ($token == 2) {
- return null;
- }
- $tag = $this->readString($token);
- $attributes = $this->readAttributes($size);
- if (($size % 2) == 1) {
- return new ProtocolNode($tag, $attributes, null, "");
- }
- $token = $this->readInt8();
- if ($this->isListTag($token)) {
- return new ProtocolNode($tag, $attributes, $this->readList($token), "");
- }
-
- return new ProtocolNode($tag, $attributes, null, $this->readString($token));
- }
-
- protected function isListTag($token)
- {
- return (($token == 248) || ($token == 0) || ($token == 249));
- }
-
- protected function readList($token)
- {
- $size = $this->readListSize($token);
- $ret = array();
- for ($i = 0; $i < $size; $i++) {
- array_push($ret, $this->nextTreeInternal());
- }
-
- return $ret;
- }
-
- protected function readListSize($token)
- {
- $size = 0;
- if ($token == 0xf8) {
- $size = $this->readInt8();
- } elseif ($token == 0xf9) {
- $size = $this->readInt16();
- } else {
- throw new Exception("BinTreeNodeReader->readListSize: Invalid token $token");
- }
-
- return $size;
- }
-
- protected function peekInt24($offset = 0)
- {
- $ret = 0;
- if (strlen($this->input) >= (3 + $offset)) {
- $ret = ord(substr($this->input, $offset, 1)) << 16;
- $ret |= ord(substr($this->input, $offset + 1, 1)) << 8;
- $ret |= ord(substr($this->input, $offset + 2, 1)) << 0;
- }
-
- return $ret;
- }
-
- protected function readInt24()
- {
- $ret = $this->peekInt24();
- if (strlen($this->input) >= 3) {
- $this->input = substr($this->input, 3);
- }
-
- return $ret;
- }
-
- protected function peekInt16($offset = 0)
- {
- $ret = 0;
- if (strlen($this->input) >= (2 + $offset)) {
- $ret = ord(substr($this->input, $offset, 1)) << 8;
- $ret |= ord(substr($this->input, $offset + 1, 1)) << 0;
- }
-
- return $ret;
- }
-
- protected function readInt16()
- {
- $ret = $this->peekInt16();
- if ($ret > 0) {
- $this->input = substr($this->input, 2);
- }
-
- return $ret;
- }
-
- protected function peekInt8($offset = 0)
- {
- $ret = 0;
- if (strlen($this->input) >= (1 + $offset)) {
- $sbstr = substr($this->input, $offset, 1);
- $ret = ord($sbstr);
- }
-
- return $ret;
- }
-
- protected function readInt8()
- {
- $ret = $this->peekInt8();
- if (strlen($this->input) >= 1) {
- $this->input = substr($this->input, 1);
- }
-
- return $ret;
- }
-
- protected function fillArray($len)
- {
- $ret = "";
- if (strlen($this->input) >= $len) {
- $ret = substr($this->input, 0, $len);
- $this->input = substr($this->input, $len);
- }
-
- return $ret;
- }
-
-}
-
-class BinTreeNodeWriter
-{
- private $output;
- /** @var $key KeyStream */
- private $key;
-
- public function resetKey()
- {
- $this->key = null;
- }
-
- public function setKey($key)
- {
- $this->key = $key;
- }
-
- public function StartStream($domain, $resource)
- {
- $attributes = array();
- $header = "WA";
- $header .= $this->writeInt8(1);
- $header .= $this->writeInt8(4);
-
- $attributes["to"] = $domain;
- $attributes["resource"] = $resource;
- $this->writeListStart(count($attributes) * 2 + 1);
-
- $this->output .= "\x01";
- $this->writeAttributes($attributes);
- $ret = $header . $this->flushBuffer();
-
- return $ret;
- }
-
- /**
- * @param ProtocolNode $node
- * @return string
- */
- public function write($node, $encrypt = true)
- {
- if ($node == null) {
- $this->output .= "\x00";
- } else {
- $this->writeInternal($node);
- }
-
- return $this->flushBuffer($encrypt);
- }
-
- /**
- * @param ProtocolNode $node
- */
- protected function writeInternal($node)
- {
- $len = 1;
- if ($node->getAttributes() != null) {
- $len += count($node->getAttributes()) * 2;
- }
- if (count($node->getChildren()) > 0) {
- $len += 1;
- }
- if (strlen($node->getData()) > 0) {
- $len += 1;
- }
- $this->writeListStart($len);
- $this->writeString($node->getTag());
- $this->writeAttributes($node->getAttributes());
- if (strlen($node->getData()) > 0) {
- $this->writeBytes($node->getData());
- }
- if ($node->getChildren()) {
- $this->writeListStart(count($node->getChildren()));
- foreach ($node->getChildren() as $child) {
- $this->writeInternal($child);
- }
- }
- }
-
- protected function parseInt24($data)
- {
- $ret = ord(substr($data, 0, 1)) << 16;
- $ret |= ord(substr($data, 1, 1)) << 8;
- $ret |= ord(substr($data, 2, 1)) << 0;
- return $ret;
- }
-
- protected function flushBuffer($encrypt = true)
- {
- $size = strlen($this->output);
- $data = $this->output;
- if($this->key != null && $encrypt)
- {
- $bsize = $this->getInt24($size);
- //encrypt
- $data = $this->key->EncodeMessage($data, $size, 0, $size);
- $len = strlen($data);
- $bsize[0] = chr((8 << 4) | (($len & 16711680) >> 16));
- $bsize[1] = chr(($len & 65280) >> 8);
- $bsize[2] = chr($len & 255);
- $size = $this->parseInt24($bsize);
- }
- $ret = $this->writeInt24($size) . $data;
- $this->output = '';
- return $ret;
- }
-
- protected function getInt24($length)
- {
- $ret = '';
- $ret .= chr((($length & 0xf0000) >> 16));
- $ret .= chr((($length & 0xff00) >> 8));
- $ret .= chr(($length & 0xff));
- return $ret;
- }
-
- protected function writeToken($token)
- {
- if ($token < 0xf5) {
- $this->output .= chr($token);
- } elseif ($token <= 0x1f4) {
- $this->output .= "\xfe" . chr($token - 0xf5);
- }
- }
-
- protected function writeJid($user, $server)
- {
- $this->output .= "\xfa";
- if (strlen($user) > 0) {
- $this->writeString($user);
- } else {
- $this->writeToken(0);
- }
- $this->writeString($server);
- }
-
- protected function writeInt8($v)
- {
- $ret = chr($v & 0xff);
-
- return $ret;
- }
-
- protected function writeInt16($v)
- {
- $ret = chr(($v & 0xff00) >> 8);
- $ret .= chr(($v & 0x00ff) >> 0);
-
- return $ret;
- }
-
- protected function writeInt24($v)
- {
- $ret = chr(($v & 0xff0000) >> 16);
- $ret .= chr(($v & 0x00ff00) >> 8);
- $ret .= chr(($v & 0x0000ff) >> 0);
-
- return $ret;
- }
-
- protected function writeBytes($bytes)
- {
- $len = strlen($bytes);
- if ($len >= 0x100) {
- $this->output .= "\xfd";
- $this->output .= $this->writeInt24($len);
- } else {
- $this->output .= "\xfc";
- $this->output .= $this->writeInt8($len);
- }
- $this->output .= $bytes;
- }
-
- protected function writeString($tag)
- {
- $intVal = -1;
- $subdict = false;
- if(TokenMap::TryGetToken($tag, $subdict, $intVal))
- {
- if($subdict)
- {
- $this->writeToken(236);
- }
- $this->writeToken($intVal);
- return;
- }
- $index = strpos($tag, '@');
- if ($index) {
- $server = substr($tag, $index + 1);
- $user = substr($tag, 0, $index);
- $this->writeJid($user, $server);
- } else {
- $this->writeBytes($tag);
- }
- }
-
- protected function writeAttributes($attributes)
- {
- if ($attributes) {
- foreach ($attributes as $key => $value) {
- $this->writeString($key);
- $this->writeString($value);
- }
- }
- }
-
- protected function writeListStart($len)
- {
- if ($len == 0) {
- $this->output .= "\x00";
- } elseif ($len < 256) {
- $this->output .= "\xf8" . chr($len);
- } else {
- $this->output .= "\xf9" . $this->writeInt16($len);
- }
- }
-
}
diff --git a/src/rc4.php b/src/rc4.php
index c882e21e..d8bb8e8b 100755
--- a/src/rc4.php
+++ b/src/rc4.php
@@ -1,5 +1,4 @@
i = ($this->i + 1) & 0xff;
$this->j = ($this->j + $this->s[$this->i]) & 0xff;
$this->swap($this->i, $this->j);
- $d = ord($data{$offset});
+ $d = ord($data{$offset});
$out[$offset] = chr($d ^ $this->s[($this->s[$this->i] + $this->s[$this->j]) & 0xff]);
$offset++;
}
@@ -37,11 +36,10 @@ public function cipher($data, $offset, $length)
protected function swap($i, $j)
{
- $c = $this->s[$i];
+ $c = $this->s[$i];
$this->s[$i] = $this->s[$j];
$this->s[$j] = $c;
}
-
}
// DEPRECATED: WAUTH-1
diff --git a/src/smileys.class.php b/src/smileys.class.php
new file mode 100644
index 00000000..65b4bb99
--- /dev/null
+++ b/src/smileys.class.php
@@ -0,0 +1,905 @@
+SMILEY_XXX where XXX is the index):
+ * $s = new Smileys;
+ * echo $s->GRINNING_FACE_WITH_SMILING_EYES;
+ * // or
+ * echo $s->SMILEY_23;
+ *
+ * Complete list with images:
+ * http://www.typografie.info/3/page/artikel.htm/_/wissen/unicode-emoji-deutsch
+ * http://appamatix.com/843-whatsapp-emoticons-meanings-emoji-list/
+ */
+
+class SmileyNotFoundException extends Exception {};
+
+class Smileys {
+ private $listSmileys = array(
+ 'SMILING_FACE_WITH_OPEN_MOUTH_AND_SMILING_EYES' => 0x1F604, // Smiley 1
+ 'SMILING_FACE_WITH_OPEN_MOUTH' => 0x1F603, // Smiley 2
+ 'GRINNING_FACE_WITH_SMILING_EYES' => 0x1F600, // Smiley 3
+ 'SMILING_FACE_WITH_SMILING_EYES' => 0x1F60A, // Smiley 4
+ 'WHITE_SMILING_FACE' => 0x263A, // Smiley 5
+ 'WINKING_FACE' => 0x1F609, // Smiley 6
+ 'SMILING_FACE_WITH_HEART_SHAPED_EYES' => 0x1F60D, // Smiley 7
+ 'FACE_THROWING_A_KISS' => 0x1F618, // Smiley 8
+ 'KISSING_FACE_WITH_CLOSED_EYES' => 0x1F61A, // Smiley 9
+ 'KISSING_FACE' => 0x1F617, // Smiley 10
+ 'KISSING_FACE_WITH_SMILING_EYES' => 0x1F619, // Smiley 11
+ 'FACE_WITH_STUCK_OUT_TONGUE_AND_WINKING_EYE' => 0x1F61C, // Smiley 12
+ 'FACE_WITH_STUCK_OUT_TONGUE_AND_TIGHTLY_CLOSED_EYES' => 0x1F61D, // Smiley 13
+ 'FACE_WITH_STUCK_OUT_TONGUE' => 0x1F61B, // Smiley 14
+ 'FLUSHED_FACE' => 0x1F633, // Smiley 15
+ 'GRINNING_FACE_WITH_SMILING_EYES' => 0x1F601, // Smiley 16
+ 'PENSIVE_FACE' => 0x1F614, // Smiley 17
+ 'RELIEVED_FACE' => 0x1F60C, // Smiley 18
+ 'UNAMUSED_FACE' => 0x1F612, // Smiley 19
+ 'DISAPPOINTED_FACE' => 0x1F61E, // Smiley 20
+ 'PERSEVERING_FACE' => 0x1F623, // Smiley 21
+ 'CRYING_FACE' => 0x1F622, // Smiley 22
+ 'FACE_WITH_TEARS_OF_JOY' => 0x1F602, // Smiley 23
+ 'LOUDLY_CRYING_FACE' => 0x1F62D, // Smiley 24
+ 'SLEEPY_FACE' => 0x1F62A, // Smiley 25
+ 'DISAPPOINTED_BUT_RELIEVED_FACE' => 0x1F625, // Smiley 26
+ 'FACE_WITH_OPEN_MOUTH_AND_COLD_SWEAT' => 0x1F630, // Smiley 27
+ 'SMILING_FACE_WITH_OPEN_MOUTH_AND_COLD_SWEAT' => 0x1F605, // Smiley 28
+ 'FACE_WITH_COLD_SWEAT' => 0x1F613, // Smiley 29
+ 'WEARY_FACE' => 0x1F629, // Smiley 30
+ 'TIRED_FACE' => 0x1F62B, // Smiley 31
+ 'FEARFUL_FACE' => 0x1F628, // Smiley 32
+ 'FACE_SCREAMING_IN_FEAR' => 0x1F631, // Smiley 33
+ 'ANGRY_FACE' => 0x1F620, // Smiley 34
+ 'POUTING_FACE' => 0x1F621, // Smiley 35
+ 'FACE_WITH_LOOK_OF_TRIUMPH' => 0x1F624, // Smiley 36
+ 'CONFOUNDED_FACE' => 0x1F616, // Smiley 37
+ 'SMILING_FACE_WITH_OPEN_MOUTH_AND_TIGHTLY_CLOSED_EYES' => 0x1F606, // Smiley 38
+ 'FACE_SAVOURING_DELICIOUS_FOOD' => 0x1F60B, // Smiley 39
+ 'FACE_WITH_MEDICAL_MASK' => 0x1F637, // Smiley 40
+ 'SMILING_FACE_WITH_SUNGLASSES' => 0x1F60E, // Smiley 41
+ 'SLEEPING_FACE' => 0x1F634, // Smiley 42
+ 'DIZZY_FACE' => 0x1F635, // Smiley 43
+ 'ASTONISHED_FACE' => 0x1F632, // Smiley 44
+ 'WORRIED_FACE' => 0x1F61F, // Smiley 45
+ 'FROWNING_FACE_WITH_OPEN_MOUTH' => 0x1F626, // Smiley 46
+ 'ANGUISHED_FACE' => 0x1F627, // Smiley 47
+ 'SMILING_FACE_WITH_HORNS' => 0x1F608, // Smiley 48
+ 'IMP' => 0x1F47F, // Smiley 49
+ 'FACE_WITH_OPEN_MOUTH' => 0x1F62E, // Smiley 50
+ 'GRIMACING_FACE' => 0x1F62C, // Smiley 51
+ 'NEUTRAL_FACE' => 0x1F610, // Smiley 52
+ 'CONFUSED_FACE' => 0x1F615, // Smiley 53
+ 'HUSHED_FACE' => 0x1F62F, // Smiley 54
+ 'FACE_WITHOUT_MOUTH' => 0x1F636, // Smiley 55
+ 'SMILING_FACE_WITH_HALO' => 0x1F607, // Smiley 56
+ 'SMIRKING_FACE' => 0x1F60F, // Smiley 57
+ 'EXPRESSIONLESS_FACE' => 0x1F611, // Smiley 58
+ 'MAN_WITH_GUA_PI_MAO' => 0x1F472, // Smiley 59
+ 'MAN_WITH_TURBAN' => 0x1F473, // Smiley 60
+ 'POLICE_OFFICER' => 0x1F46E, // Smiley 61
+ 'CONSTRUCTION_WORKER' => 0x1F477, // Smiley 62
+ 'GUARDSMAN' => 0x1F482, // Smiley 63
+ 'BABY' => 0x1F476, // Smiley 64
+ 'BOY' => 0x1F466, // Smiley 65
+ 'GIRL' => 0x1F467, // Smiley 66
+ 'MAN' => 0x1F468, // Smiley 67
+ 'WOMAN' => 0x1F469, // Smiley 68
+ 'OLDER_MAN' => 0x1F474, // Smiley 69
+ 'OLDER_WOMAN' => 0x1F475, // Smiley 70
+ 'PERSON_WITH_BLONDE_HAIR' => 0x1F471, // Smiley 71
+ 'BABY_ANGEL' => 0x1F47C, // Smiley 72
+ 'PRINCESS' => 0x1F478, // Smiley 73
+ 'SMILING_CAT_FACE_WITH_OPEN_MOUTH' => 0x1F63A, // Smiley 74
+ 'GRINNING_CAT_FACE_WITH_SMILING_EYES' => 0x1F638, // Smiley 75
+ 'SMILING_FACE_FACE_WITH_HEART_SHAPED_EYES' => 0x1F63B, // Smiley 76
+ 'KISSING_CAT_FACE_WITH_CLOSED_EYES' => 0x1F63D, // Smiley 77
+ 'CAT_FACE_WITH_WRY_SMILE' => 0x1F63C, // Smiley 78
+ 'WEARY_CAT_FACE' => 0x1F640, // Smiley 79
+ 'CRYING_CAT_FACE' => 0x1F63F, // Smiley 80
+ 'CAT_FACE_WITH_TEARS_OF_JOY' => 0x1F639, // Smiley 81
+ 'POUTING_CAT_FACE' => 0x1F63E, // Smiley 82
+ 'JAPANESE_OGRE' => 0x1F479, // Smiley 83
+ 'JAPANESE_GOBLIN' => 0x1F47A, // Smiley 84
+ 'SEE_NO_EVIL_MONKEY' => 0x1F648, // Smiley 85
+ 'HEAR_NO_EVIL_MONKEY' => 0x1F649, // Smiley 86
+ 'SPEAK_NO_EVIL_MONKEY' => 0x1F64A, // Smiley 87
+ 'SKULL' => 0x1F480, // Smiley 88
+ 'EXTRATERRESTRIAL_ALIEN' => 0x1F47D, // Smiley 89
+ 'PILE_OF_POO' => 0x1F4A9, // Smiley 90
+ 'FIRE' => 0x1F525, // Smiley 91
+ 'SPARKLES' => 0x2728, // Smiley 92
+ 'GLOWING_STAR' => 0x1F31F, // Smiley 93
+ 'DIZZY_SYMBOL' => 0x1F4AB, // Smiley 94
+ 'COLLISION_SYMBOL' => 0x1F4A5, // Smiley 95
+ 'ANGER_SYMBOL' => 0x1F4A2, // Smiley 96
+ 'SPLASHING_SWEAT_SYMBOL' => 0x1F4A6, // Smiley 97
+ 'DROPLET' => 0x1F4A7, // Smiley 98
+ 'SLEEPING_SYMBOL' => 0x1F4A4, // Smiley 99
+ 'DASH_SYMBOL' => 0x1F4A8, // Smiley 100
+ 'EAR' => 0x1F442, // Smiley 101
+ 'EYES' => 0x1F440, // Smiley 102
+ 'NOSE' => 0x1F443, // Smiley 103
+ 'TONGUE' => 0x1F445, // Smiley 104
+ 'MOUTH' => 0x1F444, // Smiley 105
+ 'THUMBS_UP_SIGN' => 0x1F44D, // Smiley 106
+ 'THUMBS_DOWN_SIGN' => 0x1F44E, // Smiley 107
+ 'OK_HAND_SIGN' => 0x1F44C, // Smiley 108
+ 'FISTED_HAND_SIGN' => 0x1F44A, // Smiley 109
+ 'RAISED_FIST' => 0x270A, // Smiley 110
+ 'VICTORY_HAND' => 0x270C, // Smiley 111
+ 'WAVING_HAND_SIGN' => 0x1F44B, // Smiley 112
+ 'RAISED_HAND' => 0x270B, // Smiley 113
+ 'OPEN_HANDS_SIGN' => 0x1F450, // Smiley 114
+ 'WHITE_UP_POINTING_BACKHAND_INDEX' => 0x1F446, // Smiley 115
+ 'WHITE_DOWN_POINTING_BACKHAND_INDEX' => 0x1F447, // Smiley 116
+ 'WHITE_RIGHT_POINTING_BACKHAND_INDEX' => 0x1F449, // Smiley 117
+ 'WHITE_LEFT_POINTING_BACKHAND_INDEX' => 0x1F448, // Smiley 118
+ 'PERSON_RAISING_BOTH_HANDS_IN_CELEBRATION' => 0x1F64C, // Smiley 119
+ 'PERSON_WITH_FOLDED_HANDS' => 0x1F64F, // Smiley 120
+ 'WHITE_UP_POINTING_INDEX' => 0x261D, // Smiley 121
+ 'CLAPPING_HANDS_SIGN' => 0x1F44F, // Smiley 122
+ 'FLEXED_BICEPS' => 0x1F4AA, // Smiley 123
+ 'PEDESTRIAN' => 0x1F6B6, // Smiley 124
+ 'RUNNER' => 0x1F3C3, // Smiley 125
+ 'DANCER' => 0x1F483, // Smiley 126
+ 'MAN_AND_WOMAN_HOLDING_HANDS' => 0x1F46B, // Smiley 127
+ 'FAMILY' => 0x1F46A, // Smiley 128
+ 'TWO_MEN_HOLDING_HANDS' => 0x1F46C, // Smiley 129
+ 'TWO_WOMEN_HOLDING_HANDS' => 0x1F46D, // Smiley 130
+ 'KISS' => 0x1F48F, // Smiley 131
+ 'COUPLE_WITH_HEART' => 0x1F491, // Smiley 132
+ 'WOMEN_WITH_BUNNY_EARS' => 0x1F46F, // Smiley 133
+ 'FACE_WITH_OK_GESTURE' => 0x1F646, // Smiley 134
+ 'FACE_WITH_NO_GOOD_GESTURE' => 0x1F645, // Smiley 135
+ 'INFORMATION_DESK_PERSON' => 0x1F481, // Smiley 136
+ 'HAPPY_PERSON_RAISING_ONE_HAND' => 0x1F64B, // Smiley 137
+ 'FACE_MASSAGE' => 0x1F486, // Smiley 138
+ 'HAIRCUT' => 0x1F487, // Smiley 139
+ 'NAIL_POLISH' => 0x1F485, // Smiley 140
+ 'BRIDE_WITH_VEIL' => 0x1F470, // Smiley 141
+ 'PERSON_WITH_POUTING_FACE' => 0x1F64E, // Smiley 142
+ 'PERSON_FROWNING' => 0x1F64D, // Smiley 143
+ 'PERSON_BOWING_DEEPLY' => 0x1F647, // Smiley 144
+ 'TOP_HAT' => 0x1F3A9, // Smiley 145
+ 'CROWN' => 0x1F451, // Smiley 146
+ 'WOMANS_HAT' => 0x1F452, // Smiley 147
+ 'ATHLETIC_SHOE' => 0x1F45F, // Smiley 148
+ 'MENS_SHOE' => 0x1F45E, // Smiley 149
+ 'WOMANS_SANDAL' => 0x1F461, // Smiley 150
+ 'HIGH_HEELED_SHOE' => 0x1F460, // Smiley 151
+ 'WOMANS_BOOTS' => 0x1F462, // Smiley 152
+ 'T_SHIRT' => 0x1F455, // Smiley 153
+ 'NECKTIE' => 0x1F454, // Smiley 154
+ 'WOMANS_CLOTHES' => 0x1F45A, // Smiley 155
+ 'DRESS' => 0x1F457, // Smiley 156
+ 'RUNNING_SHIRT_WITH_SASH' => 0x1F3BD, // Smiley 157
+ 'JEANS' => 0x1F456, // Smiley 158
+ 'KIMONO' => 0x1F458, // Smiley 159
+ 'BIKINI' => 0x1F459, // Smiley 160
+ 'BRIEFCASE' => 0x1F4BC, // Smiley 161
+ 'HANDBAG' => 0x1F45C, // Smiley 162
+ 'POUCH' => 0x1F45D, // Smiley 163
+ 'PURSE' => 0x1F45B, // Smiley 164
+ 'EYEGLASSES' => 0x1F453, // Smiley 165
+ 'RIBBON' => 0x1F380, // Smiley 166
+ 'CLOSED_UMBRELLA' => 0x1F302, // Smiley 167
+ 'LIPSTICK' => 0x1F484, // Smiley 168
+ 'YELLOW_HEART' => 0x1F49B, // Smiley 169
+ 'BLUE_HEART' => 0x1F499, // Smiley 170
+ 'PURPLE_HEART' => 0x1F49C, // Smiley 171
+ 'GREEN_HEART' => 0x1F49A, // Smiley 172
+ 'HEAVY_BLACK_HEART' => 0x2764, // Smiley 173
+ 'BROKEN_HEART' => 0x1F494, // Smiley 174
+ 'GROWING_HEART' => 0x1F497, // Smiley 175
+ 'BEATING_HEART' => 0x1F493, // Smiley 176
+ 'TWO_HEARTS' => 0x1F495, // Smiley 177
+ 'SPARKLING_HEARTS' => 0x1F496, // Smiley 178
+ 'REVOLVING_HEARTS' => 0x1F49E, // Smiley 179
+ 'HEART_WITH_ARROW' => 0x1F498, // Smiley 180
+ 'LOVE_LETTER' => 0x1F48C, // Smiley 181
+ 'KISS_MARK' => 0x1F48B, // Smiley 182
+ 'RING' => 0x1F48D, // Smiley 183
+ 'GEM_STONE' => 0x1F48E, // Smiley 184
+ 'BUST_IN_SILHOUETTE' => 0x1F464, // Smiley 185
+ 'BUSTS_IN_SILHOUETTE' => 0x1F465, // Smiley 186
+ 'SPEECH_BALLOON' => 0x1F4AC, // Smiley 187
+ 'FOOTPRINTS' => 0x1F463, // Smiley 188
+ 'THOUGHT_BALLOON' => 0x1F4AD, // Smiley 189
+ 'DOG_FACE' => 0x1F436, // Smiley 190
+ 'WOLF_FACE' => 0x1F43A, // Smiley 191
+ 'CAT_FACE' => 0x1F431, // Smiley 192
+ 'MOUSE_FACE' => 0x1F42D, // Smiley 193
+ 'HAMSTER_FACE' => 0x1F439, // Smiley 194
+ 'RABBIT_FACE' => 0x1F430, // Smiley 195
+ 'FROG_FACE' => 0x1F438, // Smiley 196
+ 'TIGER_FACE' => 0x1F42F, // Smiley 197
+ 'KOALA' => 0x1F428, // Smiley 198
+ 'BEAR_FACE' => 0x1F43B, // Smiley 199
+ 'PIG_FACE' => 0x1F437, // Smiley 200
+ 'PIG_NOSE' => 0x1F43D, // Smiley 201
+ 'COW_FACE' => 0x1F42E, // Smiley 202
+ 'BOAR' => 0x1F417, // Smiley 203
+ 'MONKEY_FACE' => 0x1F435, // Smiley 204
+ 'MONKEY' => 0x1F412, // Smiley 205
+ 'HORSE_FACE' => 0x1F434, // Smiley 206
+ 'SHEEP' => 0x1F411, // Smiley 207
+ 'ELEPHANT' => 0x1F418, // Smiley 208
+ 'PANDA_FACE' => 0x1F43C, // Smiley 209
+ 'PENGUIN' => 0x1F427, // Smiley 210
+ 'BIRD' => 0x1F426, // Smiley 211
+ 'BABY_CHICK' => 0x1F424, // Smiley 212
+ 'FRONT_FACING_BABY_CHICK' => 0x1F425, // Smiley 213
+ 'HATCHING_CHICK' => 0x1F423, // Smiley 214
+ 'CHICKEN' => 0x1F414, // Smiley 215
+ 'SNAKE' => 0x1F40D, // Smiley 216
+ 'TURTLE' => 0x1F422, // Smiley 217
+ 'BUG' => 0x1F41B, // Smiley 218
+ 'HONEYBEE' => 0x1F41D, // Smiley 219
+ 'ANT' => 0x1F41C, // Smiley 220
+ 'LADY_BEETLE' => 0x1F41E, // Smiley 221
+ 'SNAIL' => 0x1F40C, // Smiley 222
+ 'OCTOPUS' => 0x1F419, // Smiley 223
+ 'SPIRAL_SHELL' => 0x1F41A, // Smiley 224
+ 'TROPICAL_FISH' => 0x1F420, // Smiley 225
+ 'FISH' => 0x1F41F, // Smiley 226
+ 'DOLPHIN' => 0x1F42C, // Smiley 227
+ 'SPOUTING_WHALE' => 0x1F433, // Smiley 228
+ 'WHALE' => 0x1F40B, // Smiley 229
+ 'COW' => 0x1F404, // Smiley 230
+ 'RAM' => 0x1F40F, // Smiley 231
+ 'RAT' => 0x1F400, // Smiley 232
+ 'WATER_BUFFALO' => 0x1F403, // Smiley 233
+ 'TIGER' => 0x1F405, // Smiley 234
+ 'RABBIT' => 0x1F407, // Smiley 235
+ 'DRAGON' => 0x1F409, // Smiley 236
+ 'HORSE' => 0x1F40E, // Smiley 237
+ 'GOAT' => 0x1F410, // Smiley 238
+ 'ROOSTER' => 0x1F413, // Smiley 239
+ 'DOG' => 0x1F415, // Smiley 240
+ 'PIG' => 0x1F416, // Smiley 241
+ 'MOUSE' => 0x1F401, // Smiley 242
+ 'OX' => 0x1F402, // Smiley 243
+ 'DRAGON_FACE' => 0x1F432, // Smiley 244
+ 'BLOWFISH' => 0x1F421, // Smiley 245
+ 'CROCODILE' => 0x1F40A, // Smiley 246
+ 'BACTRIAN_CAMEL' => 0x1F42B, // Smiley 247
+ 'DROMEDARY_CAMEL' => 0x1F42A, // Smiley 248
+ 'LEOPARD' => 0x1F406, // Smiley 249
+ 'CAT' => 0x1F408, // Smiley 250
+ 'POODLE' => 0x1F429, // Smiley 251
+ 'PAW_PRINTS' => 0x1F43E, // Smiley 252
+ 'BOUQET' => 0x1F490, // Smiley 253
+ 'CHERRY_BLOSSOM' => 0x1F338, // Smiley 254
+ 'TULIP' => 0x1F337, // Smiley 255
+ 'FOUR_LEAF_CLOVER' => 0x1F340, // Smiley 256
+ 'ROSE' => 0x1F339, // Smiley 257
+ 'SUNFLOWER' => 0x1F33B, // Smiley 258
+ 'HIBISCUS' => 0x1F33A, // Smiley 259
+ 'MAPLE_LEAF' => 0x1F341, // Smiley 260
+ 'LEAF_FLUTTERING_IN_WIND' => 0x1F343, // Smiley 261
+ 'FALLEN_LEAF' => 0x1F342, // Smiley 262
+ 'HERB' => 0x1F33F, // Smiley 263
+ 'EAR_OF_RICE' => 0x1F33E, // Smiley 264
+ 'MUSHROOM' => 0x1F344, // Smiley 265
+ 'CACTUS' => 0x1F335, // Smiley 266
+ 'PALM_TREE' => 0x1F334, // Smiley 267
+ 'EVERGREEN_TREE' => 0x1F332, // Smiley 268
+ 'DECIDUOUS_TREE' => 0x1F333, // Smiley 269
+ 'CHESTNUT' => 0x1F330, // Smiley 270
+ 'SEEDLING' => 0x1F331, // Smiley 271
+ 'BLOSSOM' => 0x1F33C, // Smiley 272
+ 'GLOBE_WITH_MERIDIANS' => 0x1F310, // Smiley 273
+ 'SUN_WITH_FACE' => 0x1F31E, // Smiley 274
+ 'FULL_MOON_WITH_FACE' => 0x1F31D, // Smiley 275
+ 'NEW_MOON_WITH_FACE' => 0x1F31A, // Smiley 276
+ 'NEW_MOON_SYMBOL' => 0x1F311, // Smiley 277
+ 'WAXING_CRESCENDING_MOON_SYMBOL' => 0x1F312, // Smiley 278
+ 'FIRST_QUARTER_MOON_SYMBOL' => 0x1F313, // Smiley 279
+ 'WAXING_GIBBOUS_MOON_SYMBOL' => 0x1F314, // Smiley 280
+ 'FULL_MOON_SYMBOL' => 0x1F315, // Smiley 281
+ 'WANNING_GIBBOUS_MOON_SYMBOL' => 0x1F316, // Smiley 282
+ 'LAST_QUARTER_MOON_SYMBOL' => 0x1F317, // Smiley 283
+ 'WANNING_CRESCENT_MOON_SYMBOL' => 0x1F318, // Smiley 284
+ 'LAST_QUARTER_MOON_WITH_FACE' => 0x1F31C, // Smiley 285
+ 'FIRST_QUARTER_MOON_WITH_FACE' => 0x1F31B, // Smiley 286
+ 'CRESCENT_MOON' => 0x1F319, // Smiley 287
+ 'EARTH_GLOBE_EUROPE_AFRICA' => 0x1F30D, // Smiley 288
+ 'EARTH_GLOBE_AMERICAS' => 0x1F30E, // Smiley 289
+ 'EARTH_GLOBE_ASIA_AUSTRALIA' => 0x1F30F, // Smiley 290
+ 'VOLCANO' => 0x1F30B, // Smiley 291
+ 'MILKY_WAY' => 0x1F30C, // Smiley 292
+ 'SHOOTING_STAR' => 0x1F320, // Smiley 293
+ 'WHITE_MEDIUM_STAR' => 0x2B50, // Smiley 294
+ 'BLACK_SUN_WITH_RAYS' => 0x2600, // Smiley 295
+ 'SUN_BEHIND_CLOUD' => 0x26C5, // Smiley 296
+ 'CLOUD' => 0x2601, // Smiley 297
+ 'HIGH_VOLTAGE_SIGN' => 0x26A1, // Smiley 298
+ 'UMBRELLA_WITH_RAIN_DROPS' => 0x2614, // Smiley 299
+ 'SNOW_FLAKE' => 0x2744, // Smiley 300
+ 'SNOWMAN_WITHOUT_SNOW' => 0x26C4, // Smiley 301
+ 'CYCLONE' => 0x1F300, // Smiley 302
+ 'FOGGY' => 0x1F301, // Smiley 303
+ 'RAINBOW' => 0x1F308, // Smiley 304
+ 'WATER_WAVE' => 0x1F30A, // Smiley 305
+ 'PINE_DECORATION' => 0x1F38D, // Smiley 306
+ 'HEART_WITH_RIBBON' => 0x1F49D, // Smiley 307
+ 'JAPANESE_DOLLS' => 0x1F38E, // Smiley 308
+ 'SCHOOL_SATCHEL' => 0x1F392, // Smiley 309
+ 'GRADUATION_CAP' => 0x1F393, // Smiley 310
+ 'CARP_STREAMER' => 0x1F38F, // Smiley 311
+ 'FIREWORKS' => 0x1F386, // Smiley 312
+ 'FIREWORKS_SPARKLER' => 0x1F387, // Smiley 313
+ 'WIND_CHIME' => 0x1F390, // Smiley 314
+ 'MOON_VIEWING_CEREMONY' => 0x1F391, // Smiley 315
+ 'JACK_O_LANTERN' => 0x1F383, // Smiley 316
+ 'GHOST' => 0x1F47B, // Smiley 317
+ 'FATHER_CHRISTMAS' => 0x1F385, // Smiley 318
+ 'CHRISTMAS_TREE' => 0x1F384, // Smiley 319
+ 'WRAPPED_PRESENT' => 0x1F381, // Smiley 320
+ 'TANABATA_TREE' => 0x1F38B, // Smiley 321
+ 'PARTY_POPPER' => 0x1F389, // Smiley 322
+ 'CONFETTI_BALL' => 0x1F38A, // Smiley 323
+ 'BALLOON' => 0x1F388, // Smiley 324
+ 'CROSSED_FLAGS' => 0x1F38C, // Smiley 325
+ 'CRYSTAL_BALL' => 0x1F52E, // Smiley 326
+ 'MOVIE_CAMERA' => 0x1F3A5, // Smiley 327
+ 'CAMERA' => 0x1F4F7, // Smiley 328
+ 'VIDEO_CAMERA' => 0x1F4F9, // Smiley 329
+ 'VIDEOCASSETTE' => 0x1F4FC, // Smiley 330
+ 'OPTICAL_DISC' => 0x1F4BF, // Smiley 331
+ 'DVD' => 0x1F4C0, // Smiley 332
+ 'MINIDISC' => 0x1F4BD, // Smiley 333
+ 'FLOPPY_DISK' => 0x1F4BE, // Smiley 334
+ 'PERSONAL_COMPUTER' => 0x1F4BB, // Smiley 335
+ 'MOBILE_PHONE' => 0x1F4F1, // Smiley 336
+ 'BLACK_TELEPHONE' => 0x260E, // Smiley 337
+ 'TELEPHONE_RECEIVER' => 0x1F4DE, // Smiley 338
+ 'PAGER' => 0x1F4DF, // Smiley 339
+ 'FAX_MACHINE' => 0x1F4E0, // Smiley 340
+ 'SATELLITE_ANTENNA' => 0x1F4E1, // Smiley 341
+ 'TELEVISION' => 0x1F4FA, // Smiley 342
+ 'RADIO' => 0x1F4FB, // Smiley 343
+ 'SPEAKER_WITH_THREE_SOUND_WAVES' => 0x1F50A, // Smiley 344
+ 'SPEAKER_WITH_ONE_SOUND_WAVE' => 0x1F509, // Smiley 345
+ 'SPEAKER' => 0x1F508, // Smiley 346
+ 'SPEAKER_WITH_CANCELLATION_STROKE' => 0x1F507, // Smiley 347
+ 'BELL' => 0x1F514, // Smiley 348
+ 'BELL_WITH_CANCELLATION_STROKE' => 0x1F515, // Smiley 349
+ 'PUBLIC_ADDRESS_LOUDSPEAKER' => 0x1F4E2, // Smiley 350
+ 'CHEERING_MEGAPHONE' => 0x1F4E3, // Smiley 351
+ 'HOURGLASS_WITH_FLOWING_SAND' => 0x23F3, // Smiley 352
+ 'HOURGLASS' => 0x231B, // Smiley 353
+ 'ALARM_CLOCK' => 0x23F0, // Smiley 354
+ 'WATCH' => 0x231A, // Smiley 355
+ 'OPEN_LOCK' => 0x1F513, // Smiley 356
+ 'LOCK' => 0x1F512, // Smiley 357
+ 'LOCK_WITH_INK_PEN' => 0x1F50F, // Smiley 358
+ 'CLOSED_LOCK_WITH_KEY' => 0x1F510, // Smiley 359
+ 'KEY' => 0x1F511, // Smiley 360
+ 'RIGHT_POINTING_MAGNIFYING_GLASS' => 0x1F50E, // Smiley 361
+ 'ELECTRIC_LIGHT_BULB' => 0x1F4A1, // Smiley 362
+ 'ELECTRIC_TORCH' => 0x1F526, // Smiley 363
+ 'HIGH_BRIGHTNESS_SYMBOL' => 0x1F506, // Smiley 364
+ 'LOW_BRIGHTNESS_SYMBOL' => 0x1F505, // Smiley 365
+ 'ELECTRIC_PLUG' => 0x1F50C, // Smiley 366
+ 'BATTERY' => 0x1F50B, // Smiley 367
+ 'LEFT_POINTING_MAGNIFYING_GLASS' => 0x1F50D, // Smiley 368
+ 'BATHTUB' => 0x1F6C1, // Smiley 369
+ 'BATH' => 0x1F6C0, // Smiley 370
+ 'SHOWER' => 0x1F6BF, // Smiley 371
+ 'TOILET' => 0x1F6BD, // Smiley 372
+ 'WRENCH' => 0x1F527, // Smiley 373
+ 'NUT_AND_BOLT' => 0x1F529, // Smiley 374
+ 'HAMMER' => 0x1F528, // Smiley 375
+ 'DOOR' => 0x1F6AA, // Smiley 376
+ 'SMOKING_SYMBOL' => 0x1F6AC, // Smiley 377
+ 'BOMB' => 0x1F4A3, // Smiley 378
+ 'PISTOL' => 0x1F52B, // Smiley 379
+ 'HOCHO' => 0x1F52A, // Smiley 380
+ 'PILL' => 0x1F48A, // Smiley 381
+ 'SYRINGE' => 0x1F489, // Smiley 382
+ 'MONEY_BAG' => 0x1F4B0, // Smiley 383
+ 'BANKNOTE_WITH_YEN_SIGN' => 0x1F4B4, // Smiley 384
+ 'BANKNOTE_WITH_DOLLAR_SIGN' => 0x1F4B5, // Smiley 385
+ 'BANKNOTE_WITH_POUND_SIGN' => 0x1F4B7, // Smiley 386
+ 'BANKNOTE_WITH_EURO_SIGN' => 0x1F4B6, // Smiley 387
+ 'CREDIT_CARD' => 0x1F4B3, // Smiley 388
+ 'MONEY_WITH_WINGS' => 0x1F4B8, // Smiley 389
+ 'MOBILE_PHONE_WITH_RIGHTWARDS_ARROW' => 0x1F4F2, // Smiley 390
+ 'E_MAIL_SYMBOL' => 0x1F4E7, // Smiley 391
+ 'INBOX_TRAY' => 0x1F4E5, // Smiley 392
+ 'OUTBOX_TRAY' => 0x1F4E4, // Smiley 393
+ 'ENVELOPE' => 0x2709, // Smiley 394
+ 'ENVELOPE_WITH_DOWNWARDS_ARROW_ABOVE' => 0x1F4E9, // Smiley 395
+ 'INCOMING_ENVELOPE' => 0x1F4E8, // Smiley 396
+ 'POSTAL_HORN' => 0x1F4EF, // Smiley 397
+ 'CLOSED_MAILBOX_WITH_RAISED_FLAG' => 0x1F4EB, // Smiley 398
+ 'CLOSED_MAILBOX_WITH_LOWERED_FLAG' => 0x1F4EA, // Smiley 399
+ 'OPEN_MAILBOX_WITH_RAISED_FLAG' => 0x1F4EC, // Smiley 400
+ 'OPEN_MAILBOX_WITH_LOWERED_FLAG' => 0x1F4ED, // Smiley 401
+ 'POSTBOX' => 0x1F4EE, // Smiley 402
+ 'PACKAGE' => 0x1F4E6, // Smiley 403
+ 'MEMO' => 0x1F4DD, // Smiley 404
+ 'PAGE_FACING_UP' => 0x1F4C4, // Smiley 405
+ 'PAGE_WITH_CURL' => 0x1F4C3, // Smiley 406
+ 'BOOKMARK_TABS' => 0x1F4D1, // Smiley 407
+ 'BAR_CHART' => 0x1F4CA, // Smiley 408
+ 'CHART_WITH_UPWARDS_TREND' => 0x1F4C8, // Smiley 409
+ 'CHART_WITH_DOWNWARD_TREND' => 0x1F4C9, // Smiley 410
+ 'SCROLL' => 0x1F4DC, // Smiley 411
+ 'CLIPBOARD' => 0x1F4CB, // Smiley 412
+ 'CALENDAR' => 0x1F4C5, // Smiley 413
+ 'TEAR_OFF_CALENDAR' => 0x1F4C6, // Smiley 414
+ 'CARD_INDEX' => 0x1F4C7, // Smiley 415
+ 'FILE_FOLDER' => 0x1F4C1, // Smiley 416
+ 'OPEN_FILE_FOLDER' => 0x1F4C2, // Smiley 417
+ 'BLACK_SCISSORS' => 0x2702, // Smiley 418
+ 'PUSHPIN' => 0x1F4CC, // Smiley 419
+ 'PAPERCLIP' => 0x1F4CE, // Smiley 420
+ 'BLACK_NIB' => 0x2712, // Smiley 421
+ 'PENCIL' => 0x270F, // Smiley 422
+ 'STRAIGHT_RULER' => 0x1F4CF, // Smiley 423
+ 'TRIANGULAR_RULER' => 0x1F4D0, // Smiley 424
+ 'CLOSED_BOOK' => 0x1F4D5, // Smiley 425
+ 'GREEN_BOOK' => 0x1F4D7, // Smiley 426
+ 'BLUE_BOOK' => 0x1F4D8, // Smiley 427
+ 'ORANGE_BOOK' => 0x1F4D9, // Smiley 428
+ 'NOTEBOOK' => 0x1F4D3, // Smiley 429
+ 'NOTEBOOK_WITH_DECORATIVE_COVER' => 0x1F4D4, // Smiley 430
+ 'LEDGER' => 0x1F4D2, // Smiley 431
+ 'BOOKS' => 0x1F4DA, // Smiley 432
+ 'OPEN_BOOK' => 0x1F4D6, // Smiley 433
+ 'BOOKMARK' => 0x1F516, // Smiley 434
+ 'NAME_BADGE' => 0x1F4DB, // Smiley 435
+ 'MICROSCOPE' => 0x1F52C, // Smiley 436
+ 'TELESCOPE' => 0x1F52D, // Smiley 437
+ 'NEWSPAPER' => 0x1F4F0, // Smiley 438
+ 'ARTIST_PALETTE' => 0x1F3A8, // Smiley 439
+ 'CLAPPER_BOARD' => 0x1F3AC, // Smiley 440
+ 'MICROPHONE' => 0x1F3A4, // Smiley 441
+ 'HEADPHONE' => 0x1F3A7, // Smiley 442
+ 'MUSICAL_SCORE' => 0x1F3BC, // Smiley 443
+ 'MUSICAL_NOTE' => 0x1F3B5, // Smiley 444
+ 'MULTIPLE_MUSICAL_NOTES' => 0x1F3B6, // Smiley 445
+ 'MUSICAL_KEYBOARD' => 0x1F3B9, // Smiley 446
+ 'VIOLIN' => 0x1F3BB, // Smiley 447
+ 'TRUMPET' => 0x1F3BA, // Smiley 448
+ 'SAXOPHONE' => 0x1F3B7, // Smiley 449
+ 'GUITAR' => 0x1F3B8, // Smiley 450
+ 'ALIEN_MONSTER' => 0x1F47E, // Smiley 451
+ 'VIDEO_GAME' => 0x1F3AE, // Smiley 452
+ 'PLAYING_CARD_BLACK_JOKER' => 0x1F0CF, // Smiley 453
+ 'FLOWER_PLAYING_CARDS' => 0x1F3B4, // Smiley 454
+ 'MAHJONG_TILE_RED_DRAGON' => 0x1F004, // Smiley 455
+ 'GAME_DIE' => 0x1F3B2, // Smiley 456
+ 'DIRECT_HIT' => 0x1F3AF, // Smiley 457
+ 'AMERICAN_FOOTBALL' => 0x1F3C8, // Smiley 458
+ 'BASKETBALL_AND_HOOP' => 0x1F3C0, // Smiley 459
+ 'SOCCER_BALL' => 0x26BD, // Smiley 460
+ 'BASEBALL' => 0x26BE, // Smiley 461
+ 'GREEN_BOOK' => 0x1F4D7, // Smiley 462
+ 'TENNIS_RACQUET_AND_BALL' => 0x1F3BE, // Smiley 463
+ 'BILLIARDS' => 0x1F3B1, // Smiley 464
+ 'RUGBY_FOOTBALL' => 0x1F3C9, // Smiley 465
+ 'BOWLING' => 0x1F3B3, // Smiley 466
+ 'FLAG_IN_HOLE' => 0x26F3, // Smiley 467
+ 'MOUNTAIN_BYCICLIST' => 0x1F6B5, // Smiley 468
+ 'BYCICLIST' => 0x1F6B4, // Smiley 469
+ 'CHECKERED_FLAG' => 0x1F3C1, // Smiley 470
+ 'HORSE_RACING' => 0x1F3C7, // Smiley 471
+ 'TROPHY' => 0x1F3C6, // Smiley 472
+ 'SKI_AND_SKI_BOOT' => 0x1F3BF, // Smiley 473
+ 'SNOWBOARDER' => 0x1F3C2, // Smiley 474
+ 'SWIMMER' => 0x1F3CA, // Smiley 475
+ 'SURFER' => 0x1F3C4, // Smiley 476
+ 'FISHING_POLE_AND_FISH' => 0x1F3A3, // Smiley 477
+ 'HOT_BEVERAGE' => 0x2615, // Smiley 478
+ 'TEACUP_WITHOUT_HANDLE' => 0x1F375, // Smiley 479
+ 'SAKE_BOTTLE_AND_CUP' => 0x1F376, // Smiley 480
+ 'BABY_BOTTLE' => 0x1F37C, // Smiley 481
+ 'BEER_MUG' => 0x1F37A, // Smiley 482
+ 'CLINKING_BEER_MUGS' => 0x1F37B, // Smiley 483
+ 'TROPICAL_DRINK' => 0x1F379, // Smiley 484
+ 'WINE_GLASS' => 0x1F377, // Smiley 485
+ 'FORK_AND_KNIFE' => 0x1F374, // Smiley 486
+ 'SLICE_OF_PIZZA' => 0x1F355, // Smiley 487
+ 'HAMBURGER' => 0x1F354, // Smiley 488
+ 'FRENCH_FRIES' => 0x1F35F, // Smiley 489
+ 'POULTRY_LEG' => 0x1F357, // Smiley 490
+ 'MEAT_ON_BONE' => 0x1F356, // Smiley 491
+ 'SPAGHETTI' => 0x1F35D, // Smiley 492
+ 'CURRY_AND_RICE' => 0x1F35B, // Smiley 493
+ 'FRIED_SHRIMP' => 0x1F364, // Smiley 494
+ 'BENTO_BOX' => 0x1F371, // Smiley 495
+ 'SUSHI' => 0x1F363, // Smiley 496
+ 'FISH_CAKE_WITH_SWIRL_DESIGN' => 0x1F365, // Smiley 497
+ 'RICE_BALL' => 0x1F359, // Smiley 498
+ 'RICE_CRACKER' => 0x1F358, // Smiley 499
+ 'COOKED_RICE' => 0x1F35A, // Smiley 500
+ 'STEAMING_BOWL' => 0x1F35C, // Smiley 501
+ 'POT_OF_FOOD' => 0x1F372, // Smiley 502
+ 'ODEN' => 0x1F362, // Smiley 503
+ 'DANGO' => 0x1F361, // Smiley 504
+ 'COOKING' => 0x1F373, // Smiley 505
+ 'BREAD' => 0x1F35E, // Smiley 506
+ 'DOUGHNUT' => 0x1F369, // Smiley 507
+ 'CUSTARD' => 0x1F36E, // Smiley 508
+ 'SOFT_ICE_CREAM' => 0x1F366, // Smiley 509
+ 'ICE_CREAM' => 0x1F368, // Smiley 510
+ 'SHAVED_ICE' => 0x1F367, // Smiley 511
+ 'BIRTHDAY_CAKE' => 0x1F382, // Smiley 512
+ 'SHORTCAKE' => 0x1F370, // Smiley 513
+ 'COOKIE' => 0x1F36A, // Smiley 514
+ 'CHOCOLATE_BAR' => 0x1F36B, // Smiley 515
+ 'CANDY' => 0x1F36C, // Smiley 516
+ 'LOLLIPOP' => 0x1F36D, // Smiley 517
+ 'HONEY_POT' => 0x1F36F, // Smiley 518
+ 'RED_APPLE' => 0x1F34E, // Smiley 519
+ 'GREEN_APPLE' => 0x1F34F, // Smiley 520
+ 'TANGERINE' => 0x1F34A, // Smiley 521
+ 'LEMON' => 0x1F34B, // Smiley 522
+ 'CHERRIES' => 0x1F352, // Smiley 523
+ 'GRAPES' => 0x1F347, // Smiley 524
+ 'WATERMELON' => 0x1F349, // Smiley 525
+ 'STRAWBERRY' => 0x1F353, // Smiley 526
+ 'PEACH' => 0x1F351, // Smiley 527
+ 'MELON' => 0x1F348, // Smiley 528
+ 'BANANA' => 0x1F34C, // Smiley 529
+ 'PEAR' => 0x1F350, // Smiley 530
+ 'PINEAPPLE' => 0x1F34D, // Smiley 531
+ 'ROASTED_SWEET_POTATO' => 0x1F360, // Smiley 532
+ 'AUBERGINE' => 0x1F346, // Smiley 533
+ 'TOMATO' => 0x1F345, // Smiley 534
+ 'EAR_OF_MAIZE' => 0x1F33D, // Smiley 535
+ 'HOUSE_BUILDING' => 0x1F3E0, // Smiley 536
+ 'HOUSE_WITH_GARDEN' => 0x1F3E1, // Smiley 537
+ 'SCHOOL' => 0x1F3EB, // Smiley 538
+ 'OFFICE_BUILDING' => 0x1F3E2, // Smiley 539
+ 'JAPANESE_POST_OFFICE' => 0x1F3E3, // Smiley 540
+ 'HOSPITAL' => 0x1F3E5, // Smiley 541
+ 'BANK' => 0x1F3E6, // Smiley 542
+ 'CONVINIENCE_STORE' => 0x1F3EA, // Smiley 543
+ 'LOVE_HOTEL' => 0x1F3E9, // Smiley 544
+ 'HOTEL' => 0x1F3E8, // Smiley 545
+ 'WEDDING' => 0x1F492, // Smiley 546
+ 'CHURCH' => 0x26EA, // Smiley 547
+ 'DEPARTMENT_STORE' => 0x1F3EC, // Smiley 548
+ 'EUROPEAN_POST_OFFICE' => 0x1F3E4, // Smiley 549
+ 'SUNSET_OVER_BUILDINGS' => 0x1F307, // Smiley 550
+ 'CITYSCAPE_AT_DUSK' => 0x1F306, // Smiley 551
+ 'JAPANESE_CASTLE' => 0x1F3EF, // Smiley 552
+ 'EUROPEAN_CASTLE' => 0x1F3F0, // Smiley 553
+ 'TENT' => 0x26FA, // Smiley 554
+ 'FACTORY' => 0x1F3ED, // Smiley 555
+ 'TOKYO_TOWER' => 0x1F5FC, // Smiley 556
+ 'SILHOUETTE_OF_JAPAN' => 0x1F5FE, // Smiley 557
+ 'MOUNT_FUJI' => 0x1F5FB, // Smiley 558
+ 'SUNRISE_OVER_MOUNTAINS' => 0x1F304, // Smiley 559
+ 'SUNRISE' => 0x1F305, // Smiley 560
+ 'NIGHT_WITH_STARS' => 0x1F303, // Smiley 561
+ 'STATUE_OF_LIBERTY' => 0x1F5FD, // Smiley 562
+ 'BRIDGE_AT_NIGHT' => 0x1F309, // Smiley 563
+ 'CAROUSEL_HORSE' => 0x1F3A0, // Smiley 564
+ 'FERRIS_WHEEL' => 0x1F3A1, // Smiley 565
+ 'FOUNTAIN' => 0x26F2, // Smiley 566
+ 'ROLLER_COASTER' => 0x1F3A2, // Smiley 567
+ 'SHIP' => 0x1F6A2, // Smiley 568
+ 'SAILBOAT' => 0x26F5, // Smiley 569
+ 'SPEEDBOAT' => 0x1F6A4, // Smiley 570
+ 'ROWBOAT' => 0x1F6A3, // Smiley 571
+ 'ANCHOR' => 0x2693, // Smiley 572
+ 'ROCKET' => 0x1F680, // Smiley 573
+ 'AIRPLANE' => 0x2708, // Smiley 574
+ 'SEAT' => 0x1F4BA, // Smiley 575
+ 'HELICOPTER' => 0x1F681, // Smiley 576
+ 'STEAM_LOCOMOTIVE' => 0x1F682, // Smiley 577
+ 'TRAM' => 0x1F68A, // Smiley 578
+ 'STATION' => 0x1F689, // Smiley 579
+ 'MOUNTAIN_RAILWAY' => 0x1F69E, // Smiley 580
+ 'TRAIN' => 0x1F686, // Smiley 581
+ 'HIGH_SPEED_TRAIN' => 0x1F684, // Smiley 582
+ 'HIGH_SPEED_TRAIN_WITH_BULLET_NOSE' => 0x1F685, // Smiley 583
+ 'LIGHT_RAIL' => 0x1F688, // Smiley 584
+ 'METRO' => 0x1F687, // Smiley 585
+ 'MONO_RAIL' => 0x1F69D, // Smiley 586
+ 'TRAM_CAR' => 0x1F68B, // Smiley 587
+ 'RAILWAY_CAR' => 0x1F683, // Smiley 588
+ 'TROLLEYBUS' => 0x1F68E, // Smiley 589
+ 'BUS' => 0x1F68C, // Smiley 590
+ 'ONCOMING_BUS' => 0x1F68D, // Smiley 591
+ 'RECREATION_VEHICLE' => 0x1F699, // Smiley 592
+ 'ONCOMING_AUTOMOBILE' => 0x1F698, // Smiley 593
+ 'AUTOMOBILE' => 0x1F697, // Smiley 594
+ 'TAXI' => 0x1F695, // Smiley 595
+ 'ONCOMING_TAXI' => 0x1F696, // Smiley 596
+ 'ARTICULATED_LORRY' => 0x1F69B, // Smiley 597
+ 'DELIVERY_TRUCK' => 0x1F69A, // Smiley 598
+ 'POLICE_CARS_REVOLVING_LIGHT' => 0x1F6A8, // Smiley 599
+ 'POLICE_CAR' => 0x1F693, // Smiley 600
+ 'ONCOMING_POLICE_CAR' => 0x1F694, // Smiley 601
+ 'FIRE_ENGINE' => 0x1F692, // Smiley 602
+ 'AMBULANCE' => 0x1F691, // Smiley 603
+ 'MINBUS' => 0x1F690, // Smiley 604
+ 'BICYCLE' => 0x1F6B2, // Smiley 605
+ 'AERIAL_TRAMWAY' => 0x1F6A1, // Smiley 606
+ 'SUSPENSION_RAILWAY' => 0x1F69F, // Smiley 607
+ 'MOUNTAIN_CABLEWAY' => 0x1F6A0, // Smiley 608
+ 'TRACTOR' => 0x1F69C, // Smiley 609
+ 'BARBER_POLE' => 0x1F488, // Smiley 610
+ 'BUS_STOP' => 0x1F68F, // Smiley 611
+ 'TICKET' => 0x1F3AB, // Smiley 612
+ 'VERTICAL_TRAFFIC_LIGHT' => 0x1F6A6, // Smiley 613
+ 'HORIZONTAL_TRAFFIC_LIGHT' => 0x1F6A5, // Smiley 614
+ 'WARNING_SIGN' => 0x26A0, // Smiley 615
+ 'CONSTRUCTION_SIGN' => 0x1F6A7, // Smiley 616
+ 'JAPANESE_SYMBOL_FOR_BEGINNER' => 0x1F530, // Smiley 617
+ 'FUEL_PUMP' => 0x26FD, // Smiley 618
+ 'IZAKAYA_LANTERN' => 0x1F3EE, // Smiley 619
+ 'SLOT_MACHINE' => 0x1F3B0, // Smiley 620
+ 'HOT_SPRINGS' => 0x2668, // Smiley 621
+ 'MOYAI' => 0x1F5FF, // Smiley 622
+ 'CIRCUS_TENT' => 0x1F3AA, // Smiley 623
+ 'PERFORMING_ARTS' => 0x1F3AD, // Smiley 624
+ 'ROUND_PUSHPIN' => 0x1F4CD, // Smiley 625
+ 'TRIANGULAR_FLAG_ON_POST' => 0x1F6A9, // Smiley 626
+ 'REGIONAL_INDICATOR_SYMBOL_LETTERS_JP' => [0xD83C, 0xDDEF, 0xD83C, 0xDDF5], // Smiley 627
+ 'REGIONAL_INDICATOR_SYMBOL_LETTERS_KR' => [0xD83C, 0xDDF0, 0xD83C, 0xDDF7], // Smiley 628
+ 'REGIONAL_INDICATOR_SYMBOL_LETTERS_DE' => [0xD83C, 0xDDE9, 0xD83C, 0xDDEA], // Smiley 629
+ 'REGIONAL_INDICATOR_SYMBOL_LETTERS_CN' => [0xD83C, 0xDDE8, 0xD83C, 0xDDF3], // Smiley 630
+ 'REGIONAL_INDICATOR_SYMBOL_LETTERS_US' => [0xD83C, 0xDDFA, 0xD83C, 0xDDF8], // Smiley 631
+ 'REGIONAL_INDICATOR_SYMBOL_LETTERS_FR' => [0xD83C, 0xDDEB, 0xD83C, 0xDDF7], // Smiley 632
+ 'REGIONAL_INDICATOR_SYMBOL_LETTERS_ES' => [0xD83C, 0xDDEA, 0xD83C, 0xDDF8], // Smiley 633
+ 'REGIONAL_INDICATOR_SYMBOL_LETTERS_IT' => [0xD83C, 0xDDEE, 0xD83C, 0xDDF9], // Smiley 634
+ 'REGIONAL_INDICATOR_SYMBOL_LETTERS_RU' => [0xD83C, 0xDDF7, 0xD83C, 0xDDFA], // Smiley 635
+ 'REGIONAL_INDICATOR_SYMBOL_LETTERS_GB' => [0xD83C, 0xDDEC, 0xD83C, 0xDDE7], // Smiley 636
+ 'KEYCAP_1' => [0x0031, 0x20E3], // Smiley 637
+ 'KEYCAP_2' => [0x0032, 0x20E3], // Smiley 638
+ 'KEYCAP_3' => [0x0033, 0x20E3], // Smiley 639
+ 'KEYCAP_4' => [0x0034, 0x20E3], // Smiley 640
+ 'KEYCAP_5' => [0x0035, 0x20E3], // Smiley 641
+ 'KEYCAP_6' => [0x0036, 0x20E3], // Smiley 642
+ 'KEYCAP_7' => [0x0037, 0x20E3], // Smiley 643
+ 'KEYCAP_8' => [0x0038, 0x20E3], // Smiley 644
+ 'KEYCAP_9' => [0x0039, 0x20E3], // Smiley 645
+ 'KEYCAP_0' => [0x0030, 0x20E3], // Smiley 646
+ 'KEYCAP_10' => 0x1F51F, // Smiley 647
+ 'INPUT_SYMBOL_FOR_NUMBERS' => 0x1F522, // Smiley 648
+ 'HASH_KEY' => [0x0023, 0x20E3], // Smiley 649
+ 'INPUT_SYMBOL_FOR_SYMBOLS' => 0x1F523, // Smiley 650
+ 'UPWARDS_BLACK_ARROW' => 0x2B06, // Smiley 651
+ 'DOWNWARDS_BLACK_ARROW' => 0x2B07, // Smiley 652
+ 'LEFTWARDS_BLACK_ARROW' => 0x2B05, // Smiley 653
+ 'BLACK_RIGHTWARDS_ARROW' => 0x27A1, // Smiley 654
+ 'INPUT_SYMBOL_FOR_LATIN_CAPITAL_LETTERS' => 0x1F520, // Smiley 655
+ 'INPUT_SYMBOL_FOR_LATIN_SMALL_LETTERS' => 0x1F521, // Smiley 656
+ 'INPUT_SYMBOL_FOR_LATIN_LETTERS' => 0x1F524, // Smiley 657
+ 'NORTH_EAST_ARROW' => 0x2197, // Smiley 658
+ 'NORTH_WEST_ARROW' => 0x2196, // Smiley 659
+ 'SOUTH_EAST_ARROW' => 0x2198, // Smiley 660
+ 'SOUTH_WEST_ARROW' => 0x2199, // Smiley 661
+ 'LEFT_RIGHT_ARROW' => 0x2194, // Smiley 662
+ 'UP_DOWN_ARROW' => 0x2195, // Smiley 663
+ 'ANTICLOCKWISE_DOWNWARDS_AND_UPWARDS_OPEN_CIRCLE_ARROWS' => 0x1F504, // Smiley 664
+ 'BLACK_LEFT_POINTING_TRIANGLE' => 0x25C0, // Smiley 665
+ 'BLACK_RIGHT_POINTING_TRIANGLE' => 0x25B6, // Smiley 666
+ 'UP_POINTING_SMALL_RED_TRIANGLE' => 0x1F53C, // Smiley 667
+ 'DOWN_POINTING_SMALL_RED_TRIANGLE' => 0x1F53D, // Smiley 668
+ 'LEFTWARDS_ARROW_WITH_HOOK' => 0x21A9, // Smiley 669
+ 'RIGHTWARDS_ARROW_WITH_HOOK' => 0x21AA, // Smiley 670
+ 'INFORMATION_SOURCE' => 0x2139, // Smiley 671
+ 'BLACK_LEFT_POINTING_DOUBLE_TRIANGLE' => 0x23EA, // Smiley 672
+ 'BLACK_RIGHT_POINTING_DOUBLE_TRIANGLE' => 0x23E9, // Smiley 673
+ 'BLACK_UP_POINTING_DOUBLE_TRIANGLE' => 0x23EB, // Smiley 674
+ 'BLACK_DOWN_POINTING_DOUBLE_TRIANGLE' => 0x23EC, // Smiley 675
+ 'ARROW_POINTING_RIGHTWARDS_THEN_CURVING_DOWNWARDS' => 0x2935, // Smiley 676
+ 'ARROW_POINTING_RIGHTWARDS_THEN_CURVING_UPWARDS' => 0x2934, // Smiley 677
+ 'SQUARED_OK' => 0x1F197, // Smiley 678
+ 'TWISTED_RIGHTWARDS_ARROWS' => 0x1F500, // Smiley 679
+ 'CLOCKWISE_RIGHTWARDS_AND_LEFTWARDS_OPEN_CIRCLE_ARROWS' => 0x1F501, // Smiley 680
+ 'CLOCKWISE_RIGHTWARDS_AND_LEFTWARDS_ARTOWS_WITH_CIRCLED_ONE_OVERLAY'=> 0x1F502, // Smiley 681
+ 'SQUARED_NEW' => 0x1F195, // Smiley 682
+ 'SQUARED_UP_WITH_EXCLAMATION_MARK' => 0x1F199, // Smiley 683
+ 'SQUARED_COOL' => 0x1F192, // Smiley 684
+ 'SQUARED_FREE' => 0x1F193, // Smiley 685
+ 'SQUARED_NG' => 0x1F196, // Smiley 686
+ 'ANTENNA_WITH_BARS' => 0x1F4F6, // Smiley 687
+ 'CINEMA' => 0x1F3A6, // Smiley 688
+ 'SQUARED_KATAKANA_KOKO' => 0x1F201, // Smiley 689
+ 'SQUARED_CJK_UNIFIED_IDEOGRAPH_6307' => 0x1F22F, // Smiley 690
+ 'SQUARED_CJK_UNIFIED_IDEOGRAPH_7A7A' => 0x1F233, // Smiley 691
+ 'SQUARED_CJK_UNIFIED_IDEOGRAPH_6E80' => 0x1F235, // Smiley 692
+ 'SQUARED_CJK_UNIFIED_IDEOGRAPH_5408' => 0x1F234, // Smiley 693
+ 'SQUARED_CJK_UNIFIED_IDEOGRAPH_7981' => 0x1F232, // Smiley 694
+ 'CIRCLED_IDEOGRAPH_ADVANTAGE' => 0x1F250, // Smiley 695
+ 'SQUARED_CJK_UNIFIED_IDEOGRAPH_5272' => 0x1F239, // Smiley 696
+ 'SQUARED_CJK_UNIFIED_IDEOGRAPH_55B6' => 0x1F23A, // Smiley 697
+ 'SQUARED_CJK_UNIFIED_IDEOGRAPH_6709' => 0x1F236, // Smiley 698
+ 'SQUARED_CJK_UNIFIED_IDEOGRAPH_7121' => 0x1F21A, // Smiley 699
+ 'RESTROOM' => 0x1F6BB, // Smiley 700
+ 'MENS_SYMBOL' => 0x1F6B9, // Smiley 701
+ 'WOMENS_SYMBOL' => 0x1F6BA, // Smiley 702
+ 'BABY_SYMBOL' => 0x1F6BC, // Smiley 703
+ 'WATER_CLOSET' => 0x1F6BE, // Smiley 704
+ 'POTABLE_WATER_SYMBOL' => 0x1F6B0, // Smiley 705
+ 'PUT_LITTER_IN_ITS_PLACE_SYMBOL' => 0x1F6AE, // Smiley 706
+ 'NEGATIVE_SQUARED_LATIN_CAPITAL_LETTER_P' => 0x1F17F, // Smiley 707
+ 'WHEELCHAIR_SYMBOL' => 0x267F, // Smiley 708
+ 'NO_SMOKING_SYMBOL' => 0x1F6AD, // Smiley 709
+ 'SQUARED_CJK_UNIFIED_IDEOGRAPH_6708' => 0x1F237, // Smiley 710
+ 'SQUARED_CJK_UNIFIED_IDEOGRAPH_6533' => 0x1F238, // Smiley 711
+ 'SQUARED_KATAKANA_SA' => 0x1F202, // Smiley 712
+ 'CIRCLED_LATIN_CAPITAL_LETTER_M' => 0x24C2, // Smiley 713
+ 'PASSPORT_CONTROL' => 0x1F6C2, // Smiley 714
+ 'BAGGAGE_CLAIM' => 0x1F6C4, // Smiley 715
+ 'LEFT_LUGGAGE' => 0x1F6C5, // Smiley 716
+ 'CUSTOMS' => 0x1F6C3, // Smiley 717
+ 'CIRCLED_IDEOGRAPH_ACCEPT' => 0x1F251, // Smiley 718
+ 'CIRCLED_IDEOGRAPH_SECRET' => 0x3299, // Smiley 719
+ 'CIRCLED_IDEOGRAPH_CONGRATULATIONS' => 0x3297, // Smiley 720
+ 'SQUARED_CL' => 0x1F191, // Smiley 721
+ 'SQUARED_SOS' => 0x1F198, // Smiley 722
+ 'SQUARED_ID' => 0x1F194, // Smiley 723
+ 'NO_ENTRY_SIGN' => 0x1F6AB, // Smiley 724
+ 'NO_ONE_UNDER_EIGHTEEN_SYMBOL' => 0x1F51E, // Smiley 725
+ 'NO_MOBILE_PHONES' => 0x1F4F5, // Smiley 726
+ 'DO_NOT_LITTER_SYMBOL' => 0x1F6AF, // Smiley 727
+ 'NON_POTABLE_WATER_SYMBOL' => 0x1F6B1, // Smiley 728
+ 'NO_BICYCLES' => 0x1F6B3, // Smiley 729
+ 'NO_PEDESTRIANS' => 0x1F6B7, // Smiley 730
+ 'CHILDREN_CROSSING' => 0x1F6B8, // Smiley 731
+ 'NO_ENTRY' => 0x26D4, // Smiley 732
+ 'EIGHT_SPOKED_ASTERISK' => 0x2733, // Smiley 733
+ 'SPARKLE' => 0x2747, // Smiley 734
+ 'NEGATIVE_SQUARED_CROSS_MARK' => 0x274E, // Smiley 735
+ 'WHITE_HEAVY_CHECK_MARK' => 0x2705, // Smiley 736
+ 'EIGHT_POINTED_BLACK_STAR' => 0x2734, // Smiley 737
+ 'HEART_DECORATION' => 0x1F49F, // Smiley 738
+ 'SQUARED_VS' => 0x1F19A, // Smiley 739
+ 'VIBRATION_MODE' => 0x1F4F3, // Smiley 740
+ 'MOBILE_PHONE_OFF' => 0x1F4F4, // Smiley 741
+ 'NEGATIVE_SQUARED_LATIN_CAPITAL_LETTER_A' => 0x1F170, // Smiley 742
+ 'NEGATIVE_SQUARED_LATIN_CAPITAL_LETTER_B' => 0x1F171, // Smiley 743
+ 'NEGATIVE_SQUARED_AB' => 0x1F18E, // Smiley 744
+ 'NEGATIVE_SQUARED_LATIN_CAPITAL_LETTER_O' => 0x1F17E, // Smiley 745
+ 'DIAMOND_SHAPE_WITH_A_DOT_INSIDE' => 0x1F4A0, // Smiley 746
+ 'DOUBLE_CURLY_LOOP' => 0x27BF, // Smiley 747
+ 'BLACK_UNIVERSAL_RECYCLING_SYMBOL' => 0x267B, // Smiley 748
+ 'ARIES' => 0x2648, // Smiley 749
+ 'TAURUS' => 0x2649, // Smiley 750
+ 'GEMINI' => 0x264A, // Smiley 751
+ 'CANCER' => 0x264B, // Smiley 752
+ 'LEO' => 0x264C, // Smiley 753
+ 'VIRGO' => 0x264D, // Smiley 754
+ 'LIBRA' => 0x264E, // Smiley 755
+ 'SCORPIUS' => 0x264F, // Smiley 756
+ 'SAGITTARIUS' => 0x2650, // Smiley 757
+ 'CAPRICORN' => 0x2651, // Smiley 758
+ 'AQUARIUS' => 0x2652, // Smiley 759
+ 'PISCES' => 0x2653, // Smiley 760
+ 'OPHIUCHUS' => 0x26CE, // Smiley 761
+ 'SIX_POINTED_STAR_WITH_MIDDLE_DOT' => 0x1F52F, // Smiley 762
+ 'AUTOMATED_TELLER_MACHINE' => 0x1F3E7, // Smiley 763
+ 'CHART_WITH_UPWARDS_TREND_AND_YEN_SIGN' => 0x1F4B9, // Smiley 764
+ 'HEAVY_DOLLAR_SIGN' => 0x1F4B2, // Smiley 765
+ 'CURRENCY_EXCHANGE' => 0x1F4B1, // Smiley 766
+ 'COPYRIGHT_SIGN' => 0x00A9, // Smiley 767
+ 'REGISTERED_SIGN' => 0x00AE, // Smiley 768
+ 'TRADEMARK_SIGN' => 0x2122, // Smiley 769
+ 'CROSS_MARK' => 0x274C, // Smiley 770
+ 'DOUBLE_EXCLAMATION_MARK' => 0x203C, // Smiley 771
+ 'EXCLAMATION_QUESTION_MARK' => 0x2049, // Smiley 772
+ 'HEAVY_EXCLAMATION_MARK_SYMBOL' => 0x2757, // Smiley 773
+ 'BLACK_QUESTION_MARK_ORNAMENT' => 0x2753, // Smiley 774
+ 'WHITE_EXCLAMATION_MARK_ORNAMENT' => 0x2755, // Smiley 775
+ 'WHITE_QUESTION_MARK_ORNAMENT' => 0x2754, // Smiley 776
+ 'HEAVY_LARGE_CIRCLE' => 0x2B55, // Smiley 777
+ 'TOP_WITH_UPWARDS_ARROW_ABOVE' => 0x1F51D, // Smiley 778
+ 'END_WITH_LEFTWARDS_ARROW_ABOVE' => 0x1F51A, // Smiley 779
+ 'BACK_WITH_LEFTWARDS_ARROW_ABOVE' => 0x1F519, // Smiley 780
+ 'ON_WITH_EXCLAMATION_MARK_WITH_LEFT_RIGHT_ARROW_ABOVE' => 0x1F51B, // Smiley 781
+ 'SOON_WITH_RIGHTWARDS_ARROW_ABOVE' => 0x1F51C, // Smiley 782
+ 'CLOCKWISE_DOWNWARDS_AND_UPWARDS_OPEN_CIRCLE_ARROWS' => 0x1F503, // Smiley 783
+ 'CLOCK_FACE_TWELVE_OCLOCK' => 0x1F55B, // Smiley 784
+ 'CLOCK_FACE_TWELVE_THIRTY' => 0x1F567, // Smiley 785
+ 'CLOCK_FACE_ONE_OCLOCK' => 0x1F550, // Smiley 786
+ 'CLOCK_FACE_ONE_THIRTY' => 0x1F55C, // Smiley 787
+ 'CLOCK_FACE_TWO_OCLOCK' => 0x1F551, // Smiley 788
+ 'CLOCK_FACE_TWO_THIRTY' => 0x1F55D, // Smiley 789
+ 'CLOCK_FACE_THREE_OCLOCK' => 0x1F552, // Smiley 790
+ 'CLOCK_FACE_THREE_THIRTY' => 0x1F55E, // Smiley 791
+ 'CLOCK_FACE_FOUR_OCLOCK' => 0x1F553, // Smiley 792
+ 'CLOCK_FACE_FOUR_THIRTY' => 0x1F55F, // Smiley 793
+ 'CLOCK_FACE_FIVE_OCLOCK' => 0x1F554, // Smiley 794
+ 'CLOCK_FACE_FIVE_THIRTY' => 0x1F560, // Smiley 795
+ 'CLOCK_FACE_SIX_OCLOCK' => 0x1F555, // Smiley 796
+ 'CLOCK_FACE_SEVEN_OCLOCK' => 0x1F556, // Smiley 797
+ 'CLOCK_FACE_EIGHT_OCLOCK' => 0x1F557, // Smiley 798
+ 'CLOCK_FACE_NINE_OCLOCK' => 0x1F558, // Smiley 799
+ 'CLOCK_FACE_TEN_OCLOCK' => 0x1F559, // Smiley 800
+ 'CLOCK_FACE_ELEVEN_OCLOCK' => 0x1F55A, // Smiley 801
+ 'CLOCK_FACE_SIX_THIRTY' => 0x1F561, // Smiley 802
+ 'CLOCK_FACE_SEVEN_THIRTY' => 0x1F562, // Smiley 803
+ 'CLOCK_FACE_EIGHT_THIRTY' => 0x1F563, // Smiley 804
+ 'CLOCK_FACE_NINE_THIRTY' => 0x1F564, // Smiley 805
+ 'CLOCK_FACE_TEN_THIRTY' => 0x1F565, // Smiley 806
+ 'CLOCK_FACE_ELEVEN_THIRTY' => 0x1F566, // Smiley 807
+ 'HEAVY_MULTIPLICATION_SIGN' => 0x2716, // Smiley 808
+ 'HEAVY_PLUS_SIGN' => 0x2795, // Smiley 809
+ 'HEAVY_MINUS_SIGN' => 0x2796, // Smiley 810
+ 'HEAVY_DIVISION_SIGN' => 0x2797, // Smiley 811
+ 'BLACK_SPADE_SUIT' => 0x2660, // Smiley 812
+ 'BLACK_HEART_SUIT' => 0x2665, // Smiley 813
+ 'BLACK_CLUB_SUIT' => 0x2663, // Smiley 814
+ 'BLACK_DIAMOND_SUITE' => 0x2666, // Smiley 815
+ 'WHITE_FLOWER' => 0x1F4AE, // Smiley 816
+ 'HUNDRED_POINTS_SYMBOL' => 0x1F4AF, // Smiley 817
+ 'HEAVY_CHECK_MARK' => 0x2714, // Smiley 818
+ 'BALLOT_BOX_WITH_CHECK' => 0x2611, // Smiley 819
+ 'RADIO_BUTTON' => 0x1F518, // Smiley 820
+ 'LINK_SYMBOL' => 0x1F517, // Smiley 821
+ 'CURLY_LOOP' => 0x27B0, // Smiley 822
+ 'WAVY_DASH' => 0x3030, // Smiley 823
+ 'PART_ALTERNATION_MARK' => 0x303D, // Smiley 824
+ 'TRIDENT_EMBLEM' => 0x1F531, // Smiley 825
+ 'BLACK_MEDIUM_SQUARE' => 0x25FC, // Smiley 826
+ 'WHITE_MEDIUM_SQUARE' => 0x25FB, // Smiley 827
+ 'BLACK_MEDIUM_SMALL_SQUARE' => 0x25FE, // Smiley 828
+ 'WHITE_MEDIUM_SMALL_SQUARE' => 0x25FD, // Smiley 829
+ 'BLACK_SMALL_SQUARE' => 0x25AA, // Smiley 830
+ 'WHILTE_SMALL_SQUARE' => 0x25AB, // Smiley 831
+ 'UP_POINTING_RED_TRIANGLE' => 0x1F53A, // Smiley 832
+ 'BLACK_SQUARE_BUTTON' => 0x1F532, // Smiley 833
+ 'WHILTE_SQUARE_BUTTON' => 0x1F533, // Smiley 834
+ 'MEDIUM_BLACK_CIRCLE' => 0x26AB, // Smiley 835
+ 'MEDIUM_WHITE_CIRCLE' => 0x26AA, // Smiley 836
+ 'LARGE_RED_CIRCLE' => 0x1F534, // Smiley 837
+ 'LARGE_BLUE_CIRCLE' => 0x1F535, // Smiley 838
+ 'DOWN_POINTING_RED_TRIANGLE' => 0x1F53B, // Smiley 839
+ 'WHITE_LARGE_SQUARE' => 0x2B1C, // Smiley 840
+ 'BLACK_LARGE_SQUARE' => 0x2B1B, // Smiley 841
+ 'LARGE_ORANGE_DIAMOND' => 0x1F536, // Smiley 842
+ 'LARGE_BLUE_DIAMOND' => 0x1F537, // Smiley 843
+ 'SMALL_ORANGE_DIAMOND' => 0x1F538, // Smiley 844
+ 'SMALL_BLUE_DIAMOND' => 0x1F539, // Smiley 845
+ 'MIDDLEFINGER' => 0x1F595, // Smiley xxx
+ 'VULCAN_SALUTE' => 0x1F596, // Smiley xxx
+ );
+ private $listKeys = array();
+
+ public function __construct() {
+ $this->listKeys = array_keys($this->listSmileys);
+ }
+
+ public function __get($name) {
+ if (mb_strlen($name) > 7 && substr($name, 0, 7) === 'SMILEY_' && is_numeric(substr($name, 7))) {
+ $id = (int)substr($name, 7) - 1;
+ echo 'id '.$id;
+ if ($id >= 0 && $id < count($this->listSmileys))
+ return $this->__get($this->listKeys[$id]);
+ throw new SmileyNotFoundException;
+ }
+
+ if (!isset($this->listSmileys[$name]))
+ throw new SmileyNotFoundException;
+
+ if (is_array($this->listSmileys[$name]))
+ return implode(array_map(array($this, 'ord_unicode'), $this->listSmileys[$name]));
+
+ return $this->ord_unicode($this->listSmileys[$name]);
+ }
+
+ // Source: http://stackoverflow.com/a/7153133/4990350
+ private function ord_unicode($num) {
+ if ($num <= 0x7F)
+ return chr($num);
+ if ($num <= 0x7FF)
+ return chr(($num>>6)+192).chr(($num&63)+128);
+ if ($num <= 0xFFFF)
+ return chr(($num>>12)+224).chr((($num>>6)&63)+128).chr(($num&63)+128);
+ if ($num <= 0x1FFFFF)
+ return chr(($num>>18)+240).chr((($num>>12)&63)+128).chr((($num>>6)&63)+128).chr(($num&63)+128);
+ return '';
+ }
+}
diff --git a/src/token.php b/src/token.php
index 1d5caf7f..2406483a 100755
--- a/src/token.php
+++ b/src/token.php
@@ -1,21 +1,29 @@
= 236 && $token < (236 + count(self::$secondaryStrings)))
- {
+ if (!$subdict && $token >= 236 && $token < (236 + count(self::$secondaryStrings))) {
$subdict = true;
}
- $tokenMap = array();
- if($subdict)
- {
+
+ if ($subdict) {
$tokenMap = self::$secondaryStrings;
- }
- else
- {
+ } else {
$tokenMap = self::$primaryStrings;
}
- if($token < 0 || $token > count($tokenMap))
- {
+ if ($token < 0 || $token > count($tokenMap)) {
return;//fail
}
+
$string = $tokenMap[$token];
- if(!$string)
- {
+ if (!$string) {
throw new Exception("Invalid token/length in GetToken");
}
}
-}
\ No newline at end of file
+}
diff --git a/src/vCard.php b/src/vCard.php
index 81c70976..a3bdc4bb 100755
--- a/src/vCard.php
+++ b/src/vCard.php
@@ -1,12 +1,11 @@
data = array(
- 'display_name' => null,
- 'first_name' => null,
- 'last_name' => null,
- 'additional_name' => null,
- 'name_prefix' => null,
- 'name_suffix' => null,
- 'nickname' => null,
- 'title' => null,
- 'role' => null,
- 'department' => null,
- 'company' => null,
- 'work_po_box' => null,
+ 'display_name' => null,
+ 'first_name' => null,
+ 'last_name' => null,
+ 'additional_name' => null,
+ 'name_prefix' => null,
+ 'name_suffix' => null,
+ 'nickname' => null,
+ 'title' => null,
+ 'role' => null,
+ 'department' => null,
+ 'company' => null,
+ 'work_po_box' => null,
'work_extended_address' => null,
- 'work_address' => null,
- 'work_city' => null,
- 'work_state' => null,
- 'work_postal_code' => null,
- 'work_country' => null,
- 'home_po_box' => null,
+ 'work_address' => null,
+ 'work_city' => null,
+ 'work_state' => null,
+ 'work_postal_code' => null,
+ 'work_country' => null,
+ 'home_po_box' => null,
'home_extended_address' => null,
- 'home_address' => null,
- 'home_city' => null,
- 'home_state' => null,
- 'home_postal_code' => null,
- 'home_country' => null,
- 'office_tel' => null,
- 'home_tel' => null,
- 'cell_tel' => null,
- 'fax_tel' => null,
- 'pager_tel' => null,
- 'email1' => null,
- 'email2' => null,
- 'url' => null,
- 'photo' => null,
- 'birthday' => null,
- 'timezone' => null,
- 'sort_string' => null,
- 'note' => null,
+ 'home_address' => null,
+ 'home_city' => null,
+ 'home_state' => null,
+ 'home_postal_code' => null,
+ 'home_country' => null,
+ 'office_tel' => null,
+ 'home_tel' => null,
+ 'cell_tel' => null,
+ 'fax_tel' => null,
+ 'pager_tel' => null,
+ 'email1' => null,
+ 'email2' => null,
+ 'url' => null,
+ 'photo' => null,
+ 'birthday' => null,
+ 'timezone' => null,
+ 'sort_string' => null,
+ 'note' => null,
);
return true;
@@ -71,7 +70,7 @@ public function __construct()
*
* @param string $key
* Name of the property.
- * @param mixed $value
+ * @param mixed $value
* Value to set.
*
* @return vCard
@@ -125,11 +124,11 @@ function build()
$this->card .= "REV:" . $this->revisionDate . "\r\n";
$this->card .= "FN:" . $this->data['display_name'] . "\r\n";
$this->card .= "N:"
- . $this->data['last_name'] . ";"
- . $this->data['first_name'] . ";"
- . $this->data['additional_name'] . ";"
- . $this->data['name_prefix'] . ";"
- . $this->data['name_suffix'] . "\r\n";
+ . $this->data['last_name'] . ";"
+ . $this->data['first_name'] . ";"
+ . $this->data['additional_name'] . ";"
+ . $this->data['name_prefix'] . ";"
+ . $this->data['name_suffix'] . "\r\n";
if ($this->data['nickname']) {
$this->card .= "NICKNAME:" . $this->data['nickname'] . "\r\n";
}
@@ -146,24 +145,24 @@ function build()
if ($this->data['work_po_box'] || $this->data['work_extended_address'] || $this->data['work_address'] || $this->data['work_city'] || $this->data['work_state'] || $this->data['work_postal_code'] || $this->data['work_country']) {
$this->card .= "ADR;type=WORK:"
- . $this->data['work_po_box'] . ";"
- . $this->data['work_extended_address'] . ";"
- . $this->data['work_address'] . ";"
- . $this->data['work_city'] . ";"
- . $this->data['work_state'] . ";"
- . $this->data['work_postal_code'] . ";"
- . $this->data['work_country'] . "\r\n";
+ . $this->data['work_po_box'] . ";"
+ . $this->data['work_extended_address'] . ";"
+ . $this->data['work_address'] . ";"
+ . $this->data['work_city'] . ";"
+ . $this->data['work_state'] . ";"
+ . $this->data['work_postal_code'] . ";"
+ . $this->data['work_country'] . "\r\n";
}
if ($this->data['home_po_box'] || $this->data['home_extended_address'] || $this->data['home_address'] || $this->data['home_city'] || $this->data['home_state'] || $this->data['home_postal_code'] || $this->data['home_country']) {
$this->card .= "ADR;type=HOME:"
- . $this->data['home_po_box'] . ";"
- . $this->data['home_extended_address'] . ";"
- . $this->data['home_address'] . ";"
- . $this->data['home_city'] . ";"
- . $this->data['home_state'] . ";"
- . $this->data['home_postal_code'] . ";"
- . $this->data['home_country'] . "\r\n";
+ . $this->data['home_po_box'] . ";"
+ . $this->data['home_extended_address'] . ";"
+ . $this->data['home_address'] . ";"
+ . $this->data['home_city'] . ";"
+ . $this->data['home_state'] . ";"
+ . $this->data['home_postal_code'] . ";"
+ . $this->data['home_country'] . "\r\n";
}
if ($this->data['email1']) {
$this->card .= "EMAIL;type=INTERNET,pref:" . $this->data['email1'] . "\r\n";
@@ -198,8 +197,7 @@ function build()
if ($this->data['note']) {
$this->card .= "NOTE:" . $this->data['note'] . "\r\n";
}
- if($this->data['photo'])
- {
+ if ($this->data['photo']) {
$this->card .= $this->generatePhotoData();
}
$this->card .= "TZ:" . $this->data['timezone'] . "\r\n";
@@ -209,16 +207,13 @@ function build()
protected function generatePhotoData()
{
$photo = $this->data['photo'];
- $data = "PHOTO;";
+ $data = "PHOTO;";
//detect type
- if(substr($photo, 0, 4) == 'http')
- {
+ if (substr($photo, 0, 4) == 'http') {
//url
$data .= 'URL:' . $photo;
- }
- else
- {
+ } else {
//path
$bindata = file_get_contents($photo);
$bindata = base64_encode($bindata);
@@ -230,6 +225,8 @@ protected function generatePhotoData()
/**
* Streams the vcard to the browser client.
+ *
+ * @return bool
*/
function download()
{
@@ -252,7 +249,9 @@ function download()
}
/**
- * Show the vcard.
+ * Show the vCard.
+ *
+ * @return object vCard
*/
function show()
{
@@ -262,5 +261,4 @@ function show()
return $this->card;
}
-
}
diff --git a/src/wadata/media/.gitkeep b/src/wadata/media/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/src/wadata/pictures/.gitkeep b/src/wadata/pictures/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/src/whatsprot.class.php b/src/whatsprot.class.php
index f11fc9ec..65e3ca5f 100755
--- a/src/whatsprot.class.php
+++ b/src/whatsprot.class.php
@@ -1,12 +1,16 @@
writer = new BinTreeNodeWriter();
$this->reader = new BinTreeNodeReader();
$this->debug = $debug;
$this->phoneNumber = $number;
- if (!$this->checkIdentity($identity)) {
- //compute identity with pseudo_random_bytes
- $this->identity = $this->buildIdentity($identity);
- } else {
- //use provided identity hash
- $this->identity = file_get_contents($identity.'.dat');
- }
- $this->name = $nickname;
- $this->loginStatus = static::DISCONNECTED_STATUS;
+
+ //e.g. ./cache/nextChallenge.12125557788.dat
+ $this->challengeFilename = sprintf('%s%s%snextChallenge.%s.dat',
+ __DIR__,
+ DIRECTORY_SEPARATOR,
+ Constants::DATA_FOLDER . DIRECTORY_SEPARATOR,
+ $number);
+
+ $this->identity = $this->buildIdentity($identityFile);
+
+ $this->name = $nickname;
+ $this->loginStatus = Constants::DISCONNECTED_STATUS;
+ $this->eventManager = new WhatsApiEventsManager();
}
/**
- * If you need use diferent challenge fileName you can use this
+ * If you need use different challenge fileName you can use this
*
* @param string $filename
*/
- public function setChallengeName($filename){
+ public function setChallengeName($filename)
+ {
$this->challengeFilename = $filename;
}
/**
* Add message to the outgoing queue.
+ *
+ * @param $node
*/
public function addMsgOutQueue($node)
{
@@ -154,51 +149,48 @@ public function checkCredentials()
throw new Exception('The provided phone number is not valid.');
}
- if ($countryCode == null && $phone['ISO3166'] != '') {
- $countryCode = $phone['ISO3166'];
- }
- if ($countryCode == null) {
- $countryCode = 'US';
- }
- if ($langCode == null && $phone['ISO639'] != '') {
- $langCode = $phone['ISO639'];
- }
- if ($langCode == null) {
- $langCode = 'en';
- }
+ $countryCode = ($phone['ISO3166'] != '') ? $phone['ISO3166'] : 'US';
+ $langCode = ($phone['ISO639'] != '') ? $phone['ISO639'] : 'en';
// Build the url.
- $host = 'https://' . static::WHATSAPP_CHECK_HOST;
+ $host = 'https://' . Constants::WHATSAPP_CHECK_HOST;
$query = array(
'cc' => $phone['cc'],
'in' => $phone['phone'],
'id' => $this->identity,
'lg' => $langCode,
'lc' => $countryCode,
+ // 'network_radio_type' => "1"
);
$response = $this->getResponse($host, $query);
if ($response->status != 'ok') {
- $this->eventManager()->fireCredentialsBad($this->phoneNumber, $response->status, $response->reason);
- if ($this->debug) {
- print_r($query);
- print_r($response);
- }
+ $this->eventManager()->fire("onCredentialsBad",
+ array(
+ $this->phoneNumber,
+ $response->status,
+ $response->reason
+ ));
+
+ $this->debugPrint($query);
+ $this->debugPrint($response);
+
throw new Exception('There was a problem trying to request the code.');
} else {
- $this->eventManager()->fireCredentialsGood(
- $this->phoneNumber,
- $response->login,
- $response->pw,
- $response->type,
- $response->expiration,
- $response->kind,
- $response->price,
- $response->cost,
- $response->currency,
- $response->price_expiration
- );
+ $this->eventManager()->fire("onCredentialsGood",
+ array(
+ $this->phoneNumber,
+ $response->login,
+ $response->pw,
+ $response->type,
+ $response->expiration,
+ $response->kind,
+ $response->price,
+ $response->cost,
+ $response->currency,
+ $response->price_expiration
+ ));
}
return $response;
@@ -231,58 +223,54 @@ public function codeRegister($code)
throw new Exception('The provided phone number is not valid.');
}
- if ($countryCode == null && $phone['ISO3166'] != '') {
- $countryCode = $phone['ISO3166'];
- }
- if ($countryCode == null) {
- $countryCode = 'US';
- }
- if ($langCode == null && $phone['ISO639'] != '') {
- $langCode = $phone['ISO639'];
- }
- if ($langCode == null) {
- $langCode = 'en';
- }
+ //$countryCode = ($phone['ISO3166'] != '') ? $phone['ISO3166'] : 'US';
+ //$langCode = ($phone['ISO639'] != '') ? $phone['ISO639'] : 'en';
// Build the url.
- $host = 'https://' . static::WHATSAPP_REGISTER_HOST;
+ $host = 'https://' . Constants::WHATSAPP_REGISTER_HOST;
$query = array(
'cc' => $phone['cc'],
'in' => $phone['phone'],
'id' => $this->identity,
'code' => $code,
- 'lg' => $langCode,
- 'lc' => $countryCode,
+ //'lg' => $langCode,
+ //'lc' => $countryCode,
+ //'network_radio_type' => "1"
);
$response = $this->getResponse($host, $query);
if ($response->status != 'ok') {
- $this->eventManager()->fireCodeRegisterFailed(
- $this->phoneNumber,
- $response->status,
- $response->reason,
- $response->retry_after
- );
- if ($this->debug) {
- print_r($query);
- print_r($response);
- }
- throw new Exception('An error occurred registering the registration code from WhatsApp.');
+ $this->eventManager()->fire("onCodeRegisterFailed",
+ array(
+ $this->phoneNumber,
+ $response->status,
+ $response->reason,
+ isset($response->retry_after) ? $response->retry_after : null
+ ));
+
+ $this->debugPrint($query);
+ $this->debugPrint($response);
+
+ if ($response->reason == 'old_version')
+ $this->update();
+
+ throw new Exception("An error occurred registering the registration code from WhatsApp. Reason: $response->reason");
} else {
- $this->eventManager()->fireCodeRegister(
- $this->phoneNumber,
- $response->login,
- $response->pw,
- $response->type,
- $response->expiration,
- $response->kind,
- $response->price,
- $response->cost,
- $response->currency,
- $response->price_expiration
- );
+ $this->eventManager()->fire("onCodeRegister",
+ array(
+ $this->phoneNumber,
+ $response->login,
+ $response->pw,
+ $response->type,
+ $response->expiration,
+ $response->kind,
+ $response->price,
+ $response->cost,
+ $response->currency,
+ $response->price_expiration
+ ));
}
return $response;
@@ -291,12 +279,8 @@ public function codeRegister($code)
/**
* Request a registration code from WhatsApp.
*
- * @param string $method
- * Accepts only 'sms' or 'voice' as a value.
- * @param string $countryCode
- * ISO Country Code, 2 Digit.
- * @param string $langCode
- * ISO 639-1 Language Code: two-letter codes.
+ * @param string $method Accepts only 'sms' or 'voice' as a value.
+ * @param string $carrier
*
* @return object
* An object with server response.
@@ -309,165 +293,206 @@ public function codeRegister($code)
*
* @throws Exception
*/
- public function codeRequest($method = 'sms', $countryCode = null, $langCode = null)
+ public function codeRequest($method = 'sms', $carrier = "T-Mobile5")
{
if (!$phone = $this->dissectPhone()) {
throw new Exception('The provided phone number is not valid.');
}
- if ($countryCode == null && $phone['ISO3166'] != '') {
- $countryCode = $phone['ISO3166'];
- }
- if ($countryCode == null) {
- $countryCode = 'US';
- }
- if ($langCode == null && $phone['ISO639'] != '') {
- $langCode = $phone['ISO639'];
- }
- if ($langCode == null) {
- $langCode = 'en';
+ $countryCode = ($phone['ISO3166'] != '') ? $phone['ISO3166'] : 'US';
+ $langCode = ($phone['ISO639'] != '') ? $phone['ISO639'] : 'en';
+
+ if ($carrier != null) {
+ $mnc = $this->detectMnc(strtolower($countryCode), $carrier);
+ } else {
+ $mnc = $phone['mnc'];
}
// Build the token.
$token = generateRequestToken($phone['country'], $phone['phone']);
// Build the url.
- $host = 'https://' . static::WHATSAPP_REQUEST_HOST;
+ $host = 'https://' . Constants::WHATSAPP_REQUEST_HOST;
$query = array(
- 'method' => $method,
'in' => $phone['phone'],
'cc' => $phone['cc'],
'id' => $this->identity,
'lg' => $langCode,
'lc' => $countryCode,
- 'token' => urlencode($token),
- 'sim_mcc' => '000', //$phone['mcc']
- 'sim_mnc' => '000', // 001
- //'reason' => 'jailbroken',
+ //'mcc' => '000',
+ //'mnc' => '000',
+ 'sim_mcc' => $phone['mcc'],
+ 'sim_mnc' => $mnc,
+ 'method' => $method,
+ //'reason' => "self-send-jailbroken",
+ 'token' => $token,
+ //'network_radio_type' => "1"
);
- if ($this->debug) {
- print_r($query);
- }
+ $this->debugPrint($query);
$response = $this->getResponse($host, $query);
- if ($this->debug) {
- print_r($response);
- }
+ $this->debugPrint($response);
if ($response->status == 'ok') {
- $this->eventManager()->fireCodeRegister(
- $this->phoneNumber,
- $response->login,
- $response->pw,
- $response->type,
- $response->expiration,
- $response->kind,
- $response->price,
- $response->cost,
- $response->currency,
- $response->price_expiration
- );
+ $this->eventManager()->fire("onCodeRegister",
+ array(
+ $this->phoneNumber,
+ $response->login,
+ $response->pw,
+ $response->type,
+ $response->expiration,
+ $response->kind,
+ $response->price,
+ $response->cost,
+ $response->currency,
+ $response->price_expiration
+ ));
} else if ($response->status != 'sent') {
if (isset($response->reason) && $response->reason == "too_recent") {
- $this->eventManager()->fireCodeRequestFailedTooRecent(
- $this->phoneNumber,
- $method,
- $response->reason,
- $response->retry_after
- );
+ $this->eventManager()->fire("onCodeRequestFailedTooRecent",
+ array(
+ $this->phoneNumber,
+ $method,
+ $response->reason,
+ $response->retry_after
+ ));
$minutes = round($response->retry_after / 60);
throw new Exception("Code already sent. Retry after $minutes minutes.");
- } else {
- $this->eventManager()->fireCodeRequestFailed(
- $this->phoneNumber,
- $method,
- $response->reason,
- $response->param
- );
+
+ } else if (isset($response->reason) && $response->reason == "too_many_guesses") {
+ $this->eventManager()->fire("onCodeRequestFailedTooManyGuesses",
+ array(
+ $this->phoneNumber,
+ $method,
+ $response->reason,
+ $response->retry_after
+ ));
+ $minutes = round($response->retry_after / 60);
+ throw new Exception("Too many guesses. Retry after $minutes minutes.");
+
+ } else {
+ $this->eventManager()->fire("onCodeRequestFailed",
+ array(
+ $this->phoneNumber,
+ $method,
+ $response->reason,
+ isset($response->param) ? $response->param : NULL
+ ));
throw new Exception('There was a problem trying to request the code.');
}
} else {
- $this->eventManager()->fireCodeRequest(
- $this->phoneNumber,
- $method,
- $response->length
- );
+ $this->eventManager()->fire("onCodeRequest",
+ array(
+ $this->phoneNumber,
+ $method,
+ $response->length
+ ));
}
return $response;
}
+ public function update()
+ {
+ $WAData = json_decode(file_get_contents(Constants::WHATSAPP_VER_CHECKER), true);
+ $WAver = $WAData['e'];
+
+ if(Constants::WHATSAPP_VER != $WAver)
+ {
+ updateData('token.php', null, $WAData['h']);
+ updateData('Constants.php', $WAver);
+ }
+ }
+
/**
* Connect (create a socket) to the WhatsApp network.
+ *
+ * @return bool
*/
public function connect()
{
-
- $WAver = trim(file_get_contents(static::WHATSAPP_VER_CHECKER));
-
- $WAverS = str_replace(".","",$WAver);
- $ver = str_replace(".","",static::WHATSAPP_VER);
-
- if($ver>=$WAverS)
- {
- echo "\nUp to date :)\n\n";
+ if ($this->isConnected()) {
+ return true;
}
- else{
- $classesMD5 = file_get_contents('https://mgp25.com/WAToken/WhatsApp.token');
- updateData('token.php', $WAver, $classesMD5);
- updateData('whatsprot.class.php', $WAver);
-
- echo "\n Token, User Agent and version were updated :)\n";
+ /* Create a TCP/IP socket. */
+ $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
+ if ($socket !== false) {
+ $result = socket_connect($socket, "e" . rand(1, 16) . ".whatsapp.net", Constants::PORT);
+ if ($result === false) {
+ $socket = false;
+ }
}
- $Socket = fsockopen(static::WHATSAPP_HOST, static::PORT);
- if ($Socket !== false) {
- stream_set_timeout($Socket, static::TIMEOUT_SEC, static::TIMEOUT_USEC);
- $this->socket = $Socket;
- $this->eventManager()->fireConnect(
- $this->phoneNumber,
- $this->socket
+ if ($socket !== false) {
+ socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array('sec' => Constants::TIMEOUT_SEC, 'usec' => Constants::TIMEOUT_USEC));
+ socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array('sec' => Constants::TIMEOUT_SEC, 'usec' => Constants::TIMEOUT_USEC));
+
+ $this->socket = $socket;
+ $this->eventManager()->fire("onConnect",
+ array(
+ $this->phoneNumber,
+ $this->socket
+ )
);
+ return true;
} else {
- if ($this->debug) {
- print_r("Firing onConnectError\n");
- }
- $this->eventManager()->fireConnectError(
- $this->phoneNumber,
- $this->socket
+ $this->eventManager()->fire("onConnectError",
+ array(
+ $this->phoneNumber,
+ $this->socket
+ )
);
+ return false;
}
}
+ /**
+ * Do we have an active socket connection to WhatsApp?
+ *
+ * @return bool
+ */
+ public function isConnected()
+ {
+ return ($this->socket !== null);
+ }
+
+ /**
+ * Reconnect
+ */
+ public function reconnect()
+ {
+ $this->connect();
+ $this->loginWithPassword($this->password);
+ }
+
/**
* Disconnect from the WhatsApp network.
*/
public function disconnect()
{
if (is_resource($this->socket)) {
- fclose($this->socket);
- $this->eventManager()->fireDisconnect(
- $this->phoneNumber,
- $this->socket
+ @socket_shutdown($this->socket, 2);
+ @socket_close($this->socket);
+ $this->socket = null;
+ $this->loginStatus = Constants::DISCONNECTED_STATUS;
+ $this->eventManager()->fire("onDisconnect",
+ array(
+ $this->phoneNumber,
+ $this->socket
+ )
);
}
}
/**
- * Gets a new micro event dispatcher.
- *
- * @return WhatsAppEvent The event manager.
+ * @return WhatsApiEventsManager
*/
public function eventManager()
{
- if (!is_object($this->event)) {
- $this->event = new WhatsAppEvent();
- }
-
- return $this->event;
+ return $this->eventManager;
}
/**
@@ -485,7 +510,7 @@ public function getMessages()
}
/**
- * Log into the Whatsapp server.
+ * Log into the WhatsApp server.
*
* ###Warning### using this method will generate a new password
* from the WhatsApp servers each time.
@@ -497,16 +522,14 @@ public function login()
{
$this->accountInfo = (array) $this->checkCredentials();
if ($this->accountInfo['status'] == 'ok') {
- if ($this->debug) {
- print_r("New password received: " . $this->accountInfo['pw'] . "\n");
- }
+ $this->debugPrint("New password received: " . $this->accountInfo['pw'] . "\n");
$this->password = $this->accountInfo['pw'];
}
$this->doLogin();
}
/**
- * Login to the Whatsapp server with your password
+ * Login to the WhatsApp server with your password
*
* If you already know your password you can log into the Whatsapp server
* using this method.
@@ -516,26 +539,59 @@ public function login()
public function loginWithPassword($password)
{
$this->password = $password;
- $challengeData = @file_get_contents($this->challengeFilename);
- if($challengeData) {
- $this->challengeData = $challengeData;
+ if (is_readable($this->challengeFilename)) {
+ $challengeData = file_get_contents($this->challengeFilename);
+ if ($challengeData) {
+ $this->challengeData = $challengeData;
+ }
}
$this->doLogin();
}
/**
* Fetch a single message node
- * @param bool $autoReceipt
+ * @param bool $autoReceipt
+ * @param string $type
* @return bool
+ *
+ * @throws Exception
*/
- public function pollMessage($autoReceipt = true)
+ public function pollMessage($autoReceipt = true, $type = "read")
{
- $stanza = $this->readStanza();
- if($stanza)
+ if (!$this->isConnected()) {
+ throw new ConnectionException('Connection Closed!');
+ }
+
+ $r = array($this->socket);
+ $w = array();
+ $e = array();
+ $s = socket_select($r, $w, $e, Constants::TIMEOUT_SEC, Constants::TIMEOUT_USEC);
+
+ if ($s) {
+ // Something to read
+ if ($stanza = $this->readStanza()) {
+ $this->processInboundData($stanza, $autoReceipt, $type);
+ $this->timeout = null;
+ return true;
+ }
+ else {
+ $s = 0;
+ }
+ }
+
+ if ($s == 0)
{
- $this->processInboundData($stanza, $autoReceipt);
- return true;
+ if (!isset($this->timeout))
+ $this->timeout = time();
+
+ if ((time() - $this->timeout) > 300)
+ {
+ $this->timeout = null;
+ $this->disconnect();
+ throw new ConnectionException('Connectivity error');
+ }
}
+
return false;
}
@@ -548,111 +604,180 @@ public function sendActiveStatus()
$this->sendNode($messageNode);
}
+ /**
+ * Send a request to get cipher keys from an user
+ *
+ * @param $number
+ * Phone number of the user you want to get the cipher keys.
+ */
+ public function sendGetCipherKeysFromUser($number)
+ {
+ $msgId = $this->createIqId();
+
+ $userNode = new ProtocolNode("user",
+ array(
+ "jid" => $this->getJID($number)
+ ), null, null);
+ $keyNode = new ProtocolNode("key", null, array($userNode), null);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "xmlns" => "encrypt",
+ "type" => "get",
+ "to" => Constants::WHATSAPP_SERVER
+ ), array($keyNode), null);
+
+ $this->sendNode($node);
+ }
+
/**
* Send a Broadcast Message with audio.
*
- * The receiptiant MUST have your number (synced) and in their contact list
+ * The recipients MUST have your number (synced) and in their contact list
* otherwise the message will not deliver to that person.
*
* Approx 20 (unverified) is the maximum number of targets
*
- * @param array $targets An array of numbers to send to.
- * @param string $path URL or local path to the audio file to send
- * @param bool $storeURLmedia Keep a copy of the audio file on your server
+ * @param array $targets An array of numbers to send to.
+ * @param string $path URL or local path to the audio file to send
+ * @param bool $storeURLmedia Keep a copy of the audio file on your server
+ * @param int $fsize
+ * @param string $fhash
+ * @return string|null Message ID if successfully, null if not.
*/
public function sendBroadcastAudio($targets, $path, $storeURLmedia = false, $fsize = 0, $fhash = "")
{
if (!is_array($targets)) {
$targets = array($targets);
}
- $this->sendMessageAudio($targets, $path, $storeURLmedia, $fsize, $fhash);
+ // Return message ID. Make pull request for this.
+ return $this->sendMessageAudio($targets, $path, $storeURLmedia, $fsize, $fhash);
}
/**
* Send a Broadcast Message with an image.
*
- * The receiptiant MUST have your number (synced) and in their contact list
+ * The recipients MUST have your number (synced) and in their contact list
* otherwise the message will not deliver to that person.
*
* Approx 20 (unverified) is the maximum number of targets
*
- * @param array $targets An array of numbers to send to.
- * @param string $path URL or local path to the image file to send
- * @param bool $storeURLmedia Keep a copy of the audio file on your server
+ * @param array $targets An array of numbers to send to.
+ * @param string $path URL or local path to the image file to send
+ * @param bool $storeURLmedia Keep a copy of the audio file on your server
+ * @param int $fsize
+ * @param string $fhash
+ * @param string $caption
+ * @return string|null Message ID if successfully, null if not.
*/
- public function sendBroadcastImage($targets, $path, $storeURLmedia = false, $fsize = 0, $fhash = "")
+ public function sendBroadcastImage($targets, $path, $storeURLmedia = false, $fsize = 0, $fhash = "", $caption = "")
{
if (!is_array($targets)) {
$targets = array($targets);
}
- $this->sendMessageImage($targets, $path, $storeURLmedia, $fsize, $fhash);
+ // Return message ID. Make pull request for this.
+ return $this->sendMessageImage($targets, $path, $storeURLmedia, $fsize, $fhash, $caption);
}
/**
* Send a Broadcast Message with location data.
*
- * The receiptiant MUST have your number (synced) and in their contact list
+ * The recipients MUST have your number (synced) and in their contact list
* otherwise the message will not deliver to that person.
*
* If no name is supplied , receiver will see large sized google map
* thumbnail of entered Lat/Long but NO name/url for location.
*
* With name supplied, a combined map thumbnail/name box is displayed
-
* Approx 20 (unverified) is the maximum number of targets
*
- * @param array $targets An array of numbers to send to.
- * @param float $long The longitude of the location eg 54.31652
- * @param float $lat The latitude if the location eg -6.833496
- * @param string $name (Optional) A name to describe the location
- * @param string $url (Optional) A URL to link location to web resource
+ * @param array $targets An array of numbers to send to.
+ * @param float $long The longitude of the location eg 54.31652
+ * @param float $lat The latitude if the location eg -6.833496
+ * @param string $name (Optional) A name to describe the location
+ * @param string $url (Optional) A URL to link location to web resource
+ * @return string Message ID
*/
-
-
public function sendBroadcastLocation($targets, $long, $lat, $name = null, $url = null)
{
if (!is_array($targets)) {
$targets = array($targets);
}
- $this->sendMessageLocation($targets, $long, $lat, $name, $url);
+ // Return message ID. Make pull request for this.
+ return $this->sendMessageLocation($targets, $long, $lat, $name, $url);
}
/**
* Send a Broadcast Message
*
- * The receiptiant MUST have your number (synced) and in their contact list
+ * The recipients MUST have your number (synced) and in their contact list
* otherwise the message will not deliver to that person.
*
* Approx 20 (unverified) is the maximum number of targets
*
- * @param array $targets An array of numbers to send to.
- * @param string $message Your message
+ * @param array $targets An array of numbers to send to.
+ * @param string $message Your message
+ * @return string Message ID
*/
public function sendBroadcastMessage($targets, $message)
{
- $message = $this->parseMessageForEmojis($message);
$bodyNode = new ProtocolNode("body", null, null, $message);
- $this->sendBroadcast($targets, $bodyNode, "text");
+ // Return message ID. Make pull request for this.
+ return $this->sendBroadcast($targets, $bodyNode, "text");
}
/**
* Send a Broadcast Message with a video.
*
- * The receiptiant MUST have your number (synced) and in their contact list
+ * The recipients MUST have your number (synced) and in their contact list
* otherwise the message will not deliver to that person.
*
* Approx 20 (unverified) is the maximum number of targets
*
- * @param array $targets An array of numbers to send to.
- * @param string $path URL or local path to the video file to send
- * @param bool $storeURLmedia Keep a copy of the audio file on your server
+ * @param array $targets An array of numbers to send to.
+ * @param string $path URL or local path to the video file to send
+ * @param bool $storeURLmedia Keep a copy of the audio file on your server
+ * @param int $fsize
+ * @param string $fhash
+ * @param string $caption
+ * @return string|null Message ID if successfully, null if not.
*/
- public function sendBroadcastVideo($targets, $path, $storeURLmedia = false, $fsize = 0, $fhash = "")
+ public function sendBroadcastVideo($targets, $path, $storeURLmedia = false, $fsize = 0, $fhash = "", $caption = "")
{
if (!is_array($targets)) {
$targets = array($targets);
}
- $this->sendMessageVideo($targets, $path, $storeURLmedia, $fsize, $fhash);
+ // Return message ID. Make pull request for this.
+ return $this->sendMessageVideo($targets, $path, $storeURLmedia, $fsize, $fhash, $caption);
+ }
+
+ /**
+ * Delete Broadcast lists
+ *
+ * @param string array $lists
+ * Contains the broadcast-id list
+ */
+ public function sendDeleteBroadcastLists($lists)
+ {
+ $msgId = $this->createIqId();
+ $listNode = array();
+ if ($lists != null && count($lists) > 0) {
+ for ($i = 0; $i < count($lists); $i++) {
+ $listNode[$i] = new ProtocolNode("list", array("id" => $lists[$i]), null, null);
+ }
+ } else {
+ $listNode = null;
+ }
+ $deleteNode = new ProtocolNode("delete", null, $listNode, null);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "xmlns" => "w:b",
+ "type" => "set",
+ "to" => Constants::WHATSAPP_SERVER
+ ), array($deleteNode), null);
+
+ $this->sendNode($node);
}
/**
@@ -662,51 +787,85 @@ public function sendBroadcastVideo($targets, $path, $storeURLmedia = false, $fsi
*/
protected function sendClearDirty($categories)
{
- $msgId = $this->createMsgId("cleardirty");
+ $msgId = $this->createIqId();
$catnodes = array();
foreach ($categories as $category) {
$catnode = new ProtocolNode("clean", array("type" => $category), null, null);
$catnodes[] = $catnode;
}
- $node = new ProtocolNode("iq", array(
- "id" => $msgId,
- "type" => "set",
- "to" => "s.whatsapp.net",
- "xmlns" => "urn:xmpp:whatsapp:dirty"
- ), $catnodes, null);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "type" => "set",
+ "to" => Constants::WHATSAPP_SERVER,
+ "xmlns" => "urn:xmpp:whatsapp:dirty"
+ ), $catnodes, null);
+
$this->sendNode($node);
}
public function sendClientConfig()
{
- $phone = $this->dissectPhone();
-
$attr = array();
- $attr["platform"] = "none";
- $attr["lc"] = $phone["ISO3166"];
- $attr["lg"] = $phone["ISO639"];
+ $attr["platform"] = Constants::WHATSAPP_DEVICE;
+ $attr["version"] = Constants::WHATSAPP_VER;
$child = new ProtocolNode("config", $attr, null, "");
- $node = new ProtocolNode("iq", array("id" => $this->createMsgId("config"), "type" => "set", "xmlns" => "urn:xmpp:whatsapp:push", "to" => static::WHATSAPP_SERVER), array($child), null);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $this->createIqId(),
+ "type" => "set",
+ "xmlns" => "urn:xmpp:whatsapp:push",
+ "to" => Constants::WHATSAPP_SERVER
+ ), array($child), null);
+
$this->sendNode($node);
}
public function sendGetClientConfig()
{
- $msgId = $this->createMsgId("sendconfig");
- $child = new ProtocolNode("config", array("xmlns" => "urn:xmpp:whatsapp:push", "sound" => 'sound'), null, null);
- $node = new ProtocolNode("iq", array(
- "id" => $msgId,
- "type" => "set",
- "to" => static::WHATSAPP_SERVER
- ), array($child), null);
+ $msgId = $this->createIqId();
+ $child = new ProtocolNode("config", null, null, null);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "xmlns" => "urn:xmpp:whatsapp:push",
+ "type" => "get",
+ "to" => Constants::WHATSAPP_SERVER
+ ), array($child), null);
+
$this->sendNode($node);
$this->waitForServer($msgId);
}
/**
- * Send a request to return a list of groups user is currently participating
- * in.
+ * Transfer your number to new one
+ *
+ * @param string $number
+ * @param string $identity
+ */
+ public function sendChangeNumber($number, $identity)
+ {
+ $msgId = $this->createIqId();
+
+ $usernameNode = new ProtocolNode("username", null, null, $number);
+ $passwordNode = new ProtocolNode("password", null, null, urldecode($identity));
+
+ $modifyNode = new ProtocolNode("modify", null, array($usernameNode, $passwordNode), null);
+
+ $iqNode = new ProtocolNode("iq",
+ array(
+ "xmlns" => "urn:xmpp:whatsapp:account",
+ "id" => $msgId,
+ "type" => "get",
+ "to" => "c.us"
+ ), array($modifyNode), null);
+
+ $this->sendNode($iqNode);
+ }
+
+ /**
+ * Send a request to return a list of groups user is currently participating in.
*
* To capture this list you will need to bind the "onGetGroups" event.
*/
@@ -716,89 +875,116 @@ public function sendGetGroups()
}
/**
- * Send a request to get information about a specific group
+ * Send a request to get new Groups V2 info.
*
- * @param string $gjid The specific group id
+ * @param $groupID
+ * The group JID
*/
- public function sendGetGroupsInfo($gjid)
+ public function sendGetGroupV2Info($groupID)
{
- $msgId = $this->createMsgId("getgroupinfo");
+ $msgId = $this->nodeId['get_groupv2_info'] = $this->createIqId();
+
+ $queryNode = new ProtocolNode("query",
+ array(
+ "request" => "interactive"
+ ), null, null);
+
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "xmlns" => "w:g2",
+ "type" => "get",
+ "to" => $this->getJID($groupID)
+ ), array($queryNode), null);
- $child = new ProtocolNode("query", null, null, null);
- $node = new ProtocolNode("iq", array(
- "id" => $msgId,
- "type" => "get",
- "xmlns" => "w:g",
- "to" => $this->getJID($gjid)
- ), array($child), null);
$this->sendNode($node);
- $this->waitForServer($msgId);
}
+
/**
- * Send a request to return a list of groups user has started
- * in.
- *
- * To capture this list you will need to bind the "onGetGroups" event.
+ * Send a request to get a list of people you have currently blocked.
*/
- public function sendGetGroupsOwning()
+ public function sendGetPrivacyBlockedList()
{
- $this->sendGetGroupsFiltered("owning");
+ $msgId = $this->nodeId['privacy'] = $this->createIqId();
+ $child = new ProtocolNode("list",
+ array(
+ "name" => "default"
+ ), null, null);
+
+ $child2 = new ProtocolNode("query", array(), array($child), null);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "xmlns" => "jabber:iq:privacy",
+ "type" => "get"
+ ), array($child2), null);
+
+ $this->sendNode($node);
+ $this->waitForServer($msgId);
}
/**
- * Send a request to return a list of people participating in a specific
- * group.
- *
- * @param string $gjid The specific group id
+ * Send a request to get privacy settings.
*/
- public function sendGetGroupsParticipants($gjid)
+ public function sendGetPrivacySettings()
{
- $msgId = $this->createMsgId("getgroupparticipants");
+ $msgId = $this->nodeId['privacy_settings'] = $this->createIqId();
+ $privacyNode = new ProtocolNode("privacy", null, null, null);
+ $node = new ProtocolNode("iq",
+ array(
+ "to" => Constants::WHATSAPP_SERVER,
+ "id" => $msgId,
+ "xmlns" => "privacy",
+ "type" => "get"
+ ), array($privacyNode), null);
- $child = new ProtocolNode("list", null, null, null);
- $node = new ProtocolNode("iq", array(
- "id" => $msgId,
- "type" => "get",
- "xmlns" => "w:g",
- "to" => $this->getJID($gjid)
- ), array($child), null);
$this->sendNode($node);
-
$this->waitForServer($msgId);
}
/**
- * Send a request to get a list of people you have currently blocked
+ * Set privacy of 'last seen', status or profile picture to all, contacts or none.
+ *
+ * @param string $category
+ * Options: 'last', 'status' or 'profile'
+ * @param string $value
+ * Options: 'all', 'contacts' or 'none'
*/
- public function sendGetPrivacyBlockedList()
+ public function sendSetPrivacySettings($category, $value)
{
- $msgId = $this->createMsgId("getprivacy");
- $child = new ProtocolNode("list", array(
- "name" => "default"
- ), null, null);
- $child2 = new ProtocolNode("query", array(
- "xmlns" => "jabber:iq:privacy"
- ), array($child), null);
- $node = new ProtocolNode("iq", array(
- "id" => $msgId,
- "type" => "get"
- ), array($child2), null);
+ $msgId = $this->createIqId();
+ $categoryNode = new ProtocolNode("category",
+ array(
+ "name" => $category,
+ "value" => $value
+ ), null, null);
+
+ $privacyNode = new ProtocolNode("privacy", null, array($categoryNode), null);
+ $node = new ProtocolNode("iq",
+ array(
+ "to" => Constants::WHATSAPP_SERVER,
+ "type" => "set",
+ "id" => $msgId,
+ "xmlns" => "privacy"
+ ), array($privacyNode), null);
+
$this->sendNode($node);
$this->waitForServer($msgId);
}
/**
- * Get profile picture of specified user
+ * Get profile picture of specified user.
*
* @param string $number
* Number or JID of user
- *
* @param bool $large
* Request large picture
*/
public function sendGetProfilePicture($number, $large = false)
{
+ $msgId = $this->nodeId['getprofilepic'] = $this->createIqId();
+
$hash = array();
$hash["type"] = "image";
if (!$large) {
@@ -806,262 +992,558 @@ public function sendGetProfilePicture($number, $large = false)
}
$picture = new ProtocolNode("picture", $hash, null, null);
- $hash = array();
- $hash["id"] = $this->createMsgId("getpicture");
- $hash["type"] = "get";
- $hash["xmlns"] = "w:profile:picture";
- $hash["to"] = $this->getJID($number);
- $node = new ProtocolNode("iq", $hash, array($picture), null);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "type" => "get",
+ "xmlns" => "w:profile:picture",
+ "to" => $this->getJID($number)
+ ), array($picture), null);
+
$this->sendNode($node);
- $this->waitForServer($hash["id"]);
+ $this->waitForServer($msgId);
}
/**
- * Request to retrieve the last online time of specific user.
- *
- * @param string $to
- * Number or JID of user
+ * @param mixed $numbers Numbers to get profile profile photos of.
+ * @return bool
*/
- public function sendGetRequestLastSeen($to)
+ public function sendGetProfilePhotoIds($numbers)
{
- $queryNode = new ProtocolNode("query", null, null, null);
+ if (!is_array($numbers)) {
+ $numbers = array($numbers);
+ }
- $messageHash = array();
- $messageHash["to"] = $this->getJID($to);
- $messageHash["type"] = "get";
- $messageHash["id"] = $this->createMsgId("lastseen");
- $messageHash["xmlns"] = "jabber:iq:last";
+ $msgId = $this->createIqId();
- $messageNode = new ProtocolNode("iq", $messageHash, array($queryNode), "");
- $this->sendNode($messageNode);
- $this->waitForServer($messageHash["id"]);
+ $userNode = array();
+ for ($i=0; $i < count($numbers); $i++) {
+ $userNode[$i] = new ProtocolNode("user",
+ array(
+ "jid" => $this->getJID($numbers[$i])
+ ), null, null);
+ }
+
+ if (!sizeof($userNode)) {
+ return false;
+ }
+
+ $listNode = new ProtocolNode("list", null, $userNode, null);
+
+ $iqNode = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "xmlns" => "w:profile:picture",
+ "type" => "get"
+ ), array($listNode), null);
+
+ $this->sendNode($iqNode);
+ $this->waitForServer($msgId);
+
+ return true;
}
/**
- * Send a request to get the current server properties
+ * Send a request to get the current server properties.
*/
public function sendGetServerProperties()
{
+ $id = $this->createIqId();
$child = new ProtocolNode("props", null, null, null);
- $node = new ProtocolNode("iq", array(
- "id" => $this->createMsgId("getproperties"),
- "type" => "get",
- "xmlns" => "w",
- "to" => "s.whatsapp.net"
- ), array($child), null);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $id,
+ "type" => "get",
+ "xmlns" => "w",
+ "to" => Constants::WHATSAPP_SERVER
+ ), array($child), null);
+
$this->sendNode($node);
}
/**
- * Get the current status message of a specific user.
+ * Send a request to get the current service pricing.
*
- * @param string[] $jids The users' JIDs
+ * @param string $lg
+ * Language
+ * @param string $lc
+ * Country
*/
- public function sendGetStatuses($jids)
+ public function sendGetServicePricing($lg, $lc)
{
- if(!is_array($jids))
- {
- $jids = array($jids);
- }
- $children = array();
- foreach($jids as $jid)
- {
- $children[] = new ProtocolNode("user", array("jid" => $this->getJID($jid)), null, null);
- }
- $node = new ProtocolNode("iq", array(
- "to" => "s.whatsapp.net",
- "type" => "get",
- "xmlns" => "status",
- "id" => $this->createMsgId("getstatus")
- ), array(
- new ProtocolNode("status", null, $children, null)
- ), null);
+
+ $msgId = $this->createIqId();
+ $pricingNode = new ProtocolNode("pricing",
+ array(
+ "lg" => $lg,
+ "lc" => $lc
+ ), null, null);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "xmlns" => "urn:xmpp:whatsapp:account",
+ "type" => "get",
+ "to" => Constants::WHATSAPP_SERVER
+ ), array($pricingNode), null);
+
$this->sendNode($node);
}
/**
- * Create a group chat.
- *
- * @param string $subject
- * The group Subject
- * @param array $participants
- * An array with the participants numbers.
- *
- * @return string
- * The group ID.
+ * Send a request to extend the account.
*/
- public function sendGroupsChatCreate($subject, $participants = array())
+ public function sendExtendAccount()
{
- $groupHash = array();
- $groupHash["action"] = "create";
- $groupHash["subject"] = $subject;
- $group = new ProtocolNode("group", $groupHash, null, "");
- $setHash = array();
- $setHash["id"] = $this->createMsgId("creategroup");
- $setHash["type"] = "set";
- $setHash["xmlns"] = "w:g";
- $setHash["to"] = static::WHATSAPP_GROUP_SERVER;
- $groupNode = new ProtocolNode("iq", $setHash, array($group), "");
+ $msgId = $this->createIqId();
+ $extendingNode = new ProtocolNode("extend", null, null, null);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "xmlns" => "urn:xmpp:whatsapp:account",
+ "type" => "set",
+ "to" => Constants::WHATSAPP_SERVER
+ ), array($extendingNode), null);
- $this->sendNode($groupNode);
- $this->waitForServer($setHash["id"]);
- $groupId = $this->groupId;
+ $this->sendNode($node);
+ }
- if (count($participants) > 0) {
- $this->sendGroupsParticipantsAdd($groupId, $participants);
- }
+ /**
+ * Gets all the broadcast lists for an account.
+ */
+ public function sendGetBroadcastLists()
+ {
+ $msgId = $this->nodeId['get_lists'] = $this->createIqId();
+ $listsNode = new ProtocolNode("lists", null, null, null);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "xmlns" => "w:b",
+ "type" => "get",
+ "to" => Constants::WHATSAPP_SERVER
+ ), array($listsNode), null);
- return $groupId;
+ $this->sendNode($node);
}
- public function SendSetGroupSubject($gjid, $subject)
+ /**
+ * Send a request to get the normalized mobile number representing the JID.
+ *
+ * @param string $countryCode Country Code
+ * @param string $number Mobile Number
+ */
+ public function sendGetNormalizedJid($countryCode, $number)
{
- $child = new ProtocolNode("subject", array("value" => $subject), null, null);
- $node = new ProtocolNode("iq", array(
- "id" => $this->createMsgId("set_group_subject"),
- "type" => "set",
- "to" => $this->getJID($gjid),
- "xmlns" => "w:g"
- ), array($child), null);
+ $msgId = $this->createIqId();
+ $ccNode = new ProtocolNode("cc", null, null, $countryCode);
+ $inNode = new ProtocolNode("in", null, null, $number);
+ $normalizeNode = new ProtocolNode("normalize", null, array($ccNode, $inNode), null);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "xmlns" => "urn:xmpp:whatsapp:account",
+ "type" => "get",
+ "to" => Constants::WHATSAPP_SERVER
+ ), array($normalizeNode), null);
+
$this->sendNode($node);
}
/**
- * End or delete a group chat
+ * Removes an account from WhatsApp.
*
- * @param string $gjid The group ID
+ * @param string $lg Language
+ * @param string $lc Country
+ * @param string $feedback User Feedback
*/
- public function sendGroupsChatEnd($gjid)
+ public function sendRemoveAccount($lg = null, $lc = null, $feedback = null)
{
- $gjid = $this->getJID($gjid);
- $msgID = $this->createMsgId("endgroup");
+ $msgId = $this->createIqId();
+ if ($feedback != null && strlen($feedback) > 0)
+ {
+ if ($lg == null) {
+ $lg = "";
+ }
- $groupData = array();
- $groupData['id'] = $gjid;
- $groupNode = new ProtocolNode('group', $groupData, null, null);
+ if ($lc == null) {
+ $lc = "";
+ }
- $leaveData = array();
- $leaveData["action"] = "delete";
- $leaveNode = new ProtocolNode("leave", $leaveData, array($groupNode), null);
+ $child = new ProtocolNode("body",
+ array(
+ "lg" => $lg,
+ "lc" => $lc
+ ), null, $feedback);
+ $childNode = array($child);
+ } else {
+ $childNode = null;
+ }
- $iqData = array();
- $iqData["id"] = $msgID;
- $iqData["type"] = "set";
- $iqData["xmlns"] = "w:g";
- $iqData["to"] = static::WHATSAPP_GROUP_SERVER;
- $iqNode = new ProtocolNode("iq", $iqData, array($leaveNode), null);
+ $removeNode = new ProtocolNode("remove", null, $childNode, null);
+ $node = new ProtocolNode("iq",
+ array(
+ "to" => Constants::WHATSAPP_SERVER,
+ "xmlns" => "urn:xmpp:whatsapp:account",
+ "type" => "get",
+ "id" => $msgId
+ ), array($removeNode), null);
- $this->sendNode($iqNode);
- $this->waitForServer($msgID);
+ $this->sendNode($node);
+ $this->waitForServer($msgId);
}
/**
- * Leave a group chat
+ * Send a ping to the server.
+ */
+ public function sendPing()
+ {
+ $msgId = $this->createIqId();
+ $pingNode = new ProtocolNode("ping", null, null, null);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "xmlns" => "w:p",
+ "type" => "get",
+ "to" => Constants::WHATSAPP_SERVER
+ ), array($pingNode), null);
+
+ $this->sendNode($node);
+ }
+
+ /**
+ * Get VoIP information of a number or numbers.
+ *
+ * @param mixed $jids
+ */
+ public function sendGetHasVoipEnabled($jids)
+ {
+
+ $msgId = $this->createIqId();
+
+ if (!is_array($jids))
+ {
+ $jids = array($jids);
+ }
+ $userNode = array();
+ foreach ($jids as $jid)
+ {
+ $userNode[] = new ProtocolNode("user", array('jid' => $this->getJID($jid)), null, null);
+ }
+
+ $eligibleNode = new ProtocolNode("eligible", null, $userNode, null);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "xmlns" => "voip",
+ "type" => "get",
+ "to" => Constants::WHATSAPP_SERVER
+ ), array($eligibleNode), null);
+
+ $this->sendNode($node);
+ }
+
+ /**
+ * Get the current status message of a specific user.
+ *
+ * @param mixed $jids The users' JIDs
+ */
+ public function sendGetStatuses($jids)
+ {
+ if (!is_array($jids)) {
+ $jids = array($jids);
+ }
+
+ $children = array();
+ foreach ($jids as $jid) {
+ $children[] = new ProtocolNode("user", array("jid" => $this->getJID($jid)), null, null);
+ }
+
+ $iqId = $this->nodeId['getstatuses'] = $this->createIqId();
+
+ $node = new ProtocolNode("iq",
+ array(
+ "to" => Constants::WHATSAPP_SERVER,
+ "type" => "get",
+ "xmlns" => "status",
+ "id" => $iqId
+ ), array(
+ new ProtocolNode("status", null, $children, null)
+ ), null);
+
+ $this->sendNode($node);
+ $this->waitForServer($iqId);
+ }
+
+ /**
+ * Create a group chat.
+ *
+ * @param string $subject
+ * The group Subject
+ * @param array $participants
+ * An array with the participants numbers.
+ *
+ * @return string
+ * The group ID.
+ */
+ public function sendGroupsChatCreate($subject, $participants)
+ {
+ if (!is_array($participants)) {
+ $participants = array($participants);
+ }
+
+ $participantNode = array();
+ foreach ($participants as $participant) {
+ $participantNode[] = new ProtocolNode("participant", array(
+ "jid" => $this->getJID($participant)
+ ), null, null);
+ }
+
+ $id = $this->nodeId['groupcreate'] = $this->createIqId();
+
+ $createNode = new ProtocolNode("create",
+ array(
+ "subject" => $subject
+ ), $participantNode, null);
+
+ $iqNode = new ProtocolNode("iq",
+ array(
+ "xmlns" => "w:g2",
+ "id" => $id,
+ "type" => "set",
+ "to" => Constants::WHATSAPP_GROUP_SERVER
+ ), array($createNode), null);
+
+ $this->sendNode($iqNode);
+ $this->waitForServer($id);
+ $groupId = $this->groupId;
+
+ $this->eventManager()->fire("onGroupCreate",
+ array(
+ $this->phoneNumber,
+ $groupId
+ ));
+
+ return $groupId;
+ }
+
+ /**
+ * Change group's subject.
+ *
+ * @param string $gjid The group id
+ * @param string $subject The subject
+ */
+ public function sendSetGroupSubject($gjid, $subject)
+ {
+ $child = new ProtocolNode("subject", null, null, $subject);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $this->createIqId(),
+ "type" => "set",
+ "to" => $this->getJID($gjid),
+ "xmlns" => "w:g2"
+ ), array($child), null);
+
+ $this->sendNode($node);
+ }
+
+ /**
+ * Leave a group chat.
*
- * @param array $gjids An array of group IDs
+ * @param mixed $gjids Group or group's ID(s)
*/
public function sendGroupsLeave($gjids)
{
+ $msgId = $this->nodeId['leavegroup'] = $this->createIqId();
+
if (!is_array($gjids)) {
$gjids = array($this->getJID($gjids));
}
+
$nodes = array();
foreach ($gjids as $gjid) {
- $nodes[] = new ProtocolNode("group", array("id" => $this->getJID($gjid)), null, null);
+ $nodes[] = new ProtocolNode("group",
+ array(
+ "id" => $this->getJID($gjid)
+ ), null, null);
}
- $leave = new ProtocolNode("leave", array('action'=>'delete'), $nodes, null);
- $hash = array();
- $hash["id"] = $this->createMsgId("leavegroups");
- $hash["to"] = static::WHATSAPP_GROUP_SERVER;
- $hash["type"] = "set";
- $hash["xmlns"] = "w:g";
- $node = new ProtocolNode("iq", $hash, array($leave), null);
+
+ $leave = new ProtocolNode("leave",
+ array(
+ 'action'=>'delete'
+ ), $nodes, null);
+
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "to" => Constants::WHATSAPP_GROUP_SERVER,
+ "type" => "set",
+ "xmlns" => "w:g2"
+ ), array($leave), null);
+
$this->sendNode($node);
- $this->waitForServer($hash["id"]);
+ $this->waitForServer($msgId);
}
/**
* Add participant(s) to a group.
*
- * @param string $groupId
- * The group ID.
- * @param array $participants
- * An array with the participants numbers to add
+ * @param string $groupId The group ID.
+ * @param string $participants An array with the participants numbers to add
*/
- public function sendGroupsParticipantsAdd($groupId, $participants)
+ public function sendGroupsParticipantsAdd($groupId, $participant)
{
- if(!is_array($participants)) {
- $participants = array($participants);
- }
- $this->sendGroupsChangeParticipants($groupId, $participants, 'add');
+ $msgId = $this->createMsgId();
+ $this->sendGroupsChangeParticipants($groupId, $participant, 'add', $msgId);
}
/**
- * Remove participant(s) from a group.
+ * Remove participant from a group.
*
- * @param string $groupId
- * The group ID.
- * @param array $participants
- * An array with the participants numbers to remove
+ * @param string $groupId The group ID.
+ * @param string $participant The number of the participant you want to remove
*/
- public function sendGroupsParticipantsRemove($groupId, $participants)
+ public function sendGroupsParticipantsRemove($groupId, $participant)
{
- if(!is_array($participants)) {
- $participants = array($participants);
- }
- $this->sendGroupsChangeParticipants($groupId, $participants, 'remove');
+ $msgId = $this->createMsgId();
+ $this->sendGroupsChangeParticipants($groupId, $participant, 'remove', $msgId);
+ }
+
+ /**
+ * Promote participant of a group; Make a participant an admin of a group.
+ *
+ * @param string $gId The group ID.
+ * @param string $participant The number of the participant you want to promote
+ */
+ public function sendPromoteParticipants($gId, $participant)
+ {
+ $msgId = $this->createMsgId();
+ $this->sendGroupsChangeParticipants($gId, $participant, "promote", $msgId);
+ }
+
+ /**
+ * Demote participant of a group; remove participant of being admin of a group.
+ *
+ * @param string $gId The group ID.
+ * @param string $participant The number of the participant you want to demote
+ */
+ public function sendDemoteParticipants($gId, $participant)
+ {
+ $msgId = $this->createMsgId();
+ $this->sendGroupsChangeParticipants($gId, $participant, "demote", $msgId);
+ }
+
+ /**
+ * Lock group: participants cant change group subject or profile picture except admin.
+ *
+ * @param string $gId The group ID.
+ */
+ public function sendLockGroup($gId)
+ {
+ $msgId = $this->createIqId();
+ $lockedNode = new ProtocolNode("locked", null, null, null);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "xmlns" => "w:g2",
+ "type" => "set",
+ "to" => $this->getJID($gId)
+ ), array($lockedNode), null);
+
+ $this->sendNode($node);
+ $this->waitForServer($msgId);
+ }
+
+ /**
+ * Unlock group: Any participant can change group subject or profile picture.
+ *
+ *
+ * @param string $gId The group ID.
+ */
+ public function sendUnlockGroup($gId)
+ {
+ $msgId = $this->createIqId();
+ $unlockedNode = new ProtocolNode("unlocked", null, null, null);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "xmlns" => "w:g2",
+ "type" => "set",
+ "to" => $this->getJID($gId)
+ ), array($unlockedNode), null);
+
+ $this->sendNode($node);
+ $this->waitForServer($msgId);
}
/**
* Send a text message to the user/group.
*
- * @param $to
- * The recipient.
- * @param string $txt
- * The text message.
+ * @param string $to The recipient.
+ * @param string $txt The text message.
* @param $id
*
- * @return string
+ * @return string Message ID.
*/
public function sendMessage($to, $txt, $id = null)
{
- $txt = $this->parseMessageForEmojis($txt);
$bodyNode = new ProtocolNode("body", null, null, $txt);
$id = $this->sendMessageNode($to, $bodyNode, $id);
- $this->waitForServer($id);
+
+ if ($this->messageStore !== null) {
+ $this->messageStore->saveMessage($this->phoneNumber, $to, $txt, $id, time());
+ }
+
return $id;
}
/**
- * Send audio to the user/group. *
+ * Send a read receipt to a message.
*
- * @param $to
- * The recipient.
- * @param string $filepath
- * The url/uri to the audio file.
- * @param bool $storeURLmedia Keep copy of file
- * @return bool
+ * @param string $to The recipient.
+ * @param string $id
+ */
+ public function sendMessageRead($to, $id)
+ {
+ $messageNode = new ProtocolNode("receipt",
+ array(
+ "type" => "read",
+ "to" => $to,
+ "id" => $id
+ ), null, null);
+
+ $this->sendNode($messageNode);
+ }
+
+ /**
+ * Send audio to the user/group.
+ *
+ * @param string $to The recipient.
+ * @param string $filepath The url/uri to the audio file.
+ * @param bool $storeURLmedia Keep copy of file
+ * @param int $fsize
+ * @param string $fhash *
+ * @param bool $voice
+ * @return string|null Message ID if successfully, null if not.
*/
- public function sendMessageAudio($to, $filepath, $storeURLmedia = false, $fsize = 0, $fhash = "")
+ public function sendMessageAudio($to, $filepath, $storeURLmedia = false, $fsize = 0, $fhash = "", $voice = false)
{
- if ($fsize==0 || $fhash == "")
- {
- $allowedExtensions = array('3gp', 'caf', 'wav', 'mp3', 'wma', 'ogg', 'aif', 'aac', 'm4a');
- $size = 10 * 1024 * 1024; // Easy way to set maximum file size for this media type.
- return $this->sendCheckAndSendMedia($filepath, $size, $to, 'audio', $allowedExtensions, $storeURLmedia);
+ $this->voice = $voice;
+
+ if ($fsize == 0 || $fhash == "") {
+ $allowedExtensions = array('3gp', 'caf', 'wav', 'mp3', 'wma', 'ogg', 'aif', 'aac', 'm4a');
+ $size = 10 * 1024 * 1024; // Easy way to set maximum file size for this media type.
+ // Return message ID. Make pull request for this.
+ return $this->sendCheckAndSendMedia($filepath, $size, $to, 'audio', $allowedExtensions, $storeURLmedia);
+ } else {
+ // Return message ID. Make pull request for this.
+ return $this->sendRequestFileUpload($fhash, 'audio', $fsize, $filepath, $to);
}
- else{
- $this->sendRequestFileUpload($fhash, 'audio', $fsize, $filepath, $to);
- return true;
- }
}
/**
* Send the composing message status. When typing a message.
*
- * @param string $to
- * The recipient to send status to.
+ * @param string $to The recipient to send status to.
*/
public function sendMessageComposing($to)
{
@@ -1069,71 +1551,68 @@ public function sendMessageComposing($to)
}
/**
- * Send an image file to group/user
+ * Send an image file to group/user.
*
- * @param string $to
- * Recipient number
- * @param string $filepath
- * The url/uri to the image file.
- * @param bool $storeURLmedia Keep copy of file
- * @param int $fsize size of the media file
- * @param string $fhash base64 hash of the media file
- *
- * @return bool
+ * @param string $to Recipient number
+ * @param string $filepath The url/uri to the image file.
+ * @param bool $storeURLmedia Keep copy of file
+ * @param int $fsize size of the media file
+ * @param string $fhash base64 hash of the media file
+ * @param string $caption
+ * @return string|null Message ID if successfully, null if not.
*/
- public function sendMessageImage($to, $filepath, $storeURLmedia = false, $fsize = 0, $fhash = "")
+ public function sendMessageImage($to, $filepath, $storeURLmedia = false, $fsize = 0, $fhash = "", $caption = "")
{
- if ($fsize==0 || $fhash == "")
- {
- $allowedExtensions = array('jpg', 'jpeg', 'gif', 'png');
- $size = 5 * 1024 * 1024; // Easy way to set maximum file size for this media type.
- return $this->sendCheckAndSendMedia($filepath, $size, $to, 'image', $allowedExtensions, $storeURLmedia);
+ if ($fsize == 0 || $fhash == "") {
+ $allowedExtensions = array('jpg', 'jpeg', 'gif', 'png');
+ $size = 5 * 1024 * 1024; // Easy way to set maximum file size for this media type.
+ // Return message ID. Make pull request for this.
+ return $this->sendCheckAndSendMedia($filepath, $size, $to, 'image', $allowedExtensions, $storeURLmedia, $caption);
+ } else {
+ // Return message ID. Make pull request for this.
+ return $this->sendRequestFileUpload($fhash, 'image', $fsize, $filepath, $to, $caption);
}
- else{
- $this->sendRequestFileUpload($fhash, 'image', $fsize, $filepath, $to);
- return true;
- }
}
/**
* Send a location to the user/group.
*
- * If no name is supplied , receiver will see large sized google map
- * thumbnail of entered Lat/Long but NO name/url for location.
+ * If no name is supplied, the receiver will see a large google maps thumbnail of the lat/long,
+ * but NO name or url of the location.
*
- * With name supplied, a combined map thumbnail/name box is displayed
+ * When a name supplied, a combined map thumbnail/name box is displayed.
*
- * @param array|string $to The recipient(s) to send to.
- * @param float $long The longitude of the location eg 54.31652
- * @param float $lat The latitude if the location eg -6.833496
- * @param string $name (Optional) The custom name you would like to give this location.
- * @param string $url (Optional) A URL to attach to the location.
+ * @param mixed $to The recipient(s) to send the location to.
+ * @param float $long The longitude of the location, e.g. 54.31652.
+ * @param float $lat The latitude of the location, e.g. -6.833496.
+ * @param string $name (Optional) A custom name for the specified location.
+ * @param string $url (Optional) A URL to attach to the specified location.
+ * @return string Message ID
*/
public function sendMessageLocation($to, $long, $lat, $name = null, $url = null)
{
- $mediaHash = array();
- $mediaHash['xmlns'] = "urn:xmpp:whatsapp:mms";
- $mediaHash['type'] = "location";
- $mediaHash['latitude'] = $lat;
- $mediaHash['longitude'] = $long;
- $mediaHash['name'] = $name;
- $mediaHash['url'] = $url;
+ $mediaNode = new ProtocolNode("media",
+ array(
+ "type" => "location",
+ "encoding" => "raw",
+ "latitude" => $lat,
+ "longitude" => $long,
+ "name" => $name,
+ "url" => $url
+ ), null, null);
- $mediaNode = new ProtocolNode("media", $mediaHash, null, null);
+ $id = (is_array($to)) ? $this->sendBroadcast($to, $mediaNode, "media") : $this->sendMessageNode($to, $mediaNode);
- if (is_array($to)) {
- $id = $this->sendBroadcast($to, $mediaNode, "media");
- } else {
- $id = $this->sendMessageNode($to, $mediaNode);
- }
$this->waitForServer($id);
+
+ // Return message ID. Make pull request for this.
+ return $id;
}
/**
* Send the 'paused composing message' status.
*
- * @param string $to
- * The recipient number or ID.
+ * @param string $to The recipient number or ID.
*/
public function sendMessagePaused($to)
{
@@ -1142,35 +1621,36 @@ public function sendMessagePaused($to)
protected function sendChatState($to, $state)
{
- $node = new ProtocolNode("chatstate", array("to" => $this->getJID($to)), array(new ProtocolNode($state, null, null, null)), null);
+ $node = new ProtocolNode("chatstate",
+ array(
+ "to" => $this->getJID($to)
+ ), array(new ProtocolNode($state, null, null, null)), null);
+
$this->sendNode($node);
}
/**
* Send a video to the user/group.
*
- * @param string $to
- * The recipient to send.
- * @param string $filepath
- * The url/uri to the MP4/MOV video.
- * @param bool $storeURLmedia Keep a copy of media file.
- * @param int $fsize size of the media file
- * @param string $fhash base64 hash of the media file
- *
- * @return bool
- */
- public function sendMessageVideo($to, $filepath, $storeURLmedia = false, $fsize = 0, $fhash = "")
- {
- if ($fsize==0 || $fhash == "")
- {
- $allowedExtensions = array('3gp', 'mp4', 'mov', 'avi');
- $size = 20 * 1024 * 1024; // Easy way to set maximum file size for this media type.
- return $this->sendCheckAndSendMedia($filepath, $size, $to, 'video', $allowedExtensions, $storeURLmedia);
+ * @param string $to The recipient to send.
+ * @param string $filepath A URL/URI to the MP4/MOV video.
+ * @param bool $storeURLmedia Keep a copy of media file.
+ * @param int $fsize Size of the media file
+ * @param string $fhash base64 hash of the media file
+ * @param string $caption *
+ * @return string|null Message ID if successfully, null if not.
+ */
+ public function sendMessageVideo($to, $filepath, $storeURLmedia = false, $fsize = 0, $fhash = "", $caption = "")
+ {
+ if ($fsize == 0 || $fhash == "") {
+ $allowedExtensions = array('3gp', 'mp4', 'mov', 'avi');
+ $size = 20 * 1024 * 1024; // Easy way to set maximum file size for this media type.
+ // Return message ID. Make pull request for this.
+ return $this->sendCheckAndSendMedia($filepath, $size, $to, 'video', $allowedExtensions, $storeURLmedia, $caption);
+ } else {
+ // Return message ID. Make pull request for this.
+ return $this->sendRequestFileUpload($fhash, 'video', $fsize, $filepath, $to, $caption);
}
- else{
- $this->sendRequestFileUpload($fhash, 'video', $fsize, $filepath, $to);
- return true;
- }
}
/**
@@ -1198,34 +1678,35 @@ public function sendOfflineStatus()
}
/**
- * Send a pong to the whatsapp server. I'm alive!
+ * Send a pong to the WhatsApp server. I'm alive!
*
- * @param string $msgid
- * The id of the message.
+ * @param string $msgid The id of the message.
*/
public function sendPong($msgid)
{
- $messageHash = array();
- $messageHash["to"] = static::WHATSAPP_SERVER;
- $messageHash["id"] = $msgid;
- $messageHash["type"] = "result";
+ $messageNode = new ProtocolNode("iq",
+ array(
+ "to" => Constants::WHATSAPP_SERVER,
+ "id" => $msgid,
+ "type" => "result"
+ ), null, "");
- $messageNode = new ProtocolNode("iq", $messageHash, null, "");
$this->sendNode($messageNode);
- $this->eventManager()->fireSendPong(
- $this->phoneNumber,
- $msgid
- );
+ $this->eventManager()->fire("onSendPong",
+ array(
+ $this->phoneNumber,
+ $msgid
+ ));
}
public function sendAvailableForChat($nickname = null)
{
$presence = array();
- if($nickname)
- {
+ if ($nickname) {
//update nickname
$this->name = $nickname;
}
+
$presence['name'] = $this->name;
$node = new ProtocolNode("presence", $presence, null, "");
$this->sendNode($node);
@@ -1234,27 +1715,28 @@ public function sendAvailableForChat($nickname = null)
/**
* Send presence status.
*
- * @param string $type
- * The presence status.
+ * @param string $type The presence status.
*/
public function sendPresence($type = "active")
{
- $presence = array();
- $presence['type'] = $type;
- $node = new ProtocolNode("presence", $presence, null, "");
+ $node = new ProtocolNode("presence",
+ array(
+ "type" => $type
+ ), null, "");
+
$this->sendNode($node);
- $this->eventManager()->fireSendPresence(
- $this->phoneNumber,
- $presence['type'],
- $this->name
- );
+ $this->eventManager()->fire("onSendPresence",
+ array(
+ $this->phoneNumber,
+ $type,
+ $this->name
+ ));
}
/**
* Send presence subscription, automatically receive presence updates as long as the socket is open.
*
- * @param string $to
- * Phone number.
+ * @param string $to Phone number.
*/
public function sendPresenceSubscription($to)
{
@@ -1263,10 +1745,21 @@ public function sendPresenceSubscription($to)
}
/**
- * Set the picture for the group
+ * Unsubscribe, will stop subscription.
+ *
+ * @param string $to Phone number.
+ */
+ public function sendPresenceUnsubscription($to)
+ {
+ $node = new ProtocolNode("presence", array("type" => "unsubscribe", "to" => $this->getJID($to)), null, "");
+ $this->sendNode($node);
+ }
+
+ /**
+ * Set the picture for the group.
*
- * @param string $gjid The groupID
- * @param string $path The URL/URI of the image to use
+ * @param string $gjid The groupID
+ * @param string $path The URL/URI of the image to use
*/
public function sendSetGroupPicture($gjid, $path)
{
@@ -1276,201 +1769,211 @@ public function sendSetGroupPicture($gjid, $path)
/**
* Set the list of numbers you wish to block receiving from.
*
- * @param array $blockedJids Array of numbers to block messages from.
+ * @param mixed $blockedJids One or more numbers to block messages from.
*/
public function sendSetPrivacyBlockedList($blockedJids = array())
{
if (!is_array($blockedJids)) {
$blockedJids = array($blockedJids);
}
+
$items = array();
foreach ($blockedJids as $index => $jid) {
- $item = new ProtocolNode("item", array(
- "type" => "jid",
- "value" => $this->getJID($jid),
- "action" => "deny",
- "order" => $index + 1//WhatsApp stream crashes on zero index
- ), null, null);
+ $item = new ProtocolNode("item",
+ array(
+ "type" => "jid",
+ "value" => $this->getJID($jid),
+ "action" => "deny",
+ "order" => $index + 1//WhatsApp stream crashes on zero index
+ ), null, null);
$items[] = $item;
}
- $child = new ProtocolNode("list", array("name" => "default"), $items, null);
- $child2 = new ProtocolNode("query", array("xmlns" => "jabber:iq:privacy"), array($child), null);
- $node = new ProtocolNode("iq", array(
- "id" => $this->createMsgId("setprivacy"),
- "type" => "set"
- ), array($child2), null);
+
+ $child = new ProtocolNode("list",
+ array(
+ "name" => "default"
+ ), $items, null);
+
+ $child2 = new ProtocolNode("query", null, array($child), null);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $this->createIqId(),
+ "xmlns" => "jabber:iq:privacy",
+ "type" => "set"
+ ), array($child2), null);
+
$this->sendNode($node);
}
/**
- * Set your profile picture
+ * Set your profile picture.
*
- * @param string $path URL/URI of image
+ * @param string $path URL/URI of image
*/
public function sendSetProfilePicture($path)
{
$this->sendSetPicture($this->phoneNumber, $path);
}
+ /*
+ * Removes the profile photo.
+ */
+ public function sendRemoveProfilePicture()
+ {
+ $msgId = $this->createIqId();
+
+ $picture = new ProtocolNode("picture", null, null, null);
+
+ $thumb = new ProtocolNode("picture",
+ array(
+ "type" => "preview"
+ ), null, null);
+
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "to" => $this->getJID($this->phoneNumber),
+ "type" => "set",
+ "xmlns" => "w:profile:picture"
+ ), array($picture, $thumb), null);
+
+ $this->sendNode($node);
+ }
+
/**
- * Set the recovery token for your account to allow you to
- * retrieve your password at a later stage.
+ * Set the recovery token for your account to allow you to retrieve your password at a later stage.
+ *
* @param string $token A user generated token.
*/
public function sendSetRecoveryToken($token)
{
- $child = new ProtocolNode("pin", array("xmlns" => "w:ch:p"), null, $token);
- $node = new ProtocolNode("iq", array(
- "id" => $this->createMsgId("settoken"),
- "type" => "set",
- "to" => "s.whatsapp.net"
- ), array($child), null);
+ $child = new ProtocolNode("pin",
+ array(
+ "xmlns" => "w:ch:p"
+ ), null, $token);
+
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $this->createIqId(),
+ "type" => "set",
+ "to" => Constants::WHATSAPP_SERVER
+ ), array($child), null);
+
$this->sendNode($node);
}
/**
* Update the user status.
*
- * @param string $txt
- * The text of the message status to send.
+ * @param string $txt The text of the message status to send.
*/
public function sendStatusUpdate($txt)
{
$child = new ProtocolNode("status", null, null, $txt);
- $node = new ProtocolNode("iq", array(
- "to" => "s.whatsapp.net",
- "type" => "set",
- "id" => $this->createMsgId("sendstatus"),
- "xmlns" => "status"
- ), array($child), null);
+ $nodeID = $this->createIqId();
+ $node = new ProtocolNode("iq",
+ array(
+ "to" => Constants::WHATSAPP_SERVER,
+ "type" => "set",
+ "id" => $nodeID,
+ "xmlns" => "status"
+ ), array($child), null);
$this->sendNode($node);
- $this->eventManager()->fireSendStatusUpdate(
- $this->phoneNumber,
- $txt
- );
+ $this->waitForServer($nodeID);
+ $this->eventManager()->fire("onSendStatusUpdate",
+ array(
+ $this->phoneNumber,
+ $txt
+ ));
}
/**
* Send a vCard to the user/group.
*
- * @param $to
- * The recipient to send.
- * @param $name
- * The contact name.
- * @param $vCard
- * The contact vCard to send.
+ * @param string $to The recipient to send.
+ * @param string $name The contact name.
+ * @param object $vCard The contact vCard to send.
+ * @return string Message ID
*/
public function sendVcard($to, $name, $vCard)
{
- $vCardAttribs = array();
- $vCardAttribs['name'] = $name;
- $vCardNode = new ProtocolNode("vcard", $vCardAttribs, null, $vCard);
+ $vCardNode = new ProtocolNode("vcard",
+ array(
+ "name" => $name
+ ), null, $vCard);
- $mediaAttribs = array();
- $mediaAttribs["type"] = "vcard";
+ $mediaNode = new ProtocolNode("media",
+ array(
+ "type" => "vcard"
+ ), array($vCardNode), "");
- $mediaNode = new ProtocolNode("media", $mediaAttribs, array($vCardNode), "");
- $this->sendMessageNode($to, $mediaNode);
+ // Return message ID. Make pull request for this.
+ return $this->sendMessageNode($to, $mediaNode);
}
/**
* Send a vCard to the user/group as Broadcast.
*
- * @param $targets
- * Array of recipients to send.
- * @param $name
- * The vCard contact name.
- * @param $vCard
- * The contact vCard to send.
+ * @param array $targets An array of recipients to send to.
+ * @param string $name The vCard contact name.
+ * @param object $vCard The contact vCard to send.
+ * @return string Message ID
*/
- public function sendBroadcastVcard($targets, $name, $vCard)
+ public function sendBroadcastVcard($targets, $name, $vCard)
{
- $vCardAttribs = array();
- $vCardAttribs['name'] = $name;
- $vCardNode = new ProtocolNode("vcard", $vCardAttribs, null, $vCard);
+ $vCardNode = new ProtocolNode("vcard",
+ array(
+ "name" => $name
+ ), null, $vCard);
- $mediaAttribs = array();
- $mediaAttribs["type"] = "vcard";
+ $mediaNode = new ProtocolNode("media",
+ array(
+ "type" => "vcard"
+ ), array($vCardNode), "");
- $mediaNode = new ProtocolNode("media", $mediaAttribs, array($vCardNode), "");
- $this->sendBroadcast($targets, $mediaNode, "media");
+ // Return message ID. Make pull request for this.
+ return $this->sendBroadcast($targets, $mediaNode, "media");
}
- /**
- * Sets the bind of the new message.
- */
- public function setNewMessageBind($bind)
- {
- $this->newMsgBind = $bind;
- }
/**
- * Upload file to WhatsApp servers.
+ * Rejects a call
*
- * @param $file
- * The uri of the file.
- *
- * @return string|bool
- * Return the remote url or false on failure.
+ * @param array $to Phone number.
+ * @param string $id The main node id
+ * @param string $callId The call-id
*/
- public function uploadFile($file)
+ public function rejectCall($to, $id, $callId)
{
- $data['file'] = "@" . $file;
- $ch = curl_init();
- curl_setopt($ch, CURLOPT_HEADER, 0);
- curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
- curl_setopt($ch, CURLOPT_URL, static::WHATSAPP_UPLOAD_HOST);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_POST, true);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
- $response = curl_exec($ch);
- curl_close($ch);
+ $rejectNode = new ProtocolNode("reject",
+ array(
+ "call-id" => $callId
+ ), null, null);
- $xml = simplexml_load_string($response);
- $url = strip_tags($xml->dict->string[3]->asXML());
+ $callNode = new ProtocolNode("call",
+ array(
+ "id" => $id,
+ "to" => $this->getJID($to)
+ ), array($rejectNode), null);
- if (!empty($url)) {
- $this->eventManager()->fireUploadFile(
- $this->phoneNumber,
- basename($file),
- $url
- );
- return $url;
- } else {
- $this->eventManager()->fireUploadFileFailed(
- $this->phoneNumber,
- basename($file)
- );
- return false;
- }
+ $this->sendNode($callNode);
}
/**
- * Wait for message delivery notification.
+ * Sets the bind of the new message.
+ *
+ * @param $bind
*/
- public function waitForMessageReceipt()
+ public function setNewMessageBind($bind)
{
- $received = false;
- do {
- $this->pollMessage();
- $msgs = $this->getMessages();
- foreach ($msgs as $m) {
- // Process inbound messages.
- if ($m->getTag() == "message") {
- if ($m->getChild('received') != null && $m->getAttribute('retry') != null) {
- $received = true;
- } elseif ($m->getChild('received') != null && $m->getAttribute('retry') != null) {
- throw new Exception('There was a problem trying to send the message, please retry.');
- }
- }
- }
- } while (!$received);
+ $this->newMsgBind = $bind;
}
/**
- * Wait for Whatsapp server to acknowledge *it* has received message.
- * @param string $id The id of the node sent that we are awaiting acknowledgement of.
+ * Wait for WhatsApp server to acknowledge *it* has received message.
+ * @param string $id The id of the node sent that we are awaiting acknowledgement of.
+ * @param int $timeout
*/
public function waitForServer($id, $timeout = 5)
{
@@ -1482,18 +1985,16 @@ public function waitForServer($id, $timeout = 5)
}
/**
- * Authenticate with the Whatsapp Server.
+ * Authenticate with the WhatsApp Server.
*
- * @return String
- * Returns binary string
+ * @return string Returns binary string
*/
protected function authenticate()
{
$keys = KeyStream::GenerateKeys(base64_decode($this->password), $this->challengeData);
$this->inputKey = new KeyStream($keys[2], $keys[3]);
$this->outputKey = new KeyStream($keys[0], $keys[1]);
- $phone = $this->dissectPhone();
- $array = "\0\0\0\0" . $this->phoneNumber . $this->challengeData;// . time() . static::WHATSAPP_USER_AGENT . " MccMnc/" . str_pad($phone["mcc"], 3, "0", STR_PAD_LEFT) . "001";
+ $array = "\0\0\0\0" . $this->phoneNumber . $this->challengeData;// . time() . Constants::WHATSAPP_USER_AGENT . " MccMnc/" . str_pad($phone["mcc"], 3, "0", STR_PAD_LEFT) . "001";
$response = $this->outputKey->EncodeMessage($array, 0, 4, strlen($array) - 4);
return $response;
}
@@ -1501,31 +2002,28 @@ protected function authenticate()
/**
* Add the authentication nodes.
*
- * @return ProtocolNode
- * Return itself.
+ * @return ProtocolNode Returns an authentication node.
*/
protected function createAuthNode()
{
- $authHash = array();
- $authHash["xmlns"] = "urn:ietf:params:xml:ns:xmpp-sasl";
- $authHash["mechanism"] = "WAUTH-2";
- $authHash["user"] = $this->phoneNumber;
$data = $this->createAuthBlob();
- $node = new ProtocolNode("auth", $authHash, null, $data);
+ $node = new ProtocolNode("auth", array(
+ 'mechanism' => 'WAUTH-2',
+ 'user' => $this->phoneNumber
+ ), null, $data);
return $node;
}
protected function createAuthBlob()
{
- if($this->challengeData) {
+ if ($this->challengeData) {
$key = wa_pbkdf2('sha1', base64_decode($this->password), $this->challengeData, 16, 20, true);
$this->inputKey = new KeyStream($key[2], $key[3]);
$this->outputKey = new KeyStream($key[0], $key[1]);
$this->reader->setKey($this->inputKey);
//$this->writer->setKey($this->outputKey);
- $phone = $this->dissectPhone();
- $array = "\0\0\0\0" . $this->phoneNumber . $this->challengeData . time() . static::WHATSAPP_USER_AGENT . " MccMnc/" . str_pad($phone["mcc"], 3, "0", STR_PAD_LEFT) . "001";
+ $array = "\0\0\0\0" . $this->phoneNumber . $this->challengeData . time();
$this->challengeData = null;
return $this->outputKey->EncodeMessage($array, 0, strlen($array), false);
}
@@ -1535,29 +2033,25 @@ protected function createAuthBlob()
/**
* Add the auth response to protocoltreenode.
*
- * @return ProtocolNode
- * Return itself.
+ * @return ProtocolNode Returns a response node.
*/
protected function createAuthResponseNode()
{
- $resp = $this->authenticate();
- $respHash = array();
- $respHash["xmlns"] = "urn:ietf:params:xml:ns:xmpp-sasl";
- $node = new ProtocolNode("response", $respHash, null, $resp);
-
- return $node;
+ return new ProtocolNode("response", null, null, $this->authenticate());
}
/**
* Add stream features.
- * @param bool $profileSubscribe
*
- * @return ProtocolNode
- * Return itself.
+ * @return ProtocolNode Return itself.
*/
protected function createFeaturesNode()
{
- $parent = new ProtocolNode("stream:features", null, null, null);
+ $readreceipts = new ProtocolNode("readreceipts", null, null, null);
+ $groupsv2 = new ProtocolNode("groups_v2", null, null, null);
+ $privacy = new ProtocolNode("privacy", null, null, null);
+ $presencev2 = new ProtocolNode("presence", null, null, null);
+ $parent = new ProtocolNode("stream:features", null, array($readreceipts, $groupsv2, $privacy, $presencev2), null);
return $parent;
}
@@ -1565,29 +2059,47 @@ protected function createFeaturesNode()
/**
* Create a unique msg id.
*
- * @param string $prefix
* @return string
* A message id string.
*/
- protected function createMsgId($prefix)
+ protected function createMsgId()
{
- $msgid = "$prefix-" . time() . '-' . $this->messageCounter;
- $this->messageCounter++;
+ return $this->messageId . dechex($this->messageCounter++);
+ }
+
+ /**
+ * iq id
+ *
+ * @return string
+ * Iq id
+ */
+ protected function createIqId()
+ {
+ $iqId = $this->iqCounter;
+ $this->iqCounter++;
- return $msgid;
+ return dechex($iqId);
}
/**
* Print a message to the debug console.
*
- * @param string $debugMsg
- * The debug message.
+ * @param mixed $debugMsg The debug message.
+ * @return bool
*/
protected function debugPrint($debugMsg)
{
if ($this->debug) {
- echo $debugMsg;
+ if (is_array($debugMsg) || is_object($debugMsg)) {
+ print_r($debugMsg);
+ }
+ else {
+ echo $debugMsg;
+ }
+ return true;
}
+
+ return false;
}
/**
@@ -1615,8 +2127,7 @@ protected function dissectPhone()
//hook:
//fix country code for North America
- if(substr($data[1], 0, 1) == "1")
- {
+ if ($data[1][0] == "1") {
$data[1] = "1";
}
@@ -1626,17 +2137,21 @@ protected function dissectPhone()
'phone' => substr($this->phoneNumber, strlen($data[1]), strlen($this->phoneNumber)),
'mcc' => $mcc,
'ISO3166' => @$data[3],
- 'ISO639' => @$data[4]
+ 'ISO639' => @$data[4],
+ 'mnc' => $data[5]
);
- $this->eventManager()->fireDissectPhone(
- $this->phoneNumber,
- $phone['country'],
- $phone['cc'],
- $phone['phone'],
- $phone['mcc'],
- $phone['ISO3166'],
- $phone['ISO639']
+ $this->eventManager()->fire("onDissectPhone",
+ array(
+ $this->phoneNumber,
+ $phone['country'],
+ $phone['cc'],
+ $phone['phone'],
+ $phone['mcc'],
+ $phone['ISO3166'],
+ $phone['ISO639'],
+ $phone['mnc']
+ )
);
return $phone;
@@ -1645,28 +2160,60 @@ protected function dissectPhone()
fclose($handle);
}
- $this->eventManager()->fireDissectPhoneFailed(
- $this->phoneNumber
- );
+ $this->eventManager()->fire("onDissectPhoneFailed",
+ array(
+ $this->phoneNumber
+ ));
return false;
}
/**
- * Send the nodes to the Whatsapp server to log in.
+ * Detects mnc from specified carrier.
+ *
+ * @param string $lc LangCode
+ * @param string $carrierName Name of the carrier
+ * @return string
*
- * @param bool $profileSubscribe
- * Set this to true if you would like Whatsapp to send a
- * notification to your phone when one of your contacts
- * changes/update their picture.
+ * Returns mnc value
*/
- protected function doLogin()
+ protected function detectMnc($lc, $carrierName)
{
- $this->writer->resetKey();
- $this->reader->resetKey();
- $resource = static::WHATSAPP_DEVICE . '-' . static::WHATSAPP_VER . '-' . static::PORT;
- $data = $this->writer->StartStream(static::WHATSAPP_SERVER, $resource);
- $feat = $this->createFeaturesNode();
+ $fp = fopen(__DIR__ . DIRECTORY_SEPARATOR . 'networkinfo.csv', 'r');
+ $mnc = null;
+
+ while ($data = fgetcsv($fp, 0, ',')) {
+ if ($data[4] === $lc && $data[7] === $carrierName) {
+ $mnc = $data[2];
+ break;
+ }
+ }
+
+ if ($mnc == null) {
+ $mnc = '000';
+ }
+
+ fclose($fp);
+
+ return $mnc;
+ }
+
+ /**
+ * Send the nodes to the WhatsApp server to log in.
+ *
+ * @throws Exception
+ */
+ protected function doLogin()
+ {
+ if ($this->isLoggedIn()) {
+ return true;
+ }
+
+ $this->writer->resetKey();
+ $this->reader->resetKey();
+ $resource = Constants::WHATSAPP_DEVICE . '-' . Constants::WHATSAPP_VER . '-' . Constants::PORT;
+ $data = $this->writer->StartStream(Constants::WHATSAPP_SERVER, $resource);
+ $feat = $this->createFeaturesNode();
$auth = $this->createAuthNode();
$this->sendData($data);
$this->sendNode($feat);
@@ -1676,91 +2223,156 @@ protected function doLogin()
$this->pollMessage();
$this->pollMessage();
- if($this->challengeData != null) {
+ if ($this->challengeData != null) {
$data = $this->createAuthResponseNode();
$this->sendNode($data);
$this->reader->setKey($this->inputKey);
$this->writer->setKey($this->outputKey);
- $this->pollMessage();
+ while (!$this->pollMessage()) {};
}
- if(strcmp($this->loginStatus, static::DISCONNECTED_STATUS) == 0)
- {
- throw new Exception('Login Failure');
- }
- else
- {
- $this->eventManager()->fireLogin(
+ if ($this->loginStatus === Constants::DISCONNECTED_STATUS) {
+ throw new LoginFailureException();
+ }
+
+ $this->eventManager()->fire("onLogin",
+ array(
$this->phoneNumber
- );
- $this->sendAvailableForChat();
- }
+ ));
+ $this->sendAvailableForChat();
+ $this->messageId = substr(base64_encode(mcrypt_create_iv(64, MCRYPT_DEV_URANDOM)), 0, 12);
+
+ return true;
+ }
+
+ /**
+ * Have we an active connection with WhatsAPP AND a valid login already?
+ *
+ * @return bool
+ */
+ protected function isLoggedIn(){
+ //If you aren't connected you can't be logged in! ($this->isConnected())
+ //We are connected - but are we logged in? (the rest)
+ return ($this->isConnected() && !empty($this->loginStatus) && $this->loginStatus === Constants::CONNECTED_STATUS);
}
/**
* Create an identity string
*
- * @param string $identity File name where identity is going to be saved.
+ * @param mixed $identity_file IdentityFile (optional).
* @return string Correctly formatted identity
+ *
+ * @throws Exception Error when cannot write identity data to file.
*/
- protected function buildIdentity($identity)
+ protected function buildIdentity($identity_file = false)
{
- if (file_exists($identity.".dat"))
- {
- return file_get_contents($identity.'.dat');
- }
- else
- {
- $id = fopen($identity.".dat", "w");
- $bytes = "%".implode("%", str_split(strtoupper(bin2hex(openssl_random_pseudo_bytes(16))), 2));
- fwrite($id, $bytes);
- fclose($id);
+ if ($identity_file === false)
+ $identity_file = sprintf('%s%s%sid.%s.dat', __DIR__, DIRECTORY_SEPARATOR, Constants::DATA_FOLDER . DIRECTORY_SEPARATOR, $this->phoneNumber);
- return $bytes;
- }
- }
+ if (is_readable($identity_file)) {
+ $data = urldecode(file_get_contents($identity_file));
+ $length = strlen($data);
- protected function checkIdentity($identity)
- {
- if (file_exists($identity.".dat"))
- {
- return (strlen(file_get_contents($identity.'.dat')) == 48);
- }
- else
- {
- return false;
- }
+ if ($length == 20 || $length == 16) {
+ return $data;
+ }
+ }
+
+ $bytes = strtolower(openssl_random_pseudo_bytes(20));
+
+ if (file_put_contents($identity_file, urlencode($bytes)) === false) {
+ throw new Exception('Unable to write identity file to ' . $identity_file);
+ }
+
+ return $bytes;
}
- public function sendSync(array $numbers, $mode = "full", $context = "registration", $index = 0, $last = true)
+ public function sendSync(array $numbers, array $deletedNumbers = null, $syncType = 4, $index = 0, $last = true)
{
$users = array();
- foreach ($numbers as $number) { // number must start with '+' if international contact
- $users[] = new ProtocolNode("user", null, null, (substr($number, 0, 1) != '+')?('+' . $number):($number));
+
+ for ($i=0; $icreateMsgId("sendsync_");
+ if ($deletedNumbers != null || count($deletedNumbers)) {
+ for ($j=0; $j $this->getJID($deletedNumbers[$j]), "type" => "delete"), null, null);
+ }
+ }
- $node = new ProtocolNode("iq", array(
- "to" => $this->getJID($this->phoneNumber),
- "type" => "get",
- "id" => $id,
- "xmlns" => "urn:xmpp:whatsapp:sync"
- ), array(
- new ProtocolNode("sync", array(
- "mode" => $mode,
- "context" => $context,
- "sid" => "".((time() + 11644477200) * 10000000),
- "index" => "".$index,
- "last" => $last ? "true" : "false"
- ), $users, null)
- ), null);
+ switch($syncType)
+ {
+ case 0:
+ $mode = "full";
+ $context = "registration";
+ break;
+ case 1:
+ $mode = "full";
+ $context = "interactive";
+ break;
+ case 2:
+ $mode = "full";
+ $context = "background";
+ break;
+ case 3:
+ $mode = "delta";
+ $context = "interactive";
+ break;
+ case 4:
+ $mode = "delta";
+ $context = "background";
+ break;
+ case 5:
+ $mode = "query";
+ $context = "interactive";
+ break;
+ case 6:
+ $mode = "chunked";
+ $context = "registration";
+ break;
+ case 7:
+ $mode = "chunked";
+ $context = "interactive";
+ break;
+ case 8:
+ $mode = "chunked";
+ $context = "background";
+ break;
+ default:
+ $mode = "delta";
+ $context = "background";
+ }
+
+ $id = $this->createIqId();
+
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $id,
+ "xmlns" => "urn:xmpp:whatsapp:sync",
+ "type" => "get"
+ ), array(
+ new ProtocolNode("sync",
+ array(
+ "mode" => $mode,
+ "context" => $context,
+ "sid" => "".((time() + 11644477200) * 10000000),
+ "index" => "".$index,
+ "last" => $last ? "true" : "false"
+ ), $users, null)
+ ), null);
$this->sendNode($node);
+ $this->waitForServer($id);
return $id;
}
+ public function setMessageStore(MessageStoreInterface $messageStore)
+ {
+ $this->messageStore = $messageStore;
+ }
+
/**
* Process number/jid and turn it into a JID if necessary
*
@@ -1774,98 +2386,54 @@ protected function getJID($number)
//check if group message
if (stristr($number, '-')) {
//to group
- $number .= "@" . static::WHATSAPP_GROUP_SERVER;
+ $number .= "@" . Constants::WHATSAPP_GROUP_SERVER;
} else {
//to normal user
- $number .= "@" . static::WHATSAPP_SERVER;
+ $number .= "@" . Constants::WHATSAPP_SERVER;
}
}
return $number;
}
-
-
/**
* Retrieves media file and info from either a URL or localpath
*
- * @param $filepath
- * The URL or path to the mediafile you wish to send
- * @param $maxsizebytes
- * The maximum size in bytes the media file can be. Default 1MB
+ * @param string $filepath The URL or path to the mediafile you wish to send
+ * @param integer $maxsizebytes The maximum size in bytes the media file can be. Default 5MB
*
* @return bool false if file information can not be obtained.
*/
- protected function getMediaFile($filepath, $maxsizebytes = 1048576)
+ protected function getMediaFile($filepath, $maxsizebytes = 5242880)
{
if (filter_var($filepath, FILTER_VALIDATE_URL) !== false) {
$this->mediaFileInfo = array();
$this->mediaFileInfo['url'] = $filepath;
- //File is a URL. Create a curl connection but DON'T download the body content
- //because we want to see if file is too big.
- $curl = curl_init();
- curl_setopt($curl, CURLOPT_URL, "$filepath");
- curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11");
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($curl, CURLOPT_HEADER, false);
- curl_setopt($curl, CURLOPT_NOBODY, true);
-
- if (curl_exec($curl) === false) {
- return false;
- }
-
- //While we're here, get mime type and filesize and extension
- $info = curl_getinfo($curl);
- $this->mediaFileInfo['filesize'] = $info['download_content_length'];
- $this->mediaFileInfo['filemimetype'] = $info['content_type'];
- $this->mediaFileInfo['fileextension'] = pathinfo(parse_url($this->mediaFileInfo['url'], PHP_URL_PATH), PATHINFO_EXTENSION);
+ $media = file_get_contents($filepath);
+ $this->mediaFileInfo['filesize'] = strlen($media);
- //Only download file if it's not too big
- //TODO check what max file size whatsapp server accepts.
if ($this->mediaFileInfo['filesize'] < $maxsizebytes) {
- //Create temp file in media folder. Media folder must be writable!
- $this->mediaFileInfo['filepath'] = tempnam(getcwd() . '/' . static::MEDIA_FOLDER, 'WHA');
- $fp = fopen($this->mediaFileInfo['filepath'], 'w');
- if ($fp) {
- curl_setopt($curl, CURLOPT_NOBODY, false);
- curl_setopt($curl, CURLOPT_BUFFERSIZE, 1024);
- curl_setopt($curl, CURLOPT_FILE, $fp);
- curl_exec($curl);
- fclose($fp);
- } else {
- unlink($this->mediaFileInfo['filepath']);
- curl_close($curl);
- return false;
- }
- //Success
- curl_close($curl);
+ $this->mediaFileInfo['filepath'] = tempnam(__DIR__ . DIRECTORY_SEPARATOR . Constants::DATA_FOLDER . DIRECTORY_SEPARATOR . Constants::MEDIA_FOLDER, 'WHA');
+ file_put_contents($this->mediaFileInfo['filepath'], $media);
+ $this->mediaFileInfo['filemimetype'] = get_mime($this->mediaFileInfo['filepath']);
+ $this->mediaFileInfo['fileextension'] = getExtensionFromMime($this->mediaFileInfo['filemimetype']);
return true;
} else {
- //File too big. Don't Download.
- curl_close($curl);
return false;
}
} else if (file_exists($filepath)) {
//Local file
$this->mediaFileInfo['filesize'] = filesize($filepath);
if ($this->mediaFileInfo['filesize'] < $maxsizebytes) {
- $this->mediaFileInfo['filepath'] = $filepath;
+ $this->mediaFileInfo['filepath'] = $filepath;
$this->mediaFileInfo['fileextension'] = pathinfo($filepath, PATHINFO_EXTENSION);
- //TODO
- //Get Mime type using finfo.
-// $finfo = new finfo_open(FILEINFO_MIME_TYPE);
-// $this->_mediafileinfo['filemimetype'] = finfo_file($finfo, $filepath);
-// finfo_close($finfo);
- //mime_content_type deprecated
- $this->mediaFileInfo['filemimetype'] = mime_content_type($filepath);
+ $this->mediaFileInfo['filemimetype'] = get_mime($filepath);
return true;
} else {
- //File too big
return false;
}
}
- //Couldn't tell what file was, local or URL.
return false;
}
@@ -1873,17 +2441,14 @@ protected function getMediaFile($filepath, $maxsizebytes = 1048576)
* Get a decoded JSON response from Whatsapp server
*
* @param string $host The host URL
- * @param array $query A associative array of keys and values to send to server.
- * @return object NULL is returned if the json cannot be decoded or if the encoded data is deeper than the recursion limit
+ * @param array $query A associative array of keys and values to send to server.
+ *
+ * @return null|object NULL if the json cannot be decoded or if the encoded data is deeper than the recursion limit
*/
protected function getResponse($host, $query)
{
// Build the url.
- $url = $host . '?';
- foreach ($query as $key => $value) {
- $url .= $key . '=' . $value . '&';
- }
- $url = rtrim($url, '&');
+ $url = $host . '?' . http_build_query($query);
// Open connection.
$ch = curl_init();
@@ -1892,7 +2457,7 @@ protected function getResponse($host, $query)
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, 0);
- curl_setopt($ch, CURLOPT_USERAGENT, static::WHATSAPP_USER_AGENT);
+ curl_setopt($ch, CURLOPT_USERAGENT, Constants::WHATSAPP_USER_AGENT);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: text/json'));
// This makes CURL accept any peer!
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
@@ -1909,9 +2474,7 @@ protected function getResponse($host, $query)
/**
* Process the challenge.
*
- *
- * @param ProtocolNode $node
- * The node that contains the challenge.
+ * @param ProtocolNode $node The node that contains the challenge.
*/
protected function processChallenge($node)
{
@@ -1921,14 +2484,17 @@ protected function processChallenge($node)
/**
* Process inbound data.
*
- * @param string $data
- * The data to process.
+ * @param $data
+ * @param bool $autoReceipt
+ * @param $type
+ *
+ * @throws Exception
*/
- protected function processInboundData($data, $autoReceipt = true)
+ protected function processInboundData($data, $autoReceipt = true, $type = "read")
{
$node = $this->reader->nextTree($data);
- if( $node != null ) {
- $this->processInboundDataNode($node, $autoReceipt);
+ if ($node != null) {
+ $this->processInboundDataNode($node, $autoReceipt, $type);
}
}
@@ -1936,51 +2502,85 @@ protected function processInboundData($data, $autoReceipt = true)
* 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) {
+ 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" )
- {
+ } 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->loginStatus = static::DISCONNECTED_STATUS;
+ $this->eventManager()->fire("onMessageReceivedClient",
+ array(
+ $this->phoneNumber,
+ $node->getAttribute('from'),
+ $node->getAttribute('id'),
+ $node->getAttribute('type'),
+ $node->getAttribute('t'),
+ $node->getAttribute('participant')
+ ));
- }
- elseif ($node->getTag() == "success") {
- $this->loginStatus = static::CONNECTED_STATUS;
- $challengeData = $node->getData();
- file_put_contents($this->challengeFilename, $challengeData);
- $this->writer->setKey($this->outputKey);
- } elseif($node->getTag() == "failure")
- {
- $this->eventManager()->fireLoginFailed(
- $this->phoneNumber,
- $node->getChild(0)->getTag()
- );
- }
- elseif($node->getTag() == '' && $node->getAttribute("class") == "message")
- {
- $this->eventManager()->fireMessageReceivedServer(
- $this->phoneNumber,
- $node->getAttribute('from'),
- $node->getAttribute('id'),
- $node->getAttribute('class')
- );
- }
- elseif($node->getTag() == 'receipt')
- {
- $this->eventManager()->fireMessageReceivedClient(
- $this->phoneNumber,
- $node->getAttribute('from'),
- $node->getAttribute('id'),
- $node->getAttribute('class'),
- $node->getAttribute('t')
- );
+ $this->sendAck($node, 'receipt');
}
if ($node->getTag() == "message") {
array_push($this->messageQueue, $node);
@@ -1988,220 +2588,317 @@ protected function processInboundDataNode(ProtocolNode $node, $autoReceipt = tru
if ($node->hasChild('x') && $this->lastId == $node->getAttribute('id')) {
$this->sendNextMessage();
}
- if ($this->newMsgBind && $node->getChild('body')) {
+ 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($author == "")
- {
- //private chat message
- $this->eventManager()->fireGetMessage(
- $this->phoneNumber,
- $node->getAttribute('from'),
- $node->getAttribute('id'),
- $node->getAttribute('type'),
- $node->getAttribute('t'),
- $node->getAttribute("notify"),
- $node->getChild("body")->getData()
- );
+ if ($autoReceipt) {
+ $this->sendReceipt($node, $type, $author);
}
- else
- {
+ 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()->fireGetGroupMessage(
- $this->phoneNumber,
- $node->getAttribute('from'),
- $author,
- $node->getAttribute('id'),
- $node->getAttribute('type'),
- $node->getAttribute('t'),
- $node->getAttribute("notify"),
- $node->getChild("body")->getData()
- );
+ $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($autoReceipt)
- {
- $this->sendMessageReceived($node);
+ }
+ 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') {
- $this->eventManager()->fireGetImage(
- $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()
- );
+
+ 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') {
- $this->eventManager()->fireGetVideo(
- $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()
- );
+ 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') {
- $this->eventManager()->fireGetAudio(
- $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('duration'),
- $node->getChild("media")->getAttribute('acodec')
- );
+ $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') {
- $this->eventManager()->fireGetvCard(
- $this->phoneNumber,
- $node->getAttribute('from'),
- $node->getAttribute('id'),
- $node->getAttribute('type'),
- $node->getAttribute('t'),
- $node->getAttribute('notify'),
- $node->getChild("media")->getChild("vcard")->getAttribute('name'),
- $node->getChild("media")->getChild("vcard")->getData()
- );
+ 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()->fireGetLocation(
- $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()
- );
+ $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->sendMessageReceived($node);
+ if ($autoReceipt) {
+ $this->sendReceipt($node, $type);
}
}
if ($node->getChild('received') != null) {
- $this->eventManager()->fireMessageReceivedClient(
- $this->phoneNumber,
- $node->getAttribute('from'),
- $node->getAttribute('id'),
- $node->getAttribute('type'),
- $node->getAttribute('t')
- );
+ $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)
+ 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
+ if (strcmp($node->getTag(), "presence") == 0
&& strncmp($node->getAttribute('from'), $this->phoneNumber, strlen($this->phoneNumber)) != 0
- && strpos($node->getAttribute('from'), "-") == false) {
+ && strpos($node->getAttribute('from'), "-") === false) {
$presence = array();
- if($node->getAttribute('type') == null){
- $this->eventManager()->firePresence(
- $this->phoneNumber,
- $node->getAttribute('from'),
- $presence['type'] = "available"
- );
- }
- else{
- $this->eventManager()->firePresence(
- $this->phoneNumber,
- $node->getAttribute('from'),
- $presence['type'] = "unavailable"
- );
+ 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 = self::parseJID($node->getAttribute('from'));
+ $groupId = $this->parseJID($node->getAttribute('from'));
if ($node->getAttribute('add') != null) {
- $this->eventManager()->fireGroupsParticipantsAdd(
- $this->phoneNumber,
- $groupId,
- self::parseJID($node->getAttribute('add'))
- );
+ $this->eventManager()->fire("onGroupsParticipantsAdd",
+ array(
+ $this->phoneNumber,
+ $groupId,
+ $this->parseJID($node->getAttribute('add'))
+ ));
} elseif ($node->getAttribute('remove') != null) {
- $this->eventManager()->fireGroupsParticipantsRemove(
- $this->phoneNumber,
- $groupId,
- self::parseJID($node->getAttribute('remove')),
- self::parseJID($node->getAttribute('author'))
- );
+ $this->eventManager()->fire("onGroupsParticipantsRemove",
+ array(
+ $this->phoneNumber,
+ $groupId,
+ $this->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) {
+ && strpos($node->getAttribute('from'), "-") === false) {
if($node->getChild(0)->getTag() == "composing"){
- $this->eventManager()->fireMessageComposing(
- $this->phoneNumber,
- $node->getAttribute('from'),
- $node->getAttribute('id'),
- "composing",
- $node->getAttribute('t')
- );
- }
- else{
- $this->eventManager()->fireMessagePaused(
- $this->phoneNumber,
- $node->getAttribute('from'),
- $node->getAttribute('id'),
- "paused",
- $node->getAttribute('t')
- );
+ $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()->firePing(
- $this->phoneNumber,
- $node->getAttribute('id')
- );
+ $this->eventManager()->fire("onPing",
+ array(
+ $this->phoneNumber,
+ $node->getAttribute('id')
+ ));
$this->sendPong($node->getAttribute('id'));
}
if ($node->getTag() == "iq"
@@ -2230,36 +2927,44 @@ protected function processInboundDataNode(ProtocolNode $node, $autoReceipt = tru
$index = $sync->getAttribute("index");
- $result = new SyncResult($index, $sync->getAttribute("sync"), $existingUsers, $failedNumbers);
+ $result = new SyncResult($index, $sync->getAttribute("sid"), $existingUsers, $failedNumbers);
- $this->eventManager()->fireGetSyncResult($result);
+ $this->eventManager()->fire("onGetSyncResult",
+ array(
+ $result
+ ));
}
if ($node->getTag() == "receipt") {
- $this->eventManager()->fireGetReceipt(
- $node->getAttribute('from'),
- $node->getAttribute('id'),
- $node->getAttribute('offline'),
- $node->getAttribute('retry')
- );
+ $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 ($node->getChild(0)->getAttribute('xmlns') == 'jabber:iq:privacy') {
- // ToDo: We need to get explicitly list out the children as arguments
- // here.
- $this->eventManager()->fireGetPrivacyBlockedList(
- $this->phoneNumber,
- $node->getChild(0)->getChild(0)->getChildren()
- );
+ 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()->fireGetRequestLastSeen(
- $this->phoneNumber,
- $node->getAttribute('from'),
- $node->getAttribute('id'),
- $node->getChild(0)->getAttribute('seconds')
- );
- array_push($this->messageQueue, $node);
+ $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
@@ -2267,170 +2972,412 @@ protected function processInboundDataNode(ProtocolNode $node, $autoReceipt = tru
foreach($node->getChild(0)->getChildren() as $child) {
$props[$child->getAttribute("name")] = $child->getAttribute("value");
}
- $this->eventManager()->fireGetServerProperties(
- $this->phoneNumber,
- $node->getChild(0)->getAttribute("version"),
- $props
- );
+ $this->eventManager()->fire("onGetServerProperties",
+ array(
+ $this->phoneNumber,
+ $node->getChild(0)->getAttribute("version"),
+ $props
+ ));
}
if ($node->getChild("picture") != null) {
- $this->eventManager()->fireGetProfilePicture(
- $this->phoneNumber,
- $node->getAttribute("from"),
- $node->getChild("picture")->getAttribute("type"),
- $node->getChild("picture")->getData()
- );
+ $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 ($node->nodeIdContains("group")) {
+ 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();
- if ($node->getChild(0) != null) {
- foreach ($node->getChildren() as $child) {
+ $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($node->nodeIdContains('creategroup')){
+ if (isset($this->nodeId['groupcreate']) && ($this->nodeId['groupcreate'] == $node->getAttribute('id'))) {
$this->groupId = $node->getChild(0)->getAttribute('id');
- $this->eventManager()->fireGroupsChatCreate(
- $this->phoneNumber,
- $this->groupId
- );
+ $this->eventManager()->fire("onGroupsChatCreate",
+ array(
+ $this->phoneNumber,
+ $this->groupId
+ ));
}
- if($node->nodeIdContains('endgroup')){
+ if (isset($this->nodeId['leavegroup']) && ($this->nodeId['leavegroup'] == $node->getAttribute('id'))) {
$this->groupId = $node->getChild(0)->getChild(0)->getAttribute('id');
- $this->eventManager()->fireGroupsChatEnd(
- $this->phoneNumber,
- $this->groupId
- );
+ $this->eventManager()->fire("onGroupsChatEnd",
+ array(
+ $this->phoneNumber,
+ $this->groupId
+ ));
}
- if($node->nodeIdContains('getgroups')){
- $this->eventManager()->fireGetGroups(
- $this->phoneNumber,
- $groupList
- );
+ 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($node->nodeIdContains('getgroupinfo')){
- $this->eventManager()->fireGetGroupsInfo(
- $this->phoneNumber,
- $groupList
- );
+ 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($node->nodeIdContains('getgroupparticipants')){
- $groupId = self::parseJID($node->getAttribute('from'));
- $this->eventManager()->fireGetGroupParticipants(
- $this->phoneNumber,
- $groupId,
- $groupList
- );
+ }
+ }
+ 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("status") != null)
- {
+ 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()->fireGetStatus(
- $this->phoneNumber,
- $status->getAttribute("jid"),
- "requested",
- $node->getAttribute("id"),
- $status->getAttribute("t"),
- $status->getData()
- );
+ $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") {
- $this->eventManager()->fireGetError(
- $this->phoneNumber,
- $node->getAttribute( 'id' ),
- $node->getChild(0)
- );
+ $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
+ ));
}
- $children = $node->getChild(0);
- if ($node->getTag() == "stream:error" && empty($children) == false && $node->getChild(0)->getTag() == "system-shutdown")
- {
+ 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);
- throw new Exception('Error system-shutdown');
+ $iqNode = new ProtocolNode("iq",
+ array(
+ "id" => $msgId,
+ "xmlns" => "w:m",
+ "type" => "set",
+ "to" => Constants::WHATSAPP_SERVER
+ ), array($ackNode), null);
+ $this->sendNode($iqNode);
}
- if($node->getTag() == "notification")
+ $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()->fireGetStatus(
- $this->phoneNumber, //my number
- $node->getAttribute("from"), //sender
- $node->getChild(0)->getTag(), //type (set/remove)
- $node->getAttribute("id"), //message id
- $node->getAttribute("t"), //time
- $node->getChild(0)->getData()); //status message
+ $this->eventManager()->fire("onGetStatus",
+ array(
+ $this->phoneNumber, //my number
+ $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()->fireProfilePictureChanged(
- $this->phoneNumber,
- $node->getAttribute('from'),
- $node->getAttribute('id'),
- $node->getAttribute('t')
- );
+ $this->eventManager()->fire("onProfilePictureChanged",
+ array(
+ $this->phoneNumber,
+ $node->getAttribute('from'),
+ $node->getAttribute('id'),
+ $node->getAttribute('t')
+ ));
} else if ($node->hasChild('delete')) {
- $this->eventManager()->fireProfilePictureDeleted(
- $this->phoneNumber,
- $node->getAttribute('from'),
- $node->getAttribute('id'),
- $node->getAttribute('t')
- );
+ $this->eventManager()->fire("onProfilePictureDeleted",
+ array(
+ $this->phoneNumber,
+ $node->getAttribute('from'),
+ $node->getAttribute('id'),
+ $node->getAttribute('t')
+ ));
}
//TODO
break;
case "contacts":
- //TODO
+ $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 "participant":
+ 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')) {
- $this->eventManager()->fireGroupsParticipantsRemove(
- $this->phoneNumber,
- $node->getAttribute('from'),
- $node->getChild(0)->getAttribute('jid'),
- $node->getChild(0)->getAttribute('author')
- );
+ 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()->fireGroupsParticipantsAdd(
- $this->phoneNumber,
- $node->getAttribute('from'),
- $node->getChild(0)->getAttribute('jid')
+ $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'), //Group-JID
+ $node->getAttribute('t'), //Time
+ $node->getAttribute('participant'), //Issuer-JID
+ $node->getAttribute('notify'), //Issuer-Name
+ $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')
+ )
);
}
- //TODO
break;
- case "subject":
- $this->eventManager()->fireGetGroupsSubject(
- $this->phoneNumber,
- $node->getAttribute('from'),
- $node->getAttribute('t'),
- $node->getAttribute('participant'),
- $node->getAttribute('participant'),
- $node->getAttribute('notify'),
- $node->getChild(0)->getData()
- );
- //TODO
+ 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->sendNotificationAck($node);
+ $this->sendAck($node, 'notification');
}
- if($node->getTag() == "ib")
+ 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)
{
@@ -2439,6 +3386,16 @@ protected function processInboundDataNode(ProtocolNode $node, $autoReceipt = tru
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;
@@ -2447,17 +3404,19 @@ protected function processInboundDataNode(ProtocolNode $node, $autoReceipt = tru
}
}
}
- if($node->getTag() == "ack")
+
+ // Disconnect socket on stream error.
+ if ($node->getTag() == "stream:error")
{
- ////on get ack
+ $this->disconnect();
}
-
}
/**
- * @param $node ProtocolNode
+ * @param $node ProtocolNode
+ * @param $class string
*/
- protected function sendNotificationAck($node)
+ protected function sendAck($node, $class)
{
$from = $node->getAttribute("from");
$to = $node->getAttribute("to");
@@ -2466,72 +3425,58 @@ protected function sendNotificationAck($node)
$type = $node->getAttribute("type");
$attributes = array();
- if($to)
+ if ($to)
$attributes["from"] = $to;
- if($participant)
+ if ($participant)
$attributes["participant"] = $participant;
$attributes["to"] = $from;
- $attributes["class"] = "notification";
+ $attributes["class"] = $class;
$attributes["id"] = $id;
- $attributes["type"] = $type;
+ if ($type != null)
+ $attributes["type"] = $type;
+
$ack = new ProtocolNode("ack", $attributes, null, null);
+
$this->sendNode($ack);
}
/**
- * Process and save media image
+ * Process and save media image.
*
- * @param ProtocolNode $node
- * ProtocolNode containing media
+ * @param ProtocolNode $node ProtocolNode containing media
*/
protected function processMediaImage($node)
{
$media = $node->getChild("media");
+
if ($media != null) {
$filename = $media->getAttribute("file");
$url = $media->getAttribute("url");
//save thumbnail
- $data = $media->getData();
- $fp = @fopen(static::MEDIA_FOLDER . "/thumb_" . $filename, "w");
- if ($fp) {
- fwrite($fp, $data);
- fclose($fp);
- }
-
+ file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . Constants::DATA_FOLDER . DIRECTORY_SEPARATOR . Constants::MEDIA_FOLDER . DIRECTORY_SEPARATOR . 'thumb_' . $filename, $media->getData());
//download and save original
- $data = file_get_contents($url);
- $fp = @fopen(static::MEDIA_FOLDER . "/" . $filename, "w");
- if ($fp) {
- fwrite($fp, $data);
- fclose($fp);
- }
+ file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . Constants::DATA_FOLDER . DIRECTORY_SEPARATOR . Constants::MEDIA_FOLDER . DIRECTORY_SEPARATOR . $filename, file_get_contents($url));
}
}
/**
- * Processes received picture node
+ * Processes received picture node.
*
- * @param ProtocolNode $node
- * ProtocolNode containing the picture
+ * @param ProtocolNode $node ProtocolNode containing the picture
*/
protected function processProfilePicture($node)
{
$pictureNode = $node->getChild("picture");
if ($pictureNode != null) {
- $type = $pictureNode->getAttribute("type");
- $data = $pictureNode->getData();
- if ($type == "preview") {
- $filename = static::PICTURES_FOLDER . "/preview_" . $node->getAttribute("from") . ".jpg";
+ if ($pictureNode->getAttribute("type") == "preview") {
+ $filename = __DIR__ . DIRECTORY_SEPARATOR . Constants::DATA_FOLDER . DIRECTORY_SEPARATOR . Constants::PICTURES_FOLDER . DIRECTORY_SEPARATOR . 'preview_' . $node->getAttribute('from') . 'jpg';
} else {
- $filename = static::PICTURES_FOLDER . "/" . $node->getAttribute("from") . ".jpg";
- }
- $fp = @fopen($filename, "w");
- if ($fp) {
- fwrite($fp, $data);
- fclose($fp);
+ $filename = __DIR__ . DIRECTORY_SEPARATOR . Constants::DATA_FOLDER . DIRECTORY_SEPARATOR . Constants::PICTURES_FOLDER . DIRECTORY_SEPARATOR . $node->getAttribute('from') . '.jpg';
}
+
+ file_put_contents($filename, $pictureNode->getData());
}
}
@@ -2546,7 +3491,7 @@ protected function processTempMediaFile($storeURLmedia)
if (isset($this->mediaFileInfo['url'])) {
if ($storeURLmedia) {
if (is_file($this->mediaFileInfo['filepath'])) {
- rename($this->mediaFileInfo['filepath'], $this->mediaFileInfo['filepath'] . $this->mediaFileInfo['fileextension']);
+ rename($this->mediaFileInfo['filepath'], $this->mediaFileInfo['filepath'].'.'.$this->mediaFileInfo['fileextension']);
}
} else {
if (is_file($this->mediaFileInfo['filepath'])) {
@@ -2559,8 +3504,7 @@ protected function processTempMediaFile($storeURLmedia)
/**
* Process media upload response
*
- * @param ProtocolNode $node
- * Message node
+ * @param ProtocolNode $node Message node
* @return bool
*/
protected function processUploadResponse($node)
@@ -2569,13 +3513,14 @@ protected function processUploadResponse($node)
$messageNode = @$this->mediaQueue[$id];
if ($messageNode == null) {
//message not found, can't send!
- $this->eventManager()->fireMediaUploadFailed(
- $this->phoneNumber,
- $id,
- $node,
- $messageNode,
- "Message node not found in queue"
- );
+ $this->eventManager()->fire("onMediaUploadFailed",
+ array(
+ $this->phoneNumber,
+ $id,
+ $node,
+ $messageNode,
+ "Message node not found in queue"
+ ));
return false;
}
@@ -2584,11 +3529,11 @@ protected function processUploadResponse($node)
//file already on whatsapp servers
$url = $duplicate->getAttribute("url");
$filesize = $duplicate->getAttribute("size");
-// $mimetype = $duplicate->getAttribute("mimetype");
+// $mimetype = $duplicate->getAttribute("mimetype");
$filehash = $duplicate->getAttribute("filehash");
$filetype = $duplicate->getAttribute("type");
-// $width = $duplicate->getAttribute("width");
-// $height = $duplicate->getAttribute("height");
+// $width = $duplicate->getAttribute("width");
+// $height = $duplicate->getAttribute("height");
$exploded = explode("/", $url);
$filename = array_pop($exploded);
} else {
@@ -2597,102 +3542,126 @@ protected function processUploadResponse($node)
if (!$json) {
//failed upload
- $this->eventManager()->fireMediaUploadFailed(
- $this->phoneNumber,
- $id,
- $node,
- $messageNode,
- "Failed to push file to server"
- );
+ $this->eventManager()->fire("onMediaUploadFailed",
+ array(
+ $this->phoneNumber,
+ $id,
+ $node,
+ $messageNode,
+ "Failed to push file to server"
+ ));
return false;
}
$url = $json->url;
$filesize = $json->size;
-// $mimetype = $json->mimetype;
+// $mimetype = $json->mimetype;
$filehash = $json->filehash;
$filetype = $json->type;
-// $width = $json->width;
-// $height = $json->height;
+// $width = $json->width;
+// $height = $json->height;
$filename = $json->name;
}
$mediaAttribs = array();
- $mediaAttribs["xmlns"] = "urn:xmpp:whatsapp:mms";
$mediaAttribs["type"] = $filetype;
$mediaAttribs["url"] = $url;
+ $mediaAttribs["encoding"] = "raw";
$mediaAttribs["file"] = $filename;
$mediaAttribs["size"] = $filesize;
- $mediaAttribs["hash"] = $filehash;
+ if ($this->mediaQueue[$id]['caption'] != '') {
+ $mediaAttribs["caption"] = $this->mediaQueue[$id]['caption'];
+ }
+ if ($this->voice == true)
+ {
+ $mediaAttribs["origin"] = 'live';
+ $this->voice = false;
+ }
$filepath = $this->mediaQueue[$id]['filePath'];
$to = $this->mediaQueue[$id]['to'];
switch ($filetype) {
case "image":
+ $caption = $this->mediaQueue[$id]['caption'];
$icon = createIcon($filepath);
break;
case "video":
+ $caption = $this->mediaQueue[$id]['caption'];
$icon = createVideoIcon($filepath);
break;
default:
+ $caption = '';
$icon = '';
break;
}
+ //Retrieve Message ID
+ $message_id = $messageNode['message_id'];
$mediaNode = new ProtocolNode("media", $mediaAttribs, null, $icon);
if (is_array($to)) {
$this->sendBroadcast($to, $mediaNode, "media");
} else {
- $this->sendMessageNode($to, $mediaNode);
- }
- $this->eventManager()->fireMediaMessageSent(
- $this->phoneNumber,
- $to,
- $id,
- $filetype,
- $url,
- $filename,
- $filesize,
- $filehash,
- $icon
- );
+ $this->sendMessageNode($to, $mediaNode, $message_id);
+ }
+ $this->eventManager()->fire("onMediaMessageSent",
+ array(
+ $this->phoneNumber,
+ $to,
+ $id,
+ $filetype,
+ $url,
+ $filename,
+ $filesize,
+ $filehash,
+ $caption,
+ $icon
+ ));
return true;
}
/**
* Read 1024 bytes from the whatsapp server.
+ *
+ * @throws Exception
*/
public function readStanza()
{
$buff = '';
- if($this->socket != null)
- {
- $header = @fread($this->socket, 3);//read stanza header
- if(strlen($header) == 0)
- {
+ if ($this->socket != null) {
+ $header = @socket_read($this->socket, 3);//read stanza header
+ if ($header === false) {
+ $error = "socket EOF, closing socket...";
+ socket_close($this->socket);
+ $this->socket = null;
+ $this->eventManager()->fire("onClose",
+ array(
+ $this->phoneNumber,
+ $error
+ )
+ );
+ }
+
+ if (strlen($header) == 0) {
//no data received
return;
}
- if(strlen($header) != 3)
- {
- throw new Exception("Failed to read stanza header");
+ if (strlen($header) != 3) {
+ throw new ConnectionException("Failed to read stanza header");
}
- $treeLength = 0;
- $treeLength = ord($header[1]) << 8;
+ $treeLength = (ord($header[0]) & 0x0F) << 16;
+ $treeLength |= ord($header[1]) << 8;
$treeLength |= ord($header[2]) << 0;
//read full length
- $buff = @fread($this->socket, $treeLength);
- $trlen = $treeLength;
+ $buff = socket_read($this->socket, $treeLength);
+ //$trlen = $treeLength;
$len = strlen($buff);
- $prev = 0;
- while(strlen($buff) < $treeLength)
- {
+ //$prev = 0;
+ while (strlen($buff) < $treeLength) {
$toRead = $treeLength - strlen($buff);
- $buff .= @fread($this->socket, $toRead);
- if($len == strlen($buff))
- {
+ $buff .= socket_read($this->socket, $toRead);
+ if ($len == strlen($buff)) {
//no new data read, fuck it
break;
}
@@ -2700,22 +3669,15 @@ public function readStanza()
}
if (strlen($buff) != $treeLength) {
- throw new Exception("Tree length did not match received length (buff = " . strlen($buff) . " & treeLength = $treeLength)");
- } else
- if (@feof($this->socket)) {
- $error = "socket EOF, closing socket...";
- fclose($this->socket);
- $this->socket = null;
- $this->eventManager()->fireClose(
- $this->phoneNumber,
- $error
- );
+ throw new ConnectionException("Tree length did not match received length (buff = " . strlen($buff) . " & treeLength = $treeLength)");
}
$buff = $header . $buff;
- }
- else
- {
- throw new Exception("Socket closed");
+ } else {
+ $this->eventManager()->fire("onDisconnect",
+ array(
+ $this->phoneNumber,
+ $this->socket
+ ));
}
return $buff;
@@ -2724,38 +3686,42 @@ public function readStanza()
/**
* Checks that the media file to send is of allowable filetype and within size limits.
*
- * @param string $filepath The URL/URI to the media file
- * @param int $maxSize Maximim filesize allowed for media type
- * @param string $to Recipient ID/number
- * @param string $type media filetype. 'audio', 'video', 'image'
- * @param array $allowedExtensions An array of allowable file types for the media file
- * @param bool $storeURLmedia Keep a copy of the media file
- * @return bool
+ * @param string $filepath The URL/URI to the media file
+ * @param int $maxSize Maximum filesize allowed for media type
+ * @param string $to Recipient ID/number
+ * @param string $type media filetype. 'audio', 'video', 'image'
+ * @param array $allowedExtensions An array of allowable file types for the media file
+ * @param bool $storeURLmedia Keep a copy of the media file
+ * @param string $caption *
+ * @return string|null Message ID if successfully, null if not.
*/
- protected function sendCheckAndSendMedia($filepath, $maxSize, $to, $type, $allowedExtensions, $storeURLmedia)
+ protected function sendCheckAndSendMedia($filepath, $maxSize, $to, $type, $allowedExtensions, $storeURLmedia, $caption = "")
{
if ($this->getMediaFile($filepath, $maxSize) == true) {
if (in_array($this->mediaFileInfo['fileextension'], $allowedExtensions)) {
$b64hash = base64_encode(hash_file("sha256", $this->mediaFileInfo['filepath'], true));
- //request upload
- $this->sendRequestFileUpload($b64hash, $type, $this->mediaFileInfo['filesize'], $this->mediaFileInfo['filepath'], $to);
+ //request upload and get Message ID
+ $id =$this->sendRequestFileUpload($b64hash, $type, $this->mediaFileInfo['filesize'], $this->mediaFileInfo['filepath'], $to, $caption);
$this->processTempMediaFile($storeURLmedia);
- return true;
+ // Return message ID. Make pull request for this.
+ return $id;
} else {
//Not allowed file type.
$this->processTempMediaFile($storeURLmedia);
- return false;
+ return null;
}
} else {
//Didn't get media file details.
- return false;
+ return null;
}
}
/**
* Send a broadcast
- * @param array $targets Array of numbers to send to
- * @param object $node
+ * @param array $targets Array of numbers to send to
+ * @param object $node
+ * @param $type
+ * @return string
*/
protected function sendBroadcast($targets, $node, $type)
{
@@ -2763,11 +3729,6 @@ protected function sendBroadcast($targets, $node, $type)
$targets = array($targets);
}
- $serverNode = new ProtocolNode("server", null, null, "");
- $xHash = array();
- $xHash["xmlns"] = "jabber:x:event";
- $xNode = new ProtocolNode("x", $xHash, array($serverNode), "");
-
$toNodes = array();
foreach ($targets as $target) {
$jid = $this->getJID($target);
@@ -2778,52 +3739,61 @@ protected function sendBroadcast($targets, $node, $type)
$broadcastNode = new ProtocolNode("broadcast", null, $toNodes, null);
- $messageHash = array();
- $messageHash["to"] = time()."@broadcast";
- $messageHash["type"] = $type;
- $id = $this->createMsgId("broadcast");
- $messageHash["id"] = $id;
+ $msgId = $this->createMsgId();
+
+ $messageNode = new ProtocolNode("message",
+ array(
+ "to" => time()."@broadcast",
+ "type" => $type,
+ "id" => $msgId
+ ), array($node, $broadcastNode), null);
- $messageNode = new ProtocolNode("message", $messageHash, array($broadcastNode, $xNode, $node), null);
$this->sendNode($messageNode);
+ $this->waitForServer($msgId);
//listen for response
- $this->eventManager()->fireSendMessage(
- $this->phoneNumber,
- $targets,
- $messageHash["id"],
- $node
- );
- return $id;
+ $this->eventManager()->fire("onSendMessage",
+ array(
+ $this->phoneNumber,
+ $targets,
+ $msgId,
+ $node
+ ));
+
+ return $msgId;
}
/**
- * Send data to the whatsapp server.
+ * Send data to the WhatsApp server.
* @param string $data
+ *
+ * @throws Exception
*/
protected function sendData($data)
{
- if($this->socket != null)
- {
- fwrite($this->socket, $data, strlen($data));
+ if ($this->socket != null) {
+ if (socket_write($this->socket, $data, strlen($data)) === false) {
+ $this->disconnect();
+ throw new ConnectionException('Connection Closed!');
+ }
}
}
/**
- * Send the getGroupList request to Whatsapp
+ * Send the getGroupList request to WhatsApp
* @param string $type Type of list of groups to retrieve. "owning" or "participating"
*/
protected function sendGetGroupsFiltered($type)
{
- $msgID = $this->createMsgId("getgroups");
- $child = new ProtocolNode("list", array(
- "type" => $type
- ), null, null);
- $node = new ProtocolNode("iq", array(
- "id" => $msgID,
- "type" => "get",
- "xmlns" => "w:g",
- "to" => "g.us"
- ), array($child), null);
+ $msgID = $this->nodeId['getgroups'] = $this->createIqId();
+ $child = new ProtocolNode($type, null, null, null);
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $msgID,
+ "type" => "get",
+ "xmlns" => "w:g2",
+ "to" => Constants::WHATSAPP_GROUP_SERVER
+ ), array($child), null);
+
$this->sendNode($node);
$this->waitForServer($msgID);
}
@@ -2831,109 +3801,110 @@ protected function sendGetGroupsFiltered($type)
/**
* Change participants of a group.
*
- * @param string $groupId
- * The group ID.
- * @param array $participants
- * An array with the participants.
- * @param string $tag
- * The tag action. 'add' or 'remove'
+ * @param string $groupId The group ID.
+ * @param string $participant The participant.
+ * @param string $tag The tag action. 'add', 'remove', 'promote' or 'demote'
+ * @param $id
*/
- protected function sendGroupsChangeParticipants($groupId, $participants, $tag)
+ protected function sendGroupsChangeParticipants($groupId, $participant, $tag, $id)
{
- $_participants = array();
- foreach ($participants as $participant) {
- $_participants[] = new ProtocolNode("participant", array("jid" => $this->getJID($participant)), null, "");
- }
- $childHash = array();
- $child = new ProtocolNode($tag, $childHash, $_participants, "");
+ $participants = new ProtocolNode("participant", array("jid" => $this->getJID($participant)), null, "");
- $setHash = array();
- $setHash["id"] = $this->createMsgId("participants");
- $setHash["type"] = "set";
- $setHash["xmlns"] = "w:g";
- $setHash["to"] = $this->getJID($groupId);
+ $childHash = array();
+ $child = new ProtocolNode($tag, $childHash, array($participants), "");
- $node = new ProtocolNode("iq", $setHash, array($child), "");
+ $node = new ProtocolNode("iq",
+ array(
+ "id" => $id,
+ "type" => "set",
+ "xmlns" => "w:g2",
+ "to" => $this->getJID($groupId)
+ ), array($child), "");
$this->sendNode($node);
- $this->waitForServer($setHash["id"]);
+ $this->waitForServer($id);
}
/**
* Send node to the servers.
*
- * @param $to
- * The recipient to send.
- * @param $node ProtocolNode
- * The node that contains the message.
+ * @param $to
+ * @param ProtocolNode $node
+ * @param null $id
+ *
+ * @return string Message ID.
*/
protected function sendMessageNode($to, $node, $id = null)
{
- $serverNode = new ProtocolNode("server", null, null, "");
- $xHash = array();
- $xHash["xmlns"] = "jabber:x:event";
- $xNode = new ProtocolNode("x", $xHash, array($serverNode), "");
- $notify = array();
- $notify['xmlns'] = 'urn:xmpp:whatsapp';
- $notify['name'] = $this->name;
- $notnode = new ProtocolNode("notify", $notify, null, "");
- $request = array();
- $request['xmlns'] = "urn:xmpp:receipts";
- $reqnode = new ProtocolNode("request", $request, null, "");
+ $msgId = ($id == null) ? $this->createMsgId() : $id;
+ $to = $this->getJID($to);
- $messageHash = array();
- $messageHash["to"] = $this->getJID($to);
- if($node->getTag() == "body")
- {
- $messageHash["type"] = "text";
- }
- else
- {
- $messageHash["type"] = "media";
- }
- $messageHash["id"] = ($id == null?$this->createMsgId("message"):$id);
- $messageHash["t"] = time();
+ $messageNode = new ProtocolNode("message", array(
+ 'to' => $to,
+ 'type' => ($node->getTag() == "body") ? 'text' : 'media',
+ 'id' => $msgId,
+ 't' => time()
+ ), array($node), "");
- $messageNode = new ProtocolNode("message", $messageHash, array($xNode, $notnode, $reqnode, $node), "");
$this->sendNode($messageNode);
- $this->eventManager()->fireSendMessage(
- $this->phoneNumber,
- $this->getJID($to),
- $messageHash["id"],
- $node
- );
- return $messageHash["id"];
+
+ $this->eventManager()->fire("onSendMessage",
+ array(
+ $this->phoneNumber,
+ $to,
+ $msgId,
+ $node
+ ));
+
+ $this->waitForServer($msgId);
+
+ return $msgId;
}
/**
* Tell the server we received the message.
*
- * @param ProtocolNode $msg
- * The ProtocolTreeNode that contains the message.
+ * @param ProtocolNode $node The ProtocolTreeNode that contains the message.
+ * @param string $type
+ * @param string $participant
+ * @param string $callId
*/
- protected function sendMessageReceived($msg, $type = null)
+ protected function sendReceipt($node, $type = "read", $participant = null, $callId = null)
{
- if($type)
- {
+ $messageHash = array();
+ if ($type == "read") {
$messageHash["type"] = $type;
}
- $messageHash = array();
- $messageHash["to"] = $msg->getAttribute("from");
- $messageHash["id"] = $msg->getAttribute("id");
- $messageNode = new ProtocolNode("receipt", $messageHash, null, null);
+ if ($participant != null) {
+ $messageHash["participant"] = $participant;
+ }
+ $messageHash["to"] = $node->getAttribute("from");
+ $messageHash["id"] = $node->getAttribute("id");
+
+ if ($callId != null)
+ {
+ $offerNode = new ProtocolNode("offer", array("call-id" => $callId), null, null);
+ $messageNode = new ProtocolNode("receipt", $messageHash, array($offerNode), null);
+ }
+ else
+ {
+ $messageNode = new ProtocolNode("receipt", $messageHash, null, null);
+ }
$this->sendNode($messageNode);
- $this->eventManager()->fireSendMessageReceived(
- $this->phoneNumber,
- $msg->getAttribute("id"),
- $msg->getAttribute("from"),
- $type
- );
+ $this->eventManager()->fire("onSendMessageReceived",
+ array(
+ $this->phoneNumber,
+ $node->getAttribute("id"),
+ $node->getAttribute("from"),
+ $type
+ ));
}
/**
* Send node to the WhatsApp server.
* @param ProtocolNode $node
+ * @param bool $encrypt
*/
protected function sendNode($node, $encrypt = true)
{
@@ -2944,149 +3915,123 @@ protected function sendNode($node, $encrypt = true)
/**
* Send request to upload file
*
- * @param $b64hash
- * Base64 hash of file
- * @param string $type
- * File type
- * @param $size
- * File size
- * @param string $filepath
- * Path to image file
- * @param string $to
- * Recipient
+ * @param string $b64hash A base64 hash of file
+ * @param string $type File type
+ * @param string $size File size
+ * @param string $filepath Path to image file
+ * @param mixed $to Recipient(s)
+ * @param string $caption
+ * @return string Message ID
*/
- protected function sendRequestFileUpload($b64hash, $type, $size, $filepath, $to)
+ protected function sendRequestFileUpload($b64hash, $type, $size, $filepath, $to, $caption = "")
{
- $hash = array();
- $hash["hash"] = $b64hash;
- $hash["type"] = $type;
- $hash["size"] = $size;
- $mediaNode = new ProtocolNode("media", $hash, null, null);
-
- $hash = array();
- $id = $this->createMsgId("upload");
- $hash["id"] = $id;
- $hash["to"] = static::WHATSAPP_SERVER;
- $hash["type"] = "set";
- $hash["xmlns"] = "w:m";
- $node = new ProtocolNode("iq", $hash, array($mediaNode), null);
+ $id = $this->createIqId();
if (!is_array($to)) {
$to = $this->getJID($to);
}
+
+ $mediaNode = new ProtocolNode("media", array(
+ 'hash' => $b64hash,
+ 'type' => $type,
+ 'size' => $size
+ ), null, null);
+
+ $node = new ProtocolNode("iq", array(
+ 'id' => $id,
+ 'to' => Constants::WHATSAPP_SERVER,
+ 'type' => 'set',
+ 'xmlns' => 'w:m'
+ ), array($mediaNode), null);
+
//add to queue
- $messageId = $this->createMsgId("message");
- $this->mediaQueue[$id] = array("messageNode" => $node, "filePath" => $filepath, "to" => $to, "message_id" => $messageId);
+ $messageId = $this->createMsgId();
+ $this->mediaQueue[$id] = array(
+ "messageNode" => $node,
+ "filePath" => $filepath,
+ "to" => $to,
+ "message_id" => $messageId,
+ "caption" => $caption
+ );
$this->sendNode($node);
- $this->waitForServer($hash["id"]);
+ $this->waitForServer($id);
+
+ // Return message ID. Make pull request for this.
+ return $messageId;
}
/**
* Set your profile picture
*
* @param string $jid
- * @param string $filepath
- * URL or localpath to image file
+ * @param string $filepath URL or localpath to image file
*/
protected function sendSetPicture($jid, $filepath)
{
- if(stripos($filepath, 'http')!== false && !preg_match('/\s/',$filepath)){
- $extension = end(explode(".", $filepath));
- $newImageName = rand(0, 100000);
- $imagePath = static::PICTURES_FOLDER."/".$newImageName.".jpg";
- if($extension == 'jpg'){
- copy($filepath, $imagePath);
- $filepath = $imagePath;
- }
- }
- preprocessProfilePicture($filepath);
- $fp = @fopen($filepath, "r");
- if ($fp) {
- $data = fread($fp, filesize($filepath));
- if ($data) {
- //this is where the fun starts
- $picture = new ProtocolNode("picture", null, null, $data);
-
- $icon = createIconGD($filepath, 96, true);
- $thumb = new ProtocolNode("picture", array("type" => "preview"), null, $icon);
-
- $hash = array();
- $nodeID = $this->createMsgId("setphoto");
- $hash["id"] = $nodeID;
- $hash["to"] = $this->getJID($jid);
- $hash["type"] = "set";
- $hash["xmlns"] = "w:profile:picture";
- $node = new ProtocolNode("iq", $hash, array($picture, $thumb), null);
-
- $this->sendNode($node);
- $this->waitForServer($nodeID);
- }
- }
- }
- /**
- * Parse the message text for emojis
- *
- * This will look for special strings in the message text
- * that need to be replaced with a unicode character to show
- * the corresponding emoji.
- *
- * Emojis should be entered in the message text either as the
- * correct unicode character directly, or if this isn't possible,
- * by putting a placeholder of ##unicodeNumber## in the message text.
- * Include the surrounding ##
- * eg:
- * ##1f604## this will show the smiling face
- * ##1f1ec_1f1e7## this will show the UK flag.
- *
- * Notice that if 2 unicode characters are required they should be joined
- * with an underscore.
- *
- *
- * @param string $txt
- * The message to be parsed for emoji code.
- *
- * @return string
- */
- private function parseMessageForEmojis($txt)
- {
- $matches = null;
- preg_match_all('/##(.*?)##/', $txt, $matches, PREG_SET_ORDER);
- if (is_array($matches)) {
- foreach ($matches as $emoji) {
- $txt = str_ireplace($emoji[0], $this->unichr((string) $emoji[1]), $txt);
- }
- }
+ $nodeID = $this->createIqId();
- return $txt;
- }
+ $data = preprocessProfilePicture($filepath);
+ $preview = createIconGD($filepath, 96, true);
- /**
- * Creates the correct unicode character from the unicode code point
- *
- * @param int $int
- * @return string
- */
- private function unichr($int)
- {
- $string = null;
- $multiChars = explode('_', $int);
+ $picture = new ProtocolNode("picture", array("type" => "image"), null, $data);
+ $preview = new ProtocolNode("picture", array("type" => "preview"), null, $preview);
- foreach ($multiChars as $char) {
- $string .= mb_convert_encoding('' . intval($char, 16) . ';', 'UTF-8', 'HTML-ENTITIES');
- }
+ $node = new ProtocolNode("iq", array(
+ 'id' => $nodeID,
+ 'to' => $this->getJID($jid),
+ 'type' => 'set',
+ 'xmlns' => 'w:profile:picture'
+ ), array($picture, $preview), null);
- return $string;
+ $this->sendNode($node);
+ $this->waitForServer($nodeID);
}
/**
* @param string $jid
+ *
* @return string
*/
- public static function parseJID($jid)
+ private function parseJID($jid)
{
$parts = explode('@', $jid);
$parts = reset($parts);
return $parts;
}
+
+
+
+ /**
+ * @param ProtocolNode $groupNode
+ * @param mixed $fromGetGroups
+ */
+ protected function handleGroupV2InfoResponse(ProtocolNode $groupNode, $fromGetGroups = false)
+ {
+ $creator = $groupNode->getAttribute('creator');
+ $creation = $groupNode->getAttribute('creation');
+ $subject = $groupNode->getAttribute('subject');
+ $groupID = $groupNode->getAttribute('id');
+ $participants = array();
+ $admins = array();
+ if ($groupNode->getChild(0) != null) {
+ foreach ($groupNode->getChildren() as $child) {
+ $participants[] = $child->getAttribute('jid');
+ if ($child->getAttribute('type') == "admin")
+ $admins[] = $child->getAttribute('jid');
+ }
+ }
+ $this->eventManager()->fire("onGetGroupV2Info",
+ array(
+ $this->phoneNumber,
+ $groupID,
+ $creator,
+ $creation,
+ $subject,
+ $participants,
+ $admins,
+ $fromGetGroups
+ )
+ );
+ }
}
diff --git a/tests/WhatAppEventTest.php b/tests/WhatAppEventTest.php
deleted file mode 100755
index e5ca3689..00000000
--- a/tests/WhatAppEventTest.php
+++ /dev/null
@@ -1,244 +0,0 @@
-#!/usr/bin/php
-eventManager()->addEventListener($listener);
-
-// Old event bindings:
-$old_function1_called = array();
-$old_function2_called = array();
-
-// Function bound to variable:
-$oldFunction = function(
- $phone,
- $from,
- $msgid,
- $type,
- $time,
- $name,
- $message
-) use (&$old_function1_called) {
- array_push($old_function1_called, array(
- 'onGetMessage',
- array(
- $phone,
- $from,
- $msgid,
- $type,
- $time,
- $name,
- $message
- )
- ) );
-};
-
-// Named funciton:
-function oldFunction2(
- $phone,
- $from,
- $msgid,
- $type,
- $time,
- $name,
- $message
-) {
- global $old_function2_called;
-
- array_push($old_function2_called, array(
- 'onGetMessage',
- array(
- $phone,
- $from,
- $msgid,
- $type,
- $time,
- $name,
- $message
- )
- ) );
-};
-
-// Callback approach:
-$w->eventManager()->bind('onGetMessage', $oldFunction );
-// String approach:
-$w->eventManager()->bind('onGetMessage', 'oldFunction2' );
-
-print( "Test #1: onGetMessage\n" );
-/*
-rx
-rx
-rx
-rx TestMessage
-rx
-*/
-
-// To be moved to config:
-$node = new ProtocolNode("message",array(
- 'from' => '441234123456',
- 'id' => '1234567890-123',
- 'type' => 'chat',
- 't' => '1234567890'
-), array(
- new ProtocolNode('notify', array('xmlns'=>'urn:xmpp:whatsapp','name'=>'First LastName'), NULL, ''),
- new ProtocolNode('request', array('xmlns'=>'urn:xmpp:receipts'), NULL, ''),
- new ProtocolNode('body', NULL, NULL, 'TestMessage')
-), '' );
-
-// Send the data into the framework:
-$w->processInboundDataNode($node);
-
-// Analyze the results:
-$actual = $listener->getAndResetCapture();
-// Assert expected result:
-$expected = array(
- //First event raised:
- array(
- 'onSendMessageReceived',
- array(
- 0 => '$username',
- // 1 = This is the current time.
- 2 => '441234123456'
- )
- ),
- //Second event raised:
- array(
- // Event name:
- 'onGetMessage',
- // Event Arguments:
- array(
- '$username',
- '441234123456',
- '1234567890-123',
- 'chat',
- '1234567890',
- 'First LastName',
- 'TestMessage'
- )
- )
-);
-$old_expected = array($expected[1]);
-
-unset($actual[0][1][1]); // Remove the time.
-
-// Analyze the results:
-if( $expected === $actual
- && $old_expected === $old_function1_called
- && $old_expected === $old_function2_called ) {
- print( "Test 1 Passed.\n");
-} else {
- print( "Test 1 Failed!!!!!\n" );
-}
-$old_function1_called = array();
-$old_function2_called = array();
-
-$expected = array(
- //First event raised:
- array(
- // Event name:
- 'onGetMessage',
- // Event Arguments:
- array(
- '$username',
- '441234123456',
- '1234567890-123',
- 'chat',
- '1234567890',
- 'First LastName',
- 'TestMessage'
- )
- )
-);
-$old_expected = $expected;
-
-print( "Test #2: Legacy Fire\n" );
-$w->eventManager()->fire('onGetMessage', array(
- '$username',
- '441234123456',
- '1234567890-123',
- 'chat',
- '1234567890',
- 'First LastName',
- 'TestMessage'
-) );
-
-// Analyze the results:
-$actual = $listener->getAndResetCapture();
-if( $expected === $actual
- && $old_expected === $old_function1_called
- && $old_expected === $old_function2_called ) {
- print( "Test 2 Passed.\n");
-} else {
- print( "Test 2 Failed!!!!!\n" );
-}
-$old_function1_called = array();
-$old_function2_called = array();
-
-
-print( "Test #3: onGetGroupMessage\n" );
-/*
-rx
-rx
-rx
-rx Are you real, or are you a bot?
-rx
-*/
-
-$node = new ProtocolNode("message",array(
- 'from' => '441234123456-1234567890@g.us',
- 'id' => '1234567890-123',
- 'type' => 'chat',
- 't' => '1234567890',
- 'author' => '11231231234@s.whatsapp.net'
-), array(
- new ProtocolNode('notify', array('xmlns'=>'urn:xmpp:whatsapp','name'=>'Fun Guy'), NULL, ''),
- new ProtocolNode('request', array('xmlns'=>'urn:xmpp:receipts'), NULL, ''),
- new ProtocolNode('body', NULL, NULL, 'Are you real, or are you a bot?')
-), '' );
-// Send the data into the framework:
-$w->processInboundDataNode($node);
-// Analyze the results:
-$actual = $listener->getAndResetCapture();
-// Assert expected result:
-$expected = array(
- //First event raised:
- array(
- 'onSendMessageReceived',
- array(
- 0 => '$username',
- // 1 = This is the current time.
- 2 => '441234123456-1234567890@g.us'
- )
- ),
- // Second event raised:
- array(
- // Event name:
- 'onGetGroupMessage',
- // Event Arguments:
- array(
- '$username',
- '441234123456-1234567890@g.us',
- '11231231234@s.whatsapp.net',
- '1234567890-123',
- 'chat',
- '1234567890',
- 'Fun Guy',
- 'Are you real, or are you a bot?'
- )
- )
-);
-unset($actual[0][1][1]); // Remove the time.
-
-if( $expected === $actual ) {
- print( "Test 3 Passed.\n");
-} else {
- print( "Test 3 Failed!!!!!\n" );
-}
-
-
-?>
\ No newline at end of file
diff --git a/tests/WhatsAppEventListenerCapture.php b/tests/WhatsAppEventListenerCapture.php
deleted file mode 100755
index 6ff96241..00000000
--- a/tests/WhatsAppEventListenerCapture.php
+++ /dev/null
@@ -1,20 +0,0 @@
-capture, array($eventName,$arguments));
- }
-
- public function getAndResetCapture() {
- $ret = $this->capture;
- $this->capture = array();
- return $ret;
- }
-}
diff --git a/tests/whatsapp.php b/tests/whatsapp.php
index 23197d11..5c876c5c 100755
--- a/tests/whatsapp.php
+++ b/tests/whatsapp.php
@@ -1,4 +1,3 @@
-#!/usr/bin/php
connect();
$wa->loginWithPassword($password);
@@ -86,7 +84,9 @@ function fgets_u($pStdn)
while (TRUE) {
$wa->pollMessage();
$data = $wa->getMessages();
- if(!empty($data)) print_r($data);
+ if (!empty($data)) {
+ print_r($data);
+ }
sleep(1);
}
exit(0);
@@ -103,4 +103,4 @@ function fgets_u($pStdn)
echo "\n[] Send message to $dst: $msg\n";
$wa->sendMessage($dst , $msg);
-echo "\n";
\ No newline at end of file
+echo "\n";