credentials/0000755000175000017500000000000014151145042012676 5ustar nileshnileshcredentials/MD50000644000175000017500000000233014151145042013204 0ustar nileshnileshf4224fb869e80734db754d2950fd0021 *DESCRIPTION 27b6128fe44dac890bf5c9dfce3a744c *LICENSE 3f9a1504712d83a9346263ef9a199b3f *NAMESPACE e8aeb14e3452adc3f2c257b735aaa813 *NEWS c98ed9fc412b8a0b960765cd74a7fb41 *R/credential-api.R 16298fddb82887829393af2c2469d4b7 *R/credential-helpers.R 8886a4fe29155dfcfa6b35a16079a232 *R/github-pat.R c928b0f6fb79574cec5af42c164e9e98 *R/http-credential.R 65bcabd0700b0a8893f20acba9b32981 *R/onattach.R b041cd1c83a21b8a3bbd9f24370f274b *R/ssh-keys.R 0bb7d6244e9f061d4e25b55ddf84ff86 *build/vignette.rds 80410b923c609a1212e5246e9ebd02a0 *inst/WORDLIST 226d0cc912b9c84fc5942b67eda3860c *inst/ask_token.sh 75f88d641952be854fb7acaf68f51a28 *inst/doc/intro.R f81a0902330e9ff08b546e8c2b685875 *inst/doc/intro.Rmd a59567a0f026039eb497958a83bcf07f *inst/doc/intro.html a962d9619a3b1302bc2fdde3597b8dea *man/credential_api.Rd 0fdfc6bd70ccd814015d18b80e7b0f69 *man/credential_helper.Rd 796553a137098ec948b9cad44e337fb1 *man/http_credentials.Rd 6f3753307764fbe501ae52773173fd2b *man/set_github_pat.Rd ebaf4918c95fdd91b34b94413ee98ebc *man/ssh_credentials.Rd f81a0902330e9ff08b546e8c2b685875 *vignettes/intro.Rmd c6d62931c246f7f97fc1209544b1feeb *vignettes/keygen.png 41748938c3df6c35beedddc655728837 *vignettes/wincred.png credentials/DESCRIPTION0000644000175000017500000000255614151145042014414 0ustar nileshnileshPackage: credentials Type: Package Title: Tools for Managing SSH and Git Credentials Version: 1.3.2 Authors@R: person("Jeroen", "Ooms", role = c("aut", "cre"), email = "jeroen@berkeley.edu", comment = c(ORCID = "0000-0002-4035-0289")) Description: Setup and retrieve HTTPS and SSH credentials for use with 'git' and other services. For HTTPS remotes the package interfaces the 'git-credential' utility which 'git' uses to store HTTP usernames and passwords. For SSH remotes we provide convenient functions to find or generate appropriate SSH keys. The package both helps the user to setup a local git installation, and also provides a back-end for git/ssh client libraries to authenticate with existing user credentials. License: MIT + file LICENSE SystemRequirements: git (optional) Encoding: UTF-8 Imports: openssl (>= 1.3), sys (>= 2.1), curl, jsonlite, askpass Suggests: testthat, knitr, rmarkdown RoxygenNote: 7.1.1 VignetteBuilder: knitr Language: en-US URL: https://docs.ropensci.org/credentials/ (website) https://github.com/r-lib/credentials BugReports: https://github.com/r-lib/credentials/issues NeedsCompilation: no Packaged: 2021-11-29 11:40:26 UTC; jeroen Author: Jeroen Ooms [aut, cre] () Maintainer: Jeroen Ooms Repository: CRAN Date/Publication: 2021-11-29 12:40:01 UTC credentials/man/0000755000175000017500000000000014151130430013444 5ustar nileshnileshcredentials/man/http_credentials.Rd0000644000175000017500000000363414151130430017275 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/http-credential.R \name{http_credentials} \alias{http_credentials} \alias{git_credential_ask} \alias{credentials} \alias{git_credential_update} \alias{git_credential_forget} \title{Load and store git HTTPS credentials} \usage{ git_credential_ask(url = "https://github.com", save = TRUE, verbose = TRUE) git_credential_update(url = "https://github.com", verbose = TRUE) git_credential_forget(url = "https://github.com", verbose = TRUE) } \arguments{ \item{url}{target url, possibly including username or path} \item{save}{in case the user is prompted for credentials, attempt to remember them.} \item{verbose}{print errors from \verb{git credential} to stdout} } \description{ This requires you have the \code{git} command line program installed.The \link{git_credential_ask} function looks up a suitable username/password from the \href{https://git-scm.com/docs/gitcredentials}{\code{git-credential} store}. If none are available it will prompt the user for credentials which may be saved the store. On subsequent calls for the same URL, the function will then return the stored credentials without prompting the user. } \details{ The appearance and security policy of the credential store depends on your version of git, your operating system, your R frontend and which \link{credential_helper} is used. On Windows and MacOS the credentials are stored in the system password manager by default. It should be assumed that reading credentials always involves user interaction. The user may be asked to unlock the system keychain or enter new credentials. In reality, user interaction is usually only required on the first authentication attempt, but the security policy of most credential helpers prevent you from programmatically testing if the credentials are already unlocked. } \seealso{ Other credentials: \code{\link{ssh_credentials}} } \concept{credentials} credentials/man/ssh_credentials.Rd0000644000175000017500000000405414151130430017110 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ssh-keys.R \name{ssh_credentials} \alias{ssh_credentials} \alias{ssh_key_info} \alias{ssh_keygen} \alias{ssh_setup_github} \alias{ssh_home} \alias{ssh_agent_add} \alias{ssh_update_passphrase} \alias{ssh_read_key} \title{Managing Your SSH Key} \usage{ ssh_key_info(host = NULL, auto_keygen = NA) ssh_keygen(file = ssh_home("id_rsa")) ssh_setup_github() ssh_home(file = NULL) ssh_agent_add(file = NULL) ssh_update_passphrase(file = ssh_home("id_rsa")) ssh_read_key(file = ssh_home("id_rsa"), password = askpass) } \arguments{ \item{host}{target host (only matters if you have configured specific keys per host)} \item{auto_keygen}{if \code{TRUE} automatically generates a key if none exists yet. Default \code{NA} is to prompt the user what to.} \item{file}{destination path of the private key. For the public key, \code{.pub} is appended to the filename.} \item{password}{a passphrase or callback function} } \description{ Utility functions to find or generate your SSH key for use with git remotes or other ssh servers. } \details{ Use \code{\link[=ssh_key_info]{ssh_key_info()}} to find the appropriate key file on your system to connect with a given target host. In most cases this will simply be \code{ssh_home('id_rsa')} unless you have configured ssh to use specific keys for specific hosts. To use your key to authenticate with GitHub, copy the pubkey from \code{ssh_key_info()} to your profile: \url{https://github.com/settings/ssh/new}. If this is the first time you use ssh, \link{ssh_keygen} can help generate a key and save it in the default location. This will also automatically opens the above Github page in your browser where you can add the key to your profile. \code{ssh_read_key} reads a private key and caches the result (in memory) for the duration of the R session. This prevents having to enter the key passphrase many times. Only use this if \code{ssh-agent} is not available (i.e. Windows) } \seealso{ Other credentials: \code{\link{http_credentials}} } \concept{credentials} credentials/man/credential_helper.Rd0000644000175000017500000000152714151130430017411 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/credential-helpers.R \name{credential_helper} \alias{credential_helper} \alias{credential_helper_list} \alias{credential_helper_get} \alias{credential_helper_set} \title{Credential Helpers} \usage{ credential_helper_list() credential_helper_get(global = FALSE) credential_helper_set(helper, global = FALSE) } \arguments{ \item{global}{if FALSE the setting is done per git repository, if TRUE it is in your global user git configuration.} \item{helper}{string with one of the supported helpers from \link{credential_helper_list}} } \description{ Git supports several back-end stores for HTTPS credentials called helpers. Default helpers include \code{cache} and \code{store}, see the \href{https://git-scm.com/docs/gitcredentials}{git-credentials} manual page for details. } credentials/man/credential_api.Rd0000644000175000017500000000506214151130430016701 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/credential-api.R \name{credential_api} \alias{credential_api} \alias{credential_fill} \alias{credential_approve} \alias{credential_reject} \title{Retrieve and store git HTTPS credentials} \usage{ credential_fill(cred, verbose = TRUE) credential_approve(cred, verbose = TRUE) credential_reject(cred, verbose = TRUE) } \arguments{ \item{cred}{named list with at least fields \code{protocol} and \code{host} and optionally also \code{path}, \code{username} ,\code{password}.} \item{verbose}{emit some useful output about what is happening} } \description{ Low-level wrappers for the \href{https://git-scm.com/docs/git-credential}{git-credential} command line tool. Try the user-friendly \link{git_credential_ask} and \link{git_credential_update} functions first. } \details{ The \link{credential_fill} function looks up credentials for a given host, and if none exists it will attempt to prompt the user for new credentials. Upon success it returns a list with the same \code{protocol} and \code{host} fields as the \code{cred} input, and additional \code{username} and \code{password} fields. After you have tried to authenticate the provided credentials, you can report back if the credentials were valid or not. Call \link{credential_approve} and \link{credential_reject} with the \code{cred} that was returned by \link{credential_fill} in order to validate or invalidate a credential from the store. Because git credential interacts with the system password manager, the appearance of the prompts vary by OS and R frontend. Note that \link{credential_fill} should only be used interactively, because it may require the user to enter credentials or unlock the system keychain. On the other hand \link{credential_approve} and \link{credential_reject} are non-interactive and could be used to save or delete credentials in a scripted program. However note that some credential helpers (e.g. on Windows) have additional security restrictions that limit use of \link{credential_approve} and \link{credential_reject} to credentials that were actually entered by the user via \link{credential_fill}. Here it is not possible at all to update the credential store without user interaction. } \examples{ \donttest{ # Insert example cred example <- list(protocol = "https", host = "example.org", username = "test", password = "secret") credential_approve(example) # Retrieve it from the store cred <- credential_fill(list(protocol = "https", host = "example.org", path = "/foo")) print(cred) # Delete it credential_reject(cred) } } credentials/man/set_github_pat.Rd0000644000175000017500000000240614151130430016736 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/github-pat.R \name{set_github_pat} \alias{set_github_pat} \title{Set your Github Personal Access Token} \usage{ set_github_pat(force_new = FALSE, validate = interactive(), verbose = validate) } \arguments{ \item{force_new}{forget existing pat, always ask for new one.} \item{validate}{checks with the github API that this token works. Defaults to \code{TRUE} only in an interactive R session (not when running e.g. CMD check).} \item{verbose}{prints a message showing the credential helper and PAT owner.} } \value{ Returns \code{TRUE} if a valid GITHUB_PAT was set, and FALSE if not. } \description{ Populates the \code{GITHUB_PAT} environment variable using the \link[=http_credentials]{git_credential} manager, which \code{git} itself uses for storing passwords. The credential manager returns stored credentials if available, and securely prompt the user for credentials when needed. } \details{ Packages that require a \code{GITHUB_PAT} can call this function to automatically set the \code{GITHUB_PAT} when needed. Users may call this function in their \link[=Startup]{.Rprofile} script to automatically set \code{GITHUB_PAT} for each R session without hardcoding any tokens on disk in plain-text. } credentials/vignettes/0000755000175000017500000000000014151136052014707 5ustar nileshnileshcredentials/vignettes/intro.Rmd0000644000175000017500000001715214151135004016510 0ustar nileshnilesh--- title: "Managing SSH and Git Credentials in R" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Managing SSH and Git Credentials in R} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- The `credentials` package contains tools for configuring and retrieving SSH and HTTPS credentials for use with `git` or other services. It helps users to setup their git installation, and also provides a back-end for packages to authenticate with existing user credentials. ## Two types of remotes ```{r, echo=FALSE} has_git <- credentials:::has_git_cmd() && .Platform$OS.type != 'windows' delete_git_config_on_exit <- !file.exists('~/.gitconfig') credentials:::set_default_cred_helper() library <- function(package){ withCallingHandlers(base::library(credentials), packageStartupMessage = function(e) { cat(e$message) invokeRestart("muffleMessage") }) } ``` ```{r} library(credentials) ``` Git supports two types of remotes: SSH and HTTPS. These two use completely distinct authentication systems. | | SSH REMOTES | HTTPS REMOTES | |---------|------------------|------------------------| | __url__ | `git@server.com` | `https://server.com` | | __authentication__ | Personal SSH key | Password or PAT | | __stored in__ | file `id_rsa` or `ssh-agent` | `git credential` store | For HTTPS remotes, git authenticates with a username + password. With GitHub, instead of a password, you can also use a [Personal Access Token](https://github.com/settings/tokens) (PAT). PATs are preferred because they provide more granular control of permissions (via the PAT's scopes), you can have many of them for different purposes, you can give them informative names, and they can be selectively revoked. Note that, if you use 2FA with GitHub, you must authenticate with a PAT if you use the HTTPS protocol. For SSH remotes, git shells out to `ssh` on your system, and lets `ssh` take care of authentication. This means you have to setup an ssh key (usually `~/.ssh/id_rsa`) which you then [add to your git profile](https://github.com/settings/ssh/new). ### Special note for Windows Windows does not include a native `git` installation by default. We recommended to use the latest version of [Git for Windows](https://git-scm.com/download/win). This bundle also includes `ssh` and [git credential manager for windows](https://github.com/Microsoft/Git-Credential-Manager-for-Windows) which is all you need. Important: ssh keys are stored in your home directory for example: `C:\Users\Jeroen\.ssh\id_rsa`, and __not in the Documents folder__ (which is what R treats as the home sometimes). The `ssh_home()` function shows the correct `.ssh` directory on all platforms. ## Part 1: Storing HTTPS credentials HTTPS remotes do not always require authentication. You can clone from a public repository without providing any credentials. But for pushing, or private repositories, `git` will prompt for a username/password. ``` git clone https://github.com/jeroen/jsonlite ``` To save you from entering your password over and over, git includes a [credential helper](https://git-scm.com/docs/gitcredentials). It has two modes: - `cache`: Cache credentials in memory for a short period of time. - `store`: Store credentials permanently in your operating system password manager. To see which helper is configured for a given repo, run: ```{r, eval=has_git} credential_helper_get() ``` Most `git` installations default to `store` if supported because it is more convenient and secure. However the look and policy of the git credential store for entering and retrieving passwords can vary by system, because it uses the OS native password manager. ### Accessing the HTTPS Credential Store from R The `credentials` R package provides a wrapper around the `git credential` command line API for reading and saving credentials. The `git_credential_ask()` function looks up suitable credentials for a given URL from the store. If no credentials are available, it will attempt to prompt the user for credentials and return those instead. ```{r, echo=FALSE, eval=has_git} # This hack may not work on MacOS server where cred helper is osxkeychain # which always requires user interaction. Hence error=TRUE in the next block. example <- list(protocol = "https", host = "example.com", username = "jeroen", password = "supersecret") credential_approve(example) ``` ```{r error=TRUE, eval=has_git} library(credentials) git_credential_ask('https://example.com') ``` The function `git_credential_update()` looks similar but it behaves slightly different: it first removes existing credentials from the store (if any), then prompts the user for a new username/password, and saves these to the store. ```r # This should always prompt for new credentials git_credential_update('https://example.com') ``` In a terminal window this will result in an interactive password prompt. In Windows the user might see something like this (depending on the version of Windows and git configuration): ```{r, echo=FALSE, eval=has_git} credential_reject(list(protocol = "https", host = "example.com")) ``` ### Setting your GITHUB_PAT Automatically populate your `GITHUB_PAT` environment variable from the native git credential store. The credential manager will safely prompt the user for credentials when needed. ```r credentials::set_github_pat() ## Using GITHUB_PAT from Jeroen Ooms (credential helper: osxkeychain) ``` Use this function in your `.Rprofile` if you want to automatically set `GITHUB_PAT` for each R session, without hardcoding your secrets in plain text, such as in your `.Renviron` file. ### Non-interactive use Retrieving credentials is by definition interactive, because the user may be required to enter a password or unlock the system keychain. However, saving or deleting credentials can sometimes be done non-interactively, but this depends on which credential helper is used. The manual page for `credential_approve` and `credential_reject` has more details about how to call the basic git credential api. ## Part 2: Managing SSH Keys ```{r, echo = FALSE} ssh_key_info <- function(){ try({ out <- credentials:::ssh_key_info(auto_keygen = FALSE) out$pubkey = paste(substring(out$pubkey, 1, 80), "...") out }) } ``` For SSH remotes, git does not handle authentication itself. Git simply shells out to `ssh` on your system and uses your standard user ssh configuration. Hence authenticating with SSH git remotes comes down to setting up your ssh keys and copying these to your profile. The `credentials` package provides a few utility functions to make this easier. The `ssh_key_info()` function calls out to look up which key `ssh` uses to connect to a given server. This is usually `~/.ssh/id_rsa` unless you have a fancy custom ssh configuration. ```{r} ssh_key_info() ``` The output shows both the path to your (private) key as well as the ssh pubkey string. The latter is what you have to enter in your [GitHub profile](https://github.com/settings/ssh/new) to associate this key with your user account. You will then be automatically authenticated when using GitHub SSH remotes. ### Generating a key To use SSH you need a personal key, which is usually stored in `~/.ssh/id_rsa`. If you do not have a key yet, the `ssh_key_info()` function will automatically ask if you want to generate one. You can also generate a key manually elsewhere using the `ssh_keygen()` function. ```{r echo=FALSE} if(isTRUE(delete_git_config_on_exit)) unlink("~/.gitconfig") ``` credentials/vignettes/wincred.png0000755000175000017500000022640614151130430017057 0ustar nileshnileshPNG  IHDR=KsRGBgAMA a pHYs%%IR$IDATx^|֙ݻ{wnr7bv8ihb'v{odY]%7[ދEuQU+{b 0!9x88r8l*RJ)RJ)|_=0RN ׿5Əm۶B!B! cݞҕ_t $Hh۶-oߎNcPJ)RJ)\vܜ t H 󘥔RJ)RJ)>+Lw%ȯ6tVRJ)RJ)8lq :LHRRJ)RJ)4{Nea©SurJ)RJ)@:?wQ B J)RJ)@{ =WPJ)RJ)m'LYRJ)RJ)4ЎO0LRJ)RJit&PJ)RJ)4 (RJ)RK RJ)RZ@~.Զ6rkԺte5&xCw }n1xG~h7tfZp }9(RJ)V{_YpʸeX?<vil_[;toܻ݇'OχOzvy(RJ)RZ}O |W)cbМrlQd=me)mvkfȣ~nM)pyk~Re5)ymð0gB[DcJ0|T FcJ1%c\Qǡs~h]@RJ)RJi?.d'\R꽮MV772nH1~`$lvKg>p}mZ|ɧ;Hm׫[zizKg62 mI;h/Z0nd Yq0mL1eS1tx >4} C#0hh]qe;n">>lRھ)RJ)VkTA4 ;vgѕY+ۛE8a¦o[ov_oXd;Wޥ7q*fΙ'l &N1dXi/e W1n>4 ow7vCǮ}QR:]{S\Fn3u|淍aRJ)mxև0}, ϟZ~}g{,@m_CAn|'ӷc[ri3`l$L2eddq/git/KJxHn~lq V= 7vCCw>Eɪزkvjcnxqy,b^ql0!䳠M;7NYgsmn_׌7ŏ0RJ)o2LV sel_2wqߙ0Aum5ئ&ݡ_%e:H()cn#*w[˟˞o<6?:U{cjl\x9t}GUi}Et (S҅a+Fnz.SWbk]%z}Mg~[_Gk&PJ)?&T+3gRLzۏƸL&&|z<@̴ՃM039s,Q)LoNq'aLxL2ou>O?g.yd.n67Gqar|\d \_\}:<4p<hԾ. RЯ-zC:)%HcXA+g C9 =ѿ Aڼ{".}l*jSq#->7ÅL߭pS:]ǞLwSAz5 O叔˨:tw¥OEw;O "ӷgv;iͷw z^a->RJ)PLwn@\eIOXvڕRg {,I Ν;3*ힾj2DE'Lp%oo ۝1aT :GdDq˨peD\l .m: ?<e_~<26W;'wB|Axxާxvi}h,+]v^%jﺖy|^i/~> w"ͳLZۤmӰ]GV=z'ld|=h\&xzMF8ZIuʸܙgб[cRJ)R]]x6Jy欕L5;2nXFY(cٺukJyIoX ܕ;Ҏ &\}ml_O٪eowz<[l1i݋_~Y:as,Q|e'LX!&վ߮'JOƀAw0vŧ~qQ@B%L(ŷ/?X˚ m;ExONXOmHwY#\kYyw\%[ɮep*Ҿ >'o7t/OΤMxLF;|]{|x|Mf>fh zڷ6Ч}nRX=7ֵm(RJi!k0>X߱(x6mڔ}as, .XdXhZ/ʺ~OF_w5Lp%/ oeɫ㛟gbl >&e[F>Ի}K¿+/{r&ZMτi}]dL_RL݆ˤs-s.oi_i6<69mηe1U4`?~ w<0m_>cgϑ]{Εcƹʰ\kg 97隯AJ)RZ&7;+Ec+f |b(֣( V5Lp%0!hO^{->m|)͵rzwB6as,Q|zݱ0aȌM<}#LY4l6lFރ0xP1߁KJGFc~ػ'k]3<8 z(|?y5ȼwgWuucS×|IiӀ=q7L-3KWc /)?Gj^&T!> dؗxygj~c#zsMFm >*ɾϨׂ.SJ)20y:wUMͤ&PGQ uelkKai|O^{ :71`տԶKas,Q|l'L:ssleGZ<@=;]ՇpAR|2|(ҦZou*e_upѣSkRpuQv Rt5-u{r:IQ&Ezx3v׌3m_9WRJ)M"L-0w,b=Ol<:09\Ä7\ ~z]]1^w5K nZŷs5Xl'L6{Klgm֏CgnkCflЏM&Mu!ӭܤ _w pP\t`t>G3R  oBuRyOksGjs_8o˾uޱK1d:տ3')c2M^F=#Wc5< :y03?ܧ/)Ӿr}~lϋo!ڡ,){Z]Gէc+frުi"cʟV/ǟaWݮ>+?sccL1U'+tm[w72ns,Q|p'L1gkVW;s?8e~d`9.l>44˛Aҥ2}=N߀~*Kk<;m_45ІRJ)Pێue2~y}:)ƷE>X}6}[>L\q4=L`gJ'NGuose;o;J)RJ)m6}cy )8e2x,R4Z=nr~flkejh-{{t+_!;?- cRJ)RZG}s޷s8~4?ei[J)RJ)T ߓg# OS50J<}>i>=Fm(͡#'ҶRJ)Rڰm(}mif&>{GO 3ǽi6oۅ%Wa%3o1-][veֽ/gRJ)҆mC)kOS508tуj =p\cAM!#1bX/)Sg`JLL< %0bX <  S}ׁ~D)RJ)6ls ظl^>܅}CӔx*L_=wtǞ'cQ 9%%e3o!-X =Fb[Rq1N-/?I-~cȰ1رޗRJ)҆gC)kOS50؉ؽWa_ga|[Âi3`y(-0x$Ga9 1vD}SJ)RJ)m6¾64^ ?{O:}L: 3Ǹ?a &Lcq8K+6b#6m?EKשmYkJJO߁>giZIn5?x3 q:Oca;F},pJ)&iC)kOS50ARkd+F-ôs0n$L2el /^Ǡѷx: -GS[=>Kc̄Ř2k5acѥko,XgqMݤ<z~[koy5p|}pjݖ cIR=/A%0F؝kFCp U_99M}[n}_kx~ahs]4k5RJ)uƆRFkaB~#0g"-q'bLIGڌ(>zj.{b6L\T'gOG] k(Ei}tZ'„%LĽ7jJ[_@aB vu-L\CΘuAZ )L<"fu0RJ)1l(}mi&& 2 SDi$"L2 *fߠL\KO%L%M'fp帰y9l t,X|]-h߷ ;v'ze4ͪ  fy3n/@ In]ԞJRt[j<&l5d^[fT%w5a12L$J)aZKt0jj6>ݧ)x 8qr9FSo0k KVU{uQepcqq)T\$!ݳpYR\d\Hn_{۟O AxP'ue9p-XHaB^lXaBkuMܤ aB&&PJ) (i DS[i6}r CƄI1z,&$8a pq󩸨D\| qxdE-fҦcpc>h׮k@#O㍨}SkONr7ҩ16=vumžpq1}zdE"b6M͹ohjӠkw&8\=Q洺]ȹyk|A0]ߓi=d|s~yǢ= v^o2\Σ뜹TPJ)l {T-Na4^ ?ul&Na#`2|6nŲ{dnf k o7/QV/ пpCqc3ph߮+>,ϧs:Ѱζ֍gI76 魃9a|NUb 0WovckZi sQᆳ1|L~AǴ6L?>wy :m5;&}8ǔ,>]Ǩw.Ȳ˼>[{5|8sNC5gis`8?k%X7Mi׃SpiRJil {wYm5>}FOti&4:x3CGx?8QZQ?;.zd2.jZ NGFGLB!SЩ}WX֗Q&mԍƺ zоQ\O1uZAs_ʉmm67u'`u,lϣ?Z1۸Rwߦy^~|^cEt޽c4F+L[AMxypyg9 io6cy_:duQ|TmlRJ)f";[)MMn}Fݧ)]p$V?h1a4 X!CG`ȱو1vpa2\Z$*||1衑i~W@[7/L2$7kq"{6q| #Kbtg1d3zU(Xdv1c* ǘq?B=<\OL}ҷ۠q8t:ct澓 &یU?W(:8yi.‹R\6X?i<e_qZ}#޹c͚k<[/s˞m^΃mk\txDc+V;>sMGns߾>q]9 mtOyvҿX^7ӵ&#^9jQJ)S؛45Mao}OS50)x8ek>}?o~xZ2|C񟏌9S1` (|ѸəhA_E;ܬ/N1ou}ק΃: nжU{kNvQǒL׮{c [G)2Naod6?d6}~Ӕx*Lwl:ꍇPv|Jt=zEރ0xP1߁KJGFc~ػ'k]3xh8Qcln\׏ўKS l7e]`zKČw/V.J-˚-=Ig7V+^}S_U"Mso/Bz^lƓ><^G!:^[X&/V?3y2'1Z]k@k_$1~Dk4ֲǒL]OѮ|ORJq\̶Ӕx*L4n>\'P 9{EՇpAR|2|h|XG:\p\T,ڇjr:n[7[mn[m5>zYu-˯xuqjo2NOd\r.clq.Xw//c1ν5_nrӏOwz<mk9.W_z|,sS^?WI\N?Q8lK|̑ܳ_krszEPJ)0m(}mif&>u[*'W] 챾pP?ʟqcq#uU/-ƗwYOܬᅢϵz?zbR˵>[ uL;"B7h8.UQZ[مYϣ}:%2b͓5ӏZvlchO5$ktΧ,PJ)m(}mif&8|˨`ǒIqa󩸠|gdU Ul)Ɨ*Åė_/Ji}p.\omZGivNjm (RZl(}mi„20L3ظXd7l=b> FNW1d,tH[~,+_>CXmk6(>O_nXGiNÄuRJ)?6¾6ߔ:Lشxl'>h|-mC)-L{/{~-4Gzk~4RJigva2"D顣gyljv6w(ۈgи1^ihKiGO:J)ݥ U]7>u`f&>z[vRJ)RJkE^T^5vIZ}9L8I)RJ)֊Rn;}2J¾!0dJ)RJ)֔·&LimU0A- S3aJ)RJ)C7n a¾Pe¶m(RJ)RZOl֬s6S@!B!§W^0 8ÄjE0J)RJ)0 B (RJ)7L0n# (RJ)e f}äm֧>L&O6IM}RJ)RJi&LR &֭[҇rl5чɆ/RJ)&7L0naBZb0, MMt&˱D&ZRJ)R0malcݦ _J)RJ)MʜÄja&P(} 1eʔGR:}mDYKɤwLRJ)RJiRzS&50\kZ/(}xlӨGN7Sqt#nQޏ[6Q ;6S8L}ڧ+LmΟ[V55\a_J)RJ)MJ`}a”SN6d-*Jƽ\o6.SuhżmܢyFdرG4wNxucsHJ[謗GrvRJ)Ҥ \8 &~gW{0iEUPNmb5^fԇ,&8-JneavlN ˽^A2ִN;d"eӯw]A/RJ)&eP/L7Lwu/7]TɣgIg=m2! qR<-ޗ۰csVVj謓GyM cv^}ŝ?if{q5Gm3K)RJ)I Ly[R1Nn`-$Ɲ?iZ ޯMO6ͽ>LQ/RJ)&ea´UB vgtP(}H@a۹MQ*cw&.L0.م Q;ǝ?iV+DyTսu~)RJ)4)a)p[a`=sSڹPemX)Ԅ&zdޙ 'P&Lˤ"uŝ?ieykw?ٜ7z/Gg^fRJ)RJ20aZfLMWiާ.}$e>d{4KPֹuޙY2(v^}ŝ?ifU;O,RJ)Ҥ e{&mĸEiNDva?P lQg ;f3ZKُlG~eY6NSLRJ)RJiRzS&\s l„(]TɣӇB1jAԇ,KaB&Î-; ن >]Yfjvje$eqdۯ,w[1SRJ)R05 C&$rYPney\g[jرI ~΄led[ T^ڙͶ\/RJ)&?L0g0aZf] a:tsRzp;•vH\Oև)7mWRJ)So`„kZ„g2$ EoHʺ0&k_J)RJ)MJo`t /}lhRJ)RJiR&Yw(La@)K&˱D&ZRJ)R0 s03 >a6IS&Mmԧhj>)RJ)4Ia)p ?jX@)RJ)&0aC2LRJ)RJ05 Ce@)RJ)/a9p 6&HgRJ)RJ)-|a)p a:atB)RJ) u)p m:&B!B!!u)p o:*B!B!9}ȁ]v8tPB0ij{=z{u)p l>*B!B!0e@!B!u:LXH !B!&S&,S d@!B!<5&0a֣2L B!Bj Ly[_P0B!Byj2L0na6"D B!BHSa!p Vl?*B!B!0~L^ FA2L B!Bjaǎ 2eee4i&NǣX N[ LaB*Ä&B!B!5OX۠K.=z4ϟ X"UVsI 0 0&B!B!5Ow&dAL0n&B!B!@Li'A`„0B!By'P7L0n&B!B!B!B!Ģ '\: 1L B!Bj ـ_~#!B!Rd`„ۏ0B!Byj2L0n}a¬ζe@!B!<5&r|aBH,B!B!&(zÄLB!B!`@!B!X0L B!BH,&B!B!$ !B! B!BB!B!Ăa!B!Bb0B!B!`@!B!X0L B!BH,&B!B!$ !B! B!BB!B!Ăa!B!Bb0MF&+ĺih,cOEuwMJ6IPRlF:ׁsi`J7*FƩsY'.r-hc}sX52뤟0wVa<zka}zo {<z]|sX<̹9Ϝ ּeqE-,ow1OB!iL0߈e,)x+&I ƺIM!7B7Jx#an>X| XTV}V%ӠuSǹ =LЯ kӺ5$Bj nnIaz5EMB~P_Q78Jjr }$sw !ڃaoNLwtn@=͂۰ n^ _(˽w=eB/&ob*N}qDXTܔO݄^W[׿eB߻9ތ5$0žd]~Mk߰d?ƚ(?׏c 9>xaޢ~_B!u&oJ,Gݺ1Ie>n>1sm3wnjMm\䆜B='֍nk? :D("<`\zkSMkE'rFMB??5 xOX۸ai?/srw2]xr:&B!nA~n [McH ) MO&>gH~y8~4&_t=G! I Y'Jۇ?7L<k[5jϻmйg`[s~m5avl'Lc? >jz!a&$.B!#IܼeG.Mi{ `k"srAH\2~Aþ'x)V^ ױ_Cl>γ"={Nk Ca+(P-ibliaY;!=1 sD5VFmc^9jbQd<ن>_FDKԿx Bj 2yhOca +"޼7W"W1Zqgޏ̥iAL}aVk˸/}ٿD$z\A} d]vs,mCHǫϱz6-ˁ]\"'%&x8_L!{3=_D^!a ΍KߍQK|(?n&zoN9nf"pS;&}ci[Ms3̓c7=`6Bcs6ywVNsfD\lNd ņuM4OA!ku-w9o&#d~:Lכxc!Ǡm7?yfٞ/}NrB! ߙ@qBh !uWPEX@! !m蟪פЩa] B ! 6XR_a[B! B)hLoQۿ9'ug\BHR0L B!BH,&B!B!$ !B! z 7@)RJi$0L 4č7ވEcغRJ)m۸ #F Ä{pYN]`&knXG>6xp? w}.\) QJ)Ru["ia8R?$VRv#0FU"] ߚXwAɯ|]'vS+(zxŽϕHe:=7Q~Hoc׉zY SJ)RjTo$ ]"[|&XEkPY]y{wB;(A 23\Aq{:r!Z2tM0UA)RJi K2S+,8Y_E.5 Hh`n A7ʶ&g$/@)RJi{c0 .LȺz[`SS)zҶ^VAj^ν+8wr1!c^s3|VEs)(rk]Za/,LʗlRJ)4T o+$eVQ+D *>eLAlc^bqdx}t\2UY{<uq%MyzH:O`n ><}!Hr> (RJ) aB40! "[}ŏ]Dz_A㵊di~b8S^A4Ga}X`eq=߭uA}0o!D zs„K7QJ)R*ÄaLE -Px럶z =[Ȇ3h۠N 1L *"Ta)8&H'Ll3RJ)2LHC27 !\zoKBha/L=+3< S&ߤaa@)RJ0!yw&R$9}+ACPoߙ\4 ΉAx 2_¯xm\&PJ)|0!y\C CA+~t\JX:}G*$&?sQ{}żuAFW9G Jz-g J)R&$O ź_4E65]eIg_MH'?aS|{ l\A`X_zHEt8.2cy=8gl‚@62_ NӲ8m&PJ)fo٬hs,h`)+H 0ACV2r KƢUdRxuPAemg/L_\ʱ OS\4T0^b^1&|d:acҡIm:dtzӎaRJia8&Xv2n鸂d< 7L9VQBZ& y(uaD f.B)RJc*?_f;&_[pʸe aB0LH 7LH>$0LRJ)7ҵ;0y@'-PŻҿ2l~vejޯa[t\A2LH ICPߕ`$>8 5|WBaRJi0a.LYvܭnZ>O-_`+S_Mm\ Tv9UIOw__ާKSes?gg],{֧dXJ>s?h.sC{yj=mG9 jK)f.ۚӏ;w̨{Շ(]6}&+H 0.hmD[潿O4mnlOݭwgXm >ezeUoӽ sdQx ڧox'ۘ"hy)gܿ#zRg":54~'Lr{.» CrR]q?wmcǿ]rvƠ l8qhA B1̛i2{w='d=OOM ݕbb:= ä|poڦio xnnWݧw]X[e3z[0v2~_GJ{{1D1m~*A{RoZ}nnH!lhp`ijY>SQwٝy̴A'\1~nmo,ͽRυ\α+9RJ $„u֥ C%(eHSφO)L0W aBLtV럲Ӱ>_aBHc C_}˱_G1sʹ9ި;a¢{rr|gi*8x]_g:keu{5P<ݢVhVÄ=ݵLqܙ~U{-9cV߯sLi=;N4qQ},m"Ͻ}Nѵ9RJ \Ä>$ZY/H) ץϽ>ӯ9+H 0!&M~k#LT)an t_(i&|s\}6Xb[( o؛.)xtѮّ\y::t{Wi/ް6sMw.Vh~VҰq|8~ϥ6ثڄTXskӗPJ)K'Lrvm:#؁K6cԔ7reahLLZN1mF4/v򂵘B L$ÄaLhuCP!eֆOW3XXMw:'ٜ 3OM3αϏ3 ms>z569?ۄ5orp ;a’(M2& (dU޲sWirZ-WS:m$` XzW 7G]ֻ $P\u8˸e aB400T}ހ@MimQJ)M)n;kװ,݅hZgmZ^P-Sϕ{]>-e2~q0!y&B 'LX 99 a=RZ}bq ,w[V.R_ei[iYo2n鸂d< !&Tl=D)RJcIx6>Vpʸe aB0L $NbaJ)Rӹ+vncO Mt\A2LH L3l@8amG(RJ) aB0L $0aRJ)&$BHA RJ)4T 0Rw&B!ϫ7#I a!BL^&$BHA0B!? a! a@!BW 0R0L B!+Äa@)H&B!taB0L $ !B`0!y&B  B!D0ye< ! B!"2LH a!BL^&$BHA0B!? a! a@!BW 0R0L B!+Äa@)H&B!taB0L $ !B`0!y&B  B!D0ye< ! B!"2LH a!BL^&$BHA0B!? a! a@!BW 0R0L B!+Äa@)H&B!taB0L $ !B`0!y&B  B!D0ye< J4iJ1Y{5 `P9;h޲M/:yM@W ӰcWmկcaºihܨ#InĘϒ&Ѹh$A! z qnM7Nqbm](| pHM{=y"40L DtaB0Lp#\h<~R`, j&mA7F>q46U  \; CX?Fݶ(2LHTq^;FH'su*k4i&Ba0!y&o[u(oGBFQc~z m[a~[y>.+0AR3mӯZ?VeuS37ǯ?2ou2LH?ߜ[;\7`N4ic5иDeS.$Ի_{ tV8r~i*^M /Zx2Χnl:93I 7d>#G>9Iog.ZXf3IV)J[뒴q6dE!Cܾ\ҧ{h?Ob(M_0YXdc]|f, {Ĺۏ\_q^;&ws?{|Pay9qswN{?%_[#? L[k7Lc{Q (2ω|ޢݫO|f*kizTu0L 0ye< !Z| aŸL61-hpAyN:?76Yߙ˺&ݤڸ 70Ai,T}c0#L61J+6|OvaB0ߏI[A=h_s^2]H>3"U&9t-sô"g;2bI^֣Zw" L^&$f234Ĩa.Yq;2: vݷ.Q$y$„|llSPLpn΍szL$&7܄ǜM_AK5&|S5Nt`Lc:φ91>=V^2\qO-2Ga>1ص} OutM }j&w95.cy!W 0C?&[3B./aBX>s},Ϡr-%|1߇} ּet[\ 0Ae:c. 1ɦ/!x>'0_Qw0י!$p!S;4%z@QC0icRxσ;oscA>k"sŜޗ=a}T{{sBHc0!yV`on}Xwζ1k1C{n jx1 ~s8vl&̷y=|S=c+`aB>nxROÄy0/-d04s59bNS:ˬ6c6p;f!$2LHBHS„ԍʹC>1eq *ăqnöq }!hmoxq|=A :\qqspUo'sd}>2[N? a! sa" e>1*Z gL$Xq]OqM_7moX_ՅOLXehk 3vF\ S ٷsi4ND=cSA6R쾍ÙqBjW 0R0w+877Qn.Դa7g,d\jl#zn]uݐgWō>ߦ8d]Lu?'Uac 3vfOM^/ naΪϛg]c +y 3gsM(8qT~i}2MZI ׎:'W 0R0AoTS7AnF7ܶP"]@D)d Jӝ* 6)wvd17B|7TjJc[>0z܆a"c7m~jzqڇ]ad>x\}Ua/A>﷚{M ~dx- 7BH1ye< !I kA7n=Xv7Ac2/r!pspnC M/n4_ol"N>ǙcVc@6L{Ǧ&{w)WcJQ}o_Թ O!pMYs:Vx3|*!&0ye< !I!)`YBHCtaB0L $ itBHCtaB0L $ iȸ~![ !{L^&$BHA0W?@!D0ye< Hc]QGuJ{ O0L  }%!+ÄiXaºih*2W(<0:X`ࣤ/@&0~r2p@!>a0!y;:P(9#&a@!B ? aC@h2a B!`0!yg&x̌EgIXAƽN.6mkDT(*wc#}0'h}&VmXtg\6ӸhZyre>|̿oC?+>&B!taB4`DO~A J1gAKk*S+gܺHtogwqddƟ'^c)XTn7h~rOk?7_ƹ{ol6{lecJ;Ws]z_Bs,Sf0O92`S{/3͓iޗa`\qm}.s:>vWX4wE8F6^`<taB0L $u3L/`zύx77}Z}n1{C+Rx* ˣ1 b̕iQ#g{~C&Uyk+x< )hu"Uرy8ί¼=}JWmסS!\)hzxƝ'o6uq/<8_Auy8ǘ"uk/1:!0ye< !IC RGmPf<777bW!GchAbaW ;6Ϻ>Ux窝{6>a"~:QVL"oyXT< uqIhg\q>:ȵ3wi}D9SDaB}G Z\wyvlOg>S_[Dr:4nBH]taB0L RUpVAڿNU鷪{ksD 5HgœH*};ٿ; `r9_zs*ԯPT~>kܻ&\PtRH:ų/t]!P_#0L-0JiT_]`4v-.-Q7Ƥu礉:΋S<'~Âz7 y%C=1%1LH oIR Ro[W`40?BÄڂaB0!س֠"Km&j߹ȴzA@c>ۛ r>_%r@G s& 9~ w%B W 0!JvހXb1ɲUe?EaBK6 $f| p'xAs_A$15% /SoAC֟0B!W 0!O}!].f ~`Qx`I|&H@Ak8\ϗ\CQ4%hB2L B!5+Äia*F)mOuqVit$BܸLS"eۧXE~rW/q/k۠wC8~w--h^&B!taB4w&EKa+L L@`@!BW 0!VU $B ѐa@!BW 0!ߕ 0L  H0L B!+Äabݚޢ !0B!? a! a@!BW 0R0L B!+Äa@)H&B!taB0L $u)LشiRJ) 0ߘ2LH aRJiaoL^&$BHARB!R{2LH a!BL^&$BHA0B!? a! a@!BW 0R0L B!+Äa@)H&B!taB0L $ !B`0!y&B  B!D0ye< ! B!"2LH a!BL^&$BHA0B!? a! a@!BW 0R0L B!+Äa@)H&B!taB0L $ !B`0!y&B  B!D0ye< ! B!"2LH a!BL^&$BHA0B!? a! )0A^n݊kRJ);>GwjW 0RB /{ ;wRJ))9r#=+Äa@)H !LظaΜ>3g*RJ)s? aquT'uMVk!'uBΝi-RJi}Uw侧0ye< m<^X7 ŭc+}5.g?+\tb?'J)Һ!Ä ÄqMJe)VI(Zg? `Pa@)RZ7d0a4:L(Fw'h&xa@ ԋL,YjF]bޟ_~7nܤ(qرC+va}طoVScwN .xRJ)0a0!i\aSU\]+ϟOk }vݻo>@>7#S +tQ= JK1jhVN:&OIP6azΜ9Xl܋/E+WJbJߕQ\%WepF݇Wg |--XP}(///Y6qvR 0#FQP2nMض};ߏCi`:RJ)^  Ici?V&oSaǗ ,@K0p- [uYGbگۨa<0˴&=v lٳQ i)aÇ;ž~*͛O|*WBٳzXUU*5FIݯc-_amR;'(ˣ#zeeZ1L@~tƜR=?%ppS<<|~Cj;bƲ6v,ʧO >_}P3LRJaBÄaBW E|QX(VՕ0!;!w2?G/88+cGŠ˙wKԣ0@!|˗W`ѢEywa Ӌwy^}Y}cT+7;Ys1t<Ϙ 6C+f4J)f/Ä Ä * {?OBT}xESjmo.f- َ4$ɴl'Lpt~ ke2&X?9^!: 3"{F2?-LbwU9sd;) "o?QIHau?RJsWw&4<&RKA]O&K <ʟ~ a̘1:`p yfo߾ѣzՠݻ~?])g&,XjPtJ|L7ڴzjlٲEC1" J)-uDie0a@H-0!7S J[RR#FR K 7u F๷ї@sز5Z|?^y)|x <|Ӷ8zTq}BS=x=xguZ5& {|OfJc{7S>?u)w)=P2oƩq(cƻB;B9|R-+V9D9XY1P=]FMBǁѹTlO^iAa…<ʟ$wJH&!#Czw(КVw&4<&R> 6l(0s&OB;~G MP?E=^8XSo:8k;9fl3^i >򔞫S+uLӷ/̝+Wb֭#arDdROcz)!&:$Gdz4_ÄBHAR„Csj;lpL: Ta, :\y^k [" Ä ?}|*ꋁʃt#pLl:z%8Yv ]:qF?M[g$pL`K16n(ƾu8p":NSU&ǹM{*NcŎ-(W0yq} @s+L8_e'ϣӴ9xЭ{w̘9+*Y>7aϞ cB Z2Lh0L $)L8^dF3y1|4g%"zm Jyg0a8{|%PPWϏcl?"HX~tdUiA¼w`pg>ذu&&c0zN=E'ΡÌx郏бs'L-/DzgNaT0Ra@kC  „Z'NT/ԋA,_^?qܹjԟS+Lxf-ǬЁK`ʃ,AGN aT ǩ; $Hwxل=g@Qj9Z( GSEPm7)W+uqrTl<?6]uiaX/Wc0D ԼH{z6vǖPv@Zu?=Bq5A RѧCGL8 K.ƍ?tq!?~w.rQ~Oa!dz4_2Lh0L $&Q;LOۇŋc|Y.Zd}ѹs8 „v0WJKA_yJ9sz*jpl,kG=ߩܣ zx`:>yUUCp>8 T:vOky~P8>9m{Ĺ}CQAYnTTnQ틳&ȱ~vQX۹ K0A9=՘vǹ-;ظf*m;W „`ΑSh?{1^nE:9]/AS)1l( &Ci>  > YC` bI-&vN KoY1@f9J+P…}p|ݿM ˏ;ܙV`{?';;UA'oWB_-SV'NyV=]JcZv#vpV=Q\[-U]jCSR쎣a5`ہÝqns']Q-hQ.ob~`x f|rrZ v|rOW3NMwګmϮg*qJ}] [C3N%4-@an)NXHڟrGXfFFn90j^ ڣ/v성c?nzع?tܛI62L %&ڐaBÄa! )0ጺ;5(Qܹ fFyy9֭[Äؕ0A^}O +10(8aC+B3N5N䙍֎@h mgVY}SK#N}B\^Q#ۆ8OVT%jjjr2kZAWSN,&‘mqbG8C5﫱,}uu0|I7 lg,W={FcގM@A>Kժ`l: ĩ[=T)o8U}\}ENQ)A®~W_*3*+ײnrZ@{&V]`;ҷym][7qfڛ(ݸx mEK n*o Ç ԹXw`߾z&B+ hm0a0RNpF&wOn۾ӦM̙3eUǑpZüO0oz *^:`ɘZ` oSƶWźvWhӡcyXf )dM$孝 !uy=%Rw&4<&B &lٺSNٳup1<|ն{G=KAł/xN;\&Ns%9eT\mG5p`@r85R},xYhڽLx kA=5pr3?kyׁ(߹F#Vg?'`@kC  Rn޲E%98 A7)eʍUnggjqn8}&LQGym5Q-jʕo}x (oSr㧨Zǿi/ _f=U.{,Ώ|STwW༚|>_/k:`=gV^C Z2Lh0L $&HjP^ vӦMO_{ѿ /(ů^ S^߳>w`{[U|[Xs/V:SZNBY}rƛ*U?E=\\ܤoTeھjq/Ty'@7pv8zNc=> W+g~~vkT-kXT//jZ 8Q,~)5'U'qxXt0qTi]ǮC뿃sgNc(ߺNj#J'ѯ_?̚5 +W͛ʜܛI69f@+0!J!Ä BHAR„ 6?a`۷O-;޽(akAݟ{ګ\[޶p~;Xw-W+W*_/Tloj[8W˔ _WEzR]%˕yC„[̤V87NhCF38һ+2Ly)GgTx秵T{IE5>uӟE՘gQ9 f>@eǀGU)Q5os`#Al?y Z\m9u[wBX/G߾}1c Z2Lh0L $)LX~=F ?]xAܵEsbkU@U@REʖ8mTnsj)+(g*'cD\|i=fRWh?rvOhZ}oFġ/wEg'`[<*g>3ܧպOjz 8Q( jLyDھڪQMP5IGUۇC8=8> ~J8u6mߎM[u\T<_N%ӧcŊ ws^(a¼Oղ[\zÆ#{gz{Y6Sz7}>7»=VW)>5d86B 6d0a@)HC /n:5J }N:|D{tpϠb*׾SWU 5O)׿ V3K ljʱ>q46jۗQ5]}@)m%>濂pl8Ul}Uq A{p[3l{N5;vCo+o*<};zނ]߂o Q ' Zb;z k7nF:Lxl&Z|1:u:LXak&σg'߳[ּKW+LG<6B 6d0a@)HCpD@;VZAcz]nFQ^E )sVld;U9Z99*@U_TX*eRL)/ؘgPsU7Ǯnq{ 8*ie6ñMQ9aT-yXO|@yϽjj];Pp'}߾u/@?Ô%)YN'{O8Z6`z;x董X~WAHl<ر#N˗׍0!][?ɷu be>gL_KI"L#Ba@kC  „cnjʋ|8tRg!n߁kcعs&*BzrȘG2,BC9E9N9Q9V9@=>&JV/_Oz'FľrG&8ة)vxx{'?sBպww;%nV7_pƭ8nʮTP6ongoԺp͵8z ~hqՅ`ڲ:Lh2a.z۷ǔ)Sl2;vu{9dN #L`h@HvaBÄa! )0G֤J~» ^Yy6,_vů~ܭ_^9a`#j\9vc48RYNwzB.Y'ԟeP%<7N|{{?c}vc&8飘X=8nU[FմprU7jX P{ls3toʢ!ʉX_ʢp%`u8u8ɯqk0x_6Xbʗ,GO0id,UsFm۷cM5r ErHkj&LǒTi!Y!GaPc`@)HSlrW OПd aN+Lh}T PEUa*UŽrxZR}=E9C)8CEP|rrR-@e{q8GG~CU_Ù᭰ǽ8,??|ZށUoކCEL+!RR{T Qw|=qk>TcϫuR]_6c6]~A}^e=K+Vbڢhlwm0q$,Y4&ۿv.̽]Ϟܠ:\j;Wٿg1o[pl3 hm0a0RLpZU/ތMo? x {ўx=:TT{ν| >}=νk_+?TU%=K{U}TYK{W@eϟ?ǠGwwU`ڼEht=n~&9^fo;ۯtNQq0Acn=.ݚ J>LP8ᇭjMai!^toP9  P„SEV^Mʋ}xb!&Z-;U]tkM.gUZs VzRޙ0.T aVQ-C7ǷGw+wI~wUeF*)݊}v'7a{`[Ɓͭ~ߎUc;ת\NW] t*_N>}%6%ρW u|ש6?ř?FlS}S?ɢxg{K1c|_7/o|9^z5nݪQ7lHc G0!J0Lhx0L $%L\h:w *VeNlٺ ]tk˻B{Uqg`Mۡʾ>^U] Y nBUGg^=vQ,ބwo7@U @O\-`Q>Ae;^Սﰹ8Y6T ?JWjl@G?WO7\cT=rj߭19j? tU~T8Gx_=0b̜5헮ß|w}zWZ0Ra@kC  `„SÄ SNXl9?K+0mVOcH}z|/zY#<揪X'.{h[z:zF,|Zl~j>`ε88UsOQicTQ}~3M~m ZÄBHAR8a)?g!$Lر#***pq̚iV+Lt 0򷪨襊7_YSC^I/`@=@qQ%뺨ǶcLkЩsg Q:*{bR[T30CU[#z|G}-og_-ν{TuMmnuj#_, Zo W{7+g~]-76?q%Η}!>}T}=+Pwv mT66N??qoUڨu| 1*L[8Էqo}o tx?px> [c¤r*qwѦMfʕزe 0Roa@kC  ye%4jFMVI6+. 53n4qY{Yân]υ&Q/&v} ̜dZ۰ %LX*;~W?Wʢ+Q^g?P;Gʶ+U 78oPZop^nVi=' *[_9Z=^kT+8ϰ}O_;[v%3o}ۛoPoo=ñ./E ?Pe8v81٧}CE@}Q-@ lڊ9b .OZڴؒ,Ä[`=zM$&BN`z4_2Lh40.6IBd4:(1qgd44a#Ϣ0!.0A]y?>:t+*pQL oDǎ/^۪滨j=U_} ֍7~TEUQOkרujyq^ׯFKWP!7|_K>RKT'P8~'Џ`GӟbDm}rx25KqI峗>..|x1Ms D/}Ʌ꘾7;*_"_hO[-=y z4š[0f$ 6J _ƭ[oc9fؽ{{9F D*L0^Q/&4LV`c.B)@$ Äd.L[0Ť1l,Yk0翪 /BU_y El~ ^Rs*|xT[N?W g9^8/Q'' Um~3O6X *_1>s}1}6Xop{զå~8K_GC~.*o r!v\K8|U>PcE5ɫ>< xwAՍp?kW0at$ +„;&B hm0a0s4 0LH 5&V/&=r*tۻ„gchIX56Xa•-UxbU_TE{QT=>|L7P7畏] <ڽm#Uvε_Px8G8Gt|ӟfj+?BճCG?ޛ.Ö7]\3/GUa jϨq>屮/qw_#Q-kgVJ?W8vpϩO P@DFoP=0[ jf*Qxb?L9X0AtNQO>#D^(J!Ä 7 uBm#H([xhbޭ:>ʻB&Ě5.ǎ(Zg--j_g1k:WQ?gkÍ\ *L0@g!;w.ڷoo |EÄ ˛"**/‚W <{ U_ -"2z2T>M__nW_|e8y%8Ls1*TxBk8{Wp?\vQ8u_|hQuߡ[opʿŮ5νEϡ |~'S-O)oUP~|DyC#@'/n_„%0t<1i&~a޼yelܸ ܛI6?~ ?|RZ$K+J%Ä 7yxg.22_Nxoa~ 8ǥ{Z*8EtCaBc)~=FĆz)=\g>oo@̧̃qư Է05&Lctzt0WvW"ao/\KpneQO(+T.W gno"T{NkzB_[싨M>pg8/b>-?/buP#T^Uwl?l_yBՕU T<|IyR+Tި\9Oa֯9XaD 2O;&?vr>`<-ƔRZ돼둼.^(͗ & toD)tijpÍ}FSO=:1eSp n{7Ʊ$|);^PP6 t>TSÄfcИ2,XoaBK|$0A~ťj%8R Q|\>/W/?_/۾k8sWqꏪs;NAU/][gy_qmW-ph‰?~G. nj3?5lL kWרjK=L)A½J !TJFN˯?*Ng#H1e[DF%0!f4_qÁ !׊g>?^+LZ`9tt a+tPZ>GKmDN/`|xL‚P_+goU  ܭW)xg,o-_;/?]ݗU*"^ojngn<]p8g Ul_U9T^7U^?{j\j U_g~K p jW6B`AB?kA5£_„ac'`&|mܢ (҆,Ä ߙ HXa ˦4`.Ū2HO{gH7a~nd~Lv4&T`W>G|΄MVp  $@P6Sʇ.;LZCݗ쭗J~xz (6_3U~ n*N^^8yV?Z8su?_Z#=WsSUY"8N>uPI8?A{Y6J T۪+ja7ŗ0xD d 5J)!0a0!S%Q/31 tPb.*3(w&ĝ};",o<%k^ XaBŚ0/`Y L_kw"8 ,0a$Z0L{'Ey9{I)h4S;6PX؍$FJcQP,(DєT) X#;޽߯w;ۛgggqJ0!L ib~KU*:6l&T6w4r~ekS#}fWtvG>pu6ǿDj!]4ut4j[j}n?Awį={~iK(=Hȟ< aC>s-Lxn^Ӌ0qH0iW&@SJa'O>Y^|Sa€0ϿгQ/jJe001svM3ݥ~FG֝.wu)uS~C]k;~Uz a[錝mTq*?d36Zw8os>ʏi&T:}T-s/~]]|ġ]NҵL*i?ݤ*%N`7FJ,|J&҃֞i!I #}I?:nvτg „9ZhV1)T/.DD~QbmJ0!L„#G{>}ٳfa0WO}_.Ur'ﻧo}-}E?.{_' jMﲛVEkOJl*?~{ӶRIֺSyTnHhJlʹ&q]8*Pur=7ov z*?V֞KUi͟v}c'|тyoFtt01/kcu ӵi5tM7U^܀>jL I~Wzt饗{\{!{=ץ+ڒ0aB%I}  Yfig_)34W5u͟M}55;IW.]Ct`a]pZ{^*?uO=u*NYkIkOkݑ۪Im-w9rKV!n8i,uh$S&_ :Ӻ߸j'yn`q87gn.TXp׮F.Z0`q8L_Lr*.`A -SnW$druD#&lO;=oWyo4POiezd̫:ծG/6S!jL /lLu^rA #Lhx&@IR:a7kb ?L2df̜|Wcc'jJe0Pf]+?I  琢n{Z}ZyW﬊vRyU% k=9j+=t =d3Uo#[ k~o"dW$4Nm$S_R_xʹR }鋣zqD\|פ[w q*h魥'CC„ a$)Lx뭷4l0M6]Kko/S„y „լSwuNҙvS4xÞ{H:{o'҅{TU~̶ZsUqZwf*?El?5~T:7s[ꆣje:2{z#=ӕ ==x{t^rEeGxحL_bmvs x=>`RsU\i݉N ,,/-oafzvUnC<[~an: {^'GvƎ0 1$Lh&@IR„~[Ç)S2{FMV5LiW霝 wwE辧th榻y.9uTjۨȭokaR8MľpacVO}4"O=Nt i{~= ;ӳ׺y\?CyZga߿bLjun%=0nC'O׻# n=vO*jis=]&kGOԉNAQ{o ͛%K&@07 „\>&$d0OݭK^1S}y#E%%շݘi _(?v/+܋ٳ54)zorMP&.EKu]umm5];J =Tq]:{7촫* ]vr9IWSguКvJ֚CZi-TѮVwoOlsm+O+=]CXOb< SeHOw=q t?L^igLVϞ[kEK7WU*,4iE+tZ`biVxO}=aU'óSFuǝwj /„dRϭƤ!L:Rbmi; &<*==/ls%?.v f7~vBo#(0駟F\0{l=05zc"=;y[wܭkfSW  ܾt7팝;*?a{;8qU+ҷuEU к#R&Z}rcvl#;}[O)Ө <]wktqzto2 8LT>'zz4OC]s'OO^钣==wO{aO4}cCMU~- v_68lW'Mop7>v Ouo4b_O Mѕc^N_{B[ /21)D{70 ~`Avbmi; &T-|qU a„AP&puB) ?(Wv s#I}xw{-Mz췭fwE:oGo>pv*HXyW֞vAn/u*:llpZKgt{~{=UU~ BoD`W'+yZ{<Ha鿿46&<4]T;51uc n0nxM͝7O,>jL 0AzX[&4LdPz&.C&XSOWk%2:[S-I ֿ~jʻ3wSyI*t kN_}}}6Z}谹*NTڷֺZRM[Jl6ieѾLS.t<`O7著+>'xx\?W̻3= >Ln*[僞<\  TS*aWf`T_'歕5'fz# ʏP+Ì:2~|.D~je2۽~ׯUu}Fw> cٖIiO1m3Ʊ;W~b)J*LX*R~<^|U^,@K΢e~o5&;Lv:9,AP,*Ä+ڒ0aohNtѵ'[WNm# b)]e)D-) ,’p_7Ϻ>,\ByC-{P͹'s|CcmD1˵$SJ#|_gtE} K5][\/9_S_&m5;k!?ТCwCw҃wQ!鿇}_Fk;n56WEMZKǵ#Zhuf*?=}Uoxzխ{];Okc xZma/=} =}3kW:i8Ӝx꿣[Ä3tދ3ɉjͺ/ס02U?,T}Z a@ L!aBÄ0!KWl_XtQx2sZ5]k^q !79 ʨMշQ:EM zVÄ?E+yXc&;sz5c h رXMk,H& x4蝴q;ǝ;h ISEmm[kš郃h񁭴bm}wpcF 2oF\iҿ< O9Z٣&.i9 taF]a<==*]= 顳==wOi•^LzzwHXy*_V5;=\H:ȹ<}kOi=}j7Yt*ini.iS6&|ag딗g}Unְa~0a,toa6V2HUt; r}Y& &4L 7(wj4 Q<W&lpՕ &XoI]P0?Ly'Q„ ꥗^ғOiohᇟhҜ%ط9lVV>:b'}xNZz}-4s-֯7״_kfqS-Y#}e_鞋#,8pyFK9sێ4;xz{ 'i'sv Ӹ+<~7yx2}7\fsmXR+kO?ݣ}^tFZM'ezezwZ_ݯf;纟ocu޺|J^_щo5~:&>QB;]W)TOO= S4n됺 wwtOg7vO7OϞiVoO3oezk2w_#xLijfzrl v?&…e]#}sZWw7^[kՍFn~tx㖪q }{z6:oZzx4aڎxQ'xsFQ-L5&h'P w5wW0g۟BjrTmC&կ`H 9XH^IPzdKQW&4< mg6x(p.bEogDQ3c* \uM/>Om I#z7#&I@!vX;̗Q„ŋk„ zz zoǚ<0λG5nZrKͥm*]Z:tv fMӚ82,9 o4zFzƺھ lO]D.)nC7O:4=sy»<TS4{H#-|hyi 4VמˍanezrO/~_ YSEW _8u==YF|9Oka7uz|F'֡0L_"QgeL˷l颾pBW+l~ :rCC„I EY52eQW!E0GZ![mA M?]VF7[?VlB2뷟e|s~A9m-|b#qsT?`Ƀ&\/l }h#zpzmp,0&fK>҉-T~8fsMZk]*?\7պ4Q_kseZwh*r^_Opbezz&zeqeoW\-Ӱ34Fz2=qQ;W.h5+[jzd3yOSMr} n4q-lhМk뿿pmhB_m/ҽV_HJw&uwS{Vgc6ߚ6ZwZ3}ٱݿLiGiؔ:ngXu|x oz-<-[ë3W&DzW<723:eRMې؏ua07 eBR(W&#(Êd7&|]Pݕ+ j4s/ÄwÄ죶Nh6SyWWhwT [[IS+;xW8ۻNV .Ө2>;L/^DO\X/l/kK i?kzzs#-ۜw;]{|=>l.=DloD#.l\æ&?4FN7WO=&MzۍB-[fVFG5Ly>Iglg}zf[s\Pw„+ڒ0aBTQjj ~" >ԮL9إSNՐ걧Gkk{iuǝtkN37h#*VEB6Sy8ֶk,HK*msvl"]!޿LWif5f^XO\D/]D{5sWkiZz4kH#3>J h!-5mPKyW qg3TKok5onoD+.һ\젯.wٽG.qyeѴ 9r=?y:l{߭gI կL(&?-,0ʃ\|"<Ot2=oGP և ѯY!aBÄ0!yJ0"pUQ+pg̘{Ï?YVj;5}G9q]{ґMUqTKJGl[f8{lc[{+֮n!]뼴tv#ʄxGO/ݾ˧ɍ6×6רRz妦zztS3ާ^^Hh[k}jҿhCmjít&Z4r|B~J+Ƶ7o^'~k<{0ZJfMie{:MWO^>.B5fM]GQW88Rz0 |"Xo6 1 1$Lh&gF]sPsS`/Vw}zc g-\뎁tOh6s&Hl) FyצҕR7\im;O߸_y_{*?-sOKTOL/ܴwo76[i֝5g+M%u}kj&p[&kɷ7Ի6Ѭ-m4mH+1=B*N'ҢQez Oi=}5qm: N-N ,dH?~y':{O~={Kې墿i;v^{5͚5 ^xjqGFq‚U |Ӥ. iև ֝5L0B/fCC„ a$&؋`?]3>C͞=[=~aMZ\/B}9Wh/_Nꃮ뛋7le3}}U+}צcc}ծpZOc}_cmc}z`c77y6K7ռA+[^m4fzM4Mͻ nko 7ը[~hmm45oV߫޹lS-s+}^}G Ecn=k® qm9ywڛ9e2}aS 붥z,^-}B]/H Ҹq4e+87j L&4<$/a} #<;>.w>3ӛ }GmN8e+=as mU ikVdVV)q?VV~VzJk`s~=ra۶}Vfy֖}ftɭ6[k8궩?k3`K]{sֺ[-5[vBܴzw̵ޣn߫pSwkVzgg'gS8:;;;#hp4k'ڬ Z;hȑazl"=5o@/HҺuku͗RmS~NVq+umKucK~k?RljV:Q}O~[mQ{1ZqkuhV:O[[?o#:۸euo=uiNu=q{uNt;ۉI;8;n;m8trkg_ǹӵM['\S&u<^z|A?^oΝŋa8QcQ P L!aBÄ0JR + bG\]=p }{DOO{WgY Whh'_ô|{Բ2ҏOv{~/\M7{n<^v{ {s3-Aۻ ׭{Ū/=p*MD/L}G@]2f>Dq}t&N3fj޼ZxgW&B+;둽.E^!֖ (IJ+L5Z*-ۍ|l^R}o.ְK[K55z|=Vʑ33kd'OXXOHφu?1n]ڞTtEaOt}swuE:Y:rS:mor\`.]Z1(TaRaWW%aBäA W=u!+jL~~ݡ._5XF)L%KM˺t o7יoP7?PyRASAaFmߺ/GiCGOQg^=ddT?v:4+cW诮x<{/e3>Х37cB/xڻϧN/NmcP?):}~|s?X5tv 8I6 8LrǿW /_1sL͟?[gG&4LvPN/fPWI9P&l80^`r|'zJK]+u/\SV'_G]륮TknTnV~uCuݫ|@Gv֡0_]&kIΉwksojq o'w6mn$}r8|=sߞv-=kgmw?:x:_sgxm\=_];憋yzx@=&Lӧ``W~,[ơ&&@]"^kK„ a_<&$&'q|c#pSڄ0a'L0B ^w}ॗ^ҨQ裏ꡇum[nuޢ}nV޽uS>G[|u.GyWgWg:ϸθN:\GG][G;S__eޤZs.e<⚛t_mmutoOv{kSo]xco]zc/nr?;pУۿԳW/}>~? 8Ps>=ƍӤI pƾYx55/6&GQWs ~e9._OzLϋ,3?{̗ ryCFofه?*?"jkgf^\ }&xcxH|aBqs5FW=֪2!s-L@Ϙ1ܿH`[`O<c=aÆСC"ۊu];uw_~۷7ۻwop |J{`>[ӳgO_o>}m_~߆-.k!C60>+j(K.ĿqCk?:dGQW%aBä ){AxPaaEbbm9ɏj%ȏWI,!eCO $MO|mX_\ӕ Vn۷Xal} }ݡϦ +hviZ1n7*48 HbPvLnkS>{Z3-@ OJ+ b, !"@;q*#"nh^pcH0!LGb*q R 7jl+? yA/ ?1v$iK@eB/~ϵXJ)LX‚._na}P<|a KhbW$/rp>q|߹sU:g\Wh[1Gfv,߷zҙ33C W[ +--c˛Lm'6X}m[n}p{~X?-Y71S_3f&4LP.*n0!Wa$)PcU b$LH~SyhD1ƣ+bkڻW*NiW-;v]hڕ %޽w3Q wG ϗ66dښ43"#̫#aBÄ0!u.r~&[o5*s5[KlA0emZX%h!&d30 v. ,ԃ3ד퇍33(2 7 „gB`~C`- |#LD)[[UV'?I„?aU鿑s6R pYlxٞ7 F֖y3j?̨FDDč/aBÄ0!~QZA`jP/ª|,cwayЌZwMe[ovx (Lp$mXsTo7l&;bh/QxK];ϱ;XD%L(> q(+*Tz.~Ve+ϗ :"~R@ZBZF&H}W0l6dd% P&u0&@IBF_X„C% aQa a$ `D%L(> P&u0&@IBF_X„C% aQa a$ `D%L(> P&u0&@IBF_X„C% aQa a$ `D%L(> P&u0&@IBF_X„C% aQa a$ `D%L(> P&u0&@IBF_X„C% aQa a$ `D%L(> P&u0&@IBF_X„C%I0aɒ%XO: KP|$I&D@!"""b0/,aB!L0ͨ󿰄 Ň0JDDDD4&„\>&dL/;@,){3H%glm& """u040!UPV/Ϊڶ´@4o;![uu}^Ը?pږlxS& """u040jqfEdc0/Nb rk!W'Ը?s Ia""""Qa aB  L4Äs!L37 !L@DDDD3/,aB!L V/(SfG vէY=U\·Fyk@M9%ߖ̲̾{m1rczge5̾^?Y(4]*_#DDDD4&F+6Ow]=#6.V]MWq )I}kkL ؠQۮVDJ96'+gĽ2鶯>X$u ,?5o!L@DDDD3/,aB!Lp`ނ=[3j"y t0Q.)ez&m_ f_mŷ3{cl Xnrm@*kcc\IT[?s݄ XLy:{A\4qml1/E]u\'qe_X„C'Lp|!Q<~ը>=ir)2<ڧ 8aB jk_tXϜ0gM}qmllܢ3Ӟznʕ+n:8xٸEg6&„ź_f˞-4q5Ahm?\tUl„6gp/L7> =L(uIc9i 6<ֱot ]u]'/K/5k mmllܢ3Qa aBb&7(dw^l!CMOP,oX ?i?L0!f̻&?:L0o3i3'L@Dbi;7/'ߖc-j<3KpъuW/fƁ,IDAT_X„C͑+0H@4[gqCR}]x?iP/HvvMO- ʜm&l?a""ֆaǮXźm0aڵ~aaBi@umE^68(@.E6Sm6dO&8GwT;q&8 \{"lL~7x0%aBiIP%L(fmO8dz٬D;~s~9o$AG1 ̶mO/o+T_.]fM˦$md{<`7ըO!Cq'8x HG? X, JB„iX7%L( I\CFuA_2na""K„Ғ0~KP:&$"U|0N/Q;& "b „>H]U8% E] J„* L*$!L@DbIPZ&o J„zʴw71+yg:L2.OfWW~ 7hZĴږ0t L0e Ϫ4lĴWDϥ}~t„tpiu[a$ X,0+R?;P bzUg z'aBto~TLih4LXjw ц5uCGRaU/ml ~-Za}*i^vIyOJ{_rIm{' ^/Z^U{WVv66^ A*< L)+LH]sBNyJ;BŲ„g\G"my'?SmR„*E~B}*L[W9_ ~(~݄ ]&>  baboBݨ(DD@+Jv._TWN~v„3tkܰ3R'XG]+Ip WꗮO7bZV_ԕJ~U:1v+|mm J~TArv#RE]/׌W@*@\WmۦYըbY =F|vC! #F_ةJ-)9qqo,YB2I#a""˒ V>viUWhD\&SAê.S D.yӪ}0!_.IIhaBi<N^:[+r >vGTF6gJ#UP \_pMo嶲|ܠ֟ ɊAE!=v[w)l$L@DĢ WIb'goĴ>9OSaB\j=ׂeJCa»ᄏc 1R~{>^>4+r?ە 5rW';1 Z( $cX gB0{wWc+I0o<, J„$ VDl xaB9)SaB6Êlߐm"L]mEr ' q{6v„BDD, %aB0t LEIU/Rl<)GmDXe+0s<6( G+OZ=Q}T{*Î֏Ç fyC;,Dޗ\b}%L@Db oUk²ﰎkd7L5g,mJ„R##L"FP-Ƅ0%aBiIP%L(J „ |+=~jDD,V]a+I0k,, J„R0a:!2~J& "b$L(- 귄 aB s DD,0\_Ku\'aW_}~KD/„Ҁ0JDD,:V;NZYs\u['/K/ղe駟j̙XǵqqlF%L(> P& "blq7mf9qmllܢ3^z_~wnkde5ٌ: KP|$!L@DbzϸY:Xq(o&nt6N6^Q˨󿰄 Ň0JDDDD4&(Iь: KP| pȯ<,*j @jҿ hF%L(> ,LHOy_aZ/NHVm];~͆5ÄZ6_`Df_X„ÄEK.*NP[P0!UBN70!>?NCDDDD4&„&$ARGȷCZl?aBn6|)- hF%L(> 0 M୼էYQ=UFykO9G~_m|czmYOǥzW5kq~3>.W*ӳPp$ kGь: KP| XW<يjPjQPG1\Х bFB?jTAjb>&BBւ6/ ;I:N O_řH?IɺnL@DDDF%L(> Y *Eo;~p*җg+^lm=|zc Bb9OxA!_/Ä hF%L(> 1?/\XTWb4kZ&% q BWRU":„|K„> UJ& """bÖ0& 6oPFaBʄ M bSykrj&ٖ)091 Ň0!I1\+0H۞4[g b} ޘgw7ek3L(*֝m W„C۶QQ68(D.PE$ORmm wWNowF*iGݩ?QF+DDDD+aBiaBf6l"l茹/dxB*mc³'(Eʱ-$? ͿX&۱ac.I"-0'o0JP| I @z.„XA_$~-À0JP|*s^-@/tQȄ0!DDDD+aB!LH@ޫ „49J(„L1 Ň0!>6cg3䪂  a""""ƕ0&@I q$L(. P?DDDD$B LD&@"  Ha$0A LD&@"  Ha$0A LD&@"  Ha$0A LD&zIOȇ/݃uvO?ѐIUܮkT#qa:OP { @Q!LmP*V`,OadXu}Qxo0I7)^ж hxa.,]&8EmbaQJaN(*  wl&?}@P MP7hsRڨ]GA;s͟&Z.]DZ.?^5xIyUڛkZXZ9_1* 7*womB1)02j$ǧ a[GmrmƠ?P7 dbPG+ ÅQs~u/LoP4ƽ/r͗`ΨyhQtYmzzxmӰ ֧s>ݦg+7sy4q]8'WXo(„)PE+AmEߖ3 E+.`~_G)~;rk|'?[nmM~)jskC F'M[ۣ*o „0 BIZ|?_ri!'La!WWnu,ELO4ق&i| . J٨05Yu I+ʬMK~v I 1oqTdZY[gDOS„45?\@"$W^GWtg}g3rAՄ,\&6N?juq]$:#'Z&|)8L0r;߱ia.]\$-Ht)sN6kGjl-t+SB+^G_f֦\V$}>D'>$h~uib~Ll|cPQYk_?ir]Ce+$ VPR%(rY$o.*OUHEchLjz~, Y&/d&l`T!Yh-mcg1蓌uE9OF'}vD-+H$WP q9?|p@Q LH:r6 !1x(A J(: a ; , Ha$0A L/,Zy|1큯[:E>*"mAO-jQ-߂KHU5jzDQzLܿJ„z;S)Vq=ˉN2iץ-x%jرª*L+aFmHos}>װc~ (?iR@qTG)񖔺:^S֏9x&=/In0GCP ʄ{D]]*((E<ޒRWǫb8sn3_b$2$,$@A¿_2cPisOݶuld@3DN' FN*͜Uen?0[nCl'7Ֆu[^O#i_JeqeIeSJrq_)0I+ |$/al٤ǁSexYKukq\&˾f6JOYw%sp?9>meϗvdq`B}hZl^#2wfF!of 3F{&TdN2ODNɆd"vD?!ͱNp{ak]n2^Iw~{>nf1cjOzzJt~[Wwbo@b'Lw~/('ߗmIڟ\OgߎJҮlǧev> k3Z׷ǿm]A_>wvF ֝?k?c„'v$?I-9-{& el4(v2ukgIlmm@Wbì43ۘI+شp[߰}\J:֙\/IO6)R{uG^s.o=u?kTsq߰20u|2ܾp2^[w|TOgM ad!hي)*lzɉ[6gMF+a$qO4^Pɨ8'֙x+x߀\G}3/rqdkg)xδaˉU7c$t다m Zwx@oO~;?k?5Z0!?wu"l'DQ'Z v6Lw&-$lX}Zog?? ƫg%؇I4^1![s|o!/IǯA {q/d̳}8ЖW&9>>oM+_Gҿl+%Ϛs&'IN*d?apg;''FMO'׉Tb!X1HҟZ(dC4x[gBl}_ ȵNп|k{Ro^\q~Jq/o㔭Oq כW&$ϚP Ea v"P0Eh'oLlKy'Nr-'c1Hҟqw?NLg5,tokF)n\S!ߋ?mX_meۊw$@;!ǿac/QEi3<V rl#<^aSh׭;8?LOgMۚk_6> 3LNO?L Up"~>8ye>4ymIQ m']rm;KulX&iwB+)~[fZ?g3eH:^I7#'bo@u~% :jB)Tuh݆'ɶScve#Dq>s'n[>m2W׷H?_ߜYl(ͶvgMOu&''&Ǵ$'Nv2F13'`:mrL*-d9Zl$e<߈3㕔!{`O:^I7#c(\mG0~_E=)%?_\ lSo̶pe-s_W w|jj@>ufg\ @{&ll2OޠxAѩDžC]{1_0IK^~(8@݇0ac[ixAm`pzTk/~{btR׷]eP,(i66 [Kixm|*J„IiB݂/ !L WD5%~iqC_hP&@"  Ha$0A LD&@"  Ha$0A LD&@"  Ha$0A LD&@"  Ha$0A LD&@"  Ha$0A LD&@"  Ha$0A LD&@"  Ha$0A LD&@"  Ha$0A LD&@"  Ha$0A LD&@"  Ha$0A LD&@"  Ha$0A LD&@"  Ha$0A LD&@"  Ha$0'LcԼ Ha$0A LD&@"  \a|Wy G )„A9LPO&KW]uH{衁' )qLxjȐ!4hn{zG5qD͞=r^L$g3<ѣGkԨQ̙%KTg3+L0@ 3PxΝ A$&S @(7ο"aҥυzJ0!*P?~ !3D/CP&dA |x I„̀ 3H ArJ}HP „UVWXP,+A`W%@ @PXB@ %LGaB0믿ey AKzD&X_Z`;PZ"<""""""bC4ue ~5-[VtXoaT_Z|jXFmUqʕG >3-2e V*~i53?Xqj\ZE} &L k;[lX׍q/ „B""""""b]7ƍ#aBAGأ5_ب """"""u3k$51aBN ߣ DDDDDDĺnfmUg ҝi9jQADDDDDDfֶAܣGʟ >L W\qZ{x(Xͬm:kѭZ{TРÄ :瞫|.slFmYu7ި;7x.(40!*H裏W_|>?QADDDDDDfֶAܳgO 2Dw_@ QAu]0a„i""""""b]7 ^z7j`6o ۷E렑#Gjر ̬m:8~iF?~of̕ {LF7na""""""63k۠0ᡇ+ٳgޫL y=﬉'V>̟˨ """"""u3k۠ӧf9sdɒ筆ZmaB@aҤI6mZsg3j;u6.YA Ad vU¼y*ev뺙mP?Zpa laB a""""""63k۠mN<B!BhILx:mHOv B!ZxZz~ ݁!B!ZzշO88B!B;lD!B!T2x{!B!TIB!B P<B!0x!B!aB!B#!B!Ԃf<޲]}V/8I?}-} !Bt5ծ=$qKed%D%w䁧K*Wu_TQ-Y\"B!P$O\N>s삓-ӗђݗbrZ.#KI+kC!Bh!oX;{Ϲ'[׾I|=Uo I=ztm쾄h`g;ɶNuMʍԚ' f.7{f]>B!xc Nwu/>\tgL[cIYQ>-K2x;x1KyVoQ7,_nجvܣ?8v=g +תoXn}}י^="nB!?V=,zbIܗsEq7W߄\eeee%D?C&O_Vm6}Sm16oVP[Tۧgj.5-[w[ߡiԕ<B!xEu(1 M]Щ';?VҁS~~tsÉzg,ˮt}KKȎ=lJbe$n/!Fljvu)uНqۣۛ>[WSժuk6&ڬ֮ߚU3$Fp[,ԍB!B657E,#=HblxW!rմ/OjrO~}LnlTNL?̝٣ڰubbnrC K7}?O]p؇Bё6 2Xr9Sn}iMLޮcg#M ^ޗ d6oߩ9wQ{n^uM+~yx橽5ԵvQ˒V+7TQPܰBMmخ]wYgwĸ$ ^[5 ~ݣ.TYoMыT_/?4h8.K}򣦸֟,Q!BÐ6<-15Xrgui H}WԖY~faWSǎPwnjNuۆ2+T ~Lk}KGrTΗvFu57ޮ6nѿgՓ^ۋ|Q24bױO0M jwOb޺ԟHsfҜMgZ.Kj3z]M˕ B͝i/ٓ]~κԶx\MT/2j>%DUk7=jړWnf<_WOQdvߺpE/P_r@}lW6dGz-[ղkWW\uc Dq*lmڐkx7GcrcZPK䉭vC!iyw{7ޱUݲvS`D{Ŕ\&')/\[uW$sqv{ehmwLd ]f##_:>t>KߺxFWU/Q_W}hb~jun)U7Av=ѫP4䫉3.:- B- }u'^VGx)h*9Uo$nH;|5yMꪥ)ȷ/m}3Q! niZ3ˎ\hsgƥkw8!/QeUf4󲮜sզj<3V! S2 ]keC$;=*[J޾44xz}O=w={:_|+߸Lݰjv6wf>LnZZ wwwpy.#O}]I^2vH;CWl:/VuuOݵ Y& ƶoOZ\MMYgMߖ-jK\QROfԣXNv_l~mZ _UK?G"]qv)+WΫOX1X1۬xWBܑmJs͏t[xB!40EJs]Hٗ^x'y_gbW̬ۗ&*ޟAxk%?Q3GOÉ7} Q;lnYMڳ_ڬ6nSn^:~]3}D}01vpVnuw/ަ~-nS%iu.ޣ]5Yz~B[=/T?P7௮' mz CHQ,~sNs)=O9alۡ'A24˖׶kR#W"R{%V=^:C٥]gUұj?^Y\2J B!6LK/Xrv]Hٗ{'y_gbWۗܬY&*]:g}6XR^~wo=h7tM'&cZ~ڸyv5;7'v.ڣ0m_جqu~uOQ?bYfbSKfު>6K,j~RHv=\J)8.5c?P{ݿ\eۤΞd+*w_@}q%nf^z͔ZqZ~fu57M3H%.1u91>qFuiO^A]qfu?U?|}OӚL//?hoO/mϨEƶn󅷪i}b ۋǹSW^2IU*21 [g;ke gǾ͊\Ulے >@M:rB6$ A ޠ_qˠ'h,9~PWKCwtj6]1d2vߥ 65xy_BT2x}!JG'nYVZ6nVݾZݱzmJ$FnV} 1xN񋷩5fz_bmQo[oX6/׋r{ۅc܀ʕ՛kOJ'/_l~W?&>r1.ՏQٲ*Wmjke͊)=B!4~R;=m^ Uo3B/WTb\̕8L] s[/o>ȓRtqHi3f] -?ޓ՟m΅\HB!Ԏd!>/I&Ob%5};wA ^/k?YӫlMBhyߊ$ _1Jv]>Bޟ.N6Yeu;q.5-2!mW_}ʇ+G̪%~{eRq&gb٥|c;FGS3h ˕k{?kJ Ҧs$mX!B/YjK32^ 72On('n5sRn߽z&׿yZ@;ђ^y{>_7G?C}肭꽟ۮ>q]#_M {'foשO}NmN?9w6Y4_tYEpUg1Jٱ+s,KmXqP^gKA#i?IO嘜gݥ6Z~8{;r}c_ܞ橮4'Sq\Һ?s6B S#|IoEMPZ;}81B;%z$c`%7xzRmwZym.q3GS1ay٣IO<6Pv]Rv=gS)mV)7xy_BT2x23E"Iܗ㏼X2{onTag쾄dVd6'?N. =HٟǾ;+\v; bQ6|~eB!4 B3' $n}KɯuoRFڗo;)~oMtϹ}ׯ3ݗ Oު{ϽГ<˜z,QF$B!U]vՔ:ϒ+ N+JKvcߣωN$?zrM\ܲ۳;!BHΓ'^|{IhKJB!]>q~$qKed%D%w7B!B-@ ޳B!B N%гo!B!Z?? 6x*B!* ˛O'.c2rGB!*;ͩajty3[ ޯ?/Az.mwAw,rcB!B+1#Ro;\I6]V#Gw]?h?wI"}R7^8.B!Mٚ6]V#~z_^_~--{ԩRw?}Rzzlj9֬+߇B!Mٚ6]V#3_:z_W{uSզٽjj߁#jރjzfڴyZvZjڱszz̺B!B)1#/j6L5mFO>6z+益^z;:v{NiS7=Km6lQ۶TۧgjG}fdrZnö:]m!B!FGbM7__SϾ~=~!y˴:~:pNsvں}FV3TbLmVmRnQkoUΪ]ͷܞpnB!hj\|fz{u){Auqcvڹkڵg:rڴuujCӏwϾNw.9怺}Zu˭~б m}].6uchnKw7<؇Pȹeqi\Bh<4.fk>ty3[ 8ξ]V[ϪG;S7޺N?yEqqfQu!bujyz#jqmI·M7RSoX9g^],\ryDX_I̎rUPzU#ʤ;ޗҦt^*J#Í1{>gD#?ϋt1mQ&ěayߨDY]BVbM7պ[r:v>K;wmӳjj^M3'~BS_:>ZѯUlRKWR7%puB[N- FUW] 9 o] '[dˍP65Nj~ݶ%{o]KօՇ.ڥu%/ڧ>pvoWHwoU-Qtͅ[@8p C HnLCQT%0BIʵǖC i:+Wq~\_ |Fދ B`Ieʚͦ2ty3[xnu~b>ɧꃗQpȗTkղkWKoUA?!e ǔzf^L0cGlY.Z;2bhEoWbU&R9Tж\/s?9헫>r> <>' >9_)?cXϱa;wse75g:ƘƚGB5[Ml\*lŴ }6]V#RbN{ͩ巯|mjYu)u}O~YzE]f{;<%jj"X? ֫}wՉJw9eښ-yޟJ/~:+ b{q?.W ]v]!0NcOF{i;׫2f|6qy򍣕hyPOI9s}*lQiQn{vdl}侩}y:N5cro/1>R 1ۓ#^=}/ԏBuŚjbLce*gդ\v!6]uVOaf5qޱ[9<`b>x: f]}mS%iu.ޣۧbt-fnn얖lNnit1{X=;ڜ-tfe޷tQtCbmbЌCr=RǕwH1f eʓV__y-8*\Uݸ1Řt:΁vJs:|޴9Or_>7<}/U-b~yZ~!󡜏3Bk|r,Q.oI6Cdf~|uneubUԺ-oߖw^ߜةλh6z´z緩~aKת[N?3jgzc_KagXƗ-V g8Muz$G?7[`feJMFMeX˟U;[mG^csU^+ַ4M]g1;^kc,7gR9]."Ge1Oۉ:džߺ=Ac;!jGMO51[.cl4w\lmF^y 87T3;k6n_֯߬.UwRm۝< cߜQd:ob6{݋7e+vN^~en=2mw6;]ﳅCajʝźUߝm}v_s$,S/Qu>nP㥮i|Dν1{_[k3~myh e2qQwxؕg9:oys?槖y.U{\D*&2))uϱns2p>9.[_UB咫KM̖a)g٦˛jf^{?G^֯7-_X^m4Չ[R]vͪ$_ة>~6eS}{綨귿rD}ǷN~ІWǾLb=⧳cZSm|GOe˰?/*kG߫W$uRѴf~rm*Gt(`ڏ^^tqi'fN7[vwlՏI#޹{MU5lSiw\9f~={r`jBvc\rur'ruI"W;mmF=ϼz7/yF]{Juͷ[QkLWlO>pzf?xZvJz-ϖL_بgKBRU=C<&BL^k}q>ץިvef%)OGq Cyt|-"ƞ gsLh3ySa)*;YEV";_l19ʷ[r96ݬ_W̅/?!ոhl53x?{W{Jʫ<{ҟ{]ʟC8.تoez5\1;?[?R:·Z'2u罖G}]Tzs? %k핫c5ujǬvy{]R\m/$kI~cT΍U>vV cԫwww=cr.fLYa[7w8KyعLu)۲2!q1[Ѧ˛jd^/գO]+`_T__w._]?Uwzf߬sPB :& {\jkݤv:!lG.of{_ǟy;X=V/;6T6J^uTxC{7Ԋ2<އޔ,ĿVrC ެcBk\|f'}'ZbEvԽ뾯vm.-oGh@|ϱ!5.fk>ty3[ oJ=Kt7JeIYF$B @oS).j ؇Bh3BbJ{Caqhl52x?K S!B! ʍϹ~r-W65m /B!B 1#ϼ1TI6]V#7B!*I\JpmM7 ~B!Bhf٘E.lۮ^UT.B!F\k"<:~}lH/_:EU}뛷9|&tOenQl|Qoӯf;B5k!Bu_#Z7Ņ|OR&Q{'2MTUky5xps*?)lL%?^g&fR^~յB!'BSgMܸ҆U,g(ZT I32rΥf/T.s{l|/!5Nqyls]!BOEmym1 ik옚<3g&ώ˥ؙO'6lɸMI]myyy5k!Bu_#O~v [еhKum63:R`}fy]e<|i%qI9{rIdc#Ķ[U6|ek!Bu_#O^ssT%b\mϡ2sF.f]&5 >OJ71x'xyq鶾b ^oO66Nl>VqyvB!P5rO*U-dͅFsy.|mmf.emXSn|43xe'&O$Lo{&cQz8ve[12u"Bk$ ^zl ]yN~2HKYo1OL M (-_ls4r\3HRGve{cEu"Bk ^[2f!.}m2ojUUĠ&`A=~ر7XEvԑ*?9mg\v͑\m䩙Mi|2/30Ƅ>Q}'׶ pnŪ y]bA#=OO`29(QK7;xU޹euYڶ)m/X1x јAҏda<Ser"WxwZhI>m"v"2xOxo2ҧyKɭ6,3WUy-l 3!Y,o>YuY.0?BoL:~i{\K6R\O!jXȱcPj#ɏcf2ab&dZ"vA\W>_xIy9j_LS[θ8*4?U㓛o)3Rώ5~AsU]smR*#1eLOӼ%vA\WyI.nC ^hxISELJaYkT>U'H2^n6KLJa/]Qgqͩ3x H`bR\y13Nuu:G՟1jhդz & ֺuF ^q9@yO`7H;1őY"0~9>v<}>Q2xOFwW;:J (,r>YHv|XCM2&GH\H~}ߛg#gަ\gL>SU5t, :ݯ&'^]'y:֛Wni*kИ@۲m}&V/r\a܄Q"I&yʙ6 |Tr-;W>].KR8o]H>91Jr8;r;[sr*sdh'>26ф@LS6 Qz⣑1cs@&msO`- <<<<m'\{Q"wn2ڋW3>O7c;xh"{KV'FoyL<O:-qo;x0&t%*0F痬t @Gt @Gt @Gt @Gt qnRERMglc\qID/<8L\!1>$GN@n IwŌZ0'*|j_!O|&7|n $?el'mVU/2R> 0nj yh,⥾|ܜ~ Hߓ6\Ƞc[^_,s_nx|Ov4O;yx^Z65痬 ILb:䩖eLtۉlIj1bbkn}L:%T8nYN`YO#FbSe! H>/KUUǔ]a|f2Kok8_~ O!7"9O(w^RQ~ _g$O>4?F~o<|h`1&S~ ΍iOBkfLY' ODm>u%u:Iۉ-/̩Kʏi/9܇?^v dQ^\M b)2Fy_]*0Oq:b3I,ufI嘐޸G ,gre#Jde\ ߒ/!\.SS.3/I9s 6j3=n.MJ$Iȓ>/Y,ghc h ޼G"~e0xO旁_ayzo.';b lZsEJ_RͿd,(<<<Ax(W )ݗܟ&<7,}_ 0tMO IiLspIc./Wig<.3jb8d~h9҉8B_"~gx7YO[l#'He[޷ls݅k|r.a?910xSjRnS<,e,V{Wly ^bJa, tīh*ĮDVx azOk*2bG9S22>83n=7:6^"]%h> &󪟻|3g!_=}!!yNs9޺%t|-@ cbeaVXپ1 ED 1ԴŖPEn\>Bʗ禆Eh>bꗯ=ݧds1[cףc\9~C\~O~k\q M'v>MÉ'8='x_{kA,7rG2T]oxtA㑽jZVQ5^P[NyYeo˷zO 9̇֟?U׆Z<@812ִes#P0jBnМEmƞ! olv->$M惯Oǫ<_oW{ټ|MOI:QNh2&_JGm0^/./P ED{.2c@M6{Uzɹ`*=횗|4c|텏Wy>i2bs;=urQg4rFx spbys>/Ϯ惱3x}P^-Ć}[ظ,oz96vCd>k/|LHIi4dN_;mT]d>MÉI>ȱ/2 3x r_c3~"3|B)6>6b:!-`>戯m._tDdl*SC5Ϸ0E6TW`%ynNt<+lԑ\j`4K޴݋])ZUX(8ۍ-jH _t΅,ssMB˳o>dĎ]][4tW| 3xξ깟gqۮ6=_B+anNx"g>^iz{JM99OVq.x݋/L4H?E!ע Lͷ F]f>*?oA#[4Ֆ K΅i|0͔Va }XPj骿n!ZjE|Q| ?W.k+1 CBoJ|G1@[15]%W3tUiWftze$;X*}|3YS;3VO #`:#`:#`:#`:xsEKԢ\f;C 6u 1zS4t663Np:nΨ0.G6R+$.S粷t do? ޔ\h1_rNLV<_?mkteode35z* x3oOm]m OAl-.gɪ*7'xaj>F*&'y3^/ ɡiӻtu3b| |;6x .5qeW&aF <Gq)c`ay.Z'\h͢C7ӦE=VuLn~}0̷n3J}nv,eL~*5|8o§ Xl{o8,ی\%6=纹lc>`,gqb`i A7e}N6mפ Lb?|r~rEP߷@j1?rC;*wNۍ\aZC17 \ITU̹+2so_<9K}%&8Uu1DK3m7 *M;a/&d'}Qw]z11\5Xן_l{ _zk\@Kmq}[! sj8^Uxې+nUF ibnʝvVi'ܑucN"yj~}eד'X>Tͻqa6#j~68_RO\<}S{,稿1>WG}ͧ-{oH ~JvO˲9mO>6xI 9ю&d7`d"g9^u/>9ұTG7A0s;6_jCJ?W$|̩O}>$҇yC$:s7g5xcgE>ToCoM옚ģʺ#v|cK\ۇߺ:eյkQ|Kh|~Y_hpup]\ԟ= [նu^#}_C ae]eIM 7c%+ tIsnRJB_mTtArQϏi+aW~#x3ʅRblq1Cz7Q8ι1>m#sޕE1o~1̷̏ ngx|ml9.0[]g]b[Eu9ݧ(] ȘD]ԟv!UMB!7Yx|𔚔+b({,)]Gevn>ԙʦc}E}s4͟?n1uv}iU}s~%#[T)x:Wcg%p*?sxœQD]]T h>7oB]wJ0G5O ^>Q=i9 4rO tl)ag_EC.>Y}!_@_MSƩox|LmC_p.8]G_xܹ7)ӌR*nl77sܶYonr>dE[>MOρ뉾gVi4:rnVaxЮ'~gB%p4EU~Ҽqj|!6)'ܯoi^SyƎo qι++ [4>"ݧO'B6M.囲??)RO;_\X_ {xu\"3 8?tt.oN`4Y;srS}>ggf.u}+pra+?o[NǓGs-/X:_ϫq)6?DoP>q=1ܪt'ĝ3Pm-z0x AA B ?2ndԃGd!CY``qsUM~1#p F??a4t @Gt @G+'߄w˷zA@;g~QAVqKBn0Oc1u'ZgDB`6|U/?e< M&OdUx&4x O"f+{:XgOr~~;pmcB/5f~ ^S<10x3K[55fS<0x%k Oo߰f~'-w@L 3~ӦY&Uml-9 t BÊo`2x @F8ڿ'"_0x0x0xa So\&e;\LO&d<; (KG"74u5y3-?nec7#!i~ҿٿw?,, `JM7 ? =_d DޅȥX0鉤:Br:zaӦ_Cc*\߂ ^姹K;h\`U0AnS*&Խ18n=:h|:~,Ĕ,ܐYABSq_0 `* %;Q5>H=1 ._Gp Cg>ɸ/tqU0/AV:v?${! ^?\IҦ磇*+-mo'峂ƟIUe:=}oy'|>c<3b~_6 rDBn?`&_5\DD)c&70xŎ^`A?wy~8qJ>+:G|u  ^ 勒m$"#YXTRs\ڳXS۶z,S`@v_15G1Oj9ο[^$QO] g\n:`e0Y .r-Q/tMXd:H^ϟ&97VߐSޗQOu u׷6ss  ]m|74xe 1[頋Gw<%gʉAS7?I<󧇿o F!?1ͱ9ƠuUƹ2W˷oq;LdfuyYyEyVg0x |60xOϟ& dZAwy_NG!?1?6?UQmx @0x |,L*G]Ǯ'9ra{:.Oh4> 6@OW#aOFi3̧AWi0||GAb߀9p@`*vYc@HO$ؘc*8=}˺۝&Cdb|qh3鸚2$Cl"i^<h +|#7Y$ cSœAǮ6X$=1rI=>|q({+ kOin=důU*xJs&Gfh{ -_S.ӻc~\q[r͡5ZGkp|np~^j!`Zd2@-]Ė7B!? EGslq|/0>30G3V`;pN-}`Dt @Gt @Gt @Gt @Gt @Gt @Gt @Gt @Gt @Gt @Gt @Gt @Gt @G+7=D-|%{7Hۋtsb} 3xJ5ZB1caM.KMęl5yٻyC/?reLOO{O ]PUC1ej\#TO1{e WQF,YfAL6'b\ctV]ڔy4LY63fvu) p9`CenOwc<=,3| .al|OLw*xc;xa46J)/AL^f&yw*àƬxl٬.)^y3x26Rg2 \`+W<,|0x߉KT$]co<<ce}[25y.bz*-7q&Ai7P֖%#uF{smF<(`uV>vs' ٢7xu܇|o]Y'” (oR4[~\bUǘ H7#)ϼNFeS3$¥d͵irĠ/+ACk&V7Zq\9xS/uGpD|A=>؞xR)9:>hscv!o;onu-EwB7giksJMޯADo5h97U{ Ĥz>|HL ]> \d(dLBZǨs8PC ivD9uί}n3`*v07&YDڢF9 tPۅ\7WW+dT|%8;nlwx-e=y W,h uPo/t>4Y=]V ȷ>ޓϟy7Ǹ_3v='?FݯcO$Q'7V\7bYKC0`0x |($tG2J&N)O1a7z+ש_NL岉8\6zu}|Q*U.v,EQws 蹞usAy@t[=c/B}?+/bN96ÜoPWݘnkǣq??r>Ƚن&- 6ޅcΧ:UF+wLy=f2tΓ]rte}Cܪ `[(Wϑ#ߟBzyƫw>痻~!vr^5!*&Y\(Cԅ7@0x 0'&sC)/t⤟>=&n5ɗ *L8Isd:Ɗy1JeY6GꨜH/h q9/+|񸶻1Ȝ 9߫t:v\zs fdRlӝs#Tyɧ:X!g͑:| 5 G·u*Ŏ1IM|!e!)4I> .˿g {sŮb=w54a/]geìCSipΉq}p9\98}+Ɨ?WK8W}bbLn⑞u'Bl>_']XTN+徠\u8ױ1aRuQ|y_]R䵶zǷ&CoC//*ni^֯yasT<1p_t>eDפ*5Tݿݛ%wIDžXri#W9wubg{ھ_H}8t[9Ǟ5{αt|8 ̇:\y?#fu;,7*罞fka.jŔH\}۬zm۱/&E 2IϨw^ ͑ #p3/zc k ǀX\ g?|P*9۰ԞsىWol927|cJlM\׉m^3YoZLUrM ]Ww*71#t\}{9dk0:d>TgQ7I+~݆>ls08YnW (˻N ݾ\hc$F__?e_͹Xu他+߸cʈ-arqx]H?b秮c\,༶~W~e+0|+կǢr˦s.|}W>4w ]~n ظn$=6V:ꎿBẁZʛ|G] ߦ7ꜘ9oބ]qQ8[TYS76BUNu}1=?̶u>? Ì?::zȵ)~TŘ_EjEU/uK]lTFֺXcZKn郙Ci|\ҷ/~|:=D_󹒏Qo1US4* K ]u*'ㄌ-P+K)1j兪Y>m{+/dǼ}Ƿ;l5D!熎✏ov_ה,,[!o0:&{P2ncQ9͝yc[5_u[|;{Yuc+7_=K˧_&bvi qB?3&sW˷N>!'>%|h5'~Nd:FỈ)4jSdB婰ϵ-Aն._(csE0xԧ {N~N@CbezԘZϦU7!pGfuo$Z*?|Δ_s:c-[h|]R<_{S;1s%9o01w&7!(_?qkG?#x>8ɯEs[fzmv> Һkgm;~i_Su?bԌu@-ܱ)g\cX͸g'4\6\yc#Mr8g>1b7u뼾ϴus"î.`߸7:BͿ^ f/Wð ذJ?hX7maO`jՐ/u2,؎_a8JӁv!A_@+z`E'pt @Gt @Gt @Gt @Gt @Gt @Gt @Gt @Gt @Gt @Gt @Gt ^S- `5#)<10xMk F ^S0x0btM.N Xs6'x&S8ym M4/1cA1f;m&#Uvtde:1xb&xc< ^΄_‚'xM1f2>o}1o/Q#x~J``5#F&߮U^N^a*jDfY0DŽ.L}TWԒ%KԵ^6n3y<9& ʕ+mݦnfu׫뮻Nxj͚5СC'x#FmݺUm۶MmٲEk߾}'蕓<rf;vv>6w3`3xQMޮ]'|6w<9&LC=T0w4l͝l˚1xs@3Mid;` ދ/^ID<9$7x~ '-O0xs@n]nR{z<9 7x*V<9 7x>;,` ˘% <1aÔ˛jd!B!-ial52x?cSOC+WKj]})kS27d\A!BNO?tral52xN C=^?s/+"$DB!7sk͖|Jq\V# / Mf%Y\s,S[E[Zv=c-gP]w]s!B?<ӫ%1v[fg K/Tt̵}I-[$N ,tvؽlnAB!>$oס25 yC/s‹!BP[0xd$D!fP 5xoYz+wd檳kzsϿC!BM[nUޛMa%]em߿zӅ/u^/'}>>??Pg㓟n7ߨ_h窳kz>1w>'<B!*Ӵ&-WeLsluI@ K/|)u̙;Ww.R?ruM7^}U}ӟnɏVZ ή3#gbB!PLӖ~&o m}s~k֨[7\Tm[v93I&ή B!4mտ4ycm\S~ /X2sjཝUgׄC!BMdkL<'>/iK/P2sJɗ0x!B\>𙼱5x'?%ORc%6s?|r[n}qg]!B5/C)yb6x_/U}_W_~9/PtG3xaB!}ԾE<ȵ^?|կ~U}[RO~ ^zdΞ{r[n)w!B5]xjҥjý}cO^Md~W'Kz=r\=r80x!B\C;v`یMލ7ިVZ #I?fm}ή B!ejm7x{"|vv 畓ꎕ+վ{աC㉙ܶoSs'{o3Mޮ]_|I=ꡇQ`uզ }UO$r[^db\uvMN=焞<B!*Ӵo4w" 4y"y*kK@ /j=u}ɓ'b욎sR;v\9!B4mI3w" !kUYS2bCgy{<B!*۴F܉0x-H@ <9r^#tj߾CNcB!P\-D$ GWV{PGծ]^u}cB!P\-D6x+$I\nK5b ry1b =WB!`Sh]tɓjÆ mu;w&?B!Bbڔm:c\E!B+ךqral52x!B!FO<B!0x!B!aB!B#!B!PGC!BB!B!B!:" B!BuD%B!BhgB!B-tQ9*JKIENDB`credentials/build/0000755000175000017500000000000014151136052013776 5ustar nileshnileshcredentials/build/vignette.rds0000644000175000017500000000033714151136052016340 0ustar nileshnileshmP_ 0ߦQ^zK$DC-9ٙ:?vB @`KP\2 [՞)I4R8HCZpdyEiWSVeF;ݕA\6QM(ަ ץ0i2ޣ4s>ĉg:nXӀ؇sVه^4[/.|credentials/R/0000755000175000017500000000000014151130430013072 5ustar nileshnileshcredentials/R/onattach.R0000644000175000017500000000470014151130430015017 0ustar nileshnilesh# We load 'askpass' for its side effects, see below! #' @importFrom askpass ssh_askpass .onLoad <- function(libname, pkgname){ # Loading askpass automatically sets SSH_ASKPASS and GIT_ASKPASS variables askpass::ssh_askpass() # Note: isatty(stdin()) = TRUE in Windows RGui if(is_windows() || !interactive() || !isatty(stdin())){ if(is.na(Sys.getenv('GIT_TERMINAL_PROMPT', NA))){ Sys.setenv(GIT_TERMINAL_PROMPT=0) } } # Start ssh-agent if available but not running if(is_windows() && is.na(Sys.getenv('SSH_AGENT_PID', NA)) && cmd_exists('start-ssh-agent')){ # Agent not work in Windows: https://github.com/libgit2/libgit2/issues/4958 #ssh_agent_start() } # If no credential helper has been set, use the 'cache' helper if(!is_check()) set_default_cred_helper() } .onAttach <- function(libname, pkgname){ tryCatch({ gitver <- git_with_sys("--version", NULL, FALSE) packageStartupMessage(sprintf("Found %s", gitver)) helpers <- sub("^credential-", "", credential_helper_list()) packageStartupMessage(sprintf("Supported HTTPS credential helpers: %s", paste(helpers, collapse = ", "))) }, error = function(e){ if(is_windows()){ packageStartupMessage("Git for Windows is not installed.\nDownload from: https://git-scm.com/download/win") } else { packageStartupMessage("Unable to find git :-(") } }) tryCatch({ sshver <- ssh_version() packageStartupMessage(sprintf("Found %s", sshver)) tryCatch({ key <- find_ssh_key() if(length(key)){ packageStartupMessage(sprintf("Default SSH key: %s", key)) } else { packageStartupMessage("No key found. Use ssh_keygen() to generate one!") } }, error = function(e){ packageStartupMessage("Failed to lookup key file") }) }, error = function(e){ packageStartupMessage(e$message) }) #agent_output <- ssh_agent_start() #if(length(agent_output) && nchar(agent_output)){ # packageStartupMessage(trimws(agent_output)) #} } set_default_cred_helper <- function(){ if(has_git_cmd()){ invisible(tryCatch({ credential_helper_get() }, error = function(...){ tryCatch({ helper <- credential_helper_list()[1] credential_helper_set(helper, global = TRUE) }, error = function(e){ packageStartupMessage(e$message) }) })) } } is_check <- function(){ grepl('credentials.Rcheck', getwd(), fixed = TRUE) } credentials/R/http-credential.R0000644000175000017500000000427014151130430016307 0ustar nileshnilesh#' Load and store git HTTPS credentials #' #' This requires you have the `git` command line program installed.The #' [git_credential_ask] function looks up a suitable username/password #' from the [`git-credential` store](https://git-scm.com/docs/gitcredentials). #' If none are available it will prompt the user for credentials which #' may be saved the store. On subsequent calls for the same URL, the #' function will then return the stored credentials without prompting #' the user. #' #' The appearance and security policy of the credential store depends #' on your version of git, your operating system, your R frontend and #' which [credential_helper] is used. On Windows and MacOS the credentials #' are stored in the system password manager by default. #' #' It should be assumed that reading credentials always involves user #' interaction. The user may be asked to unlock the system keychain or #' enter new credentials. In reality, user interaction is usually only #' required on the first authentication attempt, but the security policy #' of most credential helpers prevent you from programmatically testing #' if the credentials are already unlocked. #' #' @export #' @family credentials #' @rdname http_credentials #' @name http_credentials #' @aliases credentials #' @param url target url, possibly including username or path #' @param save in case the user is prompted for credentials, attempt to #' remember them. #' @param verbose print errors from `git credential` to stdout git_credential_ask <- function(url = "https://github.com", save = TRUE, verbose = TRUE){ cred <- parse_url(url) out <- credential_fill(cred = cred, verbose = verbose) if(isTRUE(save) && length(out) && length(out$password) && !is.na(out$password)) credential_approve(out, verbose = verbose) out } #' @export #' @rdname http_credentials git_credential_update <- function(url = "https://github.com", verbose = TRUE){ cred <- parse_url(url) credential_reject(cred) out <- credential_fill(cred = cred, verbose = verbose) credential_approve(out) } #' @export #' @rdname http_credentials git_credential_forget <- function(url = "https://github.com", verbose = TRUE){ cred <- parse_url(url) credential_reject(cred) } credentials/R/ssh-keys.R0000644000175000017500000002056514151130430014773 0ustar nileshnilesh#' Managing Your SSH Key #' #' Utility functions to find or generate your SSH key for use with git remotes #' or other ssh servers. #' #' Use [ssh_key_info()] to find the appropriate key file on your system to connect with a #' given target host. In most cases this will simply be `ssh_home('id_rsa')` unless #' you have configured ssh to use specific keys for specific hosts. #' #' To use your key to authenticate with GitHub, copy the pubkey from `ssh_key_info()` to #' your profile: \url{https://github.com/settings/ssh/new}. #' #' If this is the first time you use ssh, [ssh_keygen] can help generate a key and #' save it in the default location. This will also automatically opens the above Github #' page in your browser where you can add the key to your profile. #' #' `ssh_read_key` reads a private key and caches the result (in memory) for the #' duration of the R session. This prevents having to enter the key passphrase many #' times. Only use this if `ssh-agent` is not available (i.e. Windows) #' #' @export #' @family credentials #' @rdname ssh_credentials #' @name ssh_credentials #' @param host target host (only matters if you have configured specific keys per host) #' @param auto_keygen if `TRUE` automatically generates a key if none exists yet. #' Default `NA` is to prompt the user what to. ssh_key_info <- function(host = NULL, auto_keygen = NA){ keyfile <- find_ssh_key(host = host) if(is.null(keyfile)){ if(isTRUE(auto_keygen) || (is.na(auto_keygen) && ask_user("No SSH key found. Generate one now?"))){ keyfile <- ssh_home('id_rsa') ssh_keygen(keyfile) } else { stop(sprintf("Failed to find ssh key file for %s", host)) } } pubfile <- paste0(keyfile, ".pub") if(!file.exists(pubfile)){ key <- ssh_read_key(keyfile) try(openssl::write_ssh(key$pubkey, pubfile), silent = TRUE) } list( key = keyfile, pubkey = openssl::write_ssh(openssl::read_pubkey(pubfile)) ) } #' @export #' @rdname ssh_credentials #' @param file destination path of the private key. For the public key, `.pub` #' is appended to the filename. #' @importFrom openssl write_ssh write_pem read_key write_pkcs1 read_pubkey ssh_keygen <- function(file = ssh_home('id_rsa')){ private_key <- normalizePath(file, mustWork = FALSE) pubkey_path <- paste0(private_key, ".pub") if(file.exists(private_key)){ cat(sprintf("Found existing RSA keyspair at: %s\n", private_key), file = stderr()) pubkey <- if(file.exists(pubkey_path)){ read_pubkey(pubkey_path) } else { ssh_read_key(private_key)$pubkey } } else { cat(sprintf("Generating new RSA keyspair at: %s\n", private_key), file = stderr()) key <- openssl::rsa_keygen() pubkey <- key$pubkey dir.create(dirname(private_key), showWarnings = FALSE) write_pkcs1(key, private_key) write_ssh(pubkey, pubkey_path) Sys.chmod(private_key, "0600") } # See https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/ conf_file <- file.path(dirname(private_key), 'config') if(is_macos() && !file.exists(conf_file)){ writeLines(c('Host *', ' AddKeysToAgent yes', ' UseKeychain yes', paste(' IdentityFile ', private_key)), con = conf_file) } list( key = private_key, pubkey = write_ssh(pubkey) ) } #' @rdname ssh_credentials #' @export ssh_setup_github <- function(){ info <- ssh_key_info() cat("Your public key:\n\n", info$pubkey, "\n\n", file = stderr()) cat("Please copy the line above to GitHub: https://github.com/settings/ssh/new\n", file = stderr()) if(interactive() && ask_user('Would you like to open a browser now?')){ utils::browseURL('https://github.com/settings/ssh/new') } } #' @export #' @rdname ssh_credentials ssh_home <- function(file = NULL){ if(length(file)){ normalizePath(file.path(normalize_home("~/.ssh"), file), mustWork = FALSE) } else { normalize_home("~/.ssh") } } #' @export #' @rdname ssh_credentials ssh_agent_add <- function(file = NULL){ if(is.na(Sys.getenv('SSH_AUTH_SOCK', NA))){ stop("ssh-agent is not running. If this is a server ssh in with: ssh -o 'ForwardAgent=yes'") } sys::exec_wait('ssh-add', as.character(file)) == 0 } # Only used on Windows for now ssh_agent_start <- function(){ if(is_windows()){ out <- sys::exec_internal('cmd', c('/C', 'start-ssh-agent 1>&2 && set'), error = FALSE) if(out$status != 0){ warning("Failed to start ssh-agent", call. = FALSE) } else{ con <- rawConnection(out$stdout) on.exit(close(con)) vars <- readLines(con) vars <- vars[grepl('^SSH_(AGENT|AUTH)', vars)] if(length(vars)){ tmp <- tempfile() writeLines(vars, tmp) readRenviron(tmp) } } return(rawToChar(out$stderr)) } } find_ssh_key <- function(host = NULL){ if(!length(host)) host <- "*" key_paths <- tryCatch(ssh_identityfiles(host = host), error = function(e){ message(e$message) file.path("~/.ssh", c("id_rsa", "id_dsa", "id_ecdsa", "id_ed25519", "id_xmss")) }) key_paths <- normalize_home(key_paths) for(i in key_paths){ if(file.exists(i)) return(i) } return(NULL) } ssh_identityfiles <- function(host){ # Note there can be multiple 'identityfile' entries conf <- ssh_config(host = host) unique(unlist(conf[names(conf) == 'identityfile'])) } # Old SSH versions (Trusty, CentOS) do not support ssh -G ssh_config <- function(host){ ssh <- find_ssh_cmd() out <- sys::exec_internal(ssh, c("-G", host), error = FALSE) if(!identical(out$status, 0L)) stop("Could not read ssh config. Using default settings.", call. = FALSE) txt <- strsplit(rawToChar(out$stdout), "\r?\n")[[1]] lines <- strsplit(txt, " ", fixed = TRUE) names <- vapply(lines, `[`, character(1), 1) values <- lapply(lines, `[`, -1) structure(values, names = names) } ssh_version <- function(){ ssh <- find_ssh_cmd() out <- sys::exec_internal(ssh, "-V") # ssh may print to stderr instead of stdout trimws(rawToChar(c(out$stdout, out$stderr))) } find_ssh_cmd <- function(ssh = getOption("ssh", "ssh")){ if(cmd_exists(ssh)) return(ssh) if(is_windows()){ # Try asking 'git.exe' where it looks for 'ssh.exe try({ gitssh <- git_with_sys(c("-c", "alias.sh=!sh", "sh", "-c", "cygpath -m $(which ssh)")) if(nchar(gitssh) && cmd_exists(gitssh)) return(Sys.which(gitssh)) }, silent = TRUE) # Fallback: try to find ssh.exe ourselves in the usual places try({ bin <- dirname(find_git_cmd()) usrbin <- file.path(dirname(bin), "usr", "bin") path <- Sys.getenv('PATH') on.exit(Sys.setenv(PATH = path)) Sys.setenv(PATH = paste(path, bin, usrbin, sep = .Platform$path.sep)) if(cmd_exists(ssh)) return(Sys.which(ssh)) }, silent = TRUE) } stop(sprintf("No '%s' command found. Using default ssh settings.", ssh), call. = FALSE) } normalize_home <- function(path = NULL){ path <- as.character(path) if(is_windows()){ homedir <- Sys.getenv('USERPROFILE') is_home <- grepl("^~", path) path[is_home] <- paste0(homedir, substring(path[is_home], 2)) } normalizePath(path, mustWork = FALSE) } ask_user <- function(str){ if(!interactive()) return(FALSE) return(utils::menu(c("Yes", "No"), title = str) == 1) } #' @export #' @rdname ssh_credentials ssh_update_passphrase <- function(file = ssh_home("id_rsa")){ key <- openssl::read_key(file, password = function(x){ askpass::askpass("Please enter your _current_ passphrase") }) new1 <- askpass::askpass("Enter the _new_ passphrase") new2 <- askpass::askpass("To confirm, the your _new_ passphrase again") if(identical(new1, new2)){ openssl::write_pkcs1(key, path = file, password = new1) } else { stop("Entered passhprases are not identical") } message("Passphrase has been updated!") # Wipe the key cache just in case environment(ssh_read_key)$store = new.env(parent = emptyenv()) } #' @export #' @rdname ssh_credentials #' @param password a passphrase or callback function #' @importFrom askpass askpass ssh_read_key <- local({ store = new.env(parent = emptyenv()) function (file = ssh_home("id_rsa"), password = askpass){ file <- normalizePath(file, mustWork = TRUE) hash <- openssl::md5(file) if(!length(store[[hash]])){ store[[hash]] <- tryCatch(openssl::read_key(file, password = password), error = function(e){ stop(sprintf("Unable to load key: %s", file), call. = FALSE) }) } return(store[[hash]]) } }) credentials/R/credential-helpers.R0000644000175000017500000000230514151130430016767 0ustar nileshnilesh#' Credential Helpers #' #' Git supports several back-end stores for HTTPS credentials called #' helpers. Default helpers include `cache` and `store`, see the #' [git-credentials](https://git-scm.com/docs/gitcredentials) manual #' page for details. #' #' @export #' @rdname credential_helper #' @name credential_helper credential_helper_list <- function(){ text <- git_with_sys(c("help", "-a")) m <- gregexpr("credential-[^ \t]+", text) regmatches(text, m)[[1]] } #' @export #' @rdname credential_helper #' @name credential_helper #' @param global if FALSE the setting is done per git repository, if #' TRUE it is in your global user git configuration. credential_helper_get <- function(global = FALSE){ git <- find_git_cmd() args <- c("config", if(global) "--global", "credential.helper") git_with_sys(args) } #' @export #' @rdname credential_helper #' @name credential_helper #' @param helper string with one of the supported helpers from [credential_helper_list] credential_helper_set <- function(helper, global = FALSE){ helper <- sub("^credential-", "", helper) args <- c("config", if(global) "--global", "credential.helper", helper) git_with_sys(args) credential_helper_get(global = global) } credentials/R/credential-api.R0000644000175000017500000001377414151130430016112 0ustar nileshnilesh#' Retrieve and store git HTTPS credentials #' #' Low-level wrappers for the [git-credential](https://git-scm.com/docs/git-credential) #' command line tool. Try the user-friendly [git_credential_ask] #' and [git_credential_update] functions first. #' #' The [credential_fill] function looks up credentials for a given host, and #' if none exists it will attempt to prompt the user for new credentials. Upon #' success it returns a list with the same `protocol` and `host` fields as the #' `cred` input, and additional `username` and `password` fields. #' #' After you have tried to authenticate the provided credentials, you can report #' back if the credentials were valid or not. Call [credential_approve] and #' [credential_reject] with the `cred` that was returned by [credential_fill] #' in order to validate or invalidate a credential from the store. #' #' Because git credential interacts with the system password manager, the appearance #' of the prompts vary by OS and R frontend. Note that [credential_fill] should #' only be used interactively, because it may require the user to enter credentials #' or unlock the system keychain. On the other hand [credential_approve] and #' [credential_reject] are non-interactive and could be used to save or delete #' credentials in a scripted program. However note that some credential helpers #' (e.g. on Windows) have additional security restrictions that limit use of #' [credential_approve] and [credential_reject] to credentials that were actually #' entered by the user via [credential_fill]. Here it is not possible at all to #' update the credential store without user interaction. #' #' @export #' @rdname credential_api #' @name credential_api #' @param cred named list with at least fields `protocol` and `host` and #' optionally also `path`, `username` ,`password`. #' @param verbose emit some useful output about what is happening #' @examples \donttest{ #' # Insert example cred #' example <- list(protocol = "https", host = "example.org", #' username = "test", password = "secret") #' credential_approve(example) #' #' # Retrieve it from the store #' cred <- credential_fill(list(protocol = "https", host = "example.org", path = "/foo")) #' print(cred) #' #' # Delete it #' credential_reject(cred) #' } credential_fill <- function(cred, verbose = TRUE){ out <- credential_exec("fill", cred = cred, verbose = verbose) data <- strsplit(out, "=", fixed = TRUE) key <- vapply(data, `[`, character(1), 1) val <- vapply(data, `[`, character(1), 2) structure(as.list(structure(val, names = key)), class = 'git_credential') } #' @export #' @rdname credential_api #' @name credential_api credential_approve <- function(cred, verbose = TRUE){ credential_exec("approve", cred = cred, verbose = verbose) invisible() } #' @export #' @rdname credential_api #' @name credential_api credential_reject <- function(cred, verbose = TRUE){ credential_exec("reject", cred = cred, verbose = verbose) invisible() } credential_exec <- function(command, cred, verbose){ input <- cred_to_input(cred) on.exit(unlink(input)) if(is_windows() || is_macos() || !isatty(stdin())){ text <- git_with_sys(c("credential", command), input = input, verbose = verbose) strsplit(text, "\n", fixed = TRUE)[[1]] } else { # base::system can freeze RStudio Desktop or Windows git_with_base(c("credential", command), input = input, verbose = verbose) } } git_with_base <- function(command, input = "", verbose = TRUE){ git <- find_git_cmd() res <- system2(git, command, stdin = input, stdout = TRUE, stderr = ifelse(isTRUE(verbose), "", TRUE)) status <- attr(res, "status") if(length(status) && !identical(status, 0L)){ stop(paste(res, collapse = "\n")) } res } git_with_sys <- function(command, input = NULL, verbose = TRUE){ git <- find_git_cmd() outcon <- rawConnection(raw(0), "r+") on.exit(close(outcon), add = TRUE) timeout <- ifelse(interactive(), 120, 10) status <- sys::exec_wait(git, command, std_out = outcon, std_err = verbose, std_in = input, timeout = timeout) if(!identical(status, 0L)){ stop(sprintf("Failed to call 'git %s'", paste(command, collapse = " ")), call. = FALSE) } trimws(rawToChar(rawConnectionValue(outcon))) } find_git_cmd <- function(git = getOption("git", "git"), error = TRUE){ if(cmd_exists(git)){ return(git) } if(is_windows()){ locations <- c("C:\\PROGRA~1\\Git\\cmd\\git.exe", "C:\\Program Files\\Git\\cmd\\git.exe") for(i in locations){ if(cmd_exists(i)){ return(i) } } } if(error){ stop(sprintf("Could not find the '%s' command line util", git), call. = FALSE) } } has_git_cmd <- function(){ !is.null(find_git_cmd(error = FALSE)) } parse_url <- function(url, allow_ssh = TRUE){ out <- strsplit(url, "://", fixed = TRUE)[[1]] if(length(out) < 2){ if(!isTRUE(allow_ssh)){ stop(sprintf("URL must start with e.g. https:// (found %s)", url)) } else { protocol = 'ssh' rest = url } } else { protocol <- out[1] rest <- out[2] } password <- NULL username <- if(grepl("^[^/]+@", rest)){ auth <- strsplit(rest, "@", fixed = TRUE)[[1]] rest <- paste(auth[-1], collapse = "@") password <- if(grepl(":", auth[1], fixed = TRUE)){ auth <- strsplit(auth[1], ":", fixed = TRUE)[[1]] paste(auth[-1], collapse = ":") } auth[1] } rest <- strsplit(rest, "/", fixed = TRUE)[[1]] host <- rest[1] path <- if(length(rest) > 1){ paste(rest[-1], collapse = "/") } c( username = username, password = password, protocol = protocol, host = host, path = path ) } cred_to_input <- function(data, input = tempfile()){ str <- paste(names(data), as.character(data), collapse = "\n", sep = "=") writeBin(charToRaw(sprintf("%s\n", str)), con = input) return(input) } cmd_exists <- function(cmd){ nchar(Sys.which(cmd)) > 0 } is_windows <- function(){ identical(.Platform$OS.type, "windows") } is_macos <- function(){ identical(tolower(Sys.info()[['sysname']]), "darwin") } credentials/R/github-pat.R0000644000175000017500000000635414151130430015271 0ustar nileshnilesh#' Set your Github Personal Access Token #' #' Populates the `GITHUB_PAT` environment variable using the [git_credential][http_credentials] #' manager, which `git` itself uses for storing passwords. The credential manager #' returns stored credentials if available, and securely prompt the user for #' credentials when needed. #' #' Packages that require a `GITHUB_PAT` can call this function to automatically #' set the `GITHUB_PAT` when needed. Users may call this function in their #' [.Rprofile][Startup] script to automatically set `GITHUB_PAT` for each R #' session without hardcoding any tokens on disk in plain-text. #' #' @export #' @param force_new forget existing pat, always ask for new one. #' @param validate checks with the github API that this token works. Defaults to #' `TRUE` only in an interactive R session (not when running e.g. CMD check). #' @param verbose prints a message showing the credential helper and PAT owner. #' @return Returns `TRUE` if a valid GITHUB_PAT was set, and FALSE if not. set_github_pat <- function(force_new = FALSE, validate = interactive(), verbose = validate){ pat_user <- Sys.getenv("GITHUB_PAT_USER", 'PersonalAccessToken') pat_url <- sprintf('https://%s@github.com', pat_user) if(isTRUE(force_new)) git_credential_forget(pat_url) if(isTRUE(verbose)) message("If prompted for GitHub credentials, enter your PAT in the password field") askpass <- Sys.getenv('GIT_ASKPASS') if(nchar(askpass)){ # Hack to override prompt sentence to say "Token" instead of "Password" Sys.setenv(GIT_ASKTOKEN = askpass) Sys.setenv(GIT_ASKPASS = system.file('ask_token.sh', package = 'credentials', mustWork = TRUE)) Sys.setenv(GIT_ASKTOKEN_NAME = 'Personal Access Token (PAT)') on.exit(Sys.setenv(GIT_ASKPASS = askpass), add = TRUE) on.exit(Sys.unsetenv('GIT_ASKTOKEN_NAME'), add = TRUE) on.exit(Sys.unsetenv('GIT_ASKTOKEN'), add = TRUE) } for(i in 1:3){ # The username doesn't have to be real, Github seems to ignore username for PATs cred <- git_credential_ask(pat_url, verbose = verbose) if(length(cred$password)){ if(nchar(cred$password) < 40){ message("Please enter a token in the password field, not your master password! Let's try again :-)") message("To generate a new token, visit: https://github.com/settings/tokens") credential_reject(cred) next } if(isTRUE(validate)) { hx <- curl::handle_setheaders(curl::new_handle(), Authorization = paste("token", cred$password)) req <- curl::curl_fetch_memory("https://api.github.com/user", handle = hx) if(req$status_code >= 400){ message("Authentication failed. Token invalid.") credential_reject(cred) next } if(verbose == TRUE){ data <- jsonlite::fromJSON(rawToChar(req$content)) helper <- tryCatch(credential_helper_get()[1], error = function(e){"??"}) message(sprintf("Using GITHUB_PAT from %s (credential helper: %s)", data$name, helper)) } } return(Sys.setenv(GITHUB_PAT = cred$password)) } } if(verbose == TRUE){ message("Failed to find a valid GITHUB_PAT after 3 attempts") } return(FALSE) } message <- function(...){ base::message(...) utils::flush.console() } credentials/NEWS0000644000175000017500000000142514151135716013406 0ustar nileshnilesh1.3.2 - Disable example in vignette that would prompt for user input on Windows 1.3.1 ` - Set permission to user-read only for generated private keys 1.3.0 - PATs stored with set_github_pat() are now stored under username 'PersonalAccessToken' to match the new Git Credential Manager Core behavior. You may have to re-enter your PAT. - Small UX improvements. 1.2.1 - Quote path to $GIT_ASKTOKEN, for example when credentials is installed in "Program Files" - Fix for set_github_pat() token prompt in R.app on MacOS 1.2.0 - Bug fixes and tweaks for set_github_pat(): gains parameters to disable validation 1.1 - Fix for vignette when no 'git' is installed - Fix for vignette when credential helper requires user interaction (CRAN MAC) 1.0 - Initial CRAN release credentials/LICENSE0000644000175000017500000000005114151130430013672 0ustar nileshnileshYEAR: 2020 COPYRIGHT HOLDER: Jeroen Ooms credentials/inst/0000755000175000017500000000000014151136052013654 5ustar nileshnileshcredentials/inst/WORDLIST0000644000175000017500000000016614151130430015043 0ustar nileshnileshapi AppVeyor frontend Github hardcoding HTTPS keychain MacOS openssl PATs programmatically pubkey repo RStudio stdout credentials/inst/doc/0000755000175000017500000000000014151136052014421 5ustar nileshnileshcredentials/inst/doc/intro.R0000644000175000017500000000353414151136051015703 0ustar nileshnilesh## ---- echo=FALSE-------------------------------------------------------------- has_git <- credentials:::has_git_cmd() && .Platform$OS.type != 'windows' delete_git_config_on_exit <- !file.exists('~/.gitconfig') credentials:::set_default_cred_helper() library <- function(package){ withCallingHandlers(base::library(credentials), packageStartupMessage = function(e) { cat(e$message) invokeRestart("muffleMessage") }) } ## ----------------------------------------------------------------------------- library(credentials) ## ---- eval=has_git------------------------------------------------------------ credential_helper_get() ## ---- echo=FALSE, eval=has_git------------------------------------------------ # This hack may not work on MacOS server where cred helper is osxkeychain # which always requires user interaction. Hence error=TRUE in the next block. example <- list(protocol = "https", host = "example.com", username = "jeroen", password = "supersecret") credential_approve(example) ## ----error=TRUE, eval=has_git------------------------------------------------- library(credentials) git_credential_ask('https://example.com') ## ---- echo=FALSE, eval=has_git------------------------------------------------ credential_reject(list(protocol = "https", host = "example.com")) ## ---- echo = FALSE------------------------------------------------------------ ssh_key_info <- function(){ try({ out <- credentials:::ssh_key_info(auto_keygen = FALSE) out$pubkey = paste(substring(out$pubkey, 1, 80), "...") out }) } ## ----------------------------------------------------------------------------- ssh_key_info() ## ----echo=FALSE--------------------------------------------------------------- if(isTRUE(delete_git_config_on_exit)) unlink("~/.gitconfig") credentials/inst/doc/intro.Rmd0000644000175000017500000001715214151135004016222 0ustar nileshnilesh--- title: "Managing SSH and Git Credentials in R" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Managing SSH and Git Credentials in R} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- The `credentials` package contains tools for configuring and retrieving SSH and HTTPS credentials for use with `git` or other services. It helps users to setup their git installation, and also provides a back-end for packages to authenticate with existing user credentials. ## Two types of remotes ```{r, echo=FALSE} has_git <- credentials:::has_git_cmd() && .Platform$OS.type != 'windows' delete_git_config_on_exit <- !file.exists('~/.gitconfig') credentials:::set_default_cred_helper() library <- function(package){ withCallingHandlers(base::library(credentials), packageStartupMessage = function(e) { cat(e$message) invokeRestart("muffleMessage") }) } ``` ```{r} library(credentials) ``` Git supports two types of remotes: SSH and HTTPS. These two use completely distinct authentication systems. | | SSH REMOTES | HTTPS REMOTES | |---------|------------------|------------------------| | __url__ | `git@server.com` | `https://server.com` | | __authentication__ | Personal SSH key | Password or PAT | | __stored in__ | file `id_rsa` or `ssh-agent` | `git credential` store | For HTTPS remotes, git authenticates with a username + password. With GitHub, instead of a password, you can also use a [Personal Access Token](https://github.com/settings/tokens) (PAT). PATs are preferred because they provide more granular control of permissions (via the PAT's scopes), you can have many of them for different purposes, you can give them informative names, and they can be selectively revoked. Note that, if you use 2FA with GitHub, you must authenticate with a PAT if you use the HTTPS protocol. For SSH remotes, git shells out to `ssh` on your system, and lets `ssh` take care of authentication. This means you have to setup an ssh key (usually `~/.ssh/id_rsa`) which you then [add to your git profile](https://github.com/settings/ssh/new). ### Special note for Windows Windows does not include a native `git` installation by default. We recommended to use the latest version of [Git for Windows](https://git-scm.com/download/win). This bundle also includes `ssh` and [git credential manager for windows](https://github.com/Microsoft/Git-Credential-Manager-for-Windows) which is all you need. Important: ssh keys are stored in your home directory for example: `C:\Users\Jeroen\.ssh\id_rsa`, and __not in the Documents folder__ (which is what R treats as the home sometimes). The `ssh_home()` function shows the correct `.ssh` directory on all platforms. ## Part 1: Storing HTTPS credentials HTTPS remotes do not always require authentication. You can clone from a public repository without providing any credentials. But for pushing, or private repositories, `git` will prompt for a username/password. ``` git clone https://github.com/jeroen/jsonlite ``` To save you from entering your password over and over, git includes a [credential helper](https://git-scm.com/docs/gitcredentials). It has two modes: - `cache`: Cache credentials in memory for a short period of time. - `store`: Store credentials permanently in your operating system password manager. To see which helper is configured for a given repo, run: ```{r, eval=has_git} credential_helper_get() ``` Most `git` installations default to `store` if supported because it is more convenient and secure. However the look and policy of the git credential store for entering and retrieving passwords can vary by system, because it uses the OS native password manager. ### Accessing the HTTPS Credential Store from R The `credentials` R package provides a wrapper around the `git credential` command line API for reading and saving credentials. The `git_credential_ask()` function looks up suitable credentials for a given URL from the store. If no credentials are available, it will attempt to prompt the user for credentials and return those instead. ```{r, echo=FALSE, eval=has_git} # This hack may not work on MacOS server where cred helper is osxkeychain # which always requires user interaction. Hence error=TRUE in the next block. example <- list(protocol = "https", host = "example.com", username = "jeroen", password = "supersecret") credential_approve(example) ``` ```{r error=TRUE, eval=has_git} library(credentials) git_credential_ask('https://example.com') ``` The function `git_credential_update()` looks similar but it behaves slightly different: it first removes existing credentials from the store (if any), then prompts the user for a new username/password, and saves these to the store. ```r # This should always prompt for new credentials git_credential_update('https://example.com') ``` In a terminal window this will result in an interactive password prompt. In Windows the user might see something like this (depending on the version of Windows and git configuration): ```{r, echo=FALSE, eval=has_git} credential_reject(list(protocol = "https", host = "example.com")) ``` ### Setting your GITHUB_PAT Automatically populate your `GITHUB_PAT` environment variable from the native git credential store. The credential manager will safely prompt the user for credentials when needed. ```r credentials::set_github_pat() ## Using GITHUB_PAT from Jeroen Ooms (credential helper: osxkeychain) ``` Use this function in your `.Rprofile` if you want to automatically set `GITHUB_PAT` for each R session, without hardcoding your secrets in plain text, such as in your `.Renviron` file. ### Non-interactive use Retrieving credentials is by definition interactive, because the user may be required to enter a password or unlock the system keychain. However, saving or deleting credentials can sometimes be done non-interactively, but this depends on which credential helper is used. The manual page for `credential_approve` and `credential_reject` has more details about how to call the basic git credential api. ## Part 2: Managing SSH Keys ```{r, echo = FALSE} ssh_key_info <- function(){ try({ out <- credentials:::ssh_key_info(auto_keygen = FALSE) out$pubkey = paste(substring(out$pubkey, 1, 80), "...") out }) } ``` For SSH remotes, git does not handle authentication itself. Git simply shells out to `ssh` on your system and uses your standard user ssh configuration. Hence authenticating with SSH git remotes comes down to setting up your ssh keys and copying these to your profile. The `credentials` package provides a few utility functions to make this easier. The `ssh_key_info()` function calls out to look up which key `ssh` uses to connect to a given server. This is usually `~/.ssh/id_rsa` unless you have a fancy custom ssh configuration. ```{r} ssh_key_info() ``` The output shows both the path to your (private) key as well as the ssh pubkey string. The latter is what you have to enter in your [GitHub profile](https://github.com/settings/ssh/new) to associate this key with your user account. You will then be automatically authenticated when using GitHub SSH remotes. ### Generating a key To use SSH you need a personal key, which is usually stored in `~/.ssh/id_rsa`. If you do not have a key yet, the `ssh_key_info()` function will automatically ask if you want to generate one. You can also generate a key manually elsewhere using the `ssh_keygen()` function. ```{r echo=FALSE} if(isTRUE(delete_git_config_on_exit)) unlink("~/.gitconfig") ``` credentials/inst/doc/intro.html0000644000175000017500000047743314151136052016464 0ustar nileshnilesh Managing SSH and Git Credentials in R

