Mini Shell

Direktori : /lib64/python3.6/site-packages/pyanaconda/__pycache__/
Upload File :
Current File : //lib64/python3.6/site-packages/pyanaconda/__pycache__/geoloc.cpython-36.opt-1.pyc

3

C��e�z�@s�dZddlmZddlZddlZddlZddlZddlZddl	m
Z
ddlmZm
Z
ee�Ze
�ZddlmZddlmZddlmZmZdd	lmZmZdd
lmZejejhZGdd�de �Z!Gd
d�de �Z"Gdd�de �Z#Gdd�de �Z$Gdd�de$�Z%Gdd�de$�Z&Gdd�de$�Z'Gdd�de �Z(Gdd�de �Z)Gdd�de �Z*Gdd �d e �Z+Gd!d"�d"e �Z,da-d#d$�Z.dS)%a�
A GeoIP and WiFi location module - location detection based on IP address

How to use the geolocation module
   First call init_geolocation() with appropriate parameters - this creates
   the Geolocation singleton and specifies what geolocation provider will be
   used.

   To actually look up current position, call the refresh() function of the
   singleton, this will trigger the actual online geolocation query, which
   runs in a thread.

   It's possible to wait for the lookup to finish by calling the
   wait_for_refresh_to_finish() method of the singleton. If a lookup is in
   progress it will block until the lookup finishes or a timeout is reached.
   If no lookup is in progress it will return at once.

   After the look-up thread finishes, the results are stored in the singleton
   and can be retrieved using the territory, timezone and result properties.

   If you use these properties without calling refresh() first or if the
   look-up is currently in progress or failed to return any results, all
   properties will return None.

====================
Geolocation backends
====================

This module currently supports three geolocation backends:
   * Fedora GeoIP API
   * Hostip GeoIP
   * Google WiFi

Fedora GeoIP backend
   This is the default backend. It queries the Fedora GeoIP API for location
   data based on current public IP address. The reply is JSON formatted and
   contains the following fields:
   postal_code, latitude, longitude, region, city, country_code, country_name,
   time_zone, country_code3, area_code, metro_code, region_name and dma_code
   Anaconda currently uses just time_zone and country_code.

Hostip backend
   A GeoIP look-up backend that can be used to determine current country code
   from current public IP address. The public IP address is determined
   automatically when calling the API.
   GeoIP results from Hostip contain the current public IP and an approximate
   address. To get this detail location info, use the result property to get
   an instance of the LocationResult class, used to wrap the lookup result.

Google WiFi backend
   This backend is probably the most accurate one, at least as long as the
   computer has a working WiFi hardware and there are some WiFi APs nearby.
   It sends data about nearby APs (ssid, MAC address & signal strength)
   acquired from Network Manager to a Google API to get approximate
   geographic coordinates. If there are enough AP nearby (such as in a
   normal city) it can be very accurate, even up to currently determining
   which building is the computer currently in.
   But this only returns current geographic coordinates, to get country code
   the Nominatim reverse-geocoding API is called to convert the coordinates
   to an address, which includes a country code.

While having many advantages, this backend also has some severe disadvantages:
   * needs working WiFi hardware
   * tells your public IP address & possibly quite precise geographic coordinates
     to two external entities (Google and Nominatim)

This could have severe privacy issues and should be carefully considered before
enabling it to be used by default. Also the Google WiFi geolocation API seems to
lack official documentation.

As a result its long-term stability might not be guaranteed.



Possible issues with GeoIP
   "I'm in Switzerland connected to corporate VPN and anaconda tells me
   I'm in the Netherlands."
   The public IP address is not directly mapped to the physical location
   of a computer. So while your world visible IP address is registered to
   an IP block assigned to an ISP in Netherlands, it is just the external
   address of the Internet gateway of  your corporate network.
   As VPNs and proxies can connect two computers anywhere on Earth,
   this issue is unfortunately probably unsolvable.


Backends that could possibly be used in the future
   * GPS geolocation

      * (+) doesn't leak your coordinates to a third party
        (not entirely true for assisted GPS)
      * (-) unassisted cold GPS startup can take tens of minutes to acquire a GPS fix
      * (+) assisted GPS startup (as used in most smartphones) can acquire a fix
        in a couple seconds

   * cell tower geolocation

