예제 #1
0
    def _ping(self):
        msgid = self._msgid()

        message = Node("iq", id=msgid, type="get", to=self.SERVER)
        message.add(Node("ping", xmlns="w:p"))

        self._write(message)
예제 #2
0
    def _ping(self):
        msgid = self._msgid()

        message = Node("iq", id=msgid, type="get", to=self.SERVER)
        message.add(Node("ping", xmlns="w:p"))

        self._write(message)
예제 #3
0
    def _received(self, node):
        request = node.child("request")
        if request is None or request["xmlns"] != "urn:xmpp:receipts":
            return

        message = Node("message", to=node["from"], id=node["id"], type="chat")
        message.add(Node("received", xmlns="urn:xmpp:receipts"))

        self._write(message)
예제 #4
0
    def _received(self, node):
        request = node.child("request")
        if request is None or request["xmlns"] != "urn:xmpp:receipts":
            return

        message = Node("message", to=node["from"], id=node["id"], type="chat")
        message.add(Node("received", xmlns="urn:xmpp:receipts"))

        self._write(message)
예제 #5
0
    def send_sync(self,
                  numbers,
                  mode="full",
                  context="registration",
                  index=0,
                  last=True):
        msgid = self._msgid("sync")
        sid = (int(time()) + 11644477200) * 10000000

        sync = Node("sync",
                    mode=mode,
                    context=context,
                    sid=str(sid),
                    index=str(index),
                    last="true" if last else "false")
        node = Node("iq",
                    to=self.number + "@" + self.SERVER,
                    type="get",
                    id=msgid,
                    xmlns="urn:xmpp:whatsapp:sync")
        node.add(sync)

        # Add numbers to node
        for number in numbers:
            if number[0] != "+":
                number = "+" + number
            sync.add(Node("user", data=number))

        self._write(node)
예제 #6
0
    def _clear_dirty(self, *categories):
        nodes = []

        for category in categories:
            nodes.append(Node("clean", type=category))

        self._write(
            Node("iq",
                 id=self._msgid("cleardirty"),
                 type="set",
                 to=self.SERVER,
                 xmlns="urn:xmpp:whatsapp:dirty",
                 children=nodes))
예제 #7
0
    def vcard(self, number, name, data):
        """
        Send a vCard to a contact. WhatsApp will display the PHOTO if it is
        embedded in the vCard data (as Base64 encoded JPEG).
        """

        vcard = Node("vcard", data=data)
        vcard["name"] = name
        media = Node("media",
                     children=[vcard],
                     xmlns="urn:xmpp:whatsapp:mms",
                     type="vcard",
                     encoding="text")
        msgid, message = self._message(number, media)
        return msgid
예제 #8
0
    def _message(self, to, node, group=False):
        msgid = self._msgid("message")
        to = self._jid(to)

        x = Node("x", xmlns="jabber:x:event", children=Node("server"))
        notify = Node("notify", xmlns="urn:xmpp:whatsapp", name=self.nickname)
        request = Node("request", xmlns="urn:xmpp:receipts")

        message = Node("message",
                       to=to,
                       type="text",
                       id=msgid,
                       t=utils.timestamp(),
                       children=[x, notify, request, node])

        return msgid, message
예제 #9
0
 def _receipt(self, node):
     self._write(
         Node("receipt",
              type="read",
              to=node["from"],
              id=node["id"],
              t=utils.timestamp()))
예제 #10
0
        def on_success(node):
            logger.info("Login attempt successfull")
            self.account_info = node.attributes

            presence = Node("presence")
            presence["name"] = self.nickname
            self._write(presence)
예제 #11
0
    def chatstate(self, number, state):
        if state not in CHATSTATES:
            raise ValueError("Invalid chatstate: %r" % state)

        node = Node(state, xmlns=CHATSTATE_NS)
        msgid, message = self._message(number, node)
        self._write(message)
        return msgid
예제 #12
0
    def vcard(self, number, name, data):
        """
        Send a vCard to a contact. WhatsApp will display the photo if it is
        embedded in the vCard data as base64 encoded JPEG.
        """

        vcard = Node("vcard", name=name, data=data)
        media = Node("media",
                     children=[vcard],
                     xmlns="urn:xmpp:whatsapp:mms",
                     type="vcard",
                     encoding="text")

        msgid, message = self._message(number, media)

        self._write(message)
        return msgid
예제 #13
0
        def on_success(node):
            self.auth_blob = node.data
            self.account_info = node.attributes

            if node["status"] == "expired":
                self._disconnect()
                raise LoginError("Account marked as expired.")

            self._write(Node("presence", name=self.nickname))
