Ñò ›ˆQc@sdZddkZddkZddkZddkZddkZddkZddkZddkZddk Z ddk Z ddk Z ddk l Z eZy:ddklZddklZddklZeZWnej onXyddk lZWn#ej oddklZnXeieƒZdZd Zd Zd efd „ƒYZd efd„ƒYZ defd„ƒYZ!defd„ƒYZ"defd„ƒYZ#defd„ƒYZ$d„Z%de&fd„ƒYZ'de&fd„ƒYZ(de&fd„ƒYZ)de&fd„ƒYZ*d e(fd!„ƒYZ+d"e+fd#„ƒYZ,d$e+fd%„ƒYZ-eo;d&e-fd'„ƒYZ.ei/e'ƒƒZ0e1ed(„Z2nd)„Z3d*„Z4d+e1e1d,d-„Z5e1d+e1d.„Z6d/e)fd0„ƒYZ7e1d1„Z8dS(2sPAn OAuth 2.0 client. Tools for interacting with OAuth 2.0 protected resources. iÿÿÿÿN(t simplejson(tSigner(tmake_signed_jwt(tverify_signed_jwt_with_certs(t parse_qsls%Y-%m-%dT%H:%M:%SZs*https://www.googleapis.com/oauth2/v1/certssurn:ietf:wg:oauth:2.0:oobtErrorcBseZdZRS(sBase error for this module.(t__name__t __module__t__doc__(((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyR>stFlowExchangeErrorcBseZdZRS(sDError trying to exchange an authorization grant for an access token.(RRR(((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyR CstAccessTokenRefreshErrorcBseZdZRS(s0Error trying to refresh an expired access token.(RRR(((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyR HstUnknownClientSecretsFlowErrorcBseZdZRS(sFThe client secrets file called for an unknown type of OAuth 2.0 flow. (RRR(((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyR LstAccessTokenCredentialsErrorcBseZdZRS(s:Having only the access_token means no refresh is possible.(RRR(((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyR QstVerifyJwtTokenErrorcBseZdZRS(s.Could on retrieve certificates for validation.(RRR(((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyR VscCstdƒ‚dS(Ns"You need to override this function(tNotImplementedError(((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyt _abstract[st MemoryCachecBs2eZdZd„Zd„Zd„Zd„ZRS(s8httplib2 Cache implementation which only caches locally.cCs h|_dS(N(tcache(tself((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyt__init__bscCs|ii|ƒS(N(Rtget(Rtkey((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyRescCs||i|R?((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyR7Ïs ( RRRR2RRR!R1R3t classmethodRDR7(((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyRos    tFlowcBseZdZRS(s Base class for all Flow objects.(RRR(((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyRFÞstStoragecBsVeZdZd„Zd„Zd„Zd„Zd„Zd„Zd„Z d„Z RS( sºBase class for all Storage objects. Store and retrieve a single credential. This class supports locking such that multiple processes and threads can operate on a single store. cCsdS(sYAcquires any lock necessary to access this Storage. This lock is not reentrant. N((R((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyt acquire_lockëscCsdS(soRelease the Storage lock. Trying to release a lock that isn't held will result in a RuntimeError. N((R((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyt release_lockòscCs tƒdS(s…Retrieve credential. The Storage lock must be held when this is called. Returns: oauth2client.client.Credentials N(R(R((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyt locked_getúscCs tƒdS(s•Write a credential. The Storage lock must be held when this is called. Args: credentials: Credentials, the credentials to store. N(R(Rt credentials((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyt locked_putscCs tƒdS(sQDelete a credential. The Storage lock must be held when this is called. N(R(R((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyt locked_deletescCs*|iƒz|iƒSWd|iƒXdS(s‹Retrieve credential. The Storage lock must *not* be held when this is called. Returns: oauth2client.client.Credentials N(RHRJRI(R((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyRs cCs-|iƒz|i|ƒWd|iƒXdS(s•Write a credential. The Storage lock must be held when this is called. Args: credentials: Credentials, the credentials to store. N(RHRLRI(RRK((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pytput#s cCs*|iƒz|iƒSWd|iƒXdS(s®Delete credential. Frees any resources associated with storing the credential. The Storage lock must *not* be held when this is called. Returns: None N(RHRMRI(R((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyR1s ( RRRRHRIRJRLRMRRNR(((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyRGãs     tOAuth2CredentialscBs¤eZdZdd„Zd„Zd„Zd„Zd„Ze d„ƒZ e d„ƒZ d„Z d „Zd „Zd „Zd „Zd „Zd„Zd„ZRS(sûCredentials object for OAuth 2.0. Credentials can be applied to an httplib2.Http object using the authorize() method, which then adds the OAuth 2.0 access token to each request. OAuth2Credentials objects may be safely pickled and unpickled. c Cs^||_||_||_||_d|_||_||_||_||_ t |_ dS(sZCreate an instance of OAuth2Credentials. This constructor is not usually called by the user, instead OAuth2Credentials objects are instantiated by the OAuth2WebServerFlow. Args: access_token: string, access token. client_id: string, client identifier. client_secret: string, client secret. refresh_token: string, refresh token. token_expiry: datetime, when the access_token expires. token_uri: string, URI of token endpoint. user_agent: string, The HTTP User-Agent to provide for this application. id_token: object, The identity of the resource owner. Notes: store: callable, A callable that when passed a Credential will store the credential back to where it came from. This is needed to store the latest access_token if it has expired and been refreshed. N( t access_tokent client_idt client_secrett refresh_tokenRRR"t token_urit user_agenttid_tokentFalsetinvalid( RRPRQRRRSR"RTRURV((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyRJs         csM|i‰dddtid‡‡fd†}||_t|idˆƒ|S(sœAuthorize an httplib2.Http instance with these credentials. The modified http.request method will add authentication headers to each request and will refresh access_tokens when a 401 is received on a request. In addition the http.request method has a credentials property, http.request.credentials, which is the Credentials object that authorized it. Args: http: An instance of httplib2.Http or something that acts like it. Returns: A modified instance of http that was passed in. Example: h = httplib2.Http() h = credentials.authorize(h) You can't create a new OAuth subclass of httplib2.Authenication because it never gets passed the absolute URI, which is needed for signing. So instead we have to overload 'request' with a closure that adds in the Authorization header and then calls the original version of 'request()'. tGETcsˆiptidƒˆiˆƒn|djo h}nˆi|ƒˆidj o;d|joˆid|d|dR?R@tretval((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyR7Ès&   cCs\|iotS|iptStiiƒ}||ijotid||iƒtStS(szTrue if the credential is expired or invalid. If the token_expiry isn't set, we assume the token doesn't expire. s2access_token is expired. Now: %s, token_expiry: %s(RXtTrueR"RWR)tutcnowR[R\(Rtnow((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pytaccess_token_expiredçs    cCs ||_dS(s=Set the Storage for the credential. Args: store: Storage, an implementation of Stroage object. This is needed to store the latest access_token if it has expired and been refreshed. This implementation uses locking to check for updates before updating the access_token. N(R(RR((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyt set_storeús cCs|ii|iƒƒdS(s-Update this Credential from another instance.N(R'tupdatet __getstate__(Rtother((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyt_updateFromCredentialscCsti|iƒ}|d=|S(s5Trim the state down to something that can be pickled.R(R&R'(RR/((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyRu scCs|ii|ƒd|_dS(s8Reconstitute the state of the object from being pickled.N(R'RtRR(Rtstate((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyt __setstate__scCs8tihdd6|id6|id6|id6ƒ}|S(s;Generate the body that will be used in the refresh request.RSt grant_typeRQRR(turllibt urlencodeRQRRRS(RRa((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyt_generate_refresh_request_bodys    cCs2hdd6}|idj o|i|dGenerate the headers that will be used in the refresh request.s!application/x-www-form-urlencodeds content-types user-agentN(RUR(RR ((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyt!_generate_refresh_request_headerss  cCs |ip|i|ƒn‚|iiƒzc|iiƒ}|o<|i o1|i|ijotidƒ|i|ƒn|i|ƒWd|ii ƒXdS(s¨Refreshes the access_token. This method first checks by reading the Storage object if available. If a refresh is still needed, it holds the Storage lock until the refresh is completed. Args: http_request: callable, a callable that matches the method signature of httplib2.Http.request, used to make the refresh request. Raises: AccessTokenRefreshError: When the refresh fails. s&Updated access_token read from StorageN( Rt_do_refresh_requestRHRJRXRPR[R\RwRI(Rt http_requesttnew_cred((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyR]*s   cCs†|iƒ}|iƒ}tidƒ||iddd|d|ƒ\}}|idjoœti|ƒ}|d|_|i d|i ƒ|_ d |jo0t i d t |d ƒƒt i iƒ|_n d|_|io|ii|ƒq‚n‹tid |ƒd |d }yUti|ƒ}d|jo5|d}t|_|io|ii|ƒqknWnnXt|ƒ‚dS(sRefresh the access_token using the refresh_token. Args: http_request: callable, a callable that matches the method signature of httplib2.Http.request, used to make the refresh request. Raises: AccessTokenRefreshError: When the refresh fails. sRefreshing access_tokenR`tPOSTRaR iÈRPRSt expires_intsecondss#Failed to retrieve access token: %ssInvalid response %s.R^terrorN(R}R~R[R\RTR^RR8RPRRSR)t timedeltatintRpR"RRRLRoRXR (RR€RaR RdReR/t error_msg((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyRGs6   $   '      N(RRRRRRRR!R3RER7tpropertyRrRsRwRuRyR}R~R]R(((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyROAs  $ E      tAccessTokenCredentialscBs/eZdZd„Zed„ƒZd„ZRS(sUCredentials object for OAuth 2.0. Credentials can be applied to an httplib2.Http object using the authorize() method, which then signs each request from that object with the OAuth 2.0 access token. This set of credentials is for the use case where you have acquired an OAuth 2.0 access_token from another place such as a JavaScript client or another web application, and wish to use it from Python. Because only the access_token is present it can not be refreshed and will in time expire. AccessTokenCredentials objects may be safely pickled and unpickled. Usage: credentials = AccessTokenCredentials('', 'my-user-agent/1.0') http = httplib2.Http() http = credentials.authorize(http) Exceptions: AccessTokenCredentialsExpired: raised when the access_token expires or is revoked. cCs,tt|ƒi|ddddd|ƒdS(sÁCreate an instance of OAuth2Credentials This is one of the few types if Credentials that you should contrust, Credentials objects are usually instantiated by a Flow. Args: access_token: string, access token. user_agent: string, The HTTP User-Agent to provide for this application. Notes: store: callable, a callable that when passed a Credential will store the credential back to where it came from. N(tsuperRŠRR(RRPRU((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyRscCs*ti|ƒ}t|d|dƒ}|S(NRPRU(RR8RŠ(R>R?R@Rn((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyR7¥s  cCstdƒ‚dS(Ns>The access_token is expired or invalid and can't be refreshed.(R (RR€((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyR]­s(RRRRRER7R](((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyRŠts tAssertionCredentialscBs,eZdZdd„Zd„Zd„ZRS(s—Abstract Credentials object used for OAuth 2.0 assertion grants. This credential does not require a flow to instantiate because it represents a two legged flow, and therefore has all of the required information to generate and refresh its own access tokens. It must be subclassed to generate the appropriate assertion string. AssertionCredentials objects may be safely pickled and unpickled. s*https://accounts.google.com/o/oauth2/tokencKs5tt|ƒiddddd||ƒ||_dS(sxConstructor for AssertionFlowCredentials. Args: assertion_type: string, assertion type that will be declared to the auth server user_agent: string, The HTTP User-Agent to provide for this application. token_uri: string, URI for token endpoint. For convenience defaults to Google's endpoints but any OAuth 2.0 provider can be used. N(R‹RŒRRtassertion_type(RRRURTt unused_kwargs((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyR½s cCs7|iƒ}tih|id6|d6dd6ƒ}|S(NRt assertionRz(t_generate_assertionR{R|R(RRRa((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyR}Ós     cCs tƒdS(sUGenerate the assertion string that will be used in the access token request. N(R(R((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyRÞs(RRRRR}R(((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyRŒ²s   tSignedJwtAssertionCredentialscBs>eZdZdZdddd„Zed„ƒZd„ZRS(sCredentials object used for OAuth 2.0 Signed JWT assertion grants. This credential does not require a flow to instantiate because it represents a two legged flow, and therefore has all of the required information to generate and refresh its own access tokens. it notasecrets*https://accounts.google.com/o/oauth2/tokencKsvtt|ƒid|d|ƒt|ƒtjodi|ƒ}n||_||_||_||_ ||_ dS(s¶Constructor for SignedJwtAssertionCredentials. Args: service_account_name: string, id for account, usually an email address. private_key: string, private key in P12 format. scope: string or list of strings, scope(s) of the credentials being requested. private_key_password: string, password for private_key. user_agent: string, HTTP User-Agent to provide for this application. token_uri: string, URI for token endpoint. For convenience defaults to Google's endpoints but any OAuth 2.0 provider can be used. kwargs: kwargs, Additional parameters to add to the JWT token, for example prn=joe@xample.org.s*http://oauth.net/grant_type/jwt/1.0/bearerRTRZN( R‹R‘RR%tlisttjointscopet private_keytprivate_key_passwordtservice_account_nametkwargs(RR˜R–R•R—RURTR™((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyRós    c CsZti|ƒ}t|d|d|d|d|d|d|dƒ}|d|_|S( NR˜R–R—R•RURTR™RX(RR8R‘RX(R>R?R@Rn((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyR7s  cCsttiƒƒ}h|id6|id6|d6|tid6|id6}|i|iƒt i t |ƒƒt t i|i|iƒ|ƒS(s8Generate the assertion that will be used in the request.taudR•tiattexptiss(tlongttimeRTR•R‘tMAX_TOKEN_LIFETIME_SECSR˜RtR™R[tdebugtstrRRt from_stringR–R—(RRqtpayload((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyR's   N( RRRR RRRER7R(((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyR‘és cCss|djo t}n|i|ƒ\}}|idjo ti|ƒ}t|||ƒStd|iƒ‚dS(sVerifies a signed JWT id_token. Args: id_token: string, A Signed JWT. audience: string, The audience 'aud' that the token should be for. http: httplib2.Http, instance to use to make the HTTP request. Callers should supply an instance that has caching enabled. cert_uri: string, URI of the certificates in JSON format to verify the JWT against. Returns: The deserialized JSON in the JWT. Raises: oauth2client.crypt.AppIdentityError if the JWT fails to verify. iÈsStatus code: %dN(Rt _cached_httpRhR^RR8RR (RVtaudienceRtcert_uriRdRetcerts((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pytverify_id_token<s  cCs8|idƒ}|ddt|ƒd}ti|ƒS(Ntasciit=i(tencodetlentbase64turlsafe_b64decode(t b64stringtpadded((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyt_urlsafe_b64decodeZscCsM|idƒ}t|ƒdjotd|ƒ‚ntit|dƒƒS(sÄExtract the JSON payload from a JWT. Does the extraction w/o checking the signature. Args: id_token: string, OAuth 2.0 id_token. Returns: object, The deserialized JSON payload. R6is%Wrong number of segments in token: %si(R<R­R RR8R²(RVtsegments((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyt_extract_id_tokenas t postmessages*https://accounts.google.com/o/oauth2/tokenc Cs@t||||d|ƒ}|i|ƒ} |i||ƒ} | S(sExchanges an authorization code for an OAuth2Credentials object. Args: client_id: string, client identifier. client_secret: string, client secret. scope: string or list of strings, scope(s) to request. code: string, An authroization code, most likely passed down from the client redirect_uri: string, this is generally set to 'postmessage' to match the redirect_uri that the client specified http: httplib2.Http, optional http instance to use to do the fetch token_uri: string, URI for token endpoint. For convenience defaults to Google's endpoints but any OAuth 2.0 provider can be used. Returns: An OAuth2Credentials object. Raises: FlowExchangeError if the authorization code cannot be exchanged for an access token s)https://accounts.google.com/o/oauth2/auth(tOAuth2WebServerFlowtstep1_get_authorize_urltstep2_exchange( RQRRR•tcodet redirect_uriRRURTtflowturiThatWeDontReallyUseRK((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pytcredentials_from_codets  c Cs7t|||ƒ}|i|ƒ}|i||ƒ}|S(s×Returns OAuth2Credentials from a clientsecrets file and an auth code. Will create the right kind of Flow based on the contents of the clientsecrets file or will raise InvalidClientSecretsError for unknown types of Flows. Args: filename: string, File name of clientsecrets. scope: string or list of strings, scope(s) to request. code: string, An authroization code, most likely passed down from the client message: string, A friendly string to display to the user if the clientsecrets file is missing or invalid. If message is provided then sys.exit will be called in the case of an error. If message in not provided then clientsecrets.InvalidClientSecretsError will be raised. redirect_uri: string, this is generally set to 'postmessage' to match the redirect_uri that the client specified http: httplib2.Http, optional http instance to use to do the fetch Returns: An OAuth2Credentials object. Raises: FlowExchangeError if the authorization code cannot be exchanged for an access token UnknownClientSecretsFlowError if the file describes an unknown kind of Flow. clientsecrets.InvalidClientSecretsError if the clientsecrets file is invalid. (tflow_from_clientsecretsR·R¸( tfilenameR•R¹tmessageRºRR»R¼RK((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyt'credentials_from_clientsecrets_and_code–s R¶cBs8eZdZdddd„Zed„Zdd„ZRS(slDoes the Web Server Flow for OAuth 2.0. OAuth2Credentials objects may be safely pickled and unpickled. s)https://accounts.google.com/o/oauth2/auths*https://accounts.google.com/o/oauth2/tokencKs‰||_||_t|ƒtjodi|ƒ}n||_||_||_||_hdd6|_ |i i |ƒd|_ dS(sÜConstructor for OAuth2WebServerFlow. Args: client_id: string, client identifier. client_secret: string client secret. scope: string or list of strings, scope(s) of the credentials being requested. user_agent: string, HTTP User-Agent to provide for this application. auth_uri: string, URI for authorization endpoint. For convenience defaults to Google's endpoints but any OAuth 2.0 provider can be used. token_uri: string, URI for token endpoint. For convenience defaults to Google's endpoints but any OAuth 2.0 provider can be used. **kwargs: dict, The keyword arguments are all optional and required parameters for the OAuth calls. RZtofflinet access_typeN( RQRRR%R“R”R•RUtauth_uriRTtparamsRtRRº(RRQRRR•RURÄRTR™((sd/Users/riccardo/git/gcloud/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.pyRÃs       cCs–||_hdd6|id6|d6|id6}|i|iƒtti|iƒƒ}|itt |dƒƒƒt i |ƒ|dst             o^ÿ4>2Q    $