Managing SSH and Git Credentials in R

The credentials package contains tools for configuring and retrieving SSH and HTTPS credentials for use with git or other services. It helps users to setup their git installation, and also provides a back-end for packages to authenticate with existing user credentials.

Two types of remotes

library(credentials)
## Found git version 2.24.3 (Apple Git-128)
## Supported HTTPS credential helpers: cache, store
## Found OpenSSH_8.1p1, LibreSSL 2.7.3
## Default SSH key: /Users/jeroen/.ssh/id_rsa

Git supports two types of remotes: SSH and HTTPS. These two use completely distinct authentication systems.

SSH REMOTES HTTPS REMOTES
url git@server.com https://server.com
authentication Personal SSH key Password or PAT
stored in file id_rsa or ssh-agent git credential store

For HTTPS remotes, git authenticates with a username + password. With GitHub, instead of a password, you can also use a Personal Access Token (PAT). PATs are preferred because they provide more granular control of permissions (via the PAT’s scopes), you can have many of them for different purposes, you can give them informative names, and they can be selectively revoked. Note that, if you use 2FA with GitHub, you must authenticate with a PAT if you use the HTTPS protocol.

For SSH remotes, git shells out to ssh on your system, and lets ssh take care of authentication. This means you have to setup an ssh key (usually ~/.ssh/id_rsa) which you then add to your git profile.