�)�requests_sessionN)�network)�get_module_logger�get_sensitive_info_logger)�	constants)�conf)�AnacondaThread�	threadMgr)�get_preferred_timezone�is_valid_timezone)�flagsc@sXeZdZdZddd�Zdd�Zdd	�Zejfd
d�Z	e
dd
��Ze
dd��Zdd�Z
dS)�GeolocationzTop level geolocation handler.NFcCsV|j||�|_tj}|dk	rF|jrF|j|�}|dkrBtjd|�n|}t|d�|_dS)aPrepare the geolocation module for handling geolocation queries.

        This sets-up the Geolocation instance with the given geolocation_provider (or using the
        default one if no provider is given. Please note that calling this method doesn't actually
        execute any queries by itself, you need to call refresh() to do that.

        :param geoloc_option: what was passed in boot or command line options
        :type geoloc_option: str or None
        :param options_override:
        :type options_override: bool
        Nz'geoloc: wrong provider id specified: %s)�provider_id)	�$_check_if_geolocation_should_be_used�_geolocation_enabledr�GEOLOC_DEFAULT_PROVIDER�_get_provider_id_from_option�log�error�LocationInfo�_location_info)�self�
geoloc_option�options_overriderZ	parsed_id�r�/usr/lib64/python3.6/geoloc.py�__init__�s
zGeolocation.__init__cCshtjjstjd�dSt|�j�dkr4tjd�dStjrZ|rLtjd�dStjd�dStjd�dS)	a~Check if geolocation can be used during this installation run.

        And set the geolocation_enabled module attribute accordingly.

        The result is based on current installation type - fully interactive vs
        fully or partially automated kickstart installation and on the state of the
        "geoloc*" boot/CLI options.

        By default geolocation is not enabled during a kickstart based installation,
        unless the geoloc_use_with_ks boot/CLI option is used.

        Also the geoloc boot/CLI option can be used to make sure geolocation
        will not be used during an installation, like this:

        inst.geoloc=0

        :param geoloc_option: what was passed in boot or command line options
        :type geoloc_option: str or None
        :param options_override: use with kickstart due to CLI/boot option override
        :type options_override: bool
        z<Geolocation is disabled for image or directory installation.F�0z-Geolocation is disabled by the geoloc option.zaGeolocation is enabled during kickstart installation due to use of the geoloc-use-with-ks option.TzFGeolocation is disabled due to automated kickstart based installation.zGeolocation is enabled.)	r�targetZis_hardwarer�info�str�striprZautomatedInstall)rrrrrrr�s




z0Geolocation._check_if_geolocation_should_be_usedcCs|jj�dS)z+Refresh information about current location.N)r�refresh)rrrrr"�szGeolocation.refreshcCsjtj�}|jjj�|jjrZ|jjj|d�|jjrBtjd|�ntj�|}tjd|�|jjj�dS)z�Wait for the Geolocation lookup to finish.

        If there is no lookup in progress (no Geolocation refresh thread
        is running), this function returns at once.

        :param float timeout: how many seconds to wait before timing out
        )�timeoutz3Waiting for Geolocation timed out after %d seconds.z$Waited %1.2f seconds for GeolocationN)	�timer�refresh_condition�acquire�refresh_in_progress�waitrr�release)rr#�
start_timeZelapsed_timerrr�wait_for_refresh_to_finish�sz&Geolocation.wait_for_refresh_to_finishcCs|jS)z!Report if geolocation is enabled.)r)rrrr�enabled�szGeolocation.enabledcCs|jjS)a/Returns the current geolocation result wrapper.

        None might be returned if:

        - no results were found
        - the refresh is still in progress

        :return: :class:LocationResult instance or None if location is unknown
        :rtype: :class:LocationResult instance or None
        )r�result)rrrrr-�szGeolocation.resultcCs |j�}|j�tkr|SdSdS)z�Get a valid provider id from a string.

        This function is used to parse command line
        arguments/boot options for the geolocation module.

        :param str option_string: option specifying the provider
        :return: provider id
        N)�lower�-OFFICIALLY_SUPPORTED_GEOLOCATION_PROVIDER_IDS)rZ
