Exemple #1
0
def location_services(device: AppleDevice) -> Union[NoReturn,
                                                    Tuple[str or float or None, str or float or None, str or None]]:
    """Gets the current location of an Apple device.

    Args:
        device: Passed when locating a particular Apple device.

    Returns:
        None or Tuple[str or float, str or float, str or float]:
        - On success, returns ``current latitude``, ``current longitude`` and ``location`` information as a ``dict``.
        - On failure, calls the ``restart()`` or ``terminator()`` function depending on the error.

    Raises:
        PyiCloudFailedLoginException: Restarts if occurs once. Uses location by IP, if occurs once again.
    """
    try:
        # tries with icloud api to get your device's location for precise location services
        if not device:
            if not (device := device_selector()):
                raise PyiCloudFailedLoginException
        raw_location = device.location()
        if not raw_location and sys._getframe(1).f_code.co_name == "locate":  # noqa
            return None, None, None
        elif not raw_location:
            raise PyiCloudAPIResponseException(reason=f"Unable to retrieve location for {device}")
        else:
            coordinates = raw_location["latitude"], raw_location["longitude"]
        os.remove("pyicloud_error") if os.path.isfile("pyicloud_error") else None
Exemple #2
0
    def request(self, method, url, **kwargs):  # pylint: disable=arguments-differ

        # Charge logging to the right service endpoint
        callee = inspect.stack()[2]
        module = inspect.getmodule(callee[0])
        request_logger = logging.getLogger(module.__name__).getChild("http")
        if self.service.password_filter not in request_logger.filters:
            request_logger.addFilter(self.service.password_filter)

        request_logger.debug("%s %s %s", method, url, kwargs.get("data", ""))

        has_retried = kwargs.get("retried")
        kwargs.pop("retried", None)
        response = super(PyiCloudSession, self).request(method, url, **kwargs)

        content_type = response.headers.get("Content-Type", "").split(";")[0]
        json_mimetypes = ["application/json", "text/json"]

        if not response.ok and content_type not in json_mimetypes:
            if has_retried is None and response.status_code == 450:
                api_error = PyiCloudAPIResponseException(response.reason,
                                                         response.status_code,
                                                         retry=True)
                request_logger.debug(api_error)
                kwargs["retried"] = True
                return self.request(method, url, **kwargs)
            self._raise_error(response.status_code, response.reason)

        if content_type not in json_mimetypes:
            return response

        try:
            data = response.json()
        except:  # pylint: disable=bare-except
            request_logger.warning(
                "Failed to parse response with JSON mimetype")
            return response

        request_logger.debug(data)

        if isinstance(data, dict):
            reason = data.get("errorMessage")
            reason = reason or data.get("reason")
            reason = reason or data.get("errorReason")
            if not reason and isinstance(data.get("error"), string_types):
                reason = data.get("error")
            if not reason and data.get("error"):
                reason = "Unknown reason"

            code = data.get("errorCode")
            if not code and data.get("serverErrorCode"):
                code = data.get("serverErrorCode")

                if reason:
                    self._raise_error(code, reason)

        return response
Exemple #3
0
    def request(self, *args, **kwargs):

        # Charge logging to the right service endpoint
        callee = inspect.stack()[2]
        module = inspect.getmodule(callee[0])
        logger = logging.getLogger(module.__name__).getChild('http')
        if self.service._password_filter not in logger.filters:
            logger.addFilter(self.service._password_filter)

        logger.debug("%s %s %s", args[0], args[1], kwargs.get('data', ''))

        kwargs.pop('retried', None)
        response = super(PyiCloudSession, self).request(*args, **kwargs)

        content_type = response.headers.get('Content-Type', '').split(';')[0]
        json_mimetypes = ['application/json', 'text/json']

        if not response.ok and content_type not in json_mimetypes:
            if kwargs.get('retried') is None and response.status_code == 450:
                api_error = PyiCloudAPIResponseException(
                    response.reason,
                    response.status_code,
                    retry=True
                )
                logger.warn(api_error)
                kwargs['retried'] = True
                return self.request(*args, **kwargs)
            self._raise_error(response.status_code, response.reason)

        if content_type not in json_mimetypes:
            return response

        try:
            json = response.json()
        except:
            logger.warning('Failed to parse response with JSON mimetype')
            return response

        logger.debug(json)

        reason = json.get('errorMessage')
        reason = reason or json.get('reason')
        reason = reason or json.get('errorReason')
        if not reason and isinstance(json.get('error'), six.string_types):
            reason = json.get('error')
        if not reason and json.get('error'):
            reason = "Unknown reason"

        code = json.get('errorCode')
        if not code and json.get('serverErrorCode'):
            code = json.get('serverErrorCode')

        if reason:
            self._raise_error(code, reason)

        return response