Special note for Windows

Windows does not include a native git installation by default. We recommended to use the latest version of Git for Windows. This bundle also includes ssh and git credential manager for windows which is all you need.

Important: ssh keys are stored in your home directory for example: C:\Users\Jeroen\.ssh\id_rsa, and not in the Documents folder (which is what R treats as the home sometimes). The ssh_home() function shows the correct .ssh directory on all platforms.

Part 1: Storing HTTPS credentials

HTTPS remotes do not always require authentication. You can clone from a public repository without providing any credentials. But for pushing, or private repositories, git will prompt for a username/password.

git clone https://github.com/jeroen/jsonlite

To save you from entering your password over and over, git includes a credential helper. It has two modes:

  • cache: Cache credentials in memory for a short period of time.
  • store: Store credentials permanently in your operating system password manager.

To see which helper is configured for a given repo, run:

credential_helper_get()
## [1] "osxkeychain"

Most git installations default to store if supported because it is more convenient and secure. However the look and policy of the git credential store for entering and retrieving passwords can vary by system, because it uses the OS native password manager.

Accessing the HTTPS Credential Store from R

The credentials R package provides a wrapper around the git credential command line API for reading and saving credentials. The git_credential_ask() function looks up suitable credentials for a given URL from the store. If no credentials are available, it will attempt to prompt the user for credentials and return those instead.