option_stringrrrrs
z(Geolocation._get_provider_id_from_option)NF)�__name__�
__module__�__qualname__�__doc__rrr"rZGEOLOC_TIMEOUTr+�propertyr,r-rrrrrr
�s
1r
c@sJeZdZdZejfdd�Zedd��Zdd�Z	edd	��Z
ed
d��ZdS)
rz�Determines current location.

    Determines current location based on IP address or
    nearby WiFi access points (depending on what backend is used)
    cCs.tjttjttjti}|j|t�}|�|_dS)z;
        :param str provider_id: GeoIP provider id
        N)	r�GEOLOC_PROVIDER_FEDORA_GEOIP�FedoraGeoIPProvider�GEOLOC_PROVIDER_HOSTIP�HostipGeoIPProviderZGEOLOC_PROVIDER_GOOGLE_WIFI�GoogleWiFiLocationProvider�get�	_provider)rrZavailable_providersZproviderrrrr$s

zLocationInfo.__init__cCs|jjS)zReturn the lookup result.)r;r-)rrrrr-0szLocationInfo.resultcCsHtjtj�rtjd�n,tj�r:tjt	tj|j
jd��n
tjd�dS)zRefresh location info.z#Geoloc: refresh already in progress)�namerz,Geolocation refresh failed - no connectivityN)
r	r:rZTHREAD_GEOLOCATION_REFRESHr�debugrZwait_for_connectivity�addrr;r"r)rrrrr"5szLocationInfo.refreshcCs|jjS)z!Report if refresh is in progress.)r;r')rrrrr'Csz LocationInfo.refresh_in_progresscCs|jjS)z�Provide access to the Refresh condition of the provider.

        So that users of this class can wait for the location lookup to finish.
        )r;r%)rrrrr%HszLocationInfo.refresh_conditionN)r0r1r2r3rrrr4r-r"r'r%rrrrrsrc@sReZdZdZddd�Zedd��Zedd	��Zed
d��Zedd
��Z	dd�Z
dS)�LocationResultz*Encapsulates the result from GeoIP lookup.N�unknowncCs"||_||_||_||_||_dS)a�
        :param territory_code: the territory code from GeoIP lookup
        :type territory_code: string
        :param timezone: the time zone from GeoIP lookup
        :type timezone: string
        :param timezone_source: specifies source of the time zone string
        :type timezone_source: string
        :param public_ip_address: current public IP address
        :type public_ip_address: string
        :param city: current city
        :type city: string
        N)�_territory_code�	_timezone�_timezone_source�_public_ip_address�_city)r�territory_code�timezone�timezone_source�public_ip_address�cityrrrrTs
zLocationResult.__init__cCs|jS)N)rA)rrrrrFhszLocationResult.territory_codecCs|jS)N)rB)rrrrrGlszLocationResult.timezonecCs|jS)N)rD)rrrrrIpsz LocationResult.public_ip_addresscCs|jS)N)rE)rrrrrJtszLocationResult.citycCsf|jr^d|j}|jr*|d|j|jf7}|jrF|d7}|d|j7}|jrZ|d|j7}|SdSdS)Nz
territory: %sz
time zone: %s (from %s)z
public IP address: z%sz	
city: %szPosition unknown)rFrGrCrIrJ)rZ
result_stringrrr�__str__xs
zLocationResult.__str__)NNr@NN)r0r1r2r3rr4rFrGrIrJrKrrrrr?Qs
r?c@sheZdZdZdd�Zedd��Zdd�Zdd	�Zed
d��Z	edd
��Z
edd��Zdd�Zdd�Z
dS)�GeolocationBackendzBase class for GeoIP backends.cCs.t�|_tj�|_t�|_tj�|_d|_	dS)NF)
r?�_result�	threadingZLock�_result_lockr�_sessionZ	Condition�_refresh_condition�_refresh_in_progress)rrrrr�s


zGeolocationBackend.__init__cCsdS)z]Get name of the backend

        :return: name of the backend
        :rtype: string
        Nr)rrrrr<�szGeolocationBackend.namecCs�tjd�tjd|j�|j�d|_WdQRXtj�}|j�tjdtj�|�|j�d|_|jj�WdQRX|jr�tjd�t	jd|j�n
tjd	�dS)
zRefresh the geolocation data.zStarting geolocation lookupzGeolocation provider: %sTNz,Geolocation lookup finished in %1.1f secondsFzgot results from geolocationzgeolocation result:
%szno results from geolocation)
rrr<rQrRr$�_refreshZ
notify_allr-�sensitive_info_log)rr*rrrr"�s