Exemple #4
0
    def _raise_error(self, code, reason):
        if (self.service.requires_2sa
                and reason == "Missing X-APPLE-WEBAUTH-TOKEN cookie"):
            raise PyiCloud2SARequiredException(self.service.user["apple_id"])
        if code in ("ZONE_NOT_FOUND", "AUTHENTICATION_FAILED"):
            reason = ("Please log into https://icloud.com/ to manually "
                      "finish setting up your iCloud service")
            api_error = PyiCloudServiceNotActivatedException(reason, code)
            LOGGER.error(api_error)

            raise (api_error)
        if code == "ACCESS_DENIED":
            reason = (
                reason + ".  Please wait a few minutes then try again."
                "The remote servers might be trying to throttle requests.")

        api_error = PyiCloudAPIResponseException(reason, code)
        LOGGER.error(api_error)
        raise api_error
Exemple #5
0
    def _raise_error(self, code, reason):
        if self.service.requires_2sa and \
                reason == 'Missing X-APPLE-WEBAUTH-TOKEN cookie':
            raise PyiCloud2SARequiredException(self.service.user['apple_id'])
        if code == 'ZONE_NOT_FOUND' or code == 'AUTHENTICATION_FAILED':
            reason = 'Please log into https://icloud.com/ to manually ' \
                'finish setting up your iCloud service'
            api_error = PyiCloudServiceNotActivatedException(reason, code)
            logger.error(api_error)

            raise(api_error)
        if code == 'ACCESS_DENIED':
            reason = reason + '.  Please wait a few minutes then try ' \
                'again.  The remote servers might be trying to ' \
                'throttle requests.'

        api_error = PyiCloudAPIResponseException(reason, code)
        logger.error(api_error)
        raise api_error
Exemple #6
0
    def request(self, method, url, **kwargs):  # pylint: disable=arguments-differ

        # Charge logging to the right service endpoint
        callee = inspect.stack()[2]
        module = inspect.getmodule(callee[0])
        request_logger = logging.getLogger(module.__name__).getChild("http")
        if self.service.password_filter not in request_logger.filters:
            request_logger.addFilter(self.service.password_filter)

        request_logger.debug("%s %s %s" %
                             (method, url, kwargs.get("data", "")))

        has_retried = kwargs.get("retried")
        kwargs.pop("retried", None)
        response = super(PyiCloudSession, self).request(method, url, **kwargs)

        content_type = response.headers.get("Content-Type", "").split(";")[0]
        json_mimetypes = ["application/json", "text/json"]

        for header in HEADER_DATA:
            if response.headers.get(header):
                session_arg = HEADER_DATA[header]
                self.service.session_data.update(
                    {session_arg: response.headers.get(header)})

        # Save session_data to file
        with open(self.service.session_path, "w") as outfile:
            json.dump(self.service.session_data, outfile)
            LOGGER.debug("Saved session data to file")

        # Save cookies to file
        self.cookies.save(ignore_discard=True, ignore_expires=True)
        LOGGER.debug("Cookies saved to %s", self.service.cookiejar_path)

        if not response.ok and (content_type not in json_mimetypes
                                or response.status_code in [421, 450, 500]):
            try:
                # pylint: disable=protected-access
                fmip_url = self.service._get_webservice_url("findme")
                if (has_retried is None
                        and response.status_code in [421, 450, 500]
                        and fmip_url in url):
                    # Handle re-authentication for Find My iPhone
                    LOGGER.debug("Re-authenticating Find My iPhone service")
                    try:
                        # If 450, authentication requires a full sign in to the account
                        service = None if response.status_code == 450 else "find"
                        self.service.authenticate(True, service)

                    except PyiCloudAPIResponseException:
                        LOGGER.debug("Re-authentication failed")
                    kwargs["retried"] = True
                    return self.request(method, url, **kwargs)
            except Exception:
                pass

            if has_retried is None and response.status_code in [421, 450, 500]:
                api_error = PyiCloudAPIResponseException(response.reason,
                                                         response.status_code,
                                                         retry=True)
                request_logger.debug(api_error)
                kwargs["retried"] = True
                return self.request(method, url, **kwargs)

            self._raise_error(response.status_code, response.reason)

        if content_type not in json_mimetypes:
            return response

        try:
            data = response.json()
        except:  # pylint: disable=bare-except
            request_logger.warning(
                "Failed to parse response with JSON mimetype")
            return response

        request_logger.debug(data)

        if isinstance(data, dict):
            reason = data.get("errorMessage")
            reason = reason or data.get("reason")
            reason = reason or data.get("errorReason")
            if not reason and isinstance(data.get("error"), string_types):
                reason = data.get("error")
            if not reason and data.get("error"):
                reason = "Unknown reason"

            code = data.get("errorCode")
            if not code and data.get("serverErrorCode"):
                code = data.get("serverErrorCode")

            if reason:
                self._raise_error(code, reason)

        return response