예제 #14
0
    def last_seen(self, number):
        msgid = self._msgid("lastseen")

        iq = Node("iq", type="get", id=msgid)
        iq["from"] = self.number + "@" + self.SERVER
        iq["to"] = number + "@" + self.SERVER
        iq.add(Node("query", xmlns="jabber:iq:last"))

        self._write(iq)

        def on_iq(node):
            if node["id"] != msgid:
                return
            if node["type"] == "error":
                return StreamError(node.child("error").children[0].name)
            return int(node.child("query")["seconds"])

        callback = Callback("iq", on_iq)
        self.register_callback_and_wait(callback)
예제 #15
0
    def last_seen(self, number):
        msgid = self._msgid("lastseen")

        iq = Node("iq", type="get", id=msgid)
        iq["from"] = self.number + "@" + self.SERVER
        iq["to"] = number + "@" + self.SERVER
        iq.add(Node("query", xmlns="jabber:iq:last"))

        self._write(iq)

        def on_iq(node):
            if node["id"] != msgid:
                return
            if node["type"] == "error":
                return StreamError(node.child("error").children[0].name)
            return int(node.child("query")["seconds"])

        callback = Callback("iq", on_iq)
        self.register_callback_and_wait(callback)
예제 #16
0
    def _message(self, to, node, group=False):
        msgid = self._msgid()

        message = Node("message", type="chat", id=msgid)
        message["to"] = to + "@" + (self.GROUPHOST if group else self.SERVER)

        x = Node("x", xmlns="jabber:x:event")
        x.add(Node("server"))

        message.add(x)
        message.add(node)

        return msgid, message
예제 #17
0
    def _challenge(self, node):
        encryption = Encryption(self.number, self.secret, node.data)
        self.writer.encrypt = encryption.encrypt
        self.reader.decrypt = encryption.decrypt

        logger.debug("Session Key: %s", encryption.export_key())

        response = Node("response",
                        xmlns="urn:ietf:params:xml:ns:xmpp-sasl",
                        data=encryption.get_response())
        self._write(response, encrypt=False)
        self._incoming()
예제 #18
0
    def _notification(self, node):
        out = Node("ack", to=node["from"], id=node["id"], type=node["type"])

        # Class is reserved keyword.
        out["class"] = "notification"

        if node.has_attribute("to"):
            out["from"] = node["to"]
        if node.has_attribute("participant"):
            out["participant"] = node["participant"]

        self._write(out)
예제 #19
0
    def _iq(self, node):
        # Node without children could be a ping reply
        if len(node.children) == 0:
            return

        iq = node.children[0]
        if node["type"] == "get" and iq.name == "ping":
            self._write(
                Node("iq", to=self.SERVER, id=node["id"], type="result"))
        elif node["type"] == "result":
            pass
        else:
            logger.debug("Unknown iq message received: %s", node["type"])
예제 #20
0
    def _challenge(self, node):
        encryption = Encryption(self.secret, node.data)
        logger.debug("Session Keys: %s",
                     [key.encode("hex") for key in encryption.keys])

        self.writer.encrypt = encryption.encrypt
        self.reader.decrypt = encryption.decrypt

        data = "%s%s%s" % (self.number, node.data, utils.timestamp())
        response = Node("response", data=encryption.encrypt(data, False))

        self._write(response, encrypt=False)
        self._incoming()
예제 #21
0
    def location(self, number, latitude, longitude):
        """
        Send a location update to a contact.
        """

        media = Node("media",
                     xmlns="urn:xmpp:whatsapp:mms",
                     type="location",
                     latitude=latitude,
                     longitude=longitude)
        msgid, message = self._message(number, media)
        self._write(message)
        return msgid
예제 #22
0
    def _read(self):
        length = self.list_start()
        token = self.peek_int8()

        if token == 0x01:
            self._consume(1)
            attributes = self.attributes(length)
            return Node("start", **attributes)
        elif token == 0x02:
            self._consume(1)
            raise EndOfStream()

        node = Node(self.string())
        node.attributes = self.attributes(length)

        if (length % 2) == 0:
            token = self.peek_int8()

            if token == 0xf8 or token == 0xf9:
                node.children = self.list()
            else:
                node.data = self.string()

        return node
예제 #23
0
    def _read(self):
        length = self.list_start()

        token = self._peek(1)
        if token == "\x01":
            self._consume(1)
            attributes = self.attributes(length)
            return Node("start", **attributes)

        if token == "\x02":
            self._consume(1)
            raise EndOfStream()

        node = Node(self.string())
        node.attributes = self.attributes(length)

        if (length % 2) == 0:
            token = self._peek(1)
            if token == "\xF8" or token == "\xF9":
                node.children = self.list()
            else:
                node.data = self.string()

        return node
예제 #24
0
    def _iq(self, node):
        # Node without children could be a ping reply
        if len(node.children) == 0:
            return

        iq = node.children[0]
        if node["type"] == "get" and iq.name == "ping":
            self._write(
                Node("iq", to=self.SERVER, id=node["id"], type="result"))
        elif node["type"] == "result" and iq.name == "query":
            self.messages.append(node)
        else:
            logger.debug("Unknown iq message received")

            if self.debug:
                print node.toxml(indent="  ") + "\n"