zGeolocationBackend.refreshcCsdS)Nr)rrrrrS�szGeolocationBackend._refreshc	Cs|j�|jSQRXdS)z*Report if location refresh is in progress.N)rQrR)rrrrr'�sz&GeolocationBackend.refresh_in_progresscCs|jS)zOReturn a Condition instance that can be used to wait for the refresh to finish.)rQ)rrrrr%�sz$GeolocationBackend.refresh_conditionc	Cs|j�|jSQRXdS)zeCurrent location.

        :return: geolocation lookup result
        :rtype: LocationResult
        N)rOrM)rrrrr-�szGeolocationBackend.resultc	Cs|j�||_WdQRXdS)N)rOrM)rZ
new_resultrrr�_set_result�szGeolocationBackend._set_resultcCs|jS)N)r<)rrrrrK�szGeolocationBackend.__str__N)r0r1r2r3rr4r<r"rSr'r%r-rUrKrrrrrL�s	
rLc@s(eZdZdZdZedd��Zdd�ZdS)r6z"The Fedora GeoIP service provider.z$https://geoip.fedoraproject.org/citycCsdS)NzFedora GeoIPr)rrrrr<�szFedoraGeoIPProvider.namecCs�y�|jj|jtjdd�}|jtjjkr||j	�}|jdd�}d}|jdd�}t
|�s^t|�}d}|sf|r�|jt
|||d��ntjd|j�Wn^tjjk
r�}ztjd	|�WYdd}~Xn.tk
r�}ztjd
|�WYdd}~XnXdS)NT)r#�verify�country_codeZGeoIPZ	time_zonezterritory code)rFrGrHz;Geoloc: Fedora GeoIP API lookup failed with status code: %sz8Geoloc: RequestException for Fedora GeoIP API lookup:
%sz'Geoloc: Unable to decode GeoIP JSON:
%s)rPr:�API_URLr�NETWORK_CONNECTION_TIMEOUT�status_code�requests�codes�ok�jsonrr
rUr?rr�
exceptions�RequestExceptionr=�
ValueError)r�replyZ
json_reply�	territoryrHZ
timezone_code�errrrS�s,
zFedoraGeoIPProvider._refreshN)r0r1r2r3rXr4r<rSrrrrr6�sr6c@s(eZdZdZdZedd��Zdd�ZdS)r8z"The Hostip GeoIP service provider.z#http://api.hostip.info/get_json.phpcCsdS)NzHostip.infor)rrrrr<�szHostipGeoIPProvider.namecCs�yx|jj|jtjdd�}|jtjjkrh|j	�}|jdd�}|dk	rv|j
t||jdd�|jdd�d��ntj
d|j�Wn^tjjk
r�}ztjd|�WYdd}~Xn.tk
r�}ztjd	|�WYdd}~XnXdS)
NT)r#rVrWZiprJ)rFrIrJz1Geoloc: Hostip lookup failed with status code: %sz1Geoloc: RequestException during Hostip lookup:
%sz(Geoloc: Unable to decode Hostip JSON:
%s)rPr:rXrrYrZr[r\r]r^rUr?rrr_r`r=ra)rrb�
reply_dictrcrdrrrrSs 

zHostipGeoIPProvider._refreshN)r0r1r2r3rXr4r<rSrrrrr8�sr8c@s8eZdZdZdZedd��Zdd�Zdd�Zd	d
�Z	dS)r9z*The Google WiFi location service provider.zUhttps://maps.googleapis.com/maps/api/browserlocation/json?browser=firefox&sensor=truecCsdS)NzGoogle WiFir)rrrrr<szGoogleWiFiLocationProvider.namec
Cs.tjd�tdd�}|j�}|�r y�|jj|j|�tjdd�}|j	�}|jdd�}|dkr�|dd	}|dd
}tjd�t
||d�}t�}	|	j|�}
|
j
}|jt|d
��n
tjd�Wn`tjjk
r�}ztjd|�WYdd}~Xn0tk
�r}ztjd|�WYdd}~XnXn
tjd�dS)Nz Scanning for WiFi access points.T)�scan_now)r#rV�statuszNOT OKZOK�location�latZlngzFound current location.)ri�lon)rFz/Geoloc: Service couldn't find current location.z6Geoloc: RequestException during Google Wifi lookup:
%sz-Geoloc: Unable to decode Google Wifi JSON:
%sz<Geoloc: No WiFi access points found - can't detect location.)rr�WifiScanner�get_resultsrPr:�_get_urlrrYr^�Coordinates�Geocoder�reverse_geocode_coordsrFrUr?r[r_r`r=ra)
r�scanner�
access_pointsrbZresult_dictrgrirjZcoords�geocoderZgeocoding_resultZt_coderdrrrrS"s2



 z#GoogleWiFiLocationProvider._refreshcCs&|j}x|D]}||j|�7}qW|S)z�Generate Google API URL for the given access points

        :param access_points: a list of WiFiAccessPoint objects
        :return Google WiFi location API URL
        :rtype: string
        )rX�_describe_access_point)rrr�url�aprrrrmAs