library(credentials)
git_credential_ask('https://example.com')
## $protocol
## [1] "https"
## 
## $host
## [1] "example.com"
## 
## $username
## [1] "jeroen"
## 
## $password
## [1] "supersecret"
## 
## attr(,"class")
## [1] "git_credential"

The function git_credential_update() looks similar but it behaves slightly different: it first removes existing credentials from the store (if any), then prompts the user for a new username/password, and saves these to the store.

# This should always prompt for new credentials
git_credential_update('https://example.com')

In a terminal window this will result in an interactive password prompt. In Windows the user might see something like this (depending on the version of Windows and git configuration):

Setting your GITHUB_PAT

Automatically populate your GITHUB_PAT environment variable from the native git credential store. The credential manager will safely prompt the user for credentials when needed.

credentials::set_github_pat()
## Using GITHUB_PAT from Jeroen Ooms (credential helper: osxkeychain)

Use this function in your .Rprofile if you want to automatically set GITHUB_PAT for each R session, without hardcoding your secrets in plain text, such as in your .Renviron file.

Non-interactive use

Retrieving credentials is by definition interactive, because the user may be required to enter a password or unlock the system keychain. However, saving or deleting credentials can sometimes be done non-interactively, but this depends on which credential helper is used.

The manual page for credential_approve and credential_reject has more details about how to call the basic git credential api.