예제 #25
0
    def audio(self, number, url, basename, size, attributes):
        valid_attributes = ("abitrate", "acodec", "asampfmt", "asampfreq",
                            "duration", "encoding", "filehash", "mimetype")

        for name, value in attributes.iteritems:
            if name not in valid_attributes:
                raise Exception("Unknown audio attribute: %r" % name)

        media = Node("media",
                     xmlns="urn:xmpp:whatsapp:mms",
                     type="audio",
                     url=url,
                     file=basename,
                     size=size,
                     **attributes)
        msgid, message = self._message(number, media)
        self._write(message)
        return msgid
예제 #26
0
    def image(self, number, url, basename, size, thumbnail=None):
        """
        Send an image to a contact.
        Url should be publicly accessible
        Basename does not have to match Url
        Size is the size of the image, in bytes
        Thumbnail should be a Base64 encoded JPEG image, if provided.
        """
        # TODO: Where does WhatsApp upload images?
        # PNG thumbnails are apparently not supported

        media = Node("media",
                     xmlns="urn:xmpp:whatsapp:mms",
                     type="image",
                     url=url,
                     file=basename,
                     size=size,
                     data=thumbnail)
        msgid, message = self._message(number, media)
        self._write(message)
        return msgid
예제 #27
0
    def send_sync(self, numbers, mode="full", context="registration", index=0,
                  last=True):
        msgid = self._msgid("sync")
        sid = (int(time()) + 11644477200) * 10000000

        sync = Node(
            "sync", mode=mode, context=context, sid=str(sid), index=str(index),
            last="true" if last else "false")
        node = Node(
            "iq", to=self.number + "@" + self.SERVER, type="get", id=msgid,
            xmlns="urn:xmpp:whatsapp:sync")
        node.add(sync)

        # Add numbers to node
        for number in numbers:
            if number[0] != "+":
                number = "+" + number
            sync.add(Node("user", data=number))

        self._write(node)
예제 #28
0
    def _read(self):
        length = self.list_start()
        token = self.peek_int8()

        if token == 0x01:
            self._consume(1)
            attributes = self.attributes(length)
            return Node("start", **attributes)
        elif token == 0x02:
            self._consume(1)
            raise EndOfStream()

        node = Node(self.string())
        node.attributes = self.attributes(length)

        if (length % 2) == 0:
            token = self.peek_int8()

            if token == 0xf8 or token == 0xf9:
                node.children = self.list()
            else:
                node.data = self.string()

        return node
예제 #29
0
    def _read(self):
        length = self.list_start()

        token = self._peek(1)
        if token == "\x01":
            self._consume(1)
            attributes = self.attributes(length)
            return Node("start", **attributes)

        if token == "\x02":
            self._consume(1)
            raise EndOfStream()

        node = Node(self.string())
        node.attributes = self.attributes(length)

        if (length % 2) == 0:
            token = self._peek(1)
            if token == "\xF8" or token == "\xF9":
                node.children = self.list()
            else:
                node.data = self.string()

        return node
예제 #30
0
    def login(self):
        assert self.socket is None

        self._connect()

        buf = self.writer.start_stream(self.SERVER, self.VERSION)
        self._write(buf)

        features = Node("stream:features")
        features.add(Node("receipt_acks"))
        features.add(Node("w:profile:picture", type="all"))
        features.add(Node("status"))

        # m1.java:48
        features.add(Node("notification", type="participant"))
        features.add(Node("groups"))

        self._write(features)

        auth = Node("auth", xmlns="urn:ietf:params:xml:ns:xmpp-sasl",
                    mechanism="WAUTH-1", user=self.number)
        self._write(auth)

        def on_success(node):
            logger.info("Login attempt successfull")
            self.account_info = node.attributes

            presence = Node("presence")
            presence["name"] = self.nickname
            self._write(presence)

        # Create new callback
        callback = Callback("success", on_success)
        self.register_callback_and_wait(callback)

        # Done
        return self.account_info != None