z#GoogleWiFiLocationProvider._get_urlcCs tjj|j�}d|j||jfS)z�Describe an access point in a format compatible with the API call

        :param access_point: a WiFiAccessPoint instance
        :return: API compatible AP description
        :rtype: string
        z&wifi=mac:%s|ssid:%s|ss:%d)�urllib�parseZ
quote_plus�ssid�bssid�rssi)rZaccess_pointZquoted_ssidrrrrtMsz1GoogleWiFiLocationProvider._describe_access_pointN)
r0r1r2r3rXr4r<rSrmrtrrrrr9sr9c@s2eZdZdZdZejfdd�Zdd�Zdd�Z	d	S)
rozTProvides online geocoding services.

    (only reverse geocoding at the moment)
    z@http://open.mapquestapi.com/nominatim/v1/reverse.php?format=jsoncCs
||_dS)z::param geocoder: a constant selecting what geocoder to useN)�	_geocoder)rrsrrrreszGeocoder.__init__cCs(|jtjkr|j|�Stjd�dSdS)z�Turn geographic coordinates to address

        :param coordinates: Coordinates (geographic coordinates)
        :type coordinates: Coordinates
        :return: GeocodingResult if the lookup succeeds or None if it fails
        zWrong Geocoder specified!N)r|rZGEOLOC_GEOCODER_NOMINATIM�_reverse_geocode_nominatimrr)r�coordinatesrrrrpis

zGeocoder.reverse_geocode_coordscCs�d|j|j|jf}y\t�j|tjdd�}|jtj	j
kr\|j�}|ddj�}t
||d�Stjd|j�dSWn^tjjk
r�}ztjd	|�WYdd}~Xn.tk
r�}ztjd
|�WYdd}~XnXdS)aPReverse geocoding using the Nominatim API.

        Reverse geocoding tries to convert geographic coordinates
        to an accurate address.

        :param coordinates: input coordinates
        :type coordinates: Coordinates
        :return: an address or None if no address was found
        :rtype: GeocodingResult or None
        z!%s&addressdetails=1&lat=%f&lon=%fT)r#rV�addressrW)r~rFz?Geoloc: Nominatim reverse geocoding failed with status code: %sNz?Geoloc: RequestException during Nominatim reverse geocoding:
%sz=Geoloc: Unable to decode Nominatim reverse geocoding JSON:
%s)�NOMINATIM_API_URL�latitude�	longituderr:rrYrZr[r\r]r^�upper�GeocodingResultrrr_r`r=ra)rr~rurbrerFrdrrrr}vs(
z#Geocoder._reverse_geocode_nominatimN)
r0r1r2r3r�rZGEOLOC_DEFAULT_GEOCODERrrpr}rrrrroYs

roc@s>eZdZdZddd�Zedd��Zedd��Zed	d
��ZdS)r�zA result from geocoding lookup.NcCs||_||_||_dS)a
        :param coordinates: geographic coordinates
        :type coordinates: Coordinates
        :param territory_code: territory code of the result
        :type territory_code: string
        :param address: a (street) address string
        :type address: string
        N)�_coordsrA�_address)rr~rFrrrrr�s	zGeocodingResult.__init__cCs|jS)N)r�)rrrrr~�szGeocodingResult.coordinatescCs|jS)N)rA)rrrrrF�szGeocodingResult.territory_codecCs|jS)N)r�)rrrrr�szGeocodingResult.address)NNN)	r0r1r2r3rr4r~rFrrrrrr��s


r�c@s:eZdZdZddd�Zedd��Zedd��Zd	d
�ZdS)rnz A set of geographic coordinates.NcCs||_||_dS)z�
        :param lat: WGS84 latitude
        :type lat: float
        :param lon: WGS84 longitude
        :type lon: float
        N)�_lat�_lon)rrirjrrrr�szCoordinates.__init__cCs|jS)N)r�)rrrrr��szCoordinates.latitudecCs|jS)N)r�)rrrrr��szCoordinates.longitudecCsd|j|jfS)Nzlat,lon: %f,%f)r�r�)rrrrrK�szCoordinates.__str__)NN)	r0r1r2r3rr4r�r�rKrrrrrn�s


rnc@s.eZdZdZdZddd�Zdd�Zdd	�Zd
S)rkzXUse the Network Manager DBus API to provide information about nearby WiFi access points.�TcCsg|_|r|j�dS)za
        :param scan_now: if an initial scan should be done
        :type scan_now: bool
        N)�
_scan_results�scan)rrfrrrr�szWifiScanner.__init__cCsd}g}y tj�}|jdd�}|j�}Wn0tjk
rX}ztjd|�WYdd}~XnXx�|D]�}|jd|�}|jdd�}||jkr`d}	d	}
xv|j	�D]j}|jd|�}tj
||	d
�}
t|
j|
d��}t|
j|
dd
d��}t|
j|
d��}t
|||d�}|j|�q�Wq`W||_dS)zScan for WiFi access points�zorg.freedesktop.NetworkManagerz/org/freedesktop/NetworkManagerz(Exception caught during WiFi AP scan: %sNz%org.freedesktop.NetworkManager.DeviceZ
DeviceTypezorg.freedesktop.DBus.Propertiesz*org.freedesktop.NetworkManager.AccessPoint)Zdbus_interfaceZ	HwAddressZSsidT)Zbyte_arraysZStrength)rzryr{)�dbusZ	SystemBusZ
get_objectZ
GetDevicesZ
DBusExceptionrr=ZGet� NETWORK_MANAGER_DEVICE_TYPE_WIFIZGetAccessPointsZ	Interfacer �int�WiFiAccessPoint�appendr�)rZdevicesrrZbusZnetwork_managerrdZdevice_pathZdeviceZdevice_typeZ
dbus_iface_idZap_idZap_pathZnetZnetwork_propertiesrzZessidr{rvrrrr��s8


zWifiScanner.scancCs|jS)z�
        :return: a list of WiFiAccessPoint objects or
                 an empty list if no APs were found or the scan failed
        )r�)rrrrrlszWifiScanner.get_resultsN)T)r0r1r2r3r�rr�rlrrrrrk�s

	,rkc@sFeZdZdZd
dd�Zedd��Zedd��Zed	d
��Zdd�Z	dS)r�z3Encapsulates information about a WiFi access point.NcCs||_||_||_dS)z�
        :param bssid: MAC address of the access point
        :param ssid: name of the access point
        :param rssi: signal strength
        N)�_bssid�_ssid�_rssi)rrzryr{rrrrszWiFiAccessPoint.__init__cCs|jS)N)r�)rrrrrzszWiFiAccessPoint.bssidcCs|jS)N)r�)rrrrry!szWiFiAccessPoint.ssidcCs|jS)N)r�)rrrrr{%szWiFiAccessPoint.rssicCsd|j|j|jfS)Nz3bssid (MAC): %s ssid: %s rssi (signal strength): %d)rzryr{)rrrrrK)szWiFiAccessPoint.__str__)NN)
r0r1r2r3rr4rzryr{rKrrrrr�s

r�cCst||d�adS)z%Initialize the geolocation singleton.)rrN)r
�geoloc)rrrrr�init_geolocation1sr�)/r3Zpyanaconda.core.utilrr[Zurllib.parserwr�rNr$Z
pyanacondarZpyanaconda.anaconda_loggersrrr0rrTZpyanaconda.corerZ&pyanaconda.core.configuration.anacondarZpyanaconda.threadingrr	Zpyanaconda.timezoner
rZpyanaconda.flagsrr5r7r/�objectr
rr?rLr6r8r9ror�rnrkr�r�r�rrrr�<module>ss@48I& A?B