Part 2: Managing SSH Keys

For SSH remotes, git does not handle authentication itself. Git simply shells out to ssh on your system and uses your standard user ssh configuration. Hence authenticating with SSH git remotes comes down to setting up your ssh keys and copying these to your profile.

The credentials package provides a few utility functions to make this easier. The ssh_key_info() function calls out to look up which key ssh uses to connect to a given server. This is usually ~/.ssh/id_rsa unless you have a fancy custom ssh configuration.

ssh_key_info()
## $key
## [1] "/Users/jeroen/.ssh/id_rsa"
## 
## $pubkey
## [1] "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDwI/G2v42n/fVFbuyOL/7eh06MOK71djbe7wr8op0L ..."

The output shows both the path to your (private) key as well as the ssh pubkey string. The latter is what you have to enter in your GitHub profile to associate this key with your user account. You will then be automatically authenticated when using GitHub SSH remotes.

Generating a key

To use SSH you need a personal key, which is usually stored in ~/.ssh/id_rsa. If you do not have a key yet, the ssh_key_info() function will automatically ask if you want to generate one.

You can also generate a key manually elsewhere using the ssh_keygen() function.

credentials/inst/ask_token.sh0000755000175000017500000000020214151130430016155 0ustar nileshnilesh#!/bin/sh # Prompt MUST end with : to support askpass_mac!! exec "$GIT_ASKTOKEN" "Please enter your ${GIT_ASKTOKEN_NAME:-TOKEN}:" credentials/NAMESPACE0000644000175000017500000000126414151130430014113 0ustar nileshnilesh# Generated by roxygen2: do not edit by hand export(credential_approve) export(credential_fill) export(credential_helper_get) export(credential_helper_list) export(credential_helper_set) export(credential_reject) export(git_credential_ask) export(git_credential_forget) export(git_credential_update) export(set_github_pat) export(ssh_agent_add) export(ssh_home) export(ssh_key_info) export(ssh_keygen) export(ssh_read_key) export(ssh_setup_github) export(ssh_update_passphrase) importFrom(askpass,askpass) importFrom(askpass,ssh_askpass) importFrom(openssl,read_key) importFrom(openssl,read_pubkey) importFrom(openssl,write_pem) importFrom(openssl,write_pkcs1) importFrom(openssl,write_ssh)