예제 #31
0
    def connect(self):
        self.reader = Reader()
        self.writer = Writer()

        self._connect()

        buf = self.writer.start_stream(self.SERVER, "%s-%s-%d" % (
            PROTOCOL_DEVICE, PROTOCOL_VERSION, PORT))
        self._write(buf)

        # Send features node
        features = Node("stream:features")
        features.add(Node("readreceipts"))
        features.add(Node("groups_v2"))
        features.add(Node("privacy"))
        features.add(Node("presence"))
        self._write(features)

        # Send auth node
        auth = Node("auth", mechanism="WAUTH-2", user=self.number)

        if self.auth_blob:
            encryption = AuthBlobEncryption(self.secret, self.auth_blob)
            logger.debug(
                "Session Keys (re-using auth challenge): %s",
                [key.encode("hex") for key in encryption.keys])

            self.reader.decrypt = encryption.decrypt

            # From WhatsAPI. It does not encrypt the data, but generates a MAC
            # based on the keys.
            data = "%s%s%s" % (self.number, self.auth_blob, utils.timestamp())
            auth.data = encryption.encrypt("", False) + data

        self._write(auth)

        def on_success(node):
            self.auth_blob = node.data
            self.account_info = node.attributes

            if node["status"] == "expired":
                self._disconnect()
                raise LoginError("Account marked as expired.")

            self._write(Node("presence", name=self.nickname))

        def on_failure(node):
            self._disconnect()
            raise LoginError("Incorrect number and/or secret.")

        # Wait for either success, or failure
        self.register_callback_and_wait(
            LoginSuccessCallback(on_success),
            LoginFailedCallback(on_failure))
예제 #32
0
 def presence(self, state):
     self._write(Node("presence", type=state))
예제 #33
0
 def group_message(self, group, text):
     msgid, message = self._message(group, Node("body", data=text), True)
     self._write(message)
     return msgid
예제 #34
0
 def message(self, number, text):
     msgid, message = self._message(number, Node("body", data=text))
     self._write(message)
     return msgid
예제 #35
0
    def send_server_properties(self):
        msgid = self._msgid("getproperties")
        node = Node("iq", id=msgid, type="get", xmlns="w", to=self.SERVER)
        node.add(Node("props"))

        self._write(node)
예제 #36
0
    def send_server_properties(self):
        msgid = self._msgid("getproperties")
        node = Node("iq", id=msgid, type="get", xmlns="w", to=self.SERVER)
        node.add(Node("props"))

        self._write(node)
예제 #37
0
    def connect(self):
        self.reader = Reader()
        self.writer = Writer()

        self._connect()

        buf = self.writer.start_stream(
            self.SERVER,
            "%s-%s-%d" % (PROTOCOL_DEVICE, PROTOCOL_VERSION, PORT))
        self._write(buf)

        # Send features node
        features = Node("stream:features")
        features.add(Node("readreceipts"))
        features.add(Node("groups_v2"))
        features.add(Node("privacy"))
        features.add(Node("presence"))
        self._write(features)

        # Send auth node
        auth = Node("auth", mechanism="WAUTH-2", user=self.number)

        if self.auth_blob:
            encryption = AuthBlobEncryption(self.secret, self.auth_blob)
            logger.debug("Session Keys (re-using auth challenge): %s",
                         [key.encode("hex") for key in encryption.keys])

            self.reader.decrypt = encryption.decrypt

            # From WhatsAPI. It does not encrypt the data, but generates a MAC
            # based on the keys.
            data = "%s%s%s" % (self.number, self.auth_blob, utils.timestamp())
            auth.data = encryption.encrypt("", False) + data

        self._write(auth)

        def on_success(node):
            self.auth_blob = node.data
            self.account_info = node.attributes

            if node["status"] == "expired":
                self._disconnect()
                raise LoginError("Account marked as expired.")

            self._write(Node("presence", name=self.nickname))

        def on_failure(node):
            self._disconnect()
            raise LoginError("Incorrect number and/or secret.")

        # Wait for either success, or failure
        self.register_callback_and_wait(LoginSuccessCallback(on_success),
                                        LoginFailedCallback(on_failure))
예제 #38
0
    def login(self):
        assert self.socket is None

        self._connect()

        buf = self.writer.start_stream(self.SERVER, self.VERSION)
        self._write(buf)

        features = Node("stream:features")
        features.add(Node("receipt_acks"))
        features.add(Node("w:profile:picture", type="all"))
        features.add(Node("status"))

        # m1.java:48
        features.add(Node("notification", type="participant"))
        features.add(Node("groups"))

        self._write(features)

        auth = Node("auth",
                    xmlns="urn:ietf:params:xml:ns:xmpp-sasl",
                    mechanism="WAUTH-1",
                    user=self.number)
        self._write(auth)

        def on_success(node):
            logger.info("Login attempt successfull")
            self.account_info = node.attributes

            presence = Node("presence")
            presence["name"] = self.nickname
            self._write(presence)

        # Create new callback
        callback = Callback("success", on_success)
        self.register_callback_and_wait(callback)

        # Done
        return self.account_info != None
예제 #39
0
    def _message(self, to, node, group=False):
        msgid = self._msgid()

        message = Node("message", type="chat", id=msgid)
        message["to"] = to + "@" + (self.GROUPHOST if group else self.SERVER)

        x = Node("x", xmlns="jabber:x:event")
        x.add(Node("server"))

        message.add(x)
        message.add(node)

        return msgid, message