emayili/0000755000176200001440000000000014155611241011703 5ustar liggesusersemayili/NAMESPACE0000644000176200001440000000344414155413632013133 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method(Ops,vctrs_address) S3method(as.character,envelope) S3method(as.character,header) S3method(as.character,vctrs_address) S3method(format,vctrs_address) S3method(print,envelope) S3method(print,vctrs_address) S3method(vec_ptype_abbr,vctrs_address) S3method(vec_ptype_full,vctrs_address) export("%>%") export(address) export(as.address) export(attachment) export(bcc) export(cc) export(comments) export(compliant) export(display) export(domain) export(encrypt) export(envelope) export(expires) export(from) export(gmail) export(html) export(importance) export(inreplyto) export(keywords) export(local) export(mailersend) export(mailgun) export(parties) export(priority) export(qp_decode) export(qp_encode) export(raw) export(references) export(render) export(reply) export(replyby) export(request_receipt_read) export(return_path) export(sender) export(sendgrid) export(sendinblue) export(sensitivity) export(server) export(subject) export(text) export(to) import(curl) import(digest) import(dplyr) import(htmltools) import(logger) import(purrr) import(stringr) import(tidyr) import(xml2) importFrom(base64enc,base64encode) importFrom(commonmark,markdown_html) importFrom(glue,glue) importFrom(httr,http_date) importFrom(magrittr,"%>%") importFrom(mime,guess_type) importFrom(rmarkdown,html_document) importFrom(rmarkdown,render) importFrom(stats,setNames) importFrom(stringi,stri_replace_all_fixed) importFrom(stringi,stri_replace_all_regex) importFrom(tools,file_ext) importFrom(urltools,url_decode) importFrom(utils,packageVersion) importFrom(vctrs,field) importFrom(vctrs,new_rcrd) importFrom(vctrs,vec_assert) importFrom(vctrs,vec_cast_common) importFrom(vctrs,vec_ptype_abbr) importFrom(vctrs,vec_ptype_full) importFrom(vctrs,vec_recycle_common) importFrom(xfun,read_utf8) emayili/tools/0000755000176200001440000000000014155162547013055 5ustar liggesusersemayili/tools/check.R0000644000176200001440000000144514155162547014261 0ustar liggesuserslibrary(dplyr) # spelling::spell_check_package() devtools::check(cran = TRUE) # Incoming feasibility checks for CRAN. # devtools::check( manual = TRUE, remote = TRUE, incoming = TRUE ) # Check it on Windows. # devtools::check_win_devel() rhub::check_for_cran() # Check on macOS. # rhub::check_for_cran( platforms = rhub::platforms() %>% filter(categories == "macOS") %>% pull(name), env_vars = c( `_R_CHECK_FORCE_SUGGESTS_` = "true", `_R_CHECK_CRAN_INCOMING_USE_ASPELL_` = "false" ) ) # # Try this one instead? # # # rhub::check(platform = c("macos-highsierra-release")) # # rhub::check_for_cran( # platforms = "macos-highsierra-release-cran", # env_vars = c( # `_R_CHECK_FORCE_SUGGESTS_` = "true", # `_R_CHECK_CRAN_INCOMING_USE_ASPELL_` = "false" # ) # ) emayili/tools/coverage.R0000644000176200001440000000020714147344744014774 0ustar liggesuserslibrary(covr) # Tests that are skipped on CRAN should still be included in coverage report. # Sys.setenv(NOT_CRAN = "true") report() emayili/README.md0000644000176200001440000002437614155410243013175 0ustar liggesusers # emayili [![CRAN status](https://www.r-pkg.org/badges/version/emayili)](https://cran.r-project.org/package=emayili) ![GitHub Actions build status](https://github.com/datawookie/emayili/actions/workflows/build.yaml/badge.svg) [![Codecov test coverage](https://img.shields.io/codecov/c/github/datawookie/emayili.svg)](https://codecov.io/github/datawookie/emayili) [![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html) `{emayili}` is a package for sending emails from R. The design goals are: - works on all manner of SMTP servers and - has minimal dependencies (or dependencies which are easily satisfied). The package name is an adaption of the Zulu word for email, *imeyili*. The documentation for `{emayili}` can be found [here](https://datawookie.github.io/emayili/). ## Installation Get the stable version from [CRAN](https://CRAN.R-project.org/package=emayili). ``` r install.packages("emayili") ``` Or grab it directly from [GitHub](https://github.com/datawookie/emayili). ``` r # Install from the master branch. remotes::install_github("datawookie/emayili") # Install from the development branch. remotes::install_github("datawookie/emayili", ref = "dev") ``` ## Usage Load the library. ``` r library(emayili) packageVersion("emayili") ``` [1] '0.7.0' Create a message object. ``` r email <- envelope() ``` ### Creating a Message The message has class `envelope`. ``` r class(email) ``` [1] "envelope" Add addresses for the sender and recipient. ``` r email <- email %>% from("alice@yahoo.com") %>% to("bob@google.com") %>% cc("craig@google.com") ``` There are also `bcc()` and `reply()` functions for setting the `Bcc` and `Reply-To` fields. You can supply multiple addresses in a variety of formats: - as a single comma-separated string - as separate strings; or - as a vector of strings. ``` r envelope() %>% to("bob@google.com, craig@google.com, erin@gmail.com") envelope() %>% to("bob@google.com", "craig@google.com", "erin@gmail.com") envelope() %>% to(c("bob@google.com", "craig@google.com", "erin@gmail.com")) ``` Add a subject. ``` r email <- email %>% subject("This is a plain text message!") ``` Add a text body. ``` r email <- email %>% text("Hello!") ``` You can use `html()` to add an HTML body. It accepts either a vector of characters or a `tagList()` from `{htmltools}`. ``` r library(htmltools) email <- email %>% html( tagList( h2("Hello"), p("World!") ) ) ``` Add an attachment. ``` r email <- email %>% attachment("image.jpg") ``` You can also create the message in a single command: ``` r email <- envelope( to = "bob@google.com", from = "alice@yahoo.com", subject = "This is a plain text message!", text = "Hello!" ) ``` Simply printing a message displays the header information. ``` r email ``` Date: Sun, 12 Dec 2021 15:09:23 GMT X-Mailer: {emayili}-0.7.0 MIME-Version: 1.0 From: alice@yahoo.com To: bob@google.com Cc: craig@google.com Subject: This is a plain text message! You can identify emails which have been sent using `{emayili}` by the presence of an `X-Mailer` header which includes both the package name and version. If you want to see the complete MIME object, just convert to a string. You can also call the `print()` method and specify `details = TRUE`. ### Options You can set the `envelope.details` option to assert that the details should always be printed. ``` r # Always print envelope details. # options(envelope.details = TRUE) ``` By default the results returned by most of the methods are invisible. You can make them visible via the `envelope.invisible` (default: `TRUE`). ``` r # Always show envelope. # options(envelope.invisible = FALSE) ``` ### Interpolating Text You can use `{glue}` syntax to interpolate content into the body of a message. ``` r name <- "Alice" envelope() %>% text("Hello {{name}}!") ``` Date: Sun, 12 Dec 2021 15:09:23 GMT X-Mailer: {emayili}-0.7.0 MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8; format=flowed Content-Disposition: inline Content-Transfer-Encoding: 7bit Content-MD5: nhjeY5ZYMzru+kSCGUzNKg== Hello Alice! ### Rendering Markdown You can render Markdown straight into a message. Use either plain Markdown. ``` r envelope() %>% # Render plain Markdown from a character vector. render( "Check out [`{emayili}`](https://cran.r-project.org/package=emayili)." ) ``` Date: Sun, 12 Dec 2021 15:09:23 GMT X-Mailer: {emayili}-0.7.0 MIME-Version: 1.0 Content-Type: text/html; charset=utf-8 Content-Disposition: inline

Check out {emayili}.

Or R Markdown. ``` r envelope() %>% # Render R Markdown from a file. render("message.Rmd") ``` In both cases the function will accept either a file path or a character vector containing Markdown text. Interpolation also works with `render()`. ### Rendered CSS When you render an R Markdown document the resulting HTML includes CSS from three sources: - [Bootstrap](https://getbootstrap.com/) - [highlightjs](https://highlightjs.org/) and - `{rmarkdown}`. You can control which of these propagate to the message using the `include_css` parameter which, by default, is set to `c("rmd", "bootstrap", "highlight")`. 🚨 *Note:* Gmail doesn’t like the Bootstrap CSS. If you want your styling to work on Gmail you should set `include_css = c("rmd", "highlight")`. ### Extra CSS You can insert extra CSS into your rendered messages. ``` r envelope() %>% render("message.Rmd", css_files = "extra.css") ``` If you are having trouble getting this to work with Gmail then it might be worthwhile taking a look at their [CSS support](https://developers.google.com/gmail/design/css). ### Adding an Inline Image Adding an inline image to an HTML message is possible. There are two ways to achieve this. *1. Base64 Encoding* First you’ll need to [Base64 encode](https://en.wikipedia.org/wiki/Base64) the image. ``` r img_base64 <- base64enc::base64encode("image.jpg") ``` Then create the HTML message body. ``` r html_body <- sprintf('', img_base64) ``` And finally add it to the email. ``` r email <- envelope() %>% html(html_body) ``` *Note:* It’s important that you specify the appropriate media type (`image/jpeg` for JPEG and `image/png` for PNG). *2. Using a CID* Unfortunately some mail clients (like Gmail) will not display Base64 encoded images. In this case using a CID is a working alternative. First create the message body which references an image by CID. ``` r html_body <- '' ``` Then attach the image and specify the `cid` argument. ``` r email <- envelope() %>% html(html_body) %>% attachment(path = "image.jpg", cid = "image") ``` ### Sending a Message Create a SMTP server object and send the message. ``` r smtp <- server( host = "smtp.gmail.com", port = 465, username = "bob@gmail.com", password = "bd40ef6d4a9413de9c1318a65cbae5d7" ) smtp(email, verbose = TRUE) ``` To see the guts of the message as passed to the SMTP server: ``` r print(email, details = TRUE) ``` ### Using STARTTLS If you’re trying to send email with a host that uses the STARTTLS security protocol (like Google Mail, Yahoo! or AOL), then it will most probably be blocked due to insufficient security. In order to circumvent this, you can grant access to less secure apps. ## Standards Documents The following (draft) standards documents relate to emails: - [RFC 2822](https://tools.ietf.org/html/rfc2822) - [RFC 5322](https://tools.ietf.org/html/rfc5322) - [RFC 6854](https://tools.ietf.org/html/rfc6854) (an update to RFC 5322). ## Similar Packages There is a selection of other R packages which also send emails: - [blastula](https://cran.r-project.org/package=blastula) - [blatr](https://cran.r-project.org/package=blatr) (Windows) - [gmailr](https://cran.r-project.org/package=gmailr) - [mail](https://cran.r-project.org/package=mail) - [mailR](https://cran.r-project.org/package=mailR) - [sendmailR](https://cran.r-project.org/package=sendmailR) - [ponyexpress](https://github.com/ropensci-archive/ponyexpress) ## Developer Notes ### Code Coverage You can find the test coverage report at [Codecov](https://app.codecov.io/gh/datawookie/emayili). For development purposes it’s more convenient to use the [`{covr}`](https://cran.r-project.org/package=covr) package. Generate a coverage report. ``` r library(covr) # Tests that are skipped on CRAN should still be included in coverage report. # Sys.setenv(NOT_CRAN = "true") report() ``` Calculate test coverage. ``` r coverage <- package_coverage() ``` Coverage statistics as a data frame. ``` r as.data.frame(coverage) ``` Show lines without coverage. ``` r zero_coverage(coverage) ``` ### Checks Check spelling. ``` r spelling::spell_check_package() ``` Use [rhub](https://r-hub.github.io/rhub/) to test on various platforms. ``` r # Check for a specific platform. # rhub::check(platform = "debian-gcc-devel") rhub::check_on_windows(check_args = "--force-multiarch") rhub::check_on_solaris() # Check on a bunch of platforms. # rhub::check_for_cran() # Check on important platforms. # rhub::check_for_cran(platforms = c( "debian-gcc-release", "ubuntu-gcc-release", "macos-m1-bigsur-release", "windows-x86_64-release", NULL )) ``` emayili/man/0000755000176200001440000000000014155404046012461 5ustar liggesusersemayili/man/new_address.Rd0000644000176200001440000000126414132315227015246 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/address.R \name{new_address} \alias{new_address} \title{Helper function for creating address objects} \usage{ new_address( email = character(), display = character(), local = character(), domain = character(), normalise = TRUE ) } \arguments{ \item{email}{Email address.} \item{display}{Display name.} \item{local}{Local part of email address.} \item{domain}{Domain part of email address.} \item{normalise}{Whether to try to normalise address to RFC-5321 requirements.} } \value{ An \code{address} object, representing an email address. } \description{ Helper function for creating address objects } emayili/man/request_receipt_read.Rd0000644000176200001440000000074114144403400017137 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/header-receipt.R \name{request_receipt_read} \alias{request_receipt_read} \title{Request read receipt} \usage{ request_receipt_read(msg, addr = NULL) } \arguments{ \item{msg}{A message object.} \item{addr}{Single address.} } \value{ A message object. } \description{ Request the recipient to acknowledge that they have read the message. Inserts MDN (Message Disposition Notification) header entries. } emayili/man/comments.Rd0000644000176200001440000000120414134234077014574 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/header-mail.R \name{comments} \alias{comments} \title{Add or query comments of message.} \usage{ comments(msg, comments = NULL) } \arguments{ \item{msg}{A message object.} \item{comments}{Comments for the message.} } \value{ A message object or the comments of the message object (if \code{comments} is \code{NULL}). } \description{ Add or query comments of message. } \examples{ # Create a message and set the comments. msg <- envelope() \%>\% comments("This is a comment") # Retrieve the comments for a message. comments(msg) } \seealso{ \code{\link{subject}} } emayili/man/sensitivity.Rd0000644000176200001440000000176314134234077015353 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/header-sensitivity.R \name{sensitivity} \alias{sensitivity} \title{Set or query message sensitivity} \usage{ sensitivity(msg, sensitivity = NULL) } \arguments{ \item{msg}{A message object.} \item{sensitivity}{Sensitivity level. One of \code{"personal"}, \code{"private"}, or \code{"company-confidential"}.} } \value{ A message object. } \description{ Manipulate the \code{Sensitivity} field as specified in \href{https://www.ietf.org/rfc/rfc2156.txt}{RFC 2156}. } \examples{ # Not sensitive. envelope() \%>\% subject("Your daily dose of spam") # Sensitive personal message. envelope() \%>\% subject("The results from your test") \%>\% sensitivity("personal") # Sensitive private message. envelope() \%>\% subject("Your OTP (don't show this to anybody!") \%>\% sensitivity("private") # Sensitive business message. envelope() \%>\% subject("Top Secret Strategy Document") \%>\% sensitivity("company-confidential") } emayili/man/addresses.Rd0000644000176200001440000000317214134234077014732 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/header-mail.R \name{addresses} \alias{addresses} \alias{to} \alias{cc} \alias{bcc} \alias{from} \alias{reply} \alias{return_path} \alias{sender} \title{Add address fields to message} \usage{ to(msg, ..., append = TRUE) cc(msg, ..., append = TRUE) bcc(msg, ..., append = TRUE) from(msg, addr = NULL) reply(msg, addr = NULL) return_path(msg, addr = NULL) sender(msg, addr = NULL) } \arguments{ \item{msg}{A message object.} \item{...}{Addresses.} \item{append}{Whether to append or replace addresses.} \item{addr}{Single address.} } \value{ A message object. } \description{ Add address fields to message } \examples{ # Populating the To field. msg <- envelope() msg \%>\% to("bob@gmail.com, alice@yahoo.com") msg \%>\% to("bob@gmail.com", "alice@yahoo.com") msg \%>\% to(c("bob@gmail.com", "alice@yahoo.com")) # Populating the Cc field. msg <- envelope() msg \%>\% cc("bob@gmail.com, alice@yahoo.com") msg \%>\% cc("bob@gmail.com", "alice@yahoo.com") msg \%>\% cc(c("bob@gmail.com", "alice@yahoo.com")) # Populating the Bcc field. msg <- envelope() msg \%>\% bcc("bob@gmail.com, alice@yahoo.com") msg \%>\% bcc("bob@gmail.com", "alice@yahoo.com") msg \%>\% bcc(c("bob@gmail.com", "alice@yahoo.com")) msg <- envelope() # Populating the From field. msg \%>\% from("craig@gmail.com") # Populating the Reply-To field. msg <- envelope() msg \%>\% reply("gerry@gmail.com") # Populating the Return-Path field. msg <- envelope() msg \%>\% return_path("bounced-mail@devnull.org") # Populating the Sender field. msg <- envelope() msg \%>\% sender("on_behalf_of@gmail.com") } emayili/man/print.vctrs_address.Rd0000644000176200001440000000067214132315227016753 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/address.R \name{print.vctrs_address} \alias{print.vctrs_address} \title{Print an address object} \usage{ \method{print}{vctrs_address}(x, ...) } \arguments{ \item{x}{An \code{address} object.} \item{...}{Further arguments passed to or from other methods.} } \description{ Print an address object } \examples{ gerry <- as.address("gerry@gmail.com") print(gerry) } emayili/man/qp.Rd0000644000176200001440000000116014153553110013361 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/encoding.R \name{qp} \alias{qp} \alias{qp_encode} \alias{qp_decode} \title{Quoted-Printable encoding} \usage{ qp_encode(x, crlf = CRLF) qp_decode(x) } \arguments{ \item{x}{A string for encoding or decoding.} \item{crlf}{End-of-line characters.} } \value{ An encoded string for \code{qp_encode()} or a decoded string for \code{qp_decode()}. } \description{ Encode to and decode from Quoted-Printable encoding. } \examples{ qp_encode("Mieux vaut être seul que mal accompagné.") qp_decode("Mieux vaut =C3=AAtre seul que mal accompagn=C3=A9.") } emayili/man/as.character.header.Rd0000644000176200001440000000076314153553110016536 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/header.R \name{as.character.header} \alias{as.character.header} \title{Create formatted header.} \usage{ \method{as.character}{header}(x, width = 28, ...) } \arguments{ \item{x}{A header object.} \item{width}{The width of the head name field.} \item{...}{Further arguments passed to or from other methods.} } \value{ A formatted header field. } \description{ Accepts a header object and formats it as a header field. } emayili/man/render.Rd0000644000176200001440000000604014155404046014227 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/render.R \name{render} \alias{render} \title{Render Markdown into email} \usage{ render( msg, input, params = NULL, squish = TRUE, css_files = c(), include_css = c("rmd", "bootstrap", "highlight"), language = FALSE, interpolate = TRUE, .open = "{{", .close = "}}", .envir = NULL ) } \arguments{ \item{msg}{A message object.} \item{input}{The input Markdown file to be rendered or a character vector of Markdown text.} \item{params}{A list of named parameters that override custom parameters specified in the YAML front-matter.} \item{squish}{Whether to clean up whitespace in rendered document.} \item{css_files}{Extra CSS files.} \item{include_css}{Whether to include rendered CSS from various sources (\code{"rmd"} — native R Markdown CSS; \code{"bootstrap"} — Bootstrap CSS; \code{"highlight"} — highlight.js CSS).} \item{language}{Langauge of content. If \code{FALSE} then will not include language field. If \code{TRUE} then will attempt to auto-detect language. Otherwise will use the specified language.} \item{interpolate}{Whether or not to interpolate into input using \link[glue]{glue}.} \item{.open}{The opening delimiter.} \item{.close}{The closing delimiter.} \item{.envir}{Environment used for \code{glue} interpolation. Defaults to \code{parent.frame()}.} } \value{ A message object. } \description{ Render either Plain Markdown or R Markdown directly into the body of an email. If \code{input} is a file then it will be interpreted as R Markdown it its extension is either \code{"Rmd"} or \code{"Rmarkdown"}. Otherwise it will be processed as Plain Markdown. } \section{Plain Markdown}{ Plain Markdown is processed with \code{\link[commonmark:commonmark]{commonmark::markdown_html()}}. } \section{R Markdown}{ R Markdown is processed with \code{\link[rmarkdown:render]{rmarkdown::render()}}. Regardless of what \code{output} type is specified in the input file, \code{render()} will always use the \code{"html_document"} output format. } \examples{ # Plain Markdown markdown <- "[This](https://www.google.com) is a link." filename <- "message.md" # Render from Markdown in character vector. msg <- envelope() \%>\% render(markdown) # Create a file containing Markdown cat(markdown, file = filename) # Render from Markdown in file. msg <- envelope() \%>\% render(filename) # Cleanup. file.remove(filename) # R Markdown filename <- "gh-doc.Rmd" # Create an Rmd document from template. rmarkdown::draft( filename, template = "github_document", package = "rmarkdown", edit = FALSE ) # Check for suitable version of Pandoc (https://pandoc.org/). # # Need to have version 2.0 or greater to support required --quiet option. # pandoc <- rmarkdown::find_pandoc() suitable_pandoc <- !is.null(pandoc$dir) && grepl("^2", pandoc$version) # Render from Rmd file. if (suitable_pandoc) { msg <- envelope() \%>\% render(filename, include_css = c("rmd", "highlight")) } # Cleanup. file.remove(filename) } \seealso{ \code{\link{text}}, \code{\link{html}} } emayili/man/precedence.Rd0000644000176200001440000000227214132315227015045 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/header-precedence.R \name{precedence} \alias{precedence} \alias{priority} \alias{importance} \title{Add fields for message importance and priority} \usage{ priority(msg, priority = NULL) importance(msg, importance = NULL) } \arguments{ \item{msg}{A message object.} \item{priority}{Priority level. One of \code{"non-urgent"}, \code{"normal"}, or \code{"urgent"}.} \item{importance}{Importance level. One of \code{"low"}, \code{"normal"}, or \code{"high"}.} } \value{ A message object. } \description{ A hint to influence transmission speed and delivery. A hint to the message recipient about how important the message is. } \details{ Does not influence transmission speed or delivery. } \examples{ # How rapidly does the message need to be delivered? # envelope() \%>\% subject("Deliver this immediately!") \%>\% priority("urgent") envelope(priority = "non-urgent") \%>\% subject("No rush with this.") # How much attention should be paid by recipient? # envelope() \%>\% subject("Read this immediately!") \%>\% importance("high") envelope(importance = "low") \%>\% subject("Not important at all. Just delete.") } emayili/man/as.character.envelope.Rd0000644000176200001440000000102514132315227017115 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/envelope.R \name{as.character.envelope} \alias{as.character.envelope} \title{Create formatted message.} \usage{ \method{as.character}{envelope}(x, ..., details = TRUE) } \arguments{ \item{x}{A message object.} \item{...}{Further arguments passed to or from other methods.} \item{details}{Whether or not to display full message content.} } \value{ A formatted message object. } \description{ Accepts a message object and formats it as a MIME document. } emayili/man/attachment.Rd0000644000176200001440000000232514134462672015110 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/attachment.R \name{attachment} \alias{attachment} \title{Add attachments to a message object} \usage{ attachment( msg, path, name = NA, type = NA, cid = NA, disposition = "attachment" ) } \arguments{ \item{msg}{A message object.} \item{path}{Path to file.} \item{name}{Name to be used for attachment (defaults to base name of \code{path}).} \item{type}{MIME type or \cite{NA}, which will result in a guess based on file extension.} \item{cid}{Content-ID or \code{NA}.} \item{disposition}{How is attachment to be presented (\code{"inline"} or \code{"attachment"})?} } \value{ A message object. } \description{ Add attachments to a message object } \examples{ path_mtcars <- tempfile(fileext = ".csv") path_scatter <- tempfile(fileext = ".png") path_cats <- system.file("cats.jpg", package = "emayili") write.csv(mtcars, path_mtcars) png(path_scatter) plot(1:10) dev.off() msg <- envelope() \%>\% attachment(path_mtcars) \%>\% # This attachment will have file name "cats.jpg". attachment(path_cats, name = "cats.jpg", type = "image/jpeg") \%>\% attachment(path_scatter, cid = "scatter") file.remove(path_scatter, path_mtcars) } emayili/man/raw.Rd0000644000176200001440000000061414132315227013537 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/address.R \name{raw} \alias{raw} \title{Extract raw email address} \usage{ raw(addr) } \arguments{ \item{addr}{An \code{address} object.} } \value{ A raw email address. } \description{ Strips the display name off an email address (if present). } \examples{ gerry <- as.address("Gerald ") raw(gerry) } emayili/man/as.character.MIME.Rd0000644000176200001440000000060214132315227016027 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/mime.R \name{as.character.MIME} \alias{as.character.MIME} \title{Convert MIME object to character vector} \usage{ \method{as.character}{MIME}(x, ...) } \arguments{ \item{x}{MIME object} \item{...}{Further arguments passed to or from other methods.} } \description{ Convert MIME object to character vector } emayili/man/text.Rd0000644000176200001440000000372214155404046013740 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/body.R \name{text} \alias{text} \title{Add a text body to a message.} \usage{ text( msg, content, disposition = "inline", charset = "utf-8", encoding = "7bit", language = FALSE, interpolate = TRUE, .open = "{{", .close = "}}", .envir = NULL ) } \arguments{ \item{msg}{A message object.} \item{content}{A string of message content.} \item{disposition}{Should the content be displayed inline or as an attachment? Valid options are \code{"inline"} and \code{"attachment"}. If set to \code{NA} then will guess appropriate value.} \item{charset}{What character set is used. Most often either \code{"UTF-8"} or \code{"ISO-8859-1"}.} \item{encoding}{How content is transformed to ASCII. Options are \code{"7bit"}, \code{"quoted-printable"} and \code{"base64"}. Use \code{NA} or \code{NULL} for no (or "identity") encoding.} \item{language}{Langauge of content. If \code{FALSE} then will not include language field. If \code{TRUE} then will attempt to auto-detect language. Otherwise will use the specified language.} \item{interpolate}{Whether or not to interpolate into input using \link[glue]{glue}.} \item{.open}{The opening delimiter.} \item{.close}{The closing delimiter.} \item{.envir}{Environment used for \code{glue} interpolation. Defaults to \code{parent.frame()}.} } \value{ A message object. } \description{ Add \code{text/plain} content to a message. } \details{ The \code{text/plain} format is described in \href{https://www.ietf.org/rfc/rfc2646.txt}{RFC 2646}. Uses \code{glue::glue()} to evaluate expressions enclosed in brackets as R code. } \examples{ msg <- envelope() \%>\% text("Hello!") # Using {glue} interpolation. # name <- "Alice" msg <- envelope() \%>\% text("Hello {name}.") print(msg, details = TRUE) # Disable {glue} interpolation. # msg <- envelope() \%>\% text("This is a set: {1, 2, 3}.", interpolate = FALSE) } \seealso{ \code{\link{html}}, \code{\link{render}} } emayili/man/domain.Rd0000644000176200001440000000054014132315227014213 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/address.R \name{domain} \alias{domain} \title{Extract domain of email address} \usage{ domain(addr) } \arguments{ \item{addr}{An \code{address} object.} } \value{ A character vector. } \description{ Extract domain of email address } \examples{ domain("alice@example.com") } emayili/man/as.address.Rd0000644000176200001440000000114014132315227014770 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/address.R \name{as.address} \alias{as.address} \title{Create an address object} \usage{ as.address(addr) } \arguments{ \item{addr}{An email address.} } \value{ An \code{address} object. } \description{ Create an address object } \examples{ as.address("gerry@gmail.com") as.address("Gerald ") as.address(c("Gerald ", "alice@yahoo.com", "jim@aol.com")) as.address("Gerald , alice@yahoo.com, jim@aol.com") as.address(c("Gerald ", "alice@yahoo.com, jim@aol.com")) } emayili/man/subject.Rd0000644000176200001440000000202414134462672014413 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/header-mail.R \name{subject} \alias{subject} \title{Add or query subject of message.} \usage{ subject( msg, subject = NULL, interpolate = TRUE, .open = "{{", .close = "}}", .envir = NULL ) } \arguments{ \item{msg}{A message object.} \item{subject}{A subject for the message.} \item{interpolate}{Whether or not to interpolate into input using \link[glue]{glue}.} \item{.open}{The opening delimiter.} \item{.close}{The closing delimiter.} \item{.envir}{Environment used for \code{glue} interpolation. Defaults to \code{parent.frame()}.} } \value{ A message object or the subject of the message object (if \code{subject} is \code{NULL}). } \description{ Add or query subject of message. } \examples{ # Create a message and set the subject msg <- envelope() \%>\% subject("Updated report") # Retrieve the subject for a message subject(msg) } \seealso{ \code{\link{to}}, \code{\link{from}}, \code{\link{cc}}, \code{\link{bcc}} and \code{\link{reply}} } emayili/man/format.vctrs_address.Rd0000644000176200001440000000071014132315227017100 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/address.R \name{format.vctrs_address} \alias{format.vctrs_address} \title{Encode email addresses in a common format} \usage{ \method{format}{vctrs_address}(x, ...) } \arguments{ \item{x}{A vector of \code{address} objects.} \item{...}{Further arguments passed to or from other methods.} } \value{ A character vector. } \description{ Encode email addresses in a common format } emayili/man/envelope.Rd0000644000176200001440000000365214153553110014566 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/envelope.R \name{envelope} \alias{envelope} \title{Create a message.} \usage{ envelope( to = NULL, from = NULL, cc = NULL, bcc = NULL, reply = NULL, subject = NULL, importance = NULL, priority = NULL, text = NULL, html = NULL, encrypt = FALSE, sign = FALSE, public_key = FALSE ) } \arguments{ \item{to}{See \code{\link[=to]{to()}}.} \item{from}{See \code{\link[=from]{from()}}.} \item{cc}{See \code{\link[=cc]{cc()}}.} \item{bcc}{See \code{\link[=bcc]{bcc()}}.} \item{reply}{See \code{\link[=reply]{reply()}}.} \item{subject}{See \code{\link[=subject]{subject()}}.} \item{importance}{See \code{\link[=importance]{importance()}}.} \item{priority}{See \code{\link[=priority]{priority()}}.} \item{text}{See \code{\link[=text]{text()}}.} \item{html}{See \code{\link[=html]{html()}}.} \item{encrypt}{Whether to encrypt the message. If \code{TRUE} then the entire message will be encrypted using the private key of the sender.} \item{sign}{Whether to sign the message. If \code{TRUE} then the entire message will be signed using the private key of the sender.} \item{public_key}{Whether to attach a public key. If \code{TRUE} then the public key of the sender will be attached.} } \value{ A message object. } \description{ Create a message. } \examples{ # Create an (empty) message object. # msg <- envelope() # Create a complete message object, specifying all available fields. # envelope( to = "bob@gmail.com", from = "craig@gmail.com", cc = "alex@gmail.com", bcc = "shannon@gmail.com", reply = "craig@yahoo.com", importance = "high", priority = "urgent", subject = "Hiya!", text = "Hi Bob, how are you?" ) } \seealso{ \code{\link[=subject]{subject()}}, \code{\link[=from]{from()}}, \code{\link[=to]{to()}}, \code{\link[=cc]{cc()}}, \code{\link[=bcc]{bcc()}}, \code{\link[=reply]{reply()}} and \code{\link[=encrypt]{encrypt()}}. } emayili/man/response.Rd0000644000176200001440000000203114134234077014604 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/header-mail.R \name{response} \alias{response} \alias{inreplyto} \alias{references} \title{Add In-Reply-To and References header fields} \usage{ inreplyto(msg, msgid, subject_prefix = "Re: ") references(msg, msgid, subject_prefix = "Re: ") } \arguments{ \item{msg}{A message object.} \item{msgid}{A message ID. This would be the contents of the \code{Message-ID} field from another message.} \item{subject_prefix}{Prefix to add to subject. If specified will be prepended onto the \code{Subject} field. Set to \code{NULL} if not required.} } \value{ A message object. } \description{ Add In-Reply-To and References header fields } \examples{ envelope() \%>\% inreplyto("<6163c08e.1c69fb81.65b78.183c@mx.google.com>") # Now for German. envelope() \%>\% inreplyto("6163c08e.1c69fb81.65b78.183c@mx.google.com", "AW: ") # And also for Danish, Norwegian and Swedish (but not Finnish!). envelope() \%>\% references("6163c08e.1c69fb81.65b78.183c@mx.google.com", "SV: ") } emayili/man/compliant.Rd0000644000176200001440000000131614132315227014734 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/address.R \name{compliant} \alias{compliant} \title{Tests whether an email address is syntactically correct} \usage{ compliant(addr, error = FALSE) } \arguments{ \item{addr}{An email address.} \item{error}{Whether to create an error if not compliant.} } \value{ A Boolean. } \description{ Checks whether an email address conforms to the \href{https://en.wikipedia.org/wiki/Email_address#Syntax}{syntax rules}. } \details{ An email address may take either of the following forms: \itemize{ \item \code{local@domain} or \item \verb{Display Name }. } } \examples{ compliant("alice@example.com") compliant("alice?example.com") } emayili/man/keywords.Rd0000644000176200001440000000163214134234077014623 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/header-mail.R \name{keywords} \alias{keywords} \title{Add or query keywords of message.} \usage{ keywords(msg, ..., append = FALSE) } \arguments{ \item{msg}{A message object.} \item{...}{Keywords.} \item{append}{Whether to append or replace keywords.} } \value{ A message object or the comments of the message object (if \code{comments} is \code{NULL}). } \description{ Add or query keywords of message. } \examples{ # Create a message and set the keywords. envelope() \%>\% keywords("newsletter, marketing") envelope() \%>\% keywords("newsletter", "marketing") envelope() \%>\% keywords(c("newsletter", "marketing")) # Retrieve the keywords for a message. msg <- envelope() \%>\% keywords("newsletter, marketing") keywords(msg) } \seealso{ \code{\link{to}}, \code{\link{from}}, \code{\link{cc}}, \code{\link{bcc}} and \code{\link{reply}} } emayili/man/figures/0000755000176200001440000000000014132315227014122 5ustar liggesusersemayili/man/figures/emayili-hex.png0000644000176200001440000007022014132315227017044 0ustar liggesusersPNG  IHDRX.' pHYs.#.#x?v IDATxyxT, !l$@uW\.Ui[[*jUZ.j\@qZEE dȈeMΜs]\&3woy@DDDk.[bx XlJDDDW&p7Ё>p}HqUlCiSDDD"P; HvEDD$`=I(W9֓KqTfz^!DDDo\d`-ֿ@ӟGDDDz|s0ҿꀫ~HO(EDDdW_  W#|`_X' [DDD]n逰_H1rF/+t@X1ND?/p0dh(2¿hտQ_ ;L}*/5&y "^`_S}r)xهN_:6P15K`?x4,,"(DmXNHb1Rp/p'tcxV6*c],"QD@$Axڍ$fCAj.;4՘j CR]EEo D 83U4h/^Oȃ\ !6(>\ **"` bCNf7u\gr=1ؾq na"e[ہvEg DS8Cߋ.AGS/œn5|ж+e˱6'>btHK:7H3U4cMJR+?fX,*"` ҿN*;IגXS%{,RO zc]ǰl2UTDvN@uhS=)3r=7T>i-3i?&amʼ. n~.7هFѤkg)iZ7(f:j*LxdQ6H.º'T=xu$idlz*_|`Gүa^aX D;^ )pهndHųwQLz`(0 8TAwb2y/ĝ`"D[]Fٓв&v=3.|F*\dμ#%B(DN&+dQx` {nˉ*:l?&_GڈM:[;ox/F,.֚,*O DzgLLLKtpMjX?n>zd6"f& )nxd˛1?$*(M_x/L$"ݠ` =[!_ $*8@bS%+b;xM&K=EEbιIXO HAE1^9 2UT$)X0UЛI)Ddri*hXɲ, "*n~u OJq~ϻ2e0dQX` -㐯MuEg\q'<{j7z Ο,*d "S1n`RpL gd7TS>6300z뒈)H+1UГA{yf.Bok-[N[#1&  $^El3&!#HIىP_;5:srK"N` &2w?)ד2$!ǚ`g|4b?XmHS0xrýMeQ8r.};٩wGj@"LdvǺhn_2y'8XӲͽ53Yv[`6,1N@bf޽㐯7HIP X7n6Yc6YT$(H,8}(:7UR",Jիx狁u&D5c|TAe\C5R`έxg1 +C>C_ߌC ORbƕR>V*W,Lw` N51/㐯'1@E fh3Yz)&7q-MM+hudm"DR*_0>9<hHR0':k jrjZM[i\df#:L4q|!=9 M2QR~ϝIre >@{AєH.TIqPϦŏGbXi,7YT$ $ڝlPb`aNd&*_#oυPT-㝯޺$bDQ, S;~:??CniY)soyG&b=xgJ m7q-odρˀM+^K{ۏד:|S%%N;ۨߨzF,ĺdQR0hp,>}L8dκ T̟EF)v dSݾd;;1TYm4ZJܙd-uYHS0;WbD631v#,OAd`o|dQP0e\SESx FdHt6ᕿ9~SeCSX!TQ]Q0r(oZ'BKh߸nE&˶w& lD6LCxt4Y5y&m_,[@% $RRK^Ȍ{g:77TIucT>ښL^&l` p*G`I)|-5UR_Z#3J`"` f(S=)8dqϝIӗ,[^4Y Q0CkO o@%E5:67Yvp-"}` }\q{NkI.TIGҟjo1Y5,*E@zT.AC2 Cg2vh0UT⇂ԞX㐍{;'x!c!8FeϝIs'&v=JS0Gp$ľs>dCμAFJ8V(DN&+kzbE%v)Ȯl<0:l? S%EbBW#6E%(u j_S}r)x!Bgm% 1=޹ k[$vYg$5!'$1s?"h$4URD!dQ~ TJLLNѤkw" ,(ć!6UГA{ ʊH}Fܙ4e6't,,G ec R!r}iMoz"b^7(f:j*LxdQ. i8䛀LSE8)iCl\;ZM~ k E%:(Ğ㰖 2UЗO>tS%Euoٻ4YE v b!Ė({ZdYw1 ΗueHE@љX`DȍwxE)8r;<@S%E$ m =hXkgu~ &d`% :j ܦʊH^wSK&n<h6YX"OY樟c˛1?5Y$5~.n e+kG5(8C!_ObN"``ߙGsMP0n.`ijbSEFP<:22vĐ@K/gzO*TQ1O zSr!HWRm4,d-t,,f(DF'X's$ '\7-DI#ޠ|ޭtT,y&J)DsBMuEg\"㝟v SE éX7U0i0 '\NO2URDC5  *fFo]S0(1'*8py\("]e){r&ͫ?2YkXaAl``XCr$d))"SuLwYktX㝍޺$ݣ`пC-0Th RF*)"m67^A 6YTvNupoS}YyNC'K)E^uUT̿;R㝯MӻI5yn_2y';E""F4y3i-_ixR05XT CB#%ED""f?{MVumE m{ Tє}(ri#2URD$TwCSX3d֙**ƺ{ &d`/5YD}Z*2=޹kԳ``6'FN]/]')DI5|y3iduh }5)8)3HdlHuST,EdX㝗,o zg8;&P4:2cHԊx >>#M8dgU){++,یuT\{HКsdCsgұd/hs7)ږq7fyE%pS%ED/iT>w]-&K\,7Y4)ܩX*8h(C&^q"";D x.kLt'&wOiH73i^ɲXOG+oC۶C| +ηY[iJWLu:1U4u~O*)"W~ bPjS)X1U7 ‰WhHtmb,j7Zxw`ɦ } w'wb""MR>w&?7Y ;eIq'A*֍X`j2__cTACHk߸yѰ|ɲ-]<ٮ`PCk\.eOL{je˰esd޽3FA$fH;?w/]&Ku2Ew?XMLNѤkw"""h#5 K;qcLd,""Q3Τ,[I?# HD`5xӳWn| Œʠ/>ݭH? mφݭ9^7(f:j*Lk&n-`8䛀LSEw?)3Hq}gN"& y>""Ѣ3_lw7eM~YmlzQ*_|`GүadQ0 G| '\Fa*w.>4 {f "=g <'w66;8S K&CY?簢D l9_Ft;]Fٓв&w5eru(:| 7YI5$D$CM\F[qP X7n6YcKRX?؇:ߒ:t_F>@&(Hd\P=M5 \.Rbpy4ošP߫z1㰎K|7p{2S05\[o]PS"ү! ,Zbw+վi- f  ֥"{4v!#r8s/c@DD-9kB4~.sorɲ`swA!_ObNqIEgHj@D챲_,3hw+`:?:ŝIXIy#(r{6U2t0rRv!"BW0y嬫ש 4P}Wx#8T7LOD :)ź@DFSgcFh*|ޭ4|d-t|~- Ƀ>sy1 M2QRg84/2M~y-+R Τcsɲ_a?onyWI~̽P4:JL͆o_E$*uvX]ɹ}{A_ÆlzW|Ci()w8ϻS$jD)ŠT/_vV qy<$\G[J~cDeOr:\qhC\$yyav!"K G?C.yqg\n9GNK59.xlR1#}ZD$!8o_i:c$B|g՟9v0ES'hd'_BO7sLx|Ǝv;l|m6A6 z73&'w~2BEQn74p?tT۩^q0p'&…0we4 nCDǂ!8_F\iZ3i]y?"S)z-uҭ[;2)hžv "'n훶'%=TQ` "vukO z\:%"w1v ݠ`" KSP0rD$6x.~ې]P0rHDbbPc|%B X~QHl5(d Xaz-JO f(HlhS0bGZ`4bYDDyXOHR0b""t.%شw_47]$^|xH[('"""` """a """` """a """` """a """` """a """` """a """` """a """` """a """` """a """` """a """` """a """` """a """` """a """` """a """`  4 .=<6$ )ȷ<.r2!?3ʴ Bzm5('݆DHqfK"ɫǃ-ن"'% IDATZV!.xzP|}s)%{<04LDb'nC^e*HJ3,VD/Ja%QBJ^e"vmnEDzIՓ?&}H$z]\p`pݭH7yy{Pz@@z = nCDvd5X@zL@zY?(I$wrZ>^WXEdTu mzY2}(^S0^~Iv!"_?/'&+Hxn@v1{A:![[7I݆=1>Ky"[RF =1#d9ݎHXID"-7˘Z> e&dݎHqD$=1HSİۏS(ҫDL͢ J8(VD޺cKnEbDeMv[q,F&KWD\Y `Y峻GٲgGÐ(HӇ݆cY£g)HR0~vY k.w瑪 t\QY i\B%uv#u?k=` Q㸒4rRVGV&U(Q#bb*H~Դe42 U|n|j!wow+"oi(QF@) H Ҽ<6H+Qq2R!; g݆N)JTKBTȲ ]R0dޞ>SȰ^Y<7/8J.<2Gq.?"d}S(c$5Y%+2nCGPeDޞ>2:١ ѫٮVu td$%GPbNn'shg.Y [(` ;ulhF$y]kr I\̰>ή>Ek[nETyZoW;rRJ?R铔7s;?娿fmd [_XI 1Й?鉽bGn2h 2ZE̯t'XWeçq4[y!9.@>Unw0 #qj:uqqyҗ* :/P!N'b^qr{= E@*X[̟±O }o3gow+"a U^O[OTzy!v*N37k.[Vow"P0z'eݴxz ) 7<{N'缿@8knEd]%q+7w^8"S:@ kr%FmwnEd $j-ZNYݭJnsYv3ޞ>G.;WW7݊N)HԻJ_8,GE-:=Oht0/ Blš/) nLdg qFc9sxUW0ĕlO݊H(#,]9VzeD^>w8;No]Pȁ>GIl 9zN)K;s9LⓂ8ʍon=r%p1.gocOwtBZG!kw"=5zj$yx${ud76vր$ϟ5̑O !uj:8^UEVœ68rSbŰ hw+QkT=xqU#_ow"` 5 NvE\~DngE/fd%9ofgWsݒj) +w-S n#*Lw#:gٚ8^?_?uBރ(%GMײ.^Xiw"F(HLzF^Wqb.=<>.80KKs^@_ǟ?qIQ0gײBJ%`-,)kqqwNtv3s)Y?å$90%Gpؿđy3Pֵw1fv)yVDswH7`EumJc;v.@w\;f0=ؑK' ] ~`=Wn_{OKe%>*q'߼Z٫m벻Q0xm g?e ;݊[#̗ͭ^+d}c${WFaFݭ٣g98b[ T-Hp ^ T̝'氟X.p( W5%Վ;uqSȓ]P*nE8;USƔe 4hm>8$͑ѯP qǙ߭"T`Վݜu10%IiCsm2̷61I-HQ0sd9rne.6б"?]7݊- $.}O9n~D{grpaݭliCIyG5|ٙw`͇;OwYşr}`[/7rXøn|c#ֶ݊@[k}}JKzxzj1) ndžή=^@=1`qpAَ/'7I:; "ߡ'"[dC99cK‘tBzV5Z1 +;v"u'A >kpEH[nM^9w83̫?nO.o"i Wwpy8}p˓?뫛yn͎H O#9=O݆HR0فJkxaܐ]^7`y\<.!!r"O DgQ:Bln a&$ttSyaھnGv-dCBzb f_'wY@ Dz}Cb, h &􊂁H/qsJy|Hi;sJnEı Dz+E7VM@B٥T5<@zicǵv"x "LWG:g@*cCVDb-A~b%?{--0D}{cfj@ >Ƥ'iA?bsS݆HQ01+uT.!_Om[ݭfN|d jqңk={mg kYز:6nE$)DЖ* ͌}?qDHS0T͋6k*Z V"A@@Pqj~H'~y|UawQduwY+s!xS嬭ﴻ=1RWZbfA0Uq DD]Ű>yf^>9`њf~VDd DN&g}-A+Yw83^]Dڶ.om k;9k DFO DͼSvO1 楆ήj:JZ:uS)8 XqS|m \R%5]E,`g 8v Ghq\|YVZàDbH `'u.LᢃC<.=M nwY]۩AHqF |Yɗ5j&}O?+]vNoxc#/}dCGuˆt|ǏHiU3w[Mm[Wإ3d!ۄFБH|Htso/ṀFؤ"ȹgqA<^  \jMğƎ P//@!-%HŇfou]\>nCW"s)gDى[{_F?l6&ɯ{̞vqr7jttKUG ;!.zj;mKC^0ƾԊB5}3\~Dq r~mG "Q ]V=4hIAGO d\Q~Sgy!>Sv]z:>**ی|mzbV%Gm!,m-#ّ7Qٴ~HIC RlxO}B}{ #&x#OK+}d)"{|D=s'AbWӪXqD<|`g g!q<{ɗ5[ v9j[:X3|qٴjAg]k>Mє ٳ7e! ҷeRO"Q|ߛy;^hc+ՇdO_%M9.Û>/$Jov0DgDIy5>O3g3: `'E'E NcQ*%ٰP(V;͇1e2ΤS%wWRWk#nz(|$*0W9dLvY?T(Qcdv"9|^͝A,N~2ۑ|d]Զw9hel2ܤܴB4ui  މa.MfjϿdT IDATC5^j`0HkkX&_KRnƔJ|3'dr܈tv2O~skEk[xw#guko `aھL#c;1~XQn1*)d2<˷#FËR8itD~z.?fCWW7]{xQ \`=z #ӸؼիmGքǽW/_hK[(?bO[^7? ѸmrDeA{zHxW&gʹݡq#ҸAd&|=v˘qSd < ~wBb‹!$լ2GI)kI۰ԛd2۽5Kyux.rӼ쑓Ȅ=3u^n;. c=Wރ90? /7iI82=;xsM mj+aCR8(en\qd?ۏ##c]^M4vtsD)I`wP6om|'gg>u-5@?DrŒ*HҷSGתvXVNZd$0$#?S^y+omu~[;f>h,qHa2ǖmS =qR(Q2>7|-}S{!b;xM&K xgҮ~hJ[sLt,_6.Ǔ=A;5;'m;w"T vp|somrY\xPf̓u]IV_U;jg'ux.^pR֛\>77;qomx\o*lo. lXPQPIDS5(X.FPAXQ}gA`,3;Nޯʗq˜羯U:'Zx᛺o=@f3oe˦>jM~[k;|[σ'/ʿ6&p}/k6Tc4 ywrCeߖ34K悯AӦhSb2+ćEac5B/VcYUƖ#'{?%:b/0n寱˩M-B&LG_N Ol7?_߫(8˭MEou9qJfͯc/G4ycOWVzSr~ͳkW))uth[Ǎ:{\00EwƢF9ݔ5ydqG>Wg;K 4Pe>5؝u%Z1QR[mdsp 鿁d'dQ{o[3%~Fݏ5]=^mRANīow܈ikmnV8+oonMP?V-֭љh@*€M׋8*<ۡlk=p0۝`85U"'Kg#W{yY޹E\0[+;w_Q"ߕ 4,Ob`l H]]?͝N Orj!YFd$Y`ס_sX}I1sVA?"}XYw|6=úO889.cL.C[<VM;>`#E.ʬ ~>>3^:[ PlZ=O`M|TUg;tDpo䆯G~9qKns"_-S⍺ř/onjb?:!]ѐ CtWcv7`G AzsUlboPXM]p+kn <T7*kޡh$xPI4jdf&lݻvDc95ONZs?y_FL=翮]EF=@$GWZ[T7Tӫ:0fx{/oduUNs}e;+۩hq~Aר\?çh UdG[5xzzKioB#ƛ]7k,mᓪ8^vjnO %yOJ 6> Fߨs7)gĬ. 0mD:/~9Qa\Ȏ%<~Y.* h}OK)A4,֣t֕܉kHP=hCbV(tE4n\N/0{g8ָ翮㍒}u0&c95/9 /LB}>­geG@SG.&Qiwh~ qv'})#ҹ󜾺zme)+CT6mEz~lq`NQ)\68-wG3H_7/q>LѰ؜nomyl5'ǥ^ fmP>*PϕCӤ(k#Kv]V0]](jЕ)ynz;_xz+w]W5pF+F931^- <nn©4pHۑ L-.1&x=Jj3/fk*O聢Bbp8OѦ9f˜\'ߎ)Ivǁ6m7yißIVґ˾ &_ǏZh `WYqFǙu!:X5`la2fMzKm~uq0,;y>2YɿJY_p'Sq歫V}jf QqxjFțxw6z6spR#M6sYܿW}g4, :n>Kj;itbh-65yQMS͜E- ē_vkAG, iJqO&ٛ碵W=-k#9l}GVEdjtqU&KA:7]}Bϋ7mL^~^ݭT}:G O1qw;0St{ll鐢 Hζ&/U#цڝSo?xt- A} fg~~HL:t嚍._:_gseEG5F{OimTRɻFN,h&^Nt+tNu}োAn˩ZnG8Wva< bRi 6?| _ۂxo/P;1Id\͝N\^7,բGeaOM08 L֝ OguT7gU+\ ]h%F.{U^bz61aG~g93z6/W" ;z?ǻۚjֽ-L>TL]PdgqH &àmT{#*lgoS{8Ў#p+ <*)%&l=l}r:7 7:=NRv|tǾ {Bh@sth+wl:_F[{88/nbKюep- hpHJXm޵ynSɂx?;𱏱{r^8 y#eƇ{ Fs^}.7KwHa83~8[;wG 7 u6\r|jPR>c!L:-b+b[:cfsY(H՞hq,}6l b=PJPxl&SRΛH:55泯ؕ9 ~u!?=EV<[զH?*UcJzGvh3"}sJ-:kQml:˷GYNYFWp 3ua?6MxrtAMr&rn1x$ Nd.L X Zl.x,jD.q:'4n.lN&OqFM`lA2 .@m@K _;]-LN4"''f0$+}Ș$& Kֳ,d%ZY9T:t7~{m脌.Qi0 -S7(s7 to2`|>O32oc lp*Cƛ $ t}|Rdb3(5}5%aG*~B-OF-՜*ДIJ{eqԸVq8kʈtWG`]nk5VߟӗKa@Z vgk 9 unJjs_԰I3/ǘ$rEtaOK[u^?3?&t*&s h;g}rt\n^juU {ZsKy^_-6 8aץgpV+mRbW]:RVʤywd&鿵74f[WcY8*cDL1YhRhN$o1 1mw_-OIuPǰM\bpowRbgG]nF5A{<$+:n*Zlz>X}M,@ o)??-SC+`GX6Szi mz;.k=vqtkK3NVlgOCxA 8uhg#ENaJ;ozJ/3X>G>:/[] bhnC%t}XZve\8d#T6o JUh v 6tZ1f?"ZL8>X$ 䞚=RX_NGݑ03i.f~V-44;WqR?Mژ6o}nĮ?~6gli1/zֺ < WhLH&_wo0#x^8@pŻۚZٲFZiJ=1Gg_%[#yx\?.EgIu.9!RXmR~PMi{?b_qRDV>†߸XK:XTD[!PvL=Ia3/*МAg46~ϵ\f ~dQUJY\|2xx?i) gOϻ)1x y/P)D3 Ɋg`xrRę; 8MT,]@7T4p һV+0d^@1y7dGѵH[*R!Ž;GSkہU) WE;G?Bмm5⹴WP{\Uq9 t9CBD;um)A@-#xy(\tE$u"Ǹ:ۨ*?zC%->N iuh09 J"".־wR9`=;AxB1) BkQII^~ot%! RV<=O# HRK*0>3w}ִ,l*?C7[@;g`Ь*T+Jωn2T ;3I.*R!沵ST|N݆?,U*| e7jVH)Ns_BDƒ唽(ri)= M sgsIDATeM䍿<; !_[fʊк; h+2Ha&]O @bAL}FS)؛jXlc<U*'AxHnCڕ*4}9^3CUE !b7x8;Z`P=RhM|o &39IÔ"RKcY82qaE t&ㅱitTc4m\emB@`<) —g3hH.IY =SU" 9ۚ(_|xm2mP/mx;?HBUB(ph{hW-Z5*CzRD㝍q: SbX!Djپ⹴oW[@!G) "8' ǀIxg!bTg>/~F8v` M`ZRD&x私U)OH g")oH!DC~9J{ @;~X2T7) bS>82U"2Ia <<:5y|UB&bϓ8ZT&֏`P0G<\*3y1&$"fl8m*c+v@@ %hG ӟi'i`=!PIԭ]4 B,Ȼ%0㝋EʠSUE U<㐫ީ2z ڑ=*CEt@# xmYI@\}qj!"M l*g_2TD') D FwRUgs`W+Dı-p.U"E01m`U 9Q0d""k6_[@;*p@U Rw~HW6|UE ;5_aU)Z*CE@+x04M=܁9-KEad9tد2v' B"Ha T9M tWQ4oRegGDl@dT&O8BUB( B5nZURPH9'IUhƨq͘IBNH!B8% p5m*C) DhWV?ySUD [WcY8*c#'#!@ ў*kuÀIkd+8iڸBel#'vBM Sh+OU&Dьduζf*HgCا*THa zZηj@+(^da!)J{ C=Je"-CD7;U SˎoϦmqȕhAdRv)Ug?N,BPEg]p+h a")A BSBьY :EUq.[;ULղq;TF/A{lP2T@x5G FL{KWVsXshU&oCP!!G#Ѧ7WhLH&_xgM5u*c=_*"MFp~ɟzwdobߐq"Ia ]<[`6*4}9^IQqѪ23U "R @nLs?N̩*"E!ϡEe.~dRHsxsTS2ț(cYGn, Ҵ Viqd RH4lbb`f<@ 竊a.T S03^@YÌQ(v } UE0v99j!ڠ2w*CIRhPg 8r.1SĔ*Vmϥbr}2YD<) D4@Yø~ t+9=t4Pel<0PzA"6~BSEጙ*RWg;ULG/ Z[*CmRh|Z/|YD 8hs T*D@Da|`;߈1.AUPȺfc:F[a"Ha bťhNPG;>*U"Hj=qMBWRX<d Mv3f\0BUN͊R :TFvpP!™"e)\8>iJ"KcyQ:딎C |2TH eMo@U)97_`0ǩ:6SV<]ߩmD;B !T&DuHq6ƿjU 0B<3I0TUdr;|F(!@;~X2TH%GGS~Lfr. oǔ)[WS֣tT-f "Ia 3/UhNC[ds:`YMVC~P,D4@ hrU&@ጙ =SUdq5Q%?{î* CKRql)݄d$-PC~Y-*EGFeH !7x FU1.~_Gޕ7xk(+~m*c+? 㐅Bbs' ӟi'i`_}_q`.Т2Xh[@Bc~ rT 9]jFê2SU !0" x?`NR*%˱Ϧ~;!/U*Bh 4%g7a9޹ͲKZv~2C"Ĥ0LF0PU`bA^s?']*F*>qw5BIa DJnfBGCѵ;XUC~9J{ DG_BIa D(@k㝛Ʋp;UZ ',D/@5m٪{bsG,o?Fƕ*cۀy8d!zB>#S@UE'R4c&CPe/bhT !G !G*>@YÌQ(H~EwI-u^whV BtBhSՍwO߸1`-;b)C~^A;y R.Ap@;*)_N!BRvg"\?B!"ޅt BNW-B1W_Az !gA[wQp^o@!B[@'?VBB8HƄ9MIENDB`emayili/man/figures/screenshot-email-rendered.png0000644000176200001440000021703414132315227021667 0ustar liggesusersPNG  IHDR.;\D8zTXtRaw profile type exifxڭiv\7c,9^~IْrY]],TD;k)fKk/=^Z>~.o_0xoם}F_o|aғ5 y=~^F|~C u}&X]*xRH} is]HS1} aϫo?-~_ֲ~Qzި"}V^eՖƎ;m`bݶ 'riN?k7|˭]?߈ZZ|u폨k-(fD,@ě"@BG[9*rX*A|B,7?#9DQ"BOTmmf=Zͨ-bY̭qP򩕰Wq B073ưOӗO_7Sͧ\m7Rn ZlvY0eRi0a<^c K;IȬ\^,D[C򽈛g.tX6U;FmճJW{#͞Mu|uXW!mJ%)Axz$B_y  J}kaH^쉆ٸ_4V=I0*Gr ;XfiYEH]Iܩo QTlDŽhJ\;z3;Vʗ@jQW!\uʭ<4.PBFFu2q>PG2* HZ#ךnOUH~RSK!֙7/jzRe7-K{ +Q'G^;7AxEp(.ߔF` ͡aؠPT@(օ[_ :J `Ki0vz"KZH)x Q]DwSlde'8b,@OJv2h^P&1)3IP0c3ln9vDld'eM~<&хO\60a5֒'cv,+,Uq.kG^m"$u|§{r?~wtz?N; 7݀]d;241R?G%n S>0IZ3K֭2Щf{U9 لW࢖\$K*ט:/ JIv$$Ik ]7TY3Ve&IH}QQRyfwXr .c *}QPqEaxoc5pʨul FзLvfå ZWfUԿdb;ň0)uJؾ/ގx"hQe+rBLChI,uaG1/ǥ}X&@fL8Ka0Ԩj#0(pjPeC]<"XUbϳ@unH]pf8 D~@sdKUBl-f5 PiQv8n {Hc3"SE%q'd8\5\r)E#)$?\\H%ˎ,pD2~#xb/YR QPTNE#eQBzΰXRZ2P!3Bl 4 ϥ#ia/gFUgWOBg_RkѴ q53YZKAõ֥8gW$=y}M t FڎFGeQߕD"ZrR{&V'l$1e K0W)BC)@@i#YISXРʆ.򰫝14^N]1</y(zx!4)U(fF;JэI$IG;H'L=>X*A:hsU{Lhș):G,&CȒRrxoXJZe`Y4nsLHf.𘍦)E񣷹 f=#S{Ji+{-T?F-v:SBzJ 3֬/+TS(yEAKtX$7jc&{p+!tpɉ͎XNu|0VK4S`N_M^[9Yp57\HFṂ $9QOpڑЕfnR/Ǣ.hS٫-b9b0Ml>Xnp- "<+kp|ÿe|rrD|'ψ n_ DVΊ}ޙE4dwP7^BGX8A&U6bY20 0R]BKE #clX(XRR׮I"{oA+]ר UԥbtRqbd,^@I@,S Y1nqu.D@`gX9U,bv~Q`GN&ۈ99p.ގ(0^$ƷVQGhygua)DYԱB@PPӍAO<H̠:# ej S O8Ж0nbۑūo2sae `pKjN!26F+: M f[- JQ,&ϭJ 吣:\Mp7^ TH]R 㨕ʒg]5"^ ܫa}I3Da4'uUXCHB.ɯQ#4QP%Dva0 ԞF>%4z{ȦbǐyOalXȷir bGZ۪/8Pw*o- #o6DjnTܕ W@'2.T-d`9i3lCfCa6%ρկX6$Qhr!Hz=:OV!}X,R:xy\,cJ["= 0Mlí;/P979`R7Uj装`@s0q,&vGt ˝\\H܊b{hD68GWmJiXu&Xb}Rj*ט1^ȩKl dQPOn3mXUhUƸ #ހ=*``q(emi^B3#وR-.OFJ:݃zîulhu71mLPv ]3£}2E]I2rz’ C{b4"CuHrHxzd9ՄD(ȳ ʨ&YU[+t$G;Ѹb[UHvBbXjhoIY,(<Ϝ=G2F>"ymT5DEf`iE[1/8 6"LfV(iTxiz[otۡ$FbG ANi+6U}9Hنn|ɇALDr77wo;06X:T:~'@ Ctejڢ&6Y#^,*$;B"lT##3EW`VdkpP/p U!^N_':pkf 刪S~TC(]Ka{~ Ad@O C = Uj雳N{&ːDOv.e> ' X3 YbߙD_ވ88 _}H-Md]sm!Lͻ}cyW_^wѺ.]8,7P@Q#N^jlѶiȯTzxC9݌;n0# qq9Ni2c]]pt`oEi kIG$<mBL/VȧβMmdRl8b8TZA\dx(C} *,3m8*ubXN`K@\Ak%s0:\n0bHn&{R-AІ(Abn zuhu0g#w#>[y#~S':QxryW54E*`N}6tHZz}6yxV7òab` \@Vǩc;)NR &Lg ]KIrI zh0Lr%UQiC.j-PdȠ7%Uϊի? JLa #J.Ks;Ǣhw/]7HQ:]ǑvO)8W' 6fal4xtéVJIb!ylgOt}|a;CeX;#EGɖ -Vo/A3$<֞r@ Ot2mnu]Nelq v ]>;a iMm$SN w5>$E ilkS28UYA2Pek{RN3w %N!/mM0}Tȉ(ֈ3H#Ptnɫ> j t}‰"hꨩN0N{/mОvGg"LQNmS΀ ƪ3`x[?TP=jj6N=ERq,>ҊswLNr<CiCCPICC profilex}=H@߶JUvP,t*BZu04$).kŪ "%~Zxq}w^f1eq!]ه$fswQ]Ur&|, xxf9YQRω #e8̰NBm̊J7e[g[@ze{o4rLbKGD pHYs  tIME  持 IDATx{p7cvL6` ݵL oB.4;1]qS{{&fD)lqv=gi;}WƥrZd&&)B udɖo/_<@N> !݅UBV`G9s٢UVm޼yڵb`%ܱy[$o-4헾uN}z{owt5Μ9s}$z~m VOoﭞu!mB]Ɖ'W*۟zcܜnz^x tGe / E{eY}pcK݉b͛?UJj 9r49(;;L׺ϼ瞁UX,Y=fѪ:uT*JnxW:l}}sϩ:h;wll6|w=s_lݰ ^Pet8sQk9g4_566j/n >V*xGI>^zӻw6w}l޲eƍnqH2ص-o{nٲew}Ivo`3}ip'ڞ:aY!?h4~_ݲe޽{o2MsT޽yEwދݻ~fcrLc?]k$.] xT~O<:5!w9p@{{\|V֞'tfP v:wn>;=:jG_u3]I\"]^bv\m}`6{^xwIvֽbhw)Z5 ]L*3?A WkԳ,foX,_c pB\.$ya!u7y睗_~_q/@s< :_04hm_}2ee 4/yy'ł;@%',C!})NR#hX<eaZ] /qBy,<jFlĒ'x$^xԎԴ~a>pC(?'>o߾y?>d^̷a4:@9 WEˁ*6eMﵒzM+ߜ&A` R,5U믿NV6lxj\ 2EG5\ $hX Yр#&zZPtMd=<L4@hmv/Pbp{9bV{m~SO@0w[Qrf8G;x.#ɜGꂿT/Rݾ@N}9N;KjJ* >'Zd1? @:09 ")Qf5{G͛Ʈr^e\>+)@7|MN{S٪䠵\{zYDB${Qb;pW^#el'Gj3=m@GFĕf۶m}gLtEQ, M CiLѵOƳuF8PFy+ozl&[2`,0ΉV& ePeں~J'6&,FCI ل/s=FEٶmPG78oM:>}4˲ {}>̌8(* =1;%<5 "MT۵%6(^ӊyx /=裏7_qĉݻw:uj۶m 7ڜ{fXmBQsg8)x'JV$2tpż»D"](&lܦ"TQh@hpBR7[ndf=czQ^H˂G2.K' ++JnԳk$ІrUh=AR淽Wmyhrus='9g߾}׹0Fu$:7qUV.h%JۼK>߯`ʥw:FJO̺zi\pV+;frm۶5nFG}W{7䶀 e[̶ 3A/ֳ@q۶#id4,϶^ݻԩS[lY:,q_800p)Q-5r+]i 9tJw_Q-U`/\<@E&㹒%B:'vr2\m ,Jl|$IDE.!dJiΈ.P,z>[m>h=al9ʩL]\4b^mW|UFKLwI5J丮мw.ĉZaj3IQv%}.Y979a" ,d ipw2L/7嵄zcj-Xhq(B|KhTB"@űTJoJ4쮇#mhTڻ:sOEh|h xV#,poӨ7z!NP,.~S4MKt ujvY/ +xzr~ A$Gj .1o t9Vu᥾iP)奠 E%m:Z!2:{&aj۶q&Q%&;zP4E`0R4+2$bda0Rv+O]F}ݧOʙLֹ,NDj$qH$ $`dOےC.KrTS}~ӦZPulʹ/Q',{mD߹sw^nJ|TϟzFP޴8=;v-w{bKIPFb x~zfǛz,{nVwv=ttt*~GPG{}T"/D7۲c o u>#4r{]XU;]Xj.Wh}vhkڪUn :JcʡG9s٢UVm޼yڵb`%ܱy[B&!PG!:B!0 / 2ZHņs[Ñ &Cӱ]I97IJo/zT`EaA*pZ?"Jj흮Ŋ 9yND*PGKEeRj:^^x_\/kOZ?_ZMK dZ2tz/-)`4bI/DA?47F\ezWuzׯԻ7! +2^gW&qݽ6zM3# 'e @^.UFwx<eC.hecs y 7ՅP_k>}s~$[ON_a/?}_T_<_NP,l:^rTۃ[H1HN7 +{<6B,(j0Pm6{otA 15J"#E\e ̠+p$iZx9D~. N#ى(vszٓm=?:*`ZpsSF>e9\aCMr5`x뵛ꓙ`<#:+51Jr0Ye3j:ɊUUsX:7Q#vXk̶ɗXwGY, \l0)(U998B]7r449x~7'-v\w,?Y<BKtnΞ8W#@?;M+Noe ްN2l@A}~g=~/{ںע۝=MB#]$誜WJ|"70h$Bwڸe<a*5%{H1AMgLt/\}~/J*`/LQ@WJeLeYR+r"V`}6m`; f=1Ǯf5Ϳm}&45 ns^@'ΞN5v\T3A_P]HղHDb }A l<@% Rbp{9b3uMK,;E5 X7 2z%t90ޠrР?mH^Tyh5ܝ]$`6TphY A Y@6_TsZ.A252[yY@PFQV\F+KBOo?S]-a/ʶx9b&L3 P-܏S||ϩgVǏaǏ֖7}OԆ r7VY3T %,XR60Hn% HK<ɉȧ&؛NP UT Dj"MOjkPDRBJiΈ.o6y"f2@44M_J9̛\BNXbGKق~e[N֠fՅvkM(`nMCвt:]Epݟ~ϰ1M dEQd6d,+cHwC@,S X`!%Wt[?BLWR]x=c-%3ȌfmVrN歝MbB)K!xB w\IDV괉^Lv]h"6`hVdfY|@쭃aQzj.;}(8;Km5wп]q$BweOZ=Ͼ>^cMS2ñp~d IDAT8\r*,2T|lda,ڭRજI ņ U<B~x o'2dcs7iX41Lȉ`<[IJ]NܝNO87ZuR)-]65yxO;q_r&@ǃ{iE€G2ۇB|ёlfcߟb/WM7G'赗 =xR`0iHNw4O{h3ܻFk.u54ϫ`s]r<=T_-rü<g|r ZBOtН'~}~Ϯ^~ԉʕW{]RUuRkѨW&l7rZ Y *@ENe!Ip'_޿~$[;q V~ajԛQ#NTeO{G RFrE˖JK'{G*FR 'K*e55\%iy@WEYN^ES!t _M'dž~w`NR|-Zm(Ni| g7'7>'6@}=ev|]ش ۹5t񒙮&={l`m#ɂ>7g~@;B#D<Dt2B%Ҭx"I4]+#ARؾ3l2SDO:ÉD"px#Po&.9>gy??}~S&8aѫ[ZkVKrz~}P^)T]7]:)#-$*?PN&5<<0xhq[2ic>FQTV+S֦g:3&xJtnvb&QS(xq\~e$2DJA].:Mr VI /[xEM&5U0{:%n'c;{ZGf"dnlv <NN tZ bɤb3@bX#;S7nڸ~~ZW#ԆԽS'~@(8q˗Sg&{bF Zc* eoM)` :@Eeuu%I˯` XPG1Y`y( '(E |X jFqPvIvh* W$VfyC=;A[/t$W :J6#}#NIOu݇5oZ}-75Zށq{GB-#]cWaoon@>*zq]i+`P0<{ !~mcٟ&(m{3Xѷ譆yEQ7.ZZMQZvswB}L*@!0B!B! uBa#B!PG!:B!0B! unu_ve X! ;J 窷fч#FFӱ]I97IRˣ^+/XBwJ61VOu;v{k`lhBNj&(7<ԫb풹Qٮm7|/;7^xc;'w/ϿNOn?)cLQV+u0R&ڝ.@3(`4#K,?'&țlRNSzWƓ!~W/ڿ*+5GC@\e$gw^SHIuK We@j' |e'r'D5k/tw޼VBCFXEXz|%63;7=V;S~苟YRHN7 +{<6B,(j0Pm6[hƨ^ MT`xStyx 2RT˵:P@ GySOD璁ýL9HA5kR ԡH82ξA1h77*I= 㨣 Z '}>;7zo] P6.W\ ܽ^9> 3,/{R=DI-W 9\V=Ȫ^U5s5i猵l|e[xm+p$+:ހٝ_㙂R]>#5{!GsAC=Oḏp'~sbb٘zCO!:{W(Hל?/.81 h`4i AWʸ}VRAS< "m^/K䑘 S)C4h:c(}{9PRp @xmd tRT5].`(rX95iýnۉ\WtnW7 9vu4m=6 hL4@hmv% F4Mma `3pδwB` K j'"c]гt30<+,@a#\IH땳lKPBax3y0bQ6Z)7r&[]NHdrD|v@SfV;G tY+V0=;/qT=suy<B_`.N l/bxVV:AZPj) jkO~Tjhb\ -㹂nc2!x@T$Ҡ+UóM鋬6JEFӗ2(`8ʐ EO,)x'nAx KU8 )E@Uv wMeNQeX}u4Mi&Qj%2faZ'Gga݅sPM`}" gwDR B=v ٢{2r 02\贰m,7XzzVXG%Z7h ~WUI=p4`\,)|=] ŀmnP~LtНf{N38z>G?~f>QRB.pf3sXeyP)&P cPKU}k+rwIVDz4I`#q7FuL+oG?!05'D&_L`_BC wi:$6Ba#Bh p`0B!B! uB]w+~_c- Be.\Z@!nBa#BC!B!PG!0B!B! uBa#B!PG!:B!0B!Ba#BC!B!PG!0B!B! uBae~usoy +ήhՎxT"*+B2=2oSz~˜ ѭzߚi+yfh5tH3mddvg&<Ba2G~xFPW؂BCf̦3S?lݲ w7B! [4Է*)u/l.e8"B. dCX;G_:!7_2H?śtosK"?e7rs`&|37"mh;LT~G- ~ȫoCXyY<B]no|vHlJ7Kн`:ӦuP^2}:ӦMܰu(% G^<0F_XtwknkK/ue/n^_zz 2t0ڜi˖[M?~#<Bf|gLޚl1 g̴v^mLn 㳓l 0ٱ0B~fnڲ%`ݪUW\>Y/:U Ծ "Bkt]~xQѪU]i/9Ӽ9B[%7R:\b-YZt[:iv;|_XܙLZ7HB!Ʌi֍-?z7[gG?elٺ:9sō9W0[FǿPiF릭[[lPݏ7B,[zzRo=)oZ2}cwt:׵vWF_ztȑ#G?۟ġ! 7|W.aQV IDATujm~itanwݖkW[o«xdj/?.\p -|Wl1f|+׮]r\p76.i|,y!3퇘KMVx&yiƭ <p1W?Л/?#Yُ>h;d֮jn{`ָ/%BL۟yȷȗ73֕kC._U回fۮu]QiwCW7MG}SXr-Q#.wsps>cƵ+`%o|7Lx"]pk!–:B!0B!B! uBC!B!PG!:B!B! uBa#BC!B!:B!0B!B! uBC!B!PG!:B!B! uBa#BC!B!×eSh>=KX!nP/bUL[wxș/yۿsxg2OZ^B!~TH>se9p0CɄrfHj-":㩈H|jT%r1W#11g{1N/`= ѭ೦G#2a '%UB^:N& ٱfMRex</6 \Ơ^*tJz̆Mp|,1 A}-BŴ,˶7[_^u?kzp:5ʢ CSTOOE1,/*7E-~J*Krhd>>~_<78:19591JX*g.x~opv"~ᖕ^NG'&''F(g'!4dgA|dbj4/}J,\ϻn=gWt#w̜EBصyLd 7RoopܺI4/w_Y/5,`VjZmh}g{Y^a%{iaV0-SJ3tazH^C bVg휨Ȥl7M"ə4V(tkA aiΫW-׳ƳᷬqJi7gmMθe}' ^~"@"ݸإz@]†0lX1zg :PNjB|0"ɳ? ˆ^?^ՁJvݯv* 8'KĮtѹeOgq)sO<ìb. A9ZgvuejFA( }EU֫j2@ (,j~}AAr /)R>n2eaEM)e >6 3iX~1Q,QV&~"} * 䑗呬hJ5$ cQ{P$\-${.eDr`H<, rك:VVBYbEI GwbYn|G7@ mqA2՗_|D fdx/?#AW|Cr ѓ$ ZAAp  uAA0# AAA0# AAA  `PGA  `PGA:  A  A  uAA0# uAA0# AAA  AAA  |Saha+9.peoͥD:hD.Pò,˰\5GHPY8$TndZ.aaKFcnԹzzŮ[?xF4F]7h*6]L_=X. ^6l[\]lT/q_F2Ky`xVu&WiV"[{zVs~Z.@R4ò,ðDp˲,/պ)]] .0ءjWaYa9Rv (]^aTw%᪱5>ԉaNaN\hBԄUKPIZ1 [KjNxаl\4|{W^uיװӋӈ+B]`U/mc0Y%=C3XN8Qy}0}3b^nai 0 S06aԆRX VJVZmAU ݸ>[aJ`ԥv6Uu%7 ]f ‹o.5x:@O\NeNjD.Į.qǗk&V\d#4UsDz:VTUIg0șrViRo.8IJĦE%Q,QCOV^)e9^,+YE&q,˲~n<- @xFPJ$/ ,U`Y$}eْ>vYX8^64_, pb EXJN8c9A5lx~b.d'Q7MJYNmT$Q#wg- w* s}%T%K e, 9e eU$bKbP=HK*E"!2.$=l,Ib5EF,ҬesuIXp51[sQg9v"U7de9(%׬]"LR3qBh8_,E͍ 5Ddɜy1@i+[aФVit 0UeYIJf+4^RI4> Ė(N~Ek_]q<{^咒:v?\]XD$mqobd6 /]xD! [4pMW ,Mfja Kg<\A<ub,Sg 0u۫^@Q=:SޡKI.%Irdol:IjGFcӵ$ITkJDqd9$/3vf 힭%Ith$nCI$K'OIqHؑ}C{璹C{g $ITOJvh8qRέ/%IR/N%Imvp_ؑ$IjNj$;4Z=$SC{t-Yڟe1=W;$IrfplsqG {gkIR;35[<ܾȾڪ 4R=dzod\$ GwH=44u$I4=?!xgcnPq əsFFڷ`ڦ9F5rWZ#|y待}is{z'/%Id_qBQƏ'YZH&Isݝ֣vjrި Gzha|nBnĩZZ#Yǎ,mW7[K:zhe/Z;Ӛs{|j&{:KGƊi Vvh76=^L X܆X:>?89}|r|zi7(fw*KG#L kVԎf`0=7oCZGmU͝3w8q*5K6.+_Z3VMZ)j%xadSҡSfӯ!?YƦkҡFKC+%+Oڥ?uU;\+ M|:%s{ Vs{i]/ +KRgEèq,nY.Et#ɪfXnDQ-Ԓ(@Tl "GqdeiS.o0BU4Aiؐ#" r Aӥwa|SQ0+W, +fi9jspZʚPvå/T!BDk(d bleȲ`1 PF>%'0b8̢@U ]kBHibۧ@9 he*X2!˰ v5V5T$N bժoNRXIlZoeR(/ .۴aF])RTQ4bZLJ70R>"FRcnWTehlmܒ%EYKvi*Y63/JEx^ceV:!(V9v*r*R%iBˡr vzh0ZKCĞT٪+4-(rc[25O&l9Morm"5NeκTS_hq x_1Y9a dEUi eԭdSB^&9[aY:=UQ9_䙦j&ܼa˪vT"bxɨ*t 3l6>zĒMbU(0 Mޓ$ r`+LU ,(N7Oc_#R~*;*_lĢ7'Nvc$镹ːidHroc=lEJ5G\Iɉ!U:=TRi1!TQq'WZ시$%+uAAڀ~GA  |T/{fِWbW+C$N VyEKtn-eQnךּ 3t“{_[w{>D'>?=%x+4|p/v}kPO.:_)cr Wꗙȭu(6S CO>zU %`KeX].tAq|̫`ǭ7_XuO8:}PP;u|l0p-Ѷoyqy.A°|&Er2'(eYX%edzRU^ǫ.tCڪye L*dQZ̩BhcS%xB@Jhn9+ % w*irѢ-nlILoJJg|h"0ZTx[uwhƴLJN]"˲,GJV 8aPl8qսLr435f򫛵?~7'z^O^/;| fnxWOR~7oe ߞc?5p[M qO'fm+yp 7k7<|_<|ޟOoQ_N=|}kߙw~>,a_碌{~~eOx_7ؚLS=k7<ppÞ7NCm9曏&>oYͿr6߼5X𵇃y?m?׾3w_϶O~~_=on~t7'_gDwoZ_B3Co0ս835,L کݳ 'J$ITkLPNVYeAF6zyjBYtdlp,MMXޞl͉7>v$k IDATInWz$yL8R$9v_7a IN ]~,]}I$/q% p~I|W/Z$Y/s=%?/*g\H vyz$h_/o-$I. o^~7I] lNݵ]^18 xU2kE9ani%^"4-x v,' banHT;[BeU>k|DOXzs)A!%4*n&U/y?oh2[EsꜢˍQ}ˍ;*Psq"#FyJ Ȓ$mm JbT5%Wu4|ڷkvjOq2Wo`MGOgş|s-uXh["Ijվwp饷wo\u~͚ ߼̝x` 1{ pl}돯7['w'g> @?:m5|+,M>_Gn䔳pi O=G?A_;,}Ooe?ss5@XxoMz$3A_)֏TҘʖ%7F(oZz8U&%F Юq7V˩TZbN5n5R K+1T'mJn[;y-㶖ϵM_c*$ I R؄vhؒ#)^rCcawȖ-XQ#QmH XT+EiE4Tꚕ @sO}򳟾x{7CfwMc3Mj!3ws{DI(`* ET$Bw6&z+ H$jYy6V+QR%PX%fjq;;Vo#(N54ΪmK饆ݚDQ\U ?ngvQ #SZz$P )bR@gUS]+dӦ]r'zw/5' /./sɸ6t'W~{c/>_>_ͤ}7_~[ͷ~Uz~5'(uoU;oڛcs-_xo;w|fo~>|s̿Rmjr^pˋOroy~{]18›9^z7/bp\LP[~]x/Y$HxFF b= J^@KU+PK@ͪrLCnD:)trr",Cst&~GGjLXf9rvxYW5I\= (jtK hv,JRlYj@bPlBו \ mN?n7=Ƀ7/``}|ܛp5z7|/qlg{#\s)_> 3WoL}|s/ۿ>xc~puY.;ƛ@ oEp#Szz77yMS3j߼@7 pw?>uo*vW?sU:xkr /}yGaYyl*ށ?݅] S=E2 تgJ4b1|03yoF7]REIS_wR!u  O'1-;J?_we L4JAA>> `PGA:  A  A  uAA0# uAA0# AAA<UQ"S(R ok>+ z̑j.Km#)TOMt/nsylTOOGZY3qp[a$3"1 7j|IdZoh JX%\Ʌ1Aص5܌÷.Ke{EeE ungk]Ko?nإ^N}acĞYuKk`T?A:i 9e²,Nje;\)UgYeyIϺk@8eXA<,M&"pDzQnsKfz!r!Nkg(qˑU46F`$CPӮ`+{O96E(,bɎZڥA`BZ Y8_ÏFpJ쮔$K"yQs]5Ǯ.q Dmhž!qfkE&q,˲lZS%X«V-b[q鼾XBɎ.Բ4nxk~n?McI/'hnӵe L 9VՆXlXq*KDyR߱Mu LEBO{Y?7z,;dh\R/ [HdnPo$IƎԒda`vjrpplٿ'N5grd8zh)IdsI,,YJC$Ivd06ݒԩ?K$=8w.YP;>QJ$KKKI;;lph*bڡx=nkBSj+;P}P/@t-IjFz$ @#GZhHszoxl-IjGF  OJTV5 . _H6{$ҡjM] 3o$MFŴvٽ}cGRǧ$Y/'NrZ'5O_Jک&;$in p,<$G/%ؑWYʇƯ7[K$Y?lxqhZ$sG2:SKR_HW;@^ʵC#ʼn㵶TZ`oqmcImi)}`ڡ#}cIj9F/uDqd>~f|z)ICK$Im~/NZJdh!Xav`1sƤVKֺ}^6ǭtԎBfO Oג]yVvfU{^[uX<KSKS<358\,m 7N&'#:)U7/ MuiVBh` O[ac9aK$FȦ4' bVi$f(+n1AlځPYUa탴ވp/it.o;b%tWCirmwa5ۘY-- Y)^ {0Vd"phA$u^(dFӵ]^1Xn rGa48EsC#V;.BP pXEhQ( ^uF < $X ,'BTbDO!"e8~H9Dqbz-g,;\U4uPa6G",$fNC\,4'y)ЂDXZW 6ľQ*~ aBJg P~u YEuFR Hx",Quvh`%4',+Z`TzvdCt 9-˭9>[K$Q䛬B9! =\tۋb)۹URE>@i%dIEQ8S0+')$i 8NW[  >'e&Uo? 8ʖ%7tY`)H6\u}a8l/Enj}U:H(W:ж2Ԇ+){P9Uk8QJau%QJ7BǭF pc& B^4-KG:}@6Hƶڦ >.:le 7hUG:|:c21=M T]~բ:]5NuqE?k2ТBN^>z$evK, G8N#Kt8*6AKWJprM(\ *WtA%:  w# uAA0_6!ky=O{#S(eYa8QsMĦ؍Q{VDrRatg" G&cOYp3"C[!+7^{sWY.Yl! de˙s6OḲ."O47ꤶuk p5I\8c6 ,O&VTVrE :Hwt+1w8z;{6UަW*{3U}ÅzrrN: EƎ,dڊ(6I4 /Ԏǧk¾ݳIrj2-_C͐MKjgW)yxMD>"'갎7ViºKcžؑM(ʺt|EsI,Nbzޡbj#cTU(7>t[;%zN4kQpi|(S^8>5<8?~6 Gx]X(Ffv`ax|[^AݎM?m'ZAN<#}׿TP`UUw ʱ8} ص}%L\9B1MF l;4+0DTE eI旤RȖm/XWwC)ti2^r`>14n (yttVȵOJ7U]j,3^\h*U(˺t([;EAX`xei@ \e 54ВJT͍JTQfn`kg3胩!iCKתQR&.9@J7z+ZoXٰ|#rŰՠqW?8КXYsRVtG5٠pkjE9ϳ$*rJDRtҤqV zso^ׇ5^߬XCUDZʤV=R33xh6Jתma#R-o%}cwȧCGoVقC"oD !62&vx|gC.`>H]>"CB5'n0TKQ+k2o܌o1r |s^ΕUJ56rx-k3]\Պnm3ժriv9=8Wx W藍GPDD\ڗQ& ET$ 氢@"iϯy;z ^7+k 텃c}u=۞ᙘZm}o}[KŁGUx,~[ pvgy=w?_]pїx~W?̼0?pG!3:^{{v=pѶ:ŝw홧z=v86m `Po":}xߙhͷNxc];g<{WDpߢ >:>vt; o?ٛh~TgMV:ot]nzո/g`PGA>jlu[gvr)({)^awO<9s>N'mYܦV<7ҺށNX|pT<5 A}>pO]`b|홹ݱZn lx{g1.K3G}޺'A|6lxAǖߏ;<,/Tnda1[:Xi|w'E1gmʼn{n)j==A2h>slqAғ$VdSO;>#1<}OS}`<}{ `P,A=:v;=%9uA:  W"wA  uAA0# AAA0# AAAV_{(\{A>+ΝGp۹h|VcYa?DƏBfڱ=3m:}"3 -26޶K00[,l;6޶B^d?쮁O|eA>lq=<_~986j!tsR/=sӁ;.E=8C¶]>|ƒ"*.99m?:^Zص?8>LT(_, \Mxt0]wb۾A A}30;ŻwC= 蕙c꓋?-ї[ߴ0pǣ4s33xfmo?n1h' 'Ǯ9{w'|c'/Ӈo;͝O?t~7xt;z4 M;w-~ihw?^m[f8p}{~B@Ο?wnOB|hg&Bb"Զ+>;p TqK_;};E1;  Ȗ:'?t T=O)p+< >sϾCqn+nvۮdnGgk8з8Jo6_q~Z6=p=76~tqǝz!ѝA0os30pӰvx̱{.egh 6i=3>|ᎧۏG>wsONinl[;/> ۮ֝Ήaf={ϝ<83>x'a|Ꭷ ś~C: ǕS?slf׳`$A@r ++dJAX+ͽQg'y__yؽ"!DDsMpؽ@@]=v:AgP.89=>~ ;<xB!gg˱ ^w* 5tyĞx'n}ֱw$Azx쁱'_:|G_z'='O~w/g։ ;|ns;DŽ?VQaޝw>pz?:qlڞ9xrsbz|nz`;4"4 fkT 8G$;wcOjH?:.ޡC 4PN5Ϟ>߿o=;kuA[Pgv<|sG}ܿw?kuKmQxg%"D%㜰dRLڕ*Xe -`]"UitZlSaF'5s0f*M쥴ז |A]>/{߽M,2vaVzthhHښ>xCư.yr]2Ƒ&d}w. ݚ<5eLuf>?m)p͏s3}u$\ŕϋP(L0.-u,ZUM{pْ{hҥE^fVDϒuo B䚻@M56Fakk|0iEr!bd apm+V&Fl| C3er g3LB]&zY[h8lzԈi#-w_<%J3DĻ+h;U\|Gݼ> e}F;V&;b/tLmS06gP+ʩ\J$VӺ]|q&%8uy/ yUϕv{LDVf4!Uު"""mt|5NT]ʂK.GJKBQe?6Yat6wn[UU,g\YSOTFYn-t~`s0xN2BYWgrt7xWeY֜']`1Lwk=ff1.SCPQ8U//.,8gE04_y7yVd-TcE^G'2$y,uMY]*ʵHJ8crYoCD9È{ێ+mE91Tjm]^Vj8s;vue=T+EgE.[Gx:m^lw٣,!͗QDZc3QQJAUse20]-K6ƨZʱgW-ɝ+"S#,T<=u- k<29ەZk[[%RO?JKv(h45\h,l(. TJKh>hySE9ڄ]k8Θ &"iށEg*P'E*{l&*l#\e%Cu6BE9:\&* [ \;WqnfWfJeqA?:ݮ폻V8ë \p=l)3؆\nIM(PTSV/+>@˜$q%RRѼwvw'2R5s+{,`,]o,,g\mPڷ* ,g丸"76ќNCGE1!!%uurxRk(P#UhzlxD~dE[#Vdž,ܵ5!O`XmYeecS2V扈lM.oYPo _L .gEXçFE9+,ŊrU &+v–7#K8##'yD s- rAH vpx/<+"̝f*Ur2D-MJ+rys|j~'\ax Mʏ`RkUk,&6 O-TWD,31 ʚ$ 9%D޲RU=t;2,ű'#:c1G^^04ϲEPc30-`B磢ȃ30(>0 B.PZ?*y q\Aؾ$ꂷ2ɐQ)֌3TvwfXXQ]DZǙ]x SQn'(GD*OeծwyԌȤrݞq 2 Bl6k;MҷꞶlpl\i~*922d쥶ZTQ|謁,F+py srBѸqOhQ*$>ԅxډD sډG(D-n+)uR[g(GTYPOz(GYk H_2in+)QYJZfߪCnlz#ȑa\UVG[D7-b RqS`AE9TP|Y28e c)yWeYuW%s,q]Z#oY2Dz,.r$3*]Sdt''#K,%yzi&0,ŕzph\>raI6^?4%eiev{]i}Xr V-}bAFY|+n}ڽ^/Zz(DŽlKˆiv͖퟿nKM.WZL>RVQe`X,.rXXh^U[UOhH6suл?Uv[Qsj0yÒ|+HN2n>.ڟ,KBweYhN_[b.EfsQ[cE9W,.}͸3Fc\nthfN$flY cug"^^\VYq Iϊ`hڿ\eoZC[sr)$fOt;2 9x^,x(R%,)?jSԡ*Up;4T١,#W55asб]:,TuRal)WVDO9+v ?=k~zIo5b[2|FRInqwq=4\\vWN%FF^k~QU [_R$Xpw;Wz0:TE r9hzXvԛ\zok筨](׶]qlZc""{{L UUnbR kɠQ0FћRl՝_S^5*#0 FF%9{\c򱽋DD|5ŤW)Qh Qm5]_)ωgu*0*vX<'թ P<:u[}\j54]WHkOa35GHɫ9{9%mԝ_>qٔPIyg&> >'f:Ǥ߳WOgjr5-~n~ȞOmFvU@%{?K66׹?/?/o͚BMOf7OdJ48nP|#1v[Udwzj6F%L?i vꨦn3zɩ̘Dipz[|ٳGjduӶnLךӒ褜ab5ӚW7NY_vPݞ,q|s<{uyK8홯6Sm\Ȋg =CՅ͉|uwW՞9O}yFkj[kiN^z#[ީ^e :1*]7scοH?~hިms[)ژݖw6څ6%S0Fq k]qZ*lƺ-+j֗lk;rm1w 0\]妔x8V[/{^^rW:ݕ]F5k-cguT<۲JMT+‰dFol9 .ޤGܟt1'/6gM }LLiL1ѶƠJ~uBӳB{ap 0l]PWi4It oM-N3uC Jߵ4Mƴ./6o w"FhQA5J&Wg̵yC/|YWm Sfޢc:O}%۷UOyq S呶Fw HHTTw?ǵH̰꺎qyo ߠө]B 5m"V ZDPjmfysEWr5vpVWT|G6&XǑpe{c -y;{T2 E@_>s_Z.~ s-.̳uk2wy3-8[n; +Zey-BC{DJ$dZΊv:2f4vƓ;-'ɴs紾E{VlTw=W@9g6w[2w[ n.9bz);w*.YqprjLF$˕9ƥ痩 [:Wu0{{KS2;sLy7[h2ɔڹ7|]ܹ2wZ)L:ǼDC)\mۢZ9Mܼ|yTZ)o^R;øh^-^3aaJ"zaHuZA?|vaAߛ+S;)..=(;S#K Zew '3*espxV6gW ,.*^U[vvE^t^ZsQӨ6ovE%˵ h3"nprY;cޝ}7Ef m\WnyKR9Dlr}L!"]L~pͮ5{BAD|yNzk5OԐHGM}ASc!j{KLo0TP5ŨZI115Q&4ė۪ Q"Rc4L٭jĊ#^_C GE"(VADz2fCb[Κ(6[]c'.S*2;iIeuRelLNED .۫fAFiS+LqzUo{N`tvnVP"# !RL9VQu<. Qqr,c8(g$&*Jt'p0^iL{|[aș鰶vjfO*y:jrXC .Y6QCvb]ZPۆuX .>Ϟ&3/t-*.6k^1DC"v-O5R᭮N.nH7?f μ (qMT:U'"ixt-̱73{ҩ"Yh4m*!jkaytVW*=85ӔXkwUǰQ̜LV8=O\cyXVG[U /iX6 .V\QWQ5imŒe[KD^{7"Fkkk>'QWQ^U[Sj ;}r&m}DoEΔ]㌏z{=l<˶و.:ۜpF8*'<ʤp9y1*"ZXo`s4*BqnF"Feʴ _]%M|^L9DK ]oWEEŰJ(Eb_+j 60FEՅ11zMg]vII&mܸ5~?-ΤRcΝ!H~ݻw;iӦs>.~߳gOg744|׭y嗌D"9w\_0`~~'N谒sagnup ?ϭVQpClߥRI6mYfa.+t&JCBBl+vmiii9ޞz &kwb.@k&##)((@_ ~6s̱cWWW3N8uN|:c~E7 ԇ?}3<_<#G~ZZڼyP|*pvڀ~aӹm۶W^y655a8YgRRҴi,X0ydAM&ɼ^oHHvuR˲ڙ3gvءhLr:\~.ŋ EIIIZZtMo1cp `Ą:1 OK$ٳg/Y gRvw0wxϏȑ: P: P: P: u: u: u+R0&A:Y"t 둺 ~AĿv|#uq8~l-BHHHddO?a:H u?yٲeO#`@_2]~aD"=z̙3۾Ԅ25P>v7|׿t 7L<9 V:r!1D[SSwaH uߟoLTk<<Ϸ\yϲ ׬Y3nܸ7N4i̙?~|wJ֑83KyΜ9ޛ2es΍3*tĉcǎ7N@B}X^x!##c޼yθq ņ Ǝb:H$w~wovi4{19 ԇW_uUVZtiUUUCCرcLmCԩS999yyy^{mCCCttދ0Jt[iӦux ZCO>;A95 :LdV,.JRi@ ?a]q뭷;vL|.y9zh<#PW=3jԨAZuI.{hGVXA0B]"dB?aÆoq'mִ'=J&L8qZ/^,>6Dzϝ; x#u"jjjr8~_ݻCCC0R?{oYXXxYTJD3g|'Mt.BcccAAJ (ʸ8sZ]]?I.KC~!!!bZP.HFaÆӧO|Z駟.[lRT|e„ yyyGF\D"y7游8Lv{}є˗qAAA'O|wFRīpC߱cNj/xu}sO'{%J|ILo:uJatapOb:2 G=CuuuUA{SN544\{W_}5@O]]@@q ,JIIIsΥ~>Y.~OOO1cƜ9sΝ;mڴ'|رcm[o~d_BіpBR0GVDoqs)ԄPr3 IDAT/y晀^{n~իVpp: Bd/[lŊeee{w~~JSL)((h uj9sFRZԄ.Hx:ujaaa|| ZvzZgoڴԩS~ꫯ{{v#+?455֎3a 5j/BUUUHHHjj?mo6,XpYn=ztw']|yիWr _5iҤ?o~px_~o655uϞ=ǎkll>~׻t7˗/ONNq R\k/~'|2$$d7N<)00'Nܼy_=pِH$^ wܡ=è pB]<ٱcǙ3gnUVm޼;Wddd7˧Lw&\vA zA566755۶m6mڄ V^]\\Lms=xwyG<[op4iRmmڵkmٲ^:%uq8K$m=M'|Gaq\n_CRippMƌuqߜ \`s=WSSy&XmF\D.gee}7ƍpw1ڗ,Y'KK$ &'\^?S\D &7\=&:n=Pp::`x:>ׯ8N%Rn6#)%ɹsAؽ{D"ӧO[#l>jԨɓAAA&M;v,z`8SSSoӧO m6F:6ϩKJJ;o޼|j4ԍ1|>ɓA{9 o=''ȑ#|WO矻n55ij}>~vu]Vj:ԉH&=>z`d:u+jHtbl#. )u@Bu@@? `HVQ" F0o|bǗ0R_+Ξ=J1x@HK+V<3sϗ_~xuG Y=z488xƌ~ɿկ+$:8{B 3a+$$̙3 ߎ5 -ꝏ֩n!DVoW^y??SNc~g}fZn㸟cƌ>.²elr75 \ѡ?u%&&>CRԩSΝ;lق 7J~QuBC]RoYn}}޼y>ɓ''Ly7vNDz".0^ 5vٳgq`zg+w.Zzu\\=ܣT*O:^{k#&ԥR\./((8~x]]ꪫƍ'ˑ#l.‘#G>cǎ566^wu=]^~LxR?p~xN.7|۶L O%#>#~*1wDr>^߳gv0 /߃~gFӵ755qv@_bR4$$u wBsY _i|v4iV YdIMM2QѦMV~;Dl2q*}~G<02B]wޔ3f$%%8q>;  ~=tBǏ/--5kN8P V\駟>Ν^|Ņ |>T==vX||O 0 ӊrdgRĉ'Nj29qz#D" |?#GtMfP#.)AAA3gΜ9s& 8p::DHFvFP::婵N{Z9BFdwp<P赵oVEEرccccZ-2{mЗ_~z饗?eHFoĉ=T*UTO… ş_c-PC~Ր `%|AAAs@DW^/[nt \?w??zg}611$v@0~믿|IIIcƌ뮻8ӟpP7X .}0Ʀ&E/T:~ѣG__q: .Yλ$HF!J-Z;\s @gprfF5xƀT2P1wRׯ'xg};{ ;@f?{!888..ȑ#}\V$[awFp>O?믟3g_k)AFp٠ T7ߌ;;0Rh1۾#oAlْ7cƌg}~Sぁ>hJJ__}ǎ5dwa,دiT!ol]Sϝ;wȑ?XP??~|Koێ;~w?###暿JUtC !vqЗrUľd6u5`yv>gqqBX,5ljIIÇf~yM7 cǎm}|?x&!H9ۖN>J84yɓ'8p̙;Ǐ>o̙SL ' 3gj} ر?s=7|s?Rt֬Y}!ƊAAA<-j'MMM}ч~8jԨGy;̙36m:rD")3_x nٲkK^ر_W*R4 `߾}=/"Joᆦݻww}O~,Yk[ϊnϧp.BCCCJJo4.\.;R~۫~?3.?䓏<ڵk<ؗDe˖ɓ'6lꫯ<ؗ9s_`A\\/7x/wB` Eۂ0=dL&{W<bŊ'|r̙}fI'!NaE,K/=MMMMMMuuu7ojkk;/r;>}7|sܹ>^(**jlllll>}m.ljjp8l\\=,(:pnGyŒ+V8|).^W'644,\RD_ܹsnjSS3jժFlhh|~iLLL}c=vIrV^';wĉhhh8x>zV'Z?~ / Ӟŷ@4 Eڀ AvEB+qvA%5쒺JXJu +MSRbK҇i8mH۴ i|*'0677#jjjq&rIlhhr;x<!D3fbuuubnNn߾}~ /h4~]b?LSwhhVJsH$8pX,߿?Ax)~ivAAoA͎>~ʔ)oy<5kּ ( lܸqѢE555}ڵk+V|WuuuSL9sf;TBDnb{o۶tz<3fxn'aQ饗z}~:)çx<]vٳ_=!!Nܿq:oF|||'v_|I.]nz1Agk`LݺuѣGB3fXhD"Z|w󺳲͛?x`TTܹsNBp8ǎ:ujjjj'Ak׮I$$_vC 8Z۳gϾ$mvl`D .ܸqT*Laxx5:::m<F~D"yv]{vjv-\0 6 Kv|wKDDV`{ƃ1z"P=yG 9CӡN׃o7g:Vw X$q?e˖Tս{~8#;o< k׮KUV-Yd`Laaٳg͛9%VZk%&&sm;o޼+EA#(@KWHII^x_~G|uuu|||89|RR_ 1bnTwE^\0ƈ6lؠAݤ xh$Y `,QM,x?0Oqίp9_FI69s*222L9v#{]*"[ڠǜs?{ Ü a6pAg0ߗUsƒT/UϷASpz3P}6lۤ!C 2$L>iKHHHII ũC SRRucyg zr~l'm԰!궦zO]/eѣGgdd;vѢEUVVˏ?s=wҥYKͦhg6m4y>x<SBg  .|2ѪU(JOO;vl^^=}4˲oCd /´if̘qAjuff& nJ(z?uLRYRR/~#Fgdd߿!tĉ3fL<׿ummms…ٳgO>eSNyv1%D,qZ'`f֭]&MzGǎ;n8˅ R 盛O<9~|͖r^/=fΝ;ߗ{?6nOk x|^9}tff˗ǎ{Ŏ)*^d8q!C.^^VRRR\\\SSzٻwիWg̘yPz?׿uYYÇ}њFdɒk׮}'-y]zڒ^{~ʔ)͛&MVZZhѢ?sx 啗 ֮[O=T}}C9?3mڴW:uj̘1.+==+..zjSS7|ӻBʃ.ݛ>b\*,,_|ũSB9sfbbO&bbb,Xj}ZzGW^=w/eܸqN3DYDGz>66׮]۹sgii)AN￟0a€ϟ׿U,$ɖ,Y"J)z7n׿h"Tlڗrĉu姟~zrͺ:矗DDD<=py#b>8qbLLL]]ݠA"##cccE"Qee%qЍ7B$ID_|89`}TUU SB"hԩ4M=\VV#>y͖?+xOKKs8"ׯe׮]7oJ-!CjjjD"Qyy }aD"ȑ# /]ԯ_Çd{rpT*yy ϟ2dHTTV{'_~իWoڴI*ӧo߾ Kׯ_(˗ϗ.]Y^^nZ$۷oYYґ#G|B/rpšʇz?z3tP3^zG Z,|bqLLLsssZZZAAAz׭y/9p@]]]TT0Pk"W_}u%Bo߾~xС/¦Mbbb?N>=11 -*ǍWׯ_ᇓ{=_ʈ#xѰ\YƮ[N,OLJ(ʚs,5zp_ '|gϞA9ڵkf9%%E$=z}UUU :j;wnᑑV]v6 kܳgF:uJ"|IIIbf۷oLLdڵkȑ#WPP0h jMHH)))q\ 믏; w@SEDV4M߸q㥗^jlln7Mӿկ6m$lٲ}?~gyW6o\[[sO>ɓK.O]xڵ&իӦM[rC***V^f=uuuvFkǏ/--]rn0a?㊊zH$u%?gٳ?On)S[UUwߍ=RPt:dzsΟvsYYY|EEc=w=.--3fLaae>CnXjkkkjjXݸqcAA~7n VW0ms]K1g'9-z]V澽KR_.1IF kkRcsCwA!4i}s﫞ƿ թBC MQr |GJiUi$IRFʕ;C0- @5OԉdKcDm9H4e7/)ǰ,Z!ӟRBhb&E; ˲첽]2m+hHRkRrZPJíqhމ?TiB6 ]9 hDf5MQEQrѮ epVb&0*$IqI UYh Nbm)($IgmDuס9Mj")j! J9I$)W[ MhPIIR\K)JF4J崦u]5-Wz FU KJlgbeJT+!խӔ(>]Kɕ{C-+cjB2tk)2.ZڦQ*XF.gua6H\SjUt֭ӤI$IZ] Jܚ2*PGH_t jC\NWOD`Gw0C,ZܢeFah#t뙙s>9a3՝jY~[4y5uTKb]0IҒreô+[n,NHWئiϘHqe$IE&ْW4tVmN]|W gVJhq ZZ7;%k&XjSd>Aݰ#[2j|65Y 1+5+ 4䯙s3Rgm"B g%/>С ℄쏮4`g.+&%zDxrlf* ;=ÁũT Wf'HX̅Օ $k:Xnjyo^ HeBXbT*yƩ:VÅ4-sGuzyR)'BrF fPN!Br'BTpKK>}سy G- cʳ,li!Ѕ?f]{aѦ-ɹּoN>;;ӈBH27Jod$+r /LX^MMoߢZlol[1-"2W׳mT!$elG*6Oe,3sY[R0I2svCZ2aQu(Bjnù CX 7Bm5Z8!}(?(q Y$6ssZ.#Xh-&Y~RNfU |%eUݠ1KJ ɝhv!81 u[FF!9&{][a74KgֱF`wUt)biKό bwZ TVNԺ=ºBrmmmny3mJAJbU^Eɥr٤5\*GB6)-!NQ K$[{3H RFo3~"J"s,@ޕ%X1kKFyuՙ;F- ٶ+ZBHʵd>9v:7BH^YTU0P3={[ I2W%cnܹl\s:Fpޤ!iz9&K(@)f9I٩ФIEH14eX[kg)B`rr *50E#SMTۡ FbXRJd-ߤڨUF&J2%D:a T*#ȖSoQRrDQFRMndLICuzvCQNbY yIRI5BRd |(ߋެViҍ[ur`srJ12R&#턐k,&*Yh*mƷmԤkn5TVX`_QM7QNUKRrU)q6nozkҠ(R'%)wήdH-Ji2*m *U:1k-fׇ(:8ۡD#Z:Ri? Xd)TF1\NB%#w]Y4Cڍ]!A$:!>[cv-!-V5 ܇8428nkÙXҢuUKo2ݤB A f%gVsA$D@b3*!zP;aT* N8s*كhX@Am!H^z`m='w@}G|t-][@wm!hߑ=nɮ-` 7px N׋rw@}G|-BD=xJ53r9|Zo!t IQ%Wl4;BA7YOcpZ9IrKmD9j2MKh-u@Du@Qu??lIENDB`emayili/man/figures/emayili-github-card.png0000644000176200001440000026765214132315227020472 0ustar liggesusersPNG  IHDRc gAMA a cHRMz&u0`:pQ<bKGD XtIME 4-L1IDATxutk&w NBRXqP@{q$@g?>>?dknzrrN?ӄ SΜi G;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;D;d1:>n٭)73qif+.`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`(`,F?+qUUҽ[/{*NJq9KʩitZݠnD[oҔSNY(Oze㗍4i\ӸJtiTwdݑuGJNY8e1:=`[<{+|u ^3; (Cqqc->XbӋMmuVGj$IT5_|UI[m鷥al F5ɚd~5(ۯP|@q^{}/hVjg{K[nԿqKݞv{FjP`*UHG]uZhq bi*6/ټd)f[̶mo0z4iSiƂ f, .,}U_tNWzzz rQ*FU+_٫fV=_|s)eˤn&UJZ&i_w_5&jqƉ'J +0tKjy=X̝ RzVVNfUrڧG~I~*V8J'Vϗ“[tRvI{\q/^ƽvݙwg^in~RunԹ! 6 ۀlRᆅn(9q8п:|߲>>>YYYEZD꾼˥:s-RZߤuL%#%IMɥXl'¦=O^ӴdjhTvy###+֯XBj>1acƄIM#F6q /Creee4+ŬRHnͻ5c쏱?p`eɱO$ E#}+Sy@ǯ$G>M:74s+L}7+S)VOdfͬ EŰqq{^xcjj*ݑE_J~;>sAj0ae_Jꨎh˿I| (PlE{]W^fze/KI?&d_#%I=\)ʫ;ehdTskijfb&+ݶ\B}[H*{ڮ#gb_G߼otKfQ׭_~ݤ3 ,(S9 z得m.\HcNv8d:::vLu:Is2lȿDJ١򄎧%9V2y;ܵ4Y=Xwi#+|-fW!ETғ.<.hF׳^z=Kڬ,iw%w7qo.cK-RQG%9p0 @#rKҍK7.!qCD)rRIg)a)EHŊHr ![QF9dq |)Xw>WlKK;XzVTض;uoRՄo?/@ҢZ,j!ztѡGRw/cN97QQ걫Ǯ7J24+~~cJ\g%Tk(,^~Cio~KF/珙֚oKiC瑔!m'M]jбңsnJ/^KP'[ \eZ&]]vue I -p4Ku:uFOZyh塕sϝ>W:u hUVyyyF'guVg;wK㧎:~zH"EܳXjzږ+DFHN5RߑLML^q,|nH!k<]9g[GvTtm?JɺW"}WߕW_uUk\׸qRGkGkGfr3ӄ SΜi{/666^lYeirRԜ9Qsl8CTFrX^4[Rvy% .^rFog͐MzCG~72zÒur=g*h*h*(S~p٫f]:u$wa8qڴYNUҬYIu{YGjVYf1/Ƽ~rޡRVm?!Ia$ZcӕDO1ppIZRzsT.Yx}v'''s{9G|ΧuZG: l 0&L3SO<%5^;o-%&&pbsɵzJ)J_:Wp?K惎_#KlGQwW2M f)䟒_jլUVͤw|Rܞ=q{ [OZy4SJK<9 p #|d! ?0ZhxX 9\KgtgKQN7^H O=ҫTM%YO$mI,NV'tR'uRR'uR'Iߗ%p_ʪ,={*TFo ;"maȆ!R]mw% ;a{pI'Irw),]|}ɒViVi„SgδZS={vft Ih&t!giZiy~/R[í68ӴGr|:m)RǛKmB2p|hV`C7uSO Vz%bvMm&%6npD 5k+wܽrK_~Z&LhSӧFcL8mڬYFʡJk9 8(QhFx{OZjO&?MZ{k-i ',:3vq$yUbL彝K3p(/fvrҹ[/J^۵lIK+[K'~y .%\~_Ju;Zj]uMӸO>R_SKN-Gbnn.-yђRt _2ʬ+l80P9TNr:5z}õC6I4*y E >v>ǮRRפwN;uVTŒ9Sm(m(؆bR *(4"Hzᮇ 4aԩ3gZFǩ=u3:%M.\T:ϥ>L:?t~p4hQlڒ`ѩJwK CJRɵqjK1FzPsCҋ"9JIc&Gז$cۍM22e4CNK64diRI[U[^3qil?Az哗OfqHjiꦩ9 3C\%缏+62TeJ)\ch=`2i~\W$m5:FJ{EQ%u3ݷG~I :6ʫW+/i灝vggg^,F5]5y=ڒkK.iԒQKF-.BKI;S;׷dZiRIqRC><3e^K2]1'/I* a^W5H>3rwGzr՟I:mi)7HEe|^Aڭݒx8!_c|ҠA B?O%ђhI4zK*UWYsHTDѢ سߚfҀ+M=={wݽ'9V8] qUN)vInSgLWMV'Fok7Y.;v3Q(AiK1]REү.xҹK.v=~xiꧩ /.q>9rK0`/W\Qqkƭ'XxB\2˂H֮IJTd&%mRѻfjc 6-ܕ68$wGIPNң#K#pȾRobzoԾQ[1#i嫖E%59\sR@홍'q͉'ݲwҌg3xJks_--u5)b̽'J/C73.C =ڦqRvM-'9O QͻFK1cKɉɉ<"|@ʫO^}iK-d&ԝPwB]ȡ#)Oyn!u㥠h]^-W@ՎK&_S)in;̒e@oI* 5YZ osoi_Hq3R|hL1~#Hm6u^K'埔CWqjj)]yxᕇRtN'+V^:RH#Em?֒#o))xV{xHu}R g0aԙ3VԿϞݺ===k^׼yIKr.ɹ$4)ϤZz8hC٥yע p`}W})L3mFk.窟+&N6m,Sc@%].⊋RaM5&06 WStGLq^JBv)$6/T Jn`Kc<_'J!>5o~(yEdQ>~W٥g,)G¤o?uש^潞zkQ~R<#uV[KKK#.Vonݎ'L?2Ro}[isgd֖PϣT;ErKЙtg,$eZ5\R1]zH/y:x acM tܤ3>s[ޯJ?(@Zqr' _6| @)I9r$VU^UyUe6lD)=Ӻ6o(_OzWcFJi*49R;0[Foکsr24db)CU}zJcRTy=Wr.Oo}z[W_!ʪѫ2ӄ SΜi{?~_JS:L0䦒J#L̙$׾g.-Sp) DF%SFgK$0Ԃ&)xsORj2aegnI^9YR SVS{OOjZiե FKb>2z+C`1: VITI}?~Kn9Q"DD sQJJ^xW[\Fy%!3ϕMU[D5zE> asI,JrߖaM]{фm羔^"uSR o>/J갱[glU|DͥecJ^^^Fo .|G'N-(쎲R .-\!]tU7bĄGJתU1'tYZV=l!Fo\Ѳ5J3 k:tOSH\x.{Ũ/F}1JjOK\RŒ 3l7ϔ)uu͖K~XRP2-/8q+oV9)Uv)ui~+fk{V#2sw%}jjZbyQmD&p`uY[g-[&%NʝDӄ SΜi{|XxRI iy字&]ti%nOV\s k5H3p=;L0]y恛A/j2]RM]>"K(:JM/e(i]Rw0I5=~ΠA;_lQc;ۓnO=I]J\\7nr]f2YH/hC?p`=1U{Q,~K)E"94tw 0z+^ RܷIl(=Yޖ)j8oyiH!m\q])ݴwޕ_ 0ij~='M(1ĄR-YfY?S%S&w~5:KOyO-Oy)??PɥuPRt+Z<"z/%i,aOs>4f6[j揥78ZJڛ7iѫ'{G{^~zYڼh͋QG=Pzͫ7 Tۏ5:su<dP=)Bg=VK3(\20].H]|_ژ}QRdcM,E\}p`Z $|ymƷJ7ްzä+xZsa;  'Hc:5zKmPx_N<}A>瞞{zJgURȡ7zL|Zy8%IRcWΫ\WI'KE5.I!pɦUKY%Y[%'O,/mVmt܅sIMF6dYgK~N~N~NFo I:뤯.fI7]*X ?V^{zeÁ]$4~B&J/K j,*wUEqoFIW| fJnR)jrx]) W In%E:N<uSGj2e'OJf|lf͎-M4KR P I?4 ?6՜l|[<_ޫ 5u$96aVn>YJŔ/Ji{7L5G,W%eSw`y4C;HFtaFZ6x<xwL&L:sjt|ٳ[7Sn~[Z?mӤ}]jٔgSMiyvNg,nE%U*\ɧD "vcvɱVՐbs'\'nhi| 7}^$ oͺֺֺVyԯM6H,,,RK-.%Xb鋥6Y]5]r\5)Jk;lrzd)el Jwf&J R3}iotaɭjEYI5>mcG˺I-s}[ndGwOI|uuut{EICo 1T @@iAȂ!㌏3>(ik[b1: ?Zh#雥,ftKI^I^I^6sOY]%K{I/Y'cpnVSsiy$iqbg 4~Sm7N;9>)IO K+{쵲4ghŊtXu-a3M0u̙VAq߿gn݌NQb=^xCڝ}w٥FyK:uS%FFFۍ558FHnҞϝA >U}bג9KL^9,^}oI=^qJz@RtCUv\?qFm藢_ )9dq8Y"!&N6m,Sc@?pN;I#݈來fܚQzɃ'l?a RTU+ Nr7U/I*`YURέZ4,-K"ۥmlc-dIocVZ\hqŅ}9Gj͋6/޽{[rYe@wq7'0gÜ Ҽ/}9K3gkiwZ{ThPRwx&9O*ߢ)+SgSgUP$yȰ>%)TZR]W^8ibo1tŦJC4DC$-^xRJ*$5|e×a<*7b6:ђ~I%iI%mZeoUvi† &lexYljr2<+W_69AFoz}W:RhXCH60TmH I RzͻB -=]{J]Ov=tƉ'nH**aV/ N9j5:>N٭)Wb_IMMM䪓N*޿{` FpE ,XvSO%5"N&/IRn3OLsRHbyR]vY+ KNs+ϭ<$S[S[S[ĉӦ͚et |>|uZ;TSAfpi:vAjݯl?aRkt*e9|Qs'K 5^uu&/e)͔I<B%56?ݼDDD/iwfߑ6 m*ORC+w.9fy?K,ckXC*|G>,Hr+%kYkYkY_bt?ߗ>dieeE^{m?ϴױ)Li<K KCe.Et`,}7l"\jWlKxH;^duh$7o?E_TZ~jy@RaM;4TrG@ڣ=#=:죳N;-$M5-״\ҽ{Ql?enގJ)Vбp3F`[9UFr.#EQkϚ%n\89ARɩ|uuutLtLⳋ.>+'wR՟W.qr-`{ǺƺƺF5fk4uSK;*eÁV_Kq!UDI)v '>Rv`;[G m (EݷoyսY25vCeؗa_[ܭ[s7ޖ2>4SIT@^oltI:J{clyNGڲk.)1Wb\6XB+% >$)uֆA&)Js*QCra0U3EIN_RשoS)B\ Vl*5HHJenVIt;>)C.kn#/<"92Ș#RZ2tsioyIǓ'{_o傖 Z.W_i)<6<6<Q6X#Fb NߓZ o.S9}WKʾ* mSzA@4Rꭍ\_2gm俳Ӻ$G)NFo j)dX$2ط=fKK lTArZϳ=ӃNҬ'z"6߷Ɔ "okʡJk9 8(QhF'^x}B*W)J!3r9Ҟ|3)RD[>:KS{n oIg֭K7{MCK:FoK1WlJn\9={EJx6_n]kkkN;}1E\(s̅$cvT<>>[o%u%N5^SK/d'x* HN^-~ 렯P6{K^]n-vC~)pP٨RڝN='*a\'n̺1H+ܺ4£ K!!!DpJ'ǟ/-`҂I3/%MI4EMK&.9 HHsC+Z)e~=:J.__lʹ.Ky.)DGcJ)RUtM EtY1}{HJK=@@((IO>94m(7NRVLY1eEIK^q/r4i'YtfҌQ3F%%MN4ن̿8DJˎ23ʡN>)0͖\S|J qtrPr]ۏ(Q V[}nI-[{rIɚښښE+ZTS=O%ܰs PNCK/JnfMf@HB.k9-s"S$\ \I5+6Kމ W)yȹL#ɣC~:&E=|a^)F;%-uϋI*&[[ҁ*UY]eu_D-^{I%c7n69 aGZk)fiנ]v ~vaϳ4!|Z3ӧ_I>%=Ur"_4I"KAA*yM_%_3ICeVw?W3w%myy[o6lݰuVRĥKR<4# uد~˺u/kv+ |&M>*}[ddd/ ^,EW]ݦ#өdy8@R͒v*{Ir瓔 J2z7^r$_,$nH)yt9\6I)æ z]E$KBk%^|W7y4.ia*bZ˒B[v%(\+)Ѯ JM ܳ\H7^ t-HJ_+IVz#b~jվ}API9;옳C~$|뺮p`)UgiE왖%{َ#;<6z+gN_IyZM'[9nIi{pL)9sxf ,ML)N z]=RJpK~f ^]/8ҽu/%XgFG͔|:jtviv\ )E?6eN); urkٷwm޵yIvu}w9{J/ݿtI^( N9j5:>N٭)Ŝ9s^8~Ƀ&ZIÒ:z^qbrH缔"7KB;in999ԩkJvhڡT$HlXl 62qif+xz ]W~뻮:)'',H|oKv\JߪIS$DžLWŔU*u-m5؛ʥ#uTu)ER)HSHժf+}|`9e^2k\;Η\/H!#kN$DW) 84ݼ3gH7dOJ3:s"E܋8p KW_er+Kcw9v$ILsU*e,۽rH>oURժ3w;pK? qԨBΑ{Cs,mute0&=6YZgۤcL9-VM<;L7əW{n ݫi&%'G6wLQ/b+vr!E.r1z; 0xwUZq]u&ǚkrL{TQA^JnBy(eCoJi6 ve3KT*~֭I>4&}\R)ZFޮYRћTV4:%|MXmϤ /;z_M,<ܛwRްa{:PJ:$A4z;[{$&gLΘ^ _n|R 78,9pRƒ l0%z1٤ M.eKo]Mzb͒9T9Fo,7JvU|C.S^I7K}4=o- *{,TazW!9Ϝ>T_ ORxS S'm%ǶމA'%U)xyϯ>>f}hhТRʰa+˓.O|HgN%< öM Vt>Z2s3z+wLCMMե 73{d u OreF|*42_*?TpRl׏{5?)-m?vB>ZUzHʭjy *4Jb6jJ*)iRҤIFogQTLTL}/>2R޴yM+;>å>\j3ܪzM4+$eNRjVrIQ1\?b:eꪌWm+NJ 9C͍N0okC>iʙSFHELmѩGL7J^.e*e )c.)fl<[gVl89 =|ztWaՒ<3D nj)̀&ن\[/m*"d[?)Ͻ;\>Lt19Ij$pKSYH9{M<*|]3'$KρU$If[> ;&u۵o׾ЇC}(E,Xm^?{ӁO>h7&u:KKi4Z We]Y}H~rQ R'B~sp5%/+I]{ï'RA\K3:rKI͌M=FVM5䔲\50K!F @GR8Oݸp`TI*{'ݽi SimRn\o,D+Yr{^4[֢!RGw<>}[)V6& r,h>et(6,F&w]LJt,OH/OJ9C]cN;M%pNF|,]ͅtB4S4A=VqG1tFo {" Ŗ̒wYN9>t{Fo {"{vggF|kLML}N@pLC$dnwo*IfWݍNKҋF;R7:bo4: PxOV2J::;ҎoG4: ؑJ*FG GRK|Oe ʱŤ;^s4CDYS咷Tܜ[!x _ N. Z eѩbtn¦.R1ҠLtlat:1 ވsCM23s;-vir]WDgҖWFl\t@j!GSm_2%2h$ǁ x;=TZR^ H/Z6-2p`;.7si`>ˤ.9 {Lj l*艻ifiSݓeѩYͩMF9W{H=EGXMN>f`[TF>R">H+?r\MjR*zR/ /2$aKY;biK?侴йQNb1KWH*UW=@%L4M V/Թs%.G6t}v8uTt4)OX S{\_skc1RtIret8%Ksu>iHNIˆDJFgOCoziP}|>s"PPR2Aϝ Q{qHMBr&-a\,R~D׍F`O, MbSwv;,/UM)㌥F!_S9 xqOpBj?o.ɷSp"}Vlʠ$S? v,F$_ȭ^ÌiSJC?yQԷctI깪RcϜ_Jit: J3c!]˴,+ekTXaW)S~J2p,ٴt˚::Y-7Ig_{zUϒ@RBSr875ɣ46F _J~Rzr*&M64iCnOJoGj%4HH.ktXd3WH&Y̮ft:w  'm}gH|ڧYJ:'sΫ9XzIMW^|~o}ߏX:ARgifٟsZRxّIH)IIEGKŊcM2Cxw) \|`I)ՕH~ct8S[)T9>RŔU2``xQ,eWKæ]5?>iO?s҄NU$emuBrX||4ڳ╔mJ#hj@/+9{jњ9Nc;+j]F4)XEїƎPBѷ,MsF|ޗWV9fut0Hzerb^[et:oP'*?G:Q3 SZ<)o/]5VmBykK9sO͌N .͏uvvr&>J|R?)tRRaѿJ%w宐џ_)qWRI_t$xC7Ik-o4h)sH .KCDRY'fe3MҬ6\~e7ilnI$nO9 -UoB38.YyDgSnAR$QYyҴa) 3W 0~]?ߌkx,9ַ4TdI*JK+Vk%k5igO\v_;YT>iHx/A)Z92 2Keq,{iy4ı:{͟?jtHZ=Ku}yuLq-Ibt6 @̈@˩qF᧽3>N_H{?t>͛7.ܜ[n\vWe< uy.oBNw;oG󽯊"n}u̵h:,?՘Rz=N_GšJ_ze΍;e6z<ᲩTdEW$ 7k4AH/ǍO-cQ_H]*b.1/>0[o/F/H|ʬLץWScc[JǦ1]_Nyw9K'5H,#[W/ݭ~deg_Wqi*JnY]w-6݊ R6SZI UNe%#ک+/m1oY4St=i&/Xie?~/ϑ+WRJ%0zSmE*[84d.&i^҉uk.(j?e/C Ilg`t*w}?9̕3=.tS~Gqx~!I6 ݩP:Ptv REq>v^υ@9&Uɰ;E-JZ>wogt::y_z_z.("T OB+)f+)wRgq.'9m40:-P`a? qҌOq6 O8F=j,Rt]I#vM1ǥLR}kϤW @xd*xrTe.w,6a)r875Jw]C]ҸcUiNaSZHJ_w![N xSNj J& . w7GN؉^a,U;y])$Z聂Yר.H@׺#˘ҩѩ |3%K(mO{u-x aɧ"+Kf$kB; LR-@Nh%7IKЖ7u7zH%tƛϩ|\!+so}hYi}N~q9^GңqNH|P|ԺMUeV̐qǥwi6>Pk\od"pxQOrʹ|?vt\4yRKwWPTH9FK؉o.Rw:\џ/]|&]tƽҳ+ѻ]g'E8pl(y8>NJp`}~fvg}_vX<𾣌 ]N9 e۵I9*3,B`|'_H?@z&M]yVHEob5>la;.IYr (DѶ@Z\a֏X\gGl½gOOO . }QwTn44uկ!?7n|0(s6zEٕ%^ϊDžEGH_bGMD|:f5<9v[J)`H{ۖEI$0T /+5JnL{PMz3ANz5.R=PnYWQ@"vG'`_=}<[tod]NO(_a Q>s܋QS1m5M XzR8bhD[8x_PlG`8:@Uc[g4jU`c ?yMGݧjyQuKRŏy\~>t+8iƌifj8;uEP[-\=dx*@v#S@bR~|Ew~jqE 7Zm4 <=x׵v˳w,dSE[g?G)HE!4\ yx`c;OdP.l3`zF~6۪ bd/w r44`t=_{LSo0";?_WC_Ӣ;xnlM:+|]~QT tQwԾ@_ !Ra+uc9K]{wL7۶w zNI%P2S2 JH?*$ۧ8d{%.ϟELhP} tBĎRyꪔF kkY˥.vHuXً 8g 80l7nN;:Z-`WәMO{c#""IE({,9x#gޥʾ_}hc4ȴS,b j 8Z h}wqfUH񈢑のgߙ1G%Ŵs]j52\=|{x/?*-RhyBd`\u_;'ޟuZLJԯQMJۗ>p}?+> -ϟiRKTUjTo|wuZmZ,s,aߡ9p72gQv7)ۀמߎIYL*ƌJĿ:BteUY4^@=QuKrFFvMSK{i> 4%6fOs j|-X쨈 a `"$o)&F'T ^Rx%Na~Qqvq *̖WRdˇ4&oD10LcG^;[h.Ll|P_VY+]#q@bf?Xlo}g-6+`WJga h !%tG Ue:]=-|x*߮{w'04@edJ+Sc KhϮn;`#c0g&%=׷۫/x?ݫtnA*P`j 翉-l@c^U닕;q_RG20og$$$/޿kQ >6t<=Bl60&.gqE+4N: t Oh?\j,0[>FМ[cqjCU #255ewxQ}GۮGkq~ZPgV).oBBǥcl>)#oliv/_}Q' qp$ۋ}+0VR|u*+\`ef玟e7tݫTRB9O=? v+XRcѪ1 ;3jQ(pw@}_]y޼W6R]iYNO~p]eU;j"".]3w~ zV0^V ?bbGTvJ1 軱y@w`~* _>ύO*vDCR^ARi$`ݲ6%}bGUCN2B6x'4`i!İbGGDDD3Uva? BňNZ rCQx}XujUݫ&ܰo6pWcӏOdd,}̜9 5z:RA25YR KE,d`־Է3?^HjϬSg-=~s VZ(`yӑ#l/0C46_huP@6[jͱgKW QlW+}םRK"hFz ZyRû/mPgʼn Y0_$+ӏ@V Vn-jŨS?4^odcTyh4&?}`}1iNMNŎ2wP$T-SxdqB+? T=\xֈ4*ͳ#5D&?Y+y&a{l`lp4cxPn:e:jEh=1Q{`1E^DDDD-ywU% JZx ݝz}<`Zy_*X *¢[EoSq;h'?2Ix}S2LPJˆUt߿2n)& T_F+k'v+㒖P!i]czg<f:vliN_){dWj!uNԾ5qB%rەx}';DMHx=_yՉ׀Z#*ͳܟYsGKr&]|eX_B8t6@Zoym7iz3BpyM[WgG|s_?i`]=剀DDtl5o|E`㙆_ )%Wx+vʣb(Qט*G>*)|Py! d@Rr_ >ƺ9m%KB#/'5JǺn5 gy)(`@" TJ(wg+,5]?4UJRER/kn3 M?NH|Wί-0UQz_.n5f-`7CL-w)~oIYmå*[wLS_7%'KMGv1c@pgᩊ кb]fQ&RÆ6JO%^u(W-;D٥X}{Sz6uDx܉NԾ}N{tjii=f oBrh.b>lc Ĺyk'\g^K`*zg@9s=˻?#@Es_IKʖK{?w  iZZg|& b"!`bt20yGp40:- ua"QbG|F4T݀ߚ6^brʯ_~=qáU4&*}$U%Ŏhx=vz-2u_70V_캤[" cqג2񏈈r#&#ݥO2QufK^33qEᄡ(qʪ LLg<.~i_'(¿Ftf7p}_6djԜ% J\buڀ$H2S2Iz#$\x:a㺽mx<QRG5 -3vg3}Z}v!aF:\+VЮ􃱫 `q3__Кq8wnH݀5\'7%vT9HW3\6di%S쨨~0A}@ʩzuM;8K({`MWPK`̧c;RIŎH1&"ý>q00!1xp.0({BoBtzhv,(jzoZx9 ` L~5.r\\\LIDATN B˰(zdWnqu\}*iߪ}sʬ,xÀ otQ-?}T2GkBO@J&˔T~JZdu5}'|u!+oOckQ=vǬW l Vc۪^>mEvG|2jySAtC}m tjp5RMY(~FDsMnOfm|R5Nh;Z%tXeuk`X0hi&K{IK)vߙ*``hEG8j**ڈ~̼//, pnsObSH[QƘHD}+Q4b<Z{,v+)(ś}Lí4:0"_/Lx\¸qE#6g73.llׇ*0#a;?\zqV_^ 0sIGG8)pYRl2}*ׯTU#@>4ixCJܑfdN^娟@뙯n#+up*PxzL^N HZ6R C:ם7[[_y)ӟYNmwQ<.T*P}ҁ-~O̗]7n >ȵ;:b ݊MRy@6{[k%?.?-MN8Uz3=~Ojl,toہg~רn_CfEĎR ~|^TFhלm@͆Z*-BJIqڮꛕݮ5X*S@O*/vd9cB2[Qmf"""T~-qpIQPAqM[WU^ Z;p6^E#Nje 膘A>p72x*'2T_Fw:\"Q "_A3%g>{ &`X,E]Q;(T7<\z7To'P F;:K.Fb/ZFt'ؤP( Q.AJeRӜQR# (7ūD{oeNTWZ5suKθ PNr<0_} MʖS8}S@Sآ/;q鷷\SV@OE2 ~! ~:M'OpԞF"OvI};B.}~MO=KGfݓ~p묏f\-%V)Eƨ-v#i} jsh2`۶"OÝ7YߺĮOɏ)[5l=X1Ⴑ!zTĎJd/gF_}W]4( I:H興r94x\WfL- 5Yrd퍝6 w^ysI㫝04 1Q9~*UJ6cTIX8y:|}:n&+)t=SFKەӏȵ@i̝؋@ъ:M=M}||7>dxj' O *=?٭x\h9WXtTӰVYUb]15-?V9OKll=֭(yōf5Ӫ;^-Q}(U4);!"+g|y2We9v~N+j\R/r>1T1;j"ɤǀo_ <<^{瑉?î/JapC`C+FCjrZiTlq TH`?bG\Yyܭwϣsf}&ߒ6;:""""c QQUJoOOuLjbGK= H(-tBR] 2RbGsiv?NG{|-| Uh5_tQ-^Qtݳ.Z]p܍WDEsYSiI/EKb E<5U4㇋Zqto>^D\"%Oy!<%&dg$?W_$eFۥ6-t'XުHŎEL3`v:)Zx;6|1.i@cY#"""9L$"#Z.3Nǝ*s'RnOUB_ ^]L>UV{B2a\⠤Liw:h.Xdqtu5ֿ-+V*^ث#ʛfV_%K;JR"Znts+qΏ_Uc:+V)wdh_/wW1gՉl;O8(FR興r.ɍ7:=5;iW*"o*;7BTTԲa' O;j""?@7`ZMgO ((Le 0q%F6[V});:Sf -\ںR 4תϒ;:% ) n ><~x2z_:@6^ @"z k/+dU׻]\}26ZD6Ȯm3~W;IDD%++%LxqN;J) e ĎNT<%q]}`DZ ڊeVɮ:ˀ0ߡWrɪ݃}[7?--vTDDDD)zq롷~ԁFSN? |x?; ^Wz wnl`E!#v7ԫc=j퉃L쁡O-_Y\=>Vs+HHM>/ZYUb]_ߡJzj-{5lkU?w0^a@: u p%vyǗߎewozSV|Kt麟>e~?R -gſu'sZx$V, m@$)$"5"+E .5 BuMq6wz<þc@+S>򅮩^@oZU+-\TB9+T 7.Jc)Cx! ׊j|<ҭSXsР҅2g &Z\mxޥ&;>ά b@758NڤlI9EG| ]Ys37Ֆ 6im\E)#--#Qg_w' 8\]w`T 厕K*/\2P /,C]kKNҐ.?& {jpE~ ]&LX+gI@3 ;tRu_88$}xOx[ķ_!Q{O-O{&0i|qvТPNaFs puΛ}xqi@c6s;&3~Hb%7qf=2ݢw/c5  T>@DUEΨTyl4(?ɸCFڱHCƛ݀+ +r\\| 7^Wd 1 y/oX%7#kSck_3Sӱ@fVҿ&&}~=>ֳ?OWͽN=? px ߏ PT=O+M@҄ ǥOuL-~#"c Sx]H@g,Sw}ܰzmʏ7kЯYwyz$Xb| >>[w U<.\ <[uCuI)e< \6) T4);PߪJbጫ>?+ 'O#ݾ77<0HqĿW<|wu_ZnL>nX0fgʘxs"\5 immwz͙N.pq}7 /[5JxǙnOڄ"f~Z轹j IL$\ GJ1v7kU/xH8P:C(JnTlc4ٸ%vl9S䢤 `[6JdY""""$W֭2؁P4uFe}Ng/T_0>3'9GBW ,uqy GW?5pk'3<Ӄ%q~wǀ#ϟĿSW/`鐿(_ҧ?*ͩOgm\Ull=ЪʤjSH\𜧼x&8 \(v + 3?Ou)dʋW ]Qbva͆0>?'vT9b+snVޔ DDD%eͷ<oL o'8KV>?Z(ѯP]ͨA3c4WPVRY@HEo)p{& %y|GC?h+n݇UnmߴpLZ0P_T@>_ADkJ/Y3s?ρ=gzݧrJ|^u_;78rGzu'Z-'Q.3p @eǣ/K.Ck.Daס4-'ƪ_;|,<ЦT:neN 05-vʓT54ҭq `[@AbGGDDD0-rNHȢ磽~h L ZD? (|RR] 0(sV - %< F )BFb};]>^¢ZFo'IjD'vâJ*Zxi▕a6^_W_F6wrSgӼ~X /D;ş ]Q0Rџg Tye죙B%Z_U^lLԀPl~ZՁO->09Bu)_Js.41Io@MٲD9GjWV*XN7\ωs^DKߟT |;Q)[SnD$&5 "Q~F%6,P رS)a:5La*$VWEG|Q>>Gao6x^R%vDDy;D@mPodY~<ߟJb&Q~ <@jNkʶWr=`۠d-*lS2wW+Yh2a2WɝĎ2هN(8l5V~`o]"p1@@1CTDTj`)a5FR}l}lLDDD枧_Ydd_%""""""""eMz X:޹oeQO;'8(vtgߡR;?Fi;ns Z̀kSU%Hʾ˟ˤL#""">L$"""J#``ȑ7*sƧ" /\K쨉Hi5If/E.k|pb'K]RyިLgk 7y[P|%ZCbG{) ;w+Cq1J2 ޗ̑8> \eߓ2T &1EIk >1uqt^QNuKzs'cH@LX*XuPmxEf%JV.7V')"9'LK4* k,@G[Sd*`J$]Y[Y>N%"""5V?^|GovJ+ӏbODDDDDDDDb~ JbEpûFN@ŎNJB-Tt4\ (rU;9:[QuQbׁFkH@WS> %97uMç$;8"""CMrʂ!{ JVE SSG yy޵+/QzѪ+"""""""" nR Xjh81TJ`Vq|LCTb5n\n?Bc5j;8%+A0mq}@B5n]bG|=r}+>}"Thb) @C"""܂ DDD逳IeV(ѯP]Դ<[Q Sts}:aM`fd+E5Fxn/ ,yJpyPe#eK\SV$v.t8[U[<(\K;Fe}4.34?5i-(߫~Тp ۥ'_ .$z&JzJ)@[Q~v)VA`,7!u*/.Qx$ )u0;JQuQ um ~CJ{Dو_%T.~oԴ+UPnK-\죨|Qͤ_\ nwt[ bGGDDDDi1=% Ps-f 10 ^#vDDDDDDDD߹-%!hDlRIe)O(ag@C&ZJ-*(#/='*}IDJpN]^#*h2ԛX652,^azx͇6b,J}r@G ~gYm\V/ETQC H& *"sjC`Nf@]fT2ZcM~(И6Orݲ*t\f{"^#xEzvt =cJu]Yi9ln4G#\Il+= nG7;WŎ2@"""""""""""""uWKLĕ%_/cmjVxLUT;`K+ 67S4|<@6P.q YdDZ8KT0 0rӼ]xA@)o_JqII {"\m5z0pd)jf*ދ$er-Hil||>*`_|a=G`@ v9Oy!5Q@uuPN4j,[c@0TKNH(Dc@2 `+$o]br #DȋbGGDDDD DDDDDDDDDDDDDH؎ɯn8- K>@LD=Ă-~U!h.O${HGT綁^#$) """""""""""""v}4 ٜp3 jRK(KZ̜u9(x{%DJ쨈( Q.`>3<(t:\IM" H]ewG}KcqE e'V$""""""""""""C;kN$N;*->ωJfrTp7 HV쨈HYHDDDDDDDDDDDD?2X88$KG .}ؗ|deĎr w/Cu>4b$JŎQɩڤ$ DDDDDDDDDDDDDI=G̏c=:W/0X0HTv X5WhM\Td]@VUŎ@""""""""""""|C)zJ+Bn_R=e^Hgq:)Ukw ܨ,f jb@DDDDDDDDDDDDDJ_I{jtYZ~`vzƅw胪b\v^TC&~vE==3쁥QQn DDDDDDDDDDDDDH Oq;"[ܶb';G ܞ%0?lŎr;V$""""""""""""*Δxu+]' ߦ;E5X&vA$;"e5}/|"뢝w$HDDDDDDDDDDDDT=Gܻ;("0ksu/ +vGܲ~R'JBTuquŎV$""""""""""""<3V'.0 pb ES46ZT,U$ C2zm=8ep˘ ~zŁ)Oo$"""HWu{nZ,g2!e'q~bG{~_P|[`G߽jH3񏈈(X~=ڣ- xhQE=^D^Z^Z^'/T  V$ @ЬY[-`p;`hhRZ1bKY..خ/P\r $A """""""""*!1_H$9%/]/|wm` t58*b גIÁy 8m}: z؁Q~@""!H>6s|qWsϩ^^Oka7 MшF4ݣu_'p渃; X6llitR@Y=gly\qB 0 }Ј ょ%4]koyTn<7R@u*ΊmK'Dg`.ϲ!n $K,>;J""""HDTP )6ۼ /]tjROy`>kr &hF{{{4U[L1ii@UV 5Ոr^ԯՉc wX?!/%шV(>X#0F%b!1@kX:=H| /\A?8˚Ts_?""""!L "*`B,BcdNŎNW)RK_,}py՗v5kt̽>|7Xb͊;Z"""""""""+1.e45"\/T`lYd ЮP:=vumI!;j9YV>5=Gj `u"Z.Eむ/U؂Yhc Q~7<:wlq` ݁#ڊܟNN7<zVZmt:ZbGKDDDDDDDDDTpu xΣ`ZօU9e}PP=XC`L@F7x@F*APz `<3Y6BY\zKx.;$p[ŧql_8s 3G+p9UJDDDDGɏos}28쾲{ɘ(vt/bhЈ#<4h X{3@BBV(~РA -QzqoKkdotS)ZPP>I?E2`_)Z0t@ዪ*=5ui8 $~Bi *NW/rZbrwBmRH9"5pL#BDDDDyL$"g  5wߎII>^>>>] .@k|b$&&&&&޽{͛7o@ݺu֭+vDD/O******@/vD?T*JǏ?N?e˖-[;Z"""""LGMіQ+ |t{$p_+;WS՟<"‰Q79лI&܁;ĿN;ozݺ3 n\ܸ^bG{ 6lذa`ccccc۷o߾Q)϶m۶mhkkkkkk׮]60hРAǏ?~Qfׯ_~Yf͚5puuuuu;J,|)J366666_"y{ŋ/D"d ,X@죮Xn=DDDDDDDDDY@"<.==0cdɏŎ*q0Tgΐ@OO_PPPPP*v4DD9/.....d2LhӧO>ڵk׮];ٳgϞ=bcccccG=zH^)Ǐ?~;jӧOEΝ;wYCD9܅烈3&Unp0s!*[(?S;""3篟~N[mo= p_Ư344L4eӔ/&_;.w\nW|bGMdإK.]%QnADDDDDDDD+o6YPO)3̔W~~SU UGlW]w885/xA81wotj|5k֬Y-d)wyݻw_ZͷjժUVbGKT񺛻|Q~@"gb܆v; HIIm|:k{pDT T|?GԏKҒv 5x H Q⺉rPrʕ+l۶m۶mbGCԩSNR]HH|.<DDDDDDDD0Q.䗒==.|9l ث~F|z繜p9K/h*""""a Q.w|?)R6,|O>Yث~Og> ,LY2ptj#"""*______yʴՁ:tAh('0Qn 8ܺs_>Lj'tEWY W HY yG@xxxxx8`ddddduP"d2zիW@PPPPP H$ PXbŊ+VX"PN:u?̬ӧO>}Z&M4iD~}Kn?BO>})ӧO>р<.:~>*wYBBBBB`nnnnnlٲe˖9Q@"\ʿgK/FzL Ḇ+B G?#6b#|_~6u,ʢKMMMMM6o޼yfWoooooSD%JÇ>|8lٲe`ڵk׮||||||>oɒ%K, =zqƍe}J*UT x|ʕ+W\9 Y=o|… .\XL2eʔ'64!ݻwޕ\MMMMM HLLLLLTTTTT'O@f |xA#ϟ?+7O,6IY>~{0- D¡P[n?Bŋ/-˅JFbQ!awJLcƌ3f~ҥK.e>aN $ ?@Zjժ?~β*CY^zUy Ç>|055555-[lٲO4)nݺuVB *T._|eW+'ܷ>|ۅz:uԩSŕW˗/_8`իWʕ+W\9|"PapҤI&M͛7o޼~?O+psΝ;wo=mѢE-DDDDDDDDDD@"\ >wMUbJh?;.w|/<_x(7nܸqgϞ={qd+tҥKNs!1LHiֵ%"fvZoڴiӦM@.]t"?Y_֡,7o޼yBDH"۷o.vԹǬYf͚ׯ_~@LLLLLLG*Rb;s̙3g ʖ_χx^PB )Qe^/Ǐ?\`fffffXYYYYY)wׯg:n߾}mۅ5lLDK?}Ԅs!!U8?+W\re[ %%!!ZjժUKM6mڴW:>iZjժU8yɓ'ߧ(T#JWi kE΅?T3* +%e6֭[nݒz* _3`Q`F=<<<<<ҷLKnoZȨ5p|޼y͛/*PlҥK.M?NHgΜ9sLǗ['%.ݳgϞ={5555lg߿+N8*)jE,]]㇌Z +jQVFb}%$>ѣG)ze-iݺu֭8&R[Yx}OYOYw fF̌b&TS B,,LtGpHf`RSf͚5k*?FhHn{eȨUf/^xEۅ sjիW^qkٲe˖-3'UT(?%l޼yZk ->{ݻwoŕ]tҥK[l 3j TTTTTTɩ8F|Qbʈie*=MZ+ŋ/^d2 BhZ::::::ⷎ'"""""""""ʪl}""R"#(2G~.E+ڟP ڌ*iӦM6Y?i O۶m۶mK?Ν;wL2eʔk׮]v[ +/_|ey+dk׮]vMveW*SL2eoTq7 Y( G[ԨQF@իW.OKK9mڴiӦe~Z c8UL$"ʥS@|;wQu³!OJfTJ A֭[n })&**Ç>(.$@(B[F:2߿WSN:u .\p!֡Sa9-"""""Bɨ+ x>~?ψpߝ4iҤIo?tСC?sΝ;wzDDD s3B$v,[?Mc`Gc!"C,}20(*T:|qy1@E堇CqКthҥoFz" zĈ#FȾB=&v>,-----?PQ"**sΝ; )ޮy%J(udJ!sŊ+Vk|۷obGXTTTTTOJJJJJWTQ-#ksE۷o_`ӧOO˗/_'4*/>>>>>^^6-%s۶m۶m 4q@*^!vTr&t'k&t.v0DDDDDD "VaSM>s?~@NN\ثRqf7uoޜ,v[Yq*.TBkݻw޽;ĖWχЊ0-`ppppppBbDZgϞ={v -/Ӷ4UT_ZB&Ew3? 5eE%ܻw޽{snEn)jɛ]nܸq ş/5kzի"Ŋ+Vо}+o߾}e

>ѷ>_:TuuuuuuQ~ϟ?&L0asB ]vUPehhhhhhjjjjj _BkNAE~>VZjUۅ|Bei[Fzzzzzzgղe˖-[5k֬Y&rʕ+W[G СC*olٲe˖e"Z*Jd3gΜ9srʕ+W.%ֿBҜz[yFF=zh̙3gΜIȑ#G֬Yfp… GK.]tI~r6QVDK\M>*h*vTDD_ɜdBX0(צ^;WЇHD"[kbMå:J+ѮBW}uu| -={ٳ'vڵk999999DEUVZ!$\(+fF:TP֑YBH!iӦM6ӧO 4444? -;vرc:MQ™)$Bϟ?^NX*#hѢEW wΞ={Y}<-E=z葋Z(1S R+kUP;(""ʷHL`رџ'"\ʲeqˑ3eRmSÆ |ow+WuW 00 Oex) TgYIPB owwwwwwW~իW^U]HɩV&Ǐx^뮲!_sr>(gHM͑;""ʯt'aQV0(9^I?I?I?$%%W&%Mzw~Ͻ XNmqtq^uz1b4x`q5WOM<98 kf/[_֭[nxȹ~Wf[[fwb؊-ZhQ}|||||r>v>VZj?Jki 8p_\ׯ\_߿~3իW^=iӦM_$t.\p!O:u)2_~[Gvӓ4hРA?yɓ'-ƜuK89]vڵ$D"IӧO>}R9kTn+(h#o.\pѣGxڵk׮]+_p}MKHeun*kbGuAD_R؁bBDDOģTpob!""` Q.guUytP<O#exx~O(("f4v6ƅ {8kvhgC7T#י>}J3ӸO>'^xq80:#]j E[x684PiV_i-Zhxܔ)SLxyyyyy\|%Tbquuuuu;cjjjjjx7nܸq!TK˗/_L544444޽{[Bk ......ӧO>G-5jԨQF3$>Lrv?ϬQF5Jq3gΜ9s8rȑ#G' ZZZZZ"SnrcD9|(2X#]Dh(X!wdŎ DD]WtEw`DIb+tA#m#mNs_~,@Nmnw}$H:`௸FjCf횵kL]]7d}y_l~lS..nC`lFˀS~NUb'N۱(( pT7o޼yW܉큛7o޼y3HF5jx ,X|bGu:tǏ~޿]~9B+ߴVsiv֭[nWx111111Ih#T@]nݺu:2 #ʠUKK̙3g/nBB5k֬YsYOZ #!P,|~YB #G92v?bĈ#F('ʬ9%^wAAAAAA={ٓ{ב_^akcbx`c""/iJu{[=\5/]!-vTDDDjb@DDcogo.T~mƭ0.] ?8?=1Dx8{t1!AH&٘ W2PA%/M<+c-\Yre?MZ5 7WꨜCHo`˗/O?۷o߾}W lҤI&MN:u 077777W:IHHHHH?Çϟ?<1|Ç+n** :tСJCu:b_~ m9E,dɒ%Kߞ 4mڴiӦ!C ԯ_~<,22222x۷osΝ;wxŋ/䉠|(Jhݙ5lذaÆ[*"&!C~ק'$ZYYYYY[6nܸqƀs}֭[nG=zh 8ӦM6mаaÆ u(PQ>i ҥK.]Z~E&/Vi ޞ={3C:t׫7o޼ypvvvvv|P/烈䲩eAX5@OPU興(\1S'mBpO예(k$W֭2؁P4uDH!>Z 4t^7@5DkHݒv},{&Z~""վ=6 7n1-[tՎRU,۫nc4iQJ͈P_~;vرcիWV?#B>|p/믿/`۷oW~ڵk׮];˗/_V~֑_G˗//OtHZ9RRRRRRe˖-[ L?^zՓ?SIIIIII@=z81,uڵk׮;E :eBRZŋ/^x@!QQH$HKhu)$dTi)J0Yz̝;wܹL2eʔQRO -% c!_H0-nJ5k֬Yxɓ'9JOwڵk׮]~<2#~2:i )SL2EVnfֶm۶m=zyw|Pޤv:::@-ZYЭvbGO9Rb4˟]09A=3""!{λEU`bGE9-=D~אq[)2zfƍ۶e}?DDy7eYo꿶@@h@hEвC˝$uI `Հ"㊌+ 4@PTTJ= z uu?ZhHeNy.wљrO@UUUUU8rȑ#Gϟ?(TPB؄_Ben׮]vWFQ/#ƍ7*t%|O:u)yB"'Tsrrrrrz_~[ }gϞ={B:mRv* OWŋ/^8~Mh}fW|U~JBkE444444rOߴru7;v쨄sj|QlȦ[HbGCDDyUnNYMT!ϗQ5Ŏ򨑒QACe7KNHNHvTQe{K+++vp\Ḣk `Χ9NFt7n!jj0_mHC;[įyqTGN6nx'`TQFUJ)yT-2#Q[G;v~mO<$-7ܰrNgOz[mY(#B;Eׯ_~}Uhr/_|UVZf[nݺ%vDDc edXӣUϒ@#r5/aґ~J7ul9ȭP&pUHV-H^upĎ']/{Q+3oE%vTDDDLL$"gJ-➋{.Y-[? RnII:7qz-Z맯"DS~wwY:MM\r (? pwwwwwWbKEf[ϲn?b=6lNiVPDZsXvo%~3?VZjUG?% Ϧ*G;63+p!Hy!ЉRYDD~fwÉpI`ٹK#^}?ЀUDDD0qԩf.DL٥ ! u?iF|PF^^w^\{C W]_FPkG<<``dlIGW-"ϕdj?Q cae+עr[1R=I)9U*L pOz̄,ם_~ntȯ4iڴٳ]t"^4kG8œ?}K7'Z@$N#p9>i16pO!""@Bޔy.6*cI< `)h/i n OzAU,~~DDDD^@Sxw`Baz߄w J9zgq~ {}\lJ]'yt:[frPpT._\w\p}\b̫ rU{M]w~dĹ\0,h38/9PKΙA~B˼qin/1v޼;8NVHNZg]v;wդIE=˞Se|C͔\ᓂ.Ɨ8PDDD$iPDDDDDDDDDDDD^iqa%4*r(mE.3^{;P1"L:5x힒l{c"*,gmaE"W>)“@8eܔWӟ#ao:'탯 ;9 FfOGJ;&Խ9 c """&+EDDDDDDDDDDDxLr*j<[LE py=|nB/kZ1Q =U2u7_\z/$s[+~-.45|ueY)~eٚT:Ͼnn 9 y0X-p˳i{/wl;H """"o p3 x)s^ͿQps̿>*0pʛDLB"^N/~XJՊ/RB:*+f;Ȳɓᇑ$97xw]gX'Apoap{ة<%fe6/ڿ}|<;9}FNu) H(FĄHl<XFKrAP c]r@v=QG13H31rM<衉X{ǑS@%vr>[#<(,Ok?fS;(Zˏ}XríI*/^u Rc9f~NOk?Kn)?Os7Co_\ NG~'<X(of^nɺ%떬 SO? ""E""""""""""">}@eΗ9CO =5\YueՕU0sE>'~|<> "=d0>Uq`eh]} Lb vY? Y(A #J1y$Z?u&ö=m1ºϮF|w%S2q;_T }ߜgsPnIDATy[![&M:(دtd7[[PA\36+H0=ϩqa ~ɸ0vʱ}-1ħ4w NF=`:n@Ț竻b„ "6B[RqT@ˑ @=ul{rSsY >sr6~koO_ӏ/{\YOaq"V?$M{}.LE\{5/et㏹KǛ>_ IX床EUt*-s˰y|JTH2`e{iA{%}ߧ4 J㏟jrlݫ`I7)A雒V4"e^|h3<_s,:e!XUR/%%%Ζw#o_h<Ї>qtWDD@1c1yw WVv^yeg6iM +߮|2XYY<{͔GIS w̘i-xyL7sV _;wwtDaḡ?ܵ" P%ҷsK?.=2L67B(i4mL||fh5&6[~;|1͜/K蓩b kn㢗ؠ6;GȚ=sI)y"Š sq0?\c,d>iimw OXDvHc}W6_wxB釁Wr&/sPT-{v aw7WF&[k{?^) ˁ D1]g_ޭ0Ќ4rtU"7,r8D\v~}t6񣃩۽{oB y`ԉQ'FJ*t Ӂd#""DDDDDDDDDDDd!!![[تbA*=caK>k/6XZstWӗ^GW>4ẹap*S|CZF9w xn:ۮߏn ӺV-(\=5[mۍE 9j<# *ur09Kx Pbg&mz>FB])9S-? M9r(zgCL Ӿ f95+84۹N0)XE~yqɯ?3e *z02,K{p'C2ȶ?pr˲g忾(ܠ?Cwf&!~}`e oZ΅>}t4s3x7]|aWU*W\""Z """""""""""iNs(޼x>1|4 I'͟46~_C_!/<Y뼽d|\R!@B5oK@:Ywa\ob;M3\/ד,fdu |jMÐbK `mcLWؿ~X?o=Z>QeXFg lJT-Ho}O?1-8m|4hK` ? i2p`Iś4{-?^Ku>'2dc_>^G3 w)d -M/k.沃@ Pw|P|5SӻѽV;ỰU Xr#(|.t/(Zhū<856s[hMU^?, >6#MTNck.znV X ?UAƲǝ :Udݳ=ƀq=Ca2XcpgFp3&w{YS r.u;Ytx0U?;-(](l}). 6L]s><,;)7?y0ۙoK* # Rز oVL~Wϔ3&LRf`*8~xh|[VݼT3?Z/$ـO?,gP nߍ$ |v?"]u #AL2ɐvYjC皝kv ]~EߋW Gy_7H6 J,JjVY`F f4*R/;}IVbvkA '/ sܨ||"7\2 wM5^\y}  9^MP/Ӊ:JϟQeXxGi EHK'/sg85jQ \f|:#l鯯,U&~Ө a`d+mF[nXG/E$2a~et%idy;ysGdONӚ,1V 휸qE`B)˜|*TryY;L wr&}ǶL ?T;?< %ج^~SfKi?*)oxH/^tʯϲ{:7}(Tطk<;vμ7<8y>w,L$0Hi;}iWىr)`٬"AHە?$2%qW06W^n;c8X0e]1wJ2f$RyYuc-C.[sMœA-mA1,Z˖wlpD͡~/g[g3|ƴce\& ~%+EKQ!G&޿MVh`7Xސ?wMc] W󏙐z.8)e ]$z]8CYںU>|X% aOy=ضm۶m`]uֵ`r1\Imy p:a7W߄ .h܌nF[}#˖0aa~ x뮥s%Swޥ#|Yp/Tp@k7.8X¸2y-#9 >ʐ۵‹A~1 R.9爺X kFL3=gKgK.g/nb? q)ؾߵgۣ܇n)lu^.Kyuy$?^~azi;׼}m0i,k¤ϏN\x25>NPꜘlm+ i y9Xoy`[+CG鑫߿O|;'Av;}qt } #uɫI^N%cpdGqM׶r?+T:nd=鹣J%Ua _ O\9t9LaԔQSFM^xF7n t]qPlhņқKo. .޻8 0dÐ p|Bعs_!a|$F&OGVYֿI-൩jV;KG99FC{E}Rri=AB\Bh#-O`G5=Tf>/[rfd4и׃β4B|0 /Nsd.eX u6 i>_ӄ $6OxR??y;:.z'c+sG4{tai/^֡/ `s/ycs -З/^fϊ? )@_ ?A?ߧWVڟ{Oyt.v=r3=4~a oҖrjs(msqlCH0i/ci_l Wދ<9Swzay醺P\ Êd>6 11zp<V7RO󯿄^AzCXS6}φͽ`IR//%%^z##<f蚡/.lE+Z9;""W? UfW],ò 2K.mC 2d <>#͓s巇?4W)[2okvwnE6e  ۉ׆aTZ-}WåP?K6[`{"^uz>vFS駙N2tvq{<3uu恆,yRچ?[{ ́X_f+d[iZ?Оdhgl搯h}q=w H7)/?3!ɯ ܥ>'188)<蘶0}:ٜ!s7 |Je Vs?0p*eKJIϵܑx H(t6cPevUf#>e&M׮]]z"""""""""""D~ ||Swm޵9^s7) 17v/}R_gI{_\/FY\1g7`hfߏ]<q0%.MMa*}}Az&10?_wgd3ÇÊߏ#JXy=O qy|؃[P>_g++KVc?fRws-Ĕ]? UxQ'|n=3Ӟ. I^b]ۃr{SI f$ʠWq |Gy4Uesi?pnh_fKEpHфjp)%6Ob~e{g^> g[b߲<ՙ -F GW-b!<̇[oC[Ew[M=jP?O,>8ڝkwhODD ϲwm׼_~?555C٦fzIAӷ64ϯu;Io9& #⭠R !)AZ\!oo/ua^)^Bs@ G7BHaGPZ7iVԼ0=0ňIpqz>oa㿲,3Gw~^ϒSTY##o6MrV .gL_l(GӔKpl@k|se1̜rO79ۋL[aQ$9f~kϔujσ)p֔B@اꞻ׷myg ̱kuyRx$m=qSڪ&pia}:H`kizwr |֛\MoCўo_\b[>j 3WB`_Ckp,QTRpȺtPJi)~{SCxw^'AdD?kIW״pg|-W۾J!<6#~٤piI/~|ﰾ[:.츰B0~a"""4c\rʵ`[mUUͫ6ڼ *0Hp%5FnjͶ)xhXp3l⒖!˵Sݝ77w!%NM[ w80 ?5y>>܂M@#ceñWiC0bxlh郞 Nyqmkۦd77,0T3s}%_O]cn4]Nw[ǎBNT}2([f}%e1&/o./%ʐ[87W̺Om̱+KǟGKt"텶^Kɗ/%ä铦O殛.5|k* f3kձcW˜ c2^Ͻ{=OȃO&8 X6Y1bxgH h"__  YR `oCğͳp-8ի0-/L cm#>!)WS@}Y ~~~@c!Xś@ӽ6c4M߭3H|`Ϸvtec &wƎreڜ&yL[ג ;mC|z:Ϝm6IٔruWݒi3|}v׌OtYCv>C^);l4CL^`7L4NM_~{'|\S&c -& 6^*g(K̝![ο}^!3s?kmG}}wlp5D7eg FwU k] ^ZrGykX2.Q04~avƍ܃|oP{Ϧ\ƛ`/9FP✁cA< Rb`oCb/C\,ԫh5<2oLAPýio_\x7 cCÉ m1-sƷ_T͸1qln`a\bZX?cCN^+ C dnNN'+.@˴ss3NW;}wfΊ<?v؇[%}6 l^ix\p]7?P&2 lʏ+_?|w݇eo*Zw'^jm`Y26XpPM%&,JwSDȝ)pbPb{t5B*pjZ?3G!]S"dK`p}n2<'l W [iz;>n'4.ws!K`N4W^%qgyGͽ: |DB9Z94FrƐ:!3"] 49B[i'Ӣm^|WWvY_߯/mV^aOš9W{EmO Y\o<#ƃ_YXs69e]߹ |ӼN?<΄CG$ IGOXw5%{(*4 -;2f;bZ")iq{?3E!3x,w*h 8BbRT ӂu子&=Zp)spTxup¶p񽡦GMW' ^>9e"?$* :*fuwmO3xD L-N? 5W7}|S/!8t~B=X;kϭe/yp̑W7@kJ=J91'qTJa[-akKA. 5gu*о_iAf{N{u vNgWH1ak Cpghx䂰͢:BO_ߒ,c"fFg̚n#v03O:t> wq:AE[m &N:u,хț=tqt""""""""""Wg0c,8Xc666(k C5)q-x*rl05t.讼&i.lc_ޛ¥92PnSP?46LfL AsV3X#}fOXm +UYmPplcx0tgEćա<`lڽ='44}|hӬ d|=x7Jy|ۀSSjbxNP%֡9 0!qJw6k |!Ci$Аx|^ 0~0saS>k"УO_UcZ6x5\1_1~ n|6N(狼-8z>,bsf]X\QfRi/~'=x,f{!/bo/G1S«Lr+%#P5c[0H,ɯk0Cir; Z{X7FMNW +qBH)JTM{!dۋB9Ws HS/6iN5%-{J+9<`(C٤IӦ͞*MW_ΰɰɰ Z.lBXz~aНAw݁t1bҽ  $ 2-=\x\u]q!)yݕWlBrY۱oa4p;awrf =}:sPPH IrHl]`xNp0ZVvܺs?& G߯6}QNϾc?,DEEL> S*89|[s!tm\sKsx1fJG?%$m̓{=/ k~8@V~K!Ռ~{=6!<53 `烞B΍ & dKD_1E={L9g眝J/DD䵠štǓO~< n[m!ƇƇƧ~kU}%zBz +rt7*6q7)N{V\~d?t?}qhl9uۘiqa=?Mgɱ_YO'['hbGwQ\BWX ٢RR pl?-Ovto`]ud.yHè=⒛]ި/ދ-ҫ΋뽖T(c\* >S?E[sB7ABrCsw_R+G9A"E]v]u[{o9os讈L'#iPJ""""""""""I&bbbh.#\FƧn EEEkh`4 < _B2tiV$r;W)X5,.q:r@ƀ3qowt" '4M_ۻ.3TT0m04U0#߇ ]uc(,{.@>Vl{:hqqMG\ݔ'.md49x<rؚ)2@Cizyfwo^yy藥_~Ypd‘`ee讈?ő `qg1c6 .; W^{`ndndn|)_@.-{>?Ln=Cit`25q/8`iuP/] y6tt"-Cn]~?O;FϱëW]7 ՠ֝-<B8NEqAFs4}ؘPЪW[1%‡Cc;΀kGǼO=6{L ߅B yxx@ 77 6th,PluVkODDY4(""""""""""" Jn*A6ma͚5k֬\[vmCl!6m˩pi~>}9)bƅ}8^el;}~HHk/D{q[}"]&GW-רgF(x7l /dQ B# XϕmW}zhgiK)*Ck&xZaϸ{{cB5Ǿ=>Huxkcb!%wdgUS?7ǎ;rYgu^TzQi(L2`#""0;x(D!븯㾎փ[n=&|l1q+\ ѕ+GW~'lb* !t_«ߵk!d!} &yڅidG7gd/m t9Yx8 mɅ{_Rų?󍃆%Om]4"4sZ@_ۂ)S^C(4 &оurɘ'?\\EX=1~;iKT=c;|tet!渟Kgfs>Ny|2ЋA'mւH%sy b;J=q) { { {!Lj#ro~)oBꧫ2̎ȟK'o4 E=zF]xvمdKvOx]5]wE@dKptSz)t푛`Ͷk#Bb2+|Ћ]29I^tB>{u"a܂:SҍӗNu Kچ6}{PSf觐'?迱 u K495)6F6lV4%%JH}{F>i-{Y'O?a.p)å 2ŬWLcMhn=DgfU"k{F |#ncSoGw`S<v߼'6-=[r((k{1+%}(NKg.Zxpt[}+@؟X r%KT;XzcҦG W`F\ {v{"<53CH vt)>zwo? _7ly~^g }8q.j5k |G|讈&N:u,хț=tqt"""""""""":~W]_Y~e3?bTN[ނLs>Jydh覈0|lhh9oYyur^殆4(a*^jXV;jp9tOϷk6 =2 RV~ls|sh>`h͆6ޛ7;)""/oҤifvt """""""""""IMjBŖ[ V.X`%FYnd9}ۧS/6NԪZQ°][!s)z:I"kM $MJ([kVT@ ߔudVԺ͇!%9!;ܽP`3۾sTݩ:S;O<0t)C@˥/ .]6hODDwHPҍI7&hC 7ް:6oۼm} *TX[- )6 [HZ!Sr&]ʁ[|CZS&G7KDD^W֚;b ǷGێX NOܰ{W%K:(s2Gaއ:%딬SL&W+PҔvtwDDD^OFG """""""""""wvB i'@߈}#`娕V{;R/ϾbM""GLJύ9Bvts./ttWDD5rY0~yWGg| taH ]3tMoi+S222;5""" :PDDDDDDDDDDD033惲ˮ.(3 h״]vMa)v.۹ k$H y$!ki tzٖy_n'_k`>Q2mstDDQ_؂!)_!١] ͱ;IV0qfƙgBGm}z!dF69;"""L ݡըYgj̪1,L.fBRq5cBGB3u̶ƍzTܾ̕Ks]ߒ#0?{&֋eOsV8W-260cChܢq-`x5ׄٳfϚ"",:PDDDDDDDDDDDDO?֚֚VX}EaB -' M\>x٘&x8f/7Bcoaؖ["SCD ѻO'l2#1 xC!ł{!O l4cXjFREG snι9&L?mP(PH`.sֈHj2L8uYv 7S={v*DDDDDDDDDDD^oO_)SΧ7ǤIӦ͞*MEDDDDDDDDDDD^sdLn2&қKoBUU888#lsƯ7;۶};[&讈RD{~N 9E.9Oyl>Ϯ򷗿mޮ{4'""&?[:nuZskͅ}w֖Y[fm(X2d"8q*T& Z@_o M7bj#!`q GwGD%O,RB|3\:&!8fep]ػwax?'G}r4|_®] aptwDDD4("""""""""""~M7}9fȚ!0tqCǁ4iR5r' n~oacAr}gVbZ߽{v_u 68o#]^ty] o6ac6ڸqoLj0^ J|f&O a@(GXO>o-~Zqa U ; 쏭-B'7AP] 䳥X(5򌽌 W\Yse޳z= >.qK)S;țKKK^:3QG0ܤsK/zVC\sVϫKϹ2Un'#|nI1 UbC}/H'lG?)젬aAwot *T*y""""""""""""o8]ݻBSkSkS+9M4(s 0|ha*ڪYsC삻;>cf:w>Q\8l,8;"`4Bzpvp=MTcӳ@Wu=4޴zpۃ59oASSDDDNkB|u,_|000w*U5WX6 {"ˇptWD fg%~_Q̺c@Pۀ> Ln0̋9/*篜r~055utWDDDuWȯ277'?0L>م.  hшk~;t5x4Ͷeq莎[NTw"bGW6O_3gIO'=]w^ak|σ""""1(""""""""""""jV˷Z´~M괫ӮLX4aфE;@θq;_!|(P^#w |ةj::e4 N N '?z5ߚ3[[H|?dhO l2?[dJ| u u u k,оW^{Aw[w[w_]N?aaa2_~CM ^z*sS;~`,<^5wK w*s""m%8n<~9?lݞM!4=v-2%kT <Xg 1 u2_ybu i׉NABi76jG^SSNMj諾_PwUUuWiii""""@q(ݞ=wà5 Z/WZ^iy\FtI{u*ؿi%IE@$DpH_g Z_N.]vncY7%1O; ?w)I<3Ng@%g +4}QG`hh讈ytI'r`ȁ!`EWtwh 9 9 9S/^*Qqxĭ^:ó^-[єGI]Bҩ0@<^AţV{[]` [R/eFC mO>|PJT94(""""""""""""5:::R)R,]z>3pnڹu*NoS'Mֵg5jR֜yXǤ߁}=5$M6| "x5/9 iLcH4KK. .\p-h"y5՜WPPP]""""""""""""b 1CuRK.rʙ+g·.ߺ|.J4$~]Qٕ!jՐ-v=:GY*1I v;{-q-Խ 5Iܪ-ת_+ۮn[iҎiPDDDDDDDDDDDD24;lK_Gab'UW_l~WϳIĄAa[>ȷ.zCUn~3G(GLttD[cm>U w1%ĘnGm9.5Fw;_PC]A[mlr66lXDDDDDDDDDDDD(~w݁1d N^:ydgggs7s7s˳]J^!?,){,BO;f=|K%NutWD~.l+6pR7x0zAO ׭'m?^l^O>]tecY6>s-Zε/ enP#o5vN۝`}gJ+*O;v> Mm[!7߃Z;7I]zYyS$ +g3q^\/0}>QB ^1c>Ǡ N= &N8i"npwtwDDDD `h1Zh8q \rpľľߛ bSĦMÍW}!mַמ48yYp3bIɝzyPbc:W*K"""""""""""""`bbٖf[m) 9搛.Gr'O +l  48r 3F0NrqXU}L5z3C9GwKl,l_ϕ\aOyM!2}Q8LLLԂS N.:v nKܖ-qtWDDDD9 ` )ܲes S&L0[Xla1mok| cןhPDDDDDDDDDDDDwp s s } ˷,߲| T]w`kkܛ 5m |W tp+mB5[@GwGr=o xV}%C1 ;#&E'HWѳ#آbpr ''̶3l Egό9I""""\yN}:6Lh3_i~Dxrg`jbA|Jv(١$t;d9sGwEDDaҤifvt2;|wfQnF9h&_4sv9`/c/cOӬq"]im21=XԹN06}h>b': B,,&\cAHZra-jI-Z&A)S͒7KDDDD64(""""""""""""8q4{RoK-[iSMכ_o~9X3Y3Y3<>)g!v]WƜ ^ۊܫ:25׶p`$۱ { CCG;IC=#7FG~g3<ڳj_\XwtwDDDDgtt"""""""""""""o"Ʈ]Cv klklklcq=Bׅ S1pi{!ϵm {C`-s~1}ݕ7}}-3U}Ju\zˮ(G]_j!y ׎\;riQӢEۋn/ U3VX5"""""/0qԩf.DL٥OZkM`lևmӷM6NG:S^f 4U);3!@ Fy߲}k 7.z(8#O? wYnL<ȑ-G٠܎s;΅nwv|Y讈&M6mlGW!o*(""""""""""""wK,PdYeEϺ6+ *T,(c-壗= N9vp^ Ґ| 9怚ΈwF3 O O OIM=UțJ'z_ ,,,rʡ+GY>Q011I@g~Kv|0?q޲^ cwZ'blZGwWa7D=9C<72`-()nsGGG@Yneݠwz7j 8q:X`IPDDDD( _y~'V[n0M|o)sRy/9?:=rF𯱷7ᚆkM麧d|? e(CL$!^1b8t7ݝt/HFFF,,F+Gl'Kx-pk`{co{9;윳3Yw׺ݕbI3gF5iM69yG[zJY-3Zf$޿z”0%Y{tѵGZkylw ߌiY۲emؒcK%{wjs $s=8`M\% expires("2030-01-01 13:25:00", "UTC") envelope() \%>\% replyby("2021-12-25 06:00:00", "GMT") } emayili/man/append.envelope.Rd0000644000176200001440000000052114132315227016026 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/envelope.R \name{append.envelope} \alias{append.envelope} \title{Append children to message} \usage{ \method{append}{envelope}(x, child) } \arguments{ \item{x}{Message object} \item{child}{A child to be appended} } \description{ Append children to message } emayili/man/pipe.Rd0000644000176200001440000000065314134462672013717 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{\%>\%} \alias{\%>\%} \title{Pipe operator} \usage{ lhs \%>\% rhs } \arguments{ \item{lhs}{A value or the magrittr placeholder.} \item{rhs}{A function call using the magrittr semantics.} } \value{ The result of calling \code{rhs(lhs)}. } \description{ See \code{magrittr::\link[magrittr:pipe]{\%>\%}} for details. } \keyword{internal} emayili/man/mime-parameters.Rd0000644000176200001440000000277714151633351016054 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/mime.R \name{mime-parameters} \alias{mime-parameters} \title{Parameters for MIME functions} \arguments{ \item{content}{A string of message content.} \item{disposition}{Should the content be displayed inline or as an attachment? Valid options are \code{"inline"} and \code{"attachment"}. If set to \code{NA} then will guess appropriate value.} \item{charset}{What character set is used. Most often either \code{"UTF-8"} or \code{"ISO-8859-1"}.} \item{encoding}{How content is transformed to ASCII. Options are \code{"7bit"}, \code{"quoted-printable"} and \code{"base64"}. Use \code{NA} or \code{NULL} for no (or "identity") encoding.} \item{language}{Langauge of content. If \code{FALSE} then will not include language field. If \code{TRUE} then will attempt to auto-detect language. Otherwise will use the specified language.} \item{description}{Description of content.} \item{name}{Name used when downloading file.} \item{filename}{Path to a file.} \item{boundary}{Boundary string.} \item{type}{The MIME type of the content.} \item{children}{List of child MIME objects.} \item{interpolate}{Whether or not to interpolate into input using \link[glue]{glue}.} \item{.open}{The opening delimiter.} \item{.close}{The closing delimiter.} \item{.envir}{Environment used for \code{glue} interpolation. Defaults to \code{parent.frame()}.} } \description{ These are parameters which occur commonly across functions for components of a MIME document. } emayili/man/local.Rd0000644000176200001440000000054414132315227014042 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/address.R \name{local} \alias{local} \title{Extract local part of email address} \usage{ local(addr) } \arguments{ \item{addr}{An \code{address} object.} } \value{ A character vector. } \description{ Extract local part of email address } \examples{ local("alice@example.com") } emayili/man/address.Rd0000644000176200001440000000162514132315227014376 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/address.R \name{address} \alias{address} \title{Email Address} \usage{ address(email = NA, display = NA, local = NA, domain = NA, normalise = TRUE) } \arguments{ \item{email}{Email address.} \item{display}{Display name.} \item{local}{Local part of email address.} \item{domain}{Domain part of email address.} \item{normalise}{Whether to try to normalise address to RFC-5321 requirements.} } \value{ An \code{address} object, representing an email address. } \description{ Create an \code{address} object which represents an email address. } \details{ Implemented as an \href{https://cran.r-project.org/package=vctrs/vignettes/s3-vector.html}{S3 vector class}. } \examples{ address("gerry@gmail.com") address("gerry@gmail.com", "Gerald") address( c("gerry@gmail.com", "alice@yahoo.com", "jim@aol.com"), c("Gerald", "Alice", NA) ) } emayili/man/encrypt.Rd0000644000176200001440000000217314153553110014432 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/encrypt.R \name{encrypt} \alias{encrypt} \title{Encrypt or sign a message} \usage{ encrypt(msg, encrypt = TRUE, sign = TRUE, public_key = TRUE) } \arguments{ \item{msg}{A message object.} \item{encrypt}{Whether to encrypt the message. If \code{TRUE} then the entire message will be encrypted using the private key of the sender.} \item{sign}{Whether to sign the message. If \code{TRUE} then the entire message will be signed using the private key of the sender.} \item{public_key}{Whether to attach a public key. If \code{TRUE} then the public key of the sender will be attached.} } \value{ A message object. } \description{ Specify whether the message should be encrypted, signed or have a public key attached. } \details{ If a recipient's email client is unable to decrypt an encrypted message then they will not be able to access the message contents. } \examples{ \dontrun{ msg <- envelope( to = "schunk@u-boat.com", subject = "Top Secret Message", text = "Immediate readiness. There are indications that the invasion has begun." ) msg \%>\% encrypt() } } emayili/man/print.envelope.Rd0000644000176200001440000000124314132315227015715 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/envelope.R \name{print.envelope} \alias{print.envelope} \title{Print a message object} \usage{ \method{print}{envelope}(x, details = NA, ...) } \arguments{ \item{x}{A message object.} \item{details}{Whether or not to display full message content.} \item{...}{Further arguments passed to or from other methods.} } \description{ The message body will be printed if \code{details} is \code{TRUE} or if the \code{envelope_details} option is \code{TRUE}. } \examples{ msg <- envelope() \%>\% text("Hello, World!") print(msg) print(msg, details = TRUE) options(envelope_details = TRUE) print(msg) } emayili/man/server.Rd0000644000176200001440000001067114134462672014271 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/server.R \name{server} \alias{server} \alias{gmail} \alias{sendgrid} \alias{mailgun} \alias{sendinblue} \alias{mailersend} \title{Create a SMTP server object.} \usage{ server( host, port = 25, username = NULL, password = NULL, insecure = FALSE, reuse = TRUE, helo = NA, protocol = NA, pause_base = 1, max_times = 5, ... ) gmail(username, password, ...) sendgrid(password, ...) mailgun(username, password, ...) sendinblue(username, password, ...) mailersend(username, password, ...) } \arguments{ \item{host}{DNS name or IP address of the SMTP server.} \item{port}{Port that the SMTP server is listening on.} \item{username}{Username for SMTP server.} \item{password}{Password for SMTP server or API key.} \item{insecure}{Whether to ignore SSL issues.} \item{reuse}{Whether the connection to the SMTP server should be left open for reuse.} \item{helo}{The HELO domain name of the sending host. If left as \code{NA} then will use local host name.} \item{protocol}{Which protocol (SMTP or SMTPS) to use for communicating with the server. Default will choose appropriate protocol based on port.} \item{pause_base}{Base delay (in seconds) for exponential backoff. See \link[purrr]{rate_backoff}.} \item{max_times}{Maximum number of times to retry.} \item{...}{Additional curl options. See \code{curl::curl_options()} for a list of supported options.} } \value{ A function which is used to send messages to the server. } \description{ Create a SMTP server object. } \section{Sendgrid}{ To use SendGrid you'll need to first \href{https://docs.sendgrid.com/for-developers/sending-email/integrating-with-the-smtp-api}{create an API key}. Then use the API key as the password. SendGrid will accept messages on ports 25, 587 and 2525 (using SMTP) as well as 465 (using SMTPS). } \section{Mailgun}{ To use Mailgun you'll need to first register a sender domain. This will then be assigned a username and password. Mailgun will accept messages on ports 25 and 587 (using SMTP) as well as 465 (using SMTPS). } \section{Sendinblue}{ To use Sendinblue you'll need to first create an account. You'll find your SMTP username and password in the SMTP & API section of your account settings. } \section{MailerSend}{ To use MailerSend you'll need to first create an account. You'll find your SMTP username and password under Domains. See \href{https://www.mailersend.com/help/smtp-relay}{How to send emails via SMTP with MailerSend}. Although this is not likely to be a problem in practice, MailerSend insists that all messages have at minimum a valid subject and either text or HTML content. } \examples{ # Set parameters for SMTP server (with username and password). smtp <- server( host = "smtp.gmail.com", port = 587, username = "bob@gmail.com", password = "bd40ef6d4a9413de9c1318a65cbae5d7" ) # Set parameters for a (fake) testing SMTP server. # # More information about this service can be found at https://www.smtpbucket.com/. # smtp <- server( host = "mail.smtpbucket.com", port = 8025 ) # Create a message msg <- envelope() \%>\% from("bob@gmail.com") \%>\% to("alice@yahoo.com") # Send message (verbose output from interactions with server) \dontrun{ smtp(msg, verbose = TRUE) } # To confirm that the message was sent, go to https://www.smtpbucket.com/ then: # # - fill in "bob@gmail.com" for the Sender field and # - fill in "alice@yahoo.com" for the Recipient field then # - press the Search button. # With explicit HELO domain. # smtp <- server(host = "mail.example.com", helo = "client.example.com") # Set parameters for Gmail SMTP server. The host and port are implicit. smtp <- gmail( username = "bob@gmail.com", password = "bd40ef6d4a9413de9c1318a65cbae5d7" ) # Set API key for SendGrid SMTP server. smtp <- sendgrid( password = "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" ) # Set username and password for Mailgun SMTP server. smtp <- mailgun( username = "postmaster@sandbox9ptce35fdf0b31338dec4284eb7aaa59.mailgun.org", password = "44d072e7g2b5f3bf23b2b642da0fe3a7-2ac825a1-a5be680a" ) # Set username and password for Sendinblue SMTP server. smtp <- sendinblue( username = "bob@gmail.com", password = "xsmtpsib-c75cf91323adc53a1747c005447cbc9a893c35888635bb7bef1a624bf773da33" ) # Set username and password for MailerSend SMTP server. smtp <- mailersend( username = "NS_Pf3ALM@gmail.com", password = "e5ATWLlTnWWDaKeE" ) } emayili/man/display.Rd0000644000176200001440000000062714132315227014417 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/address.R \name{display} \alias{display} \title{Extract display name} \usage{ display(addr) } \arguments{ \item{addr}{An \code{address} object.} } \value{ The display name or \code{NA}. } \description{ Extracts the display name from an email address. } \examples{ gerry <- as.address("Gerald ") display(gerry) } emayili/man/html.Rd0000644000176200001440000000412414155404046013715 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/body.R \name{html} \alias{html} \title{Add an HTML body to a message object.} \usage{ html( msg, content, disposition = "inline", charset = "utf-8", encoding = "quoted-printable", css_files = c(), language = FALSE, interpolate = TRUE, .open = "{{", .close = "}}", .envir = NULL ) } \arguments{ \item{msg}{A message object.} \item{content}{A string of message content.} \item{disposition}{Should the content be displayed inline or as an attachment? Valid options are \code{"inline"} and \code{"attachment"}. If set to \code{NA} then will guess appropriate value.} \item{charset}{What character set is used. Most often either \code{"UTF-8"} or \code{"ISO-8859-1"}.} \item{encoding}{How content is transformed to ASCII. Options are \code{"7bit"}, \code{"quoted-printable"} and \code{"base64"}. Use \code{NA} or \code{NULL} for no (or "identity") encoding.} \item{css_files}{Extra CSS files.} \item{language}{Langauge of content. If \code{FALSE} then will not include language field. If \code{TRUE} then will attempt to auto-detect language. Otherwise will use the specified language.} \item{interpolate}{Whether or not to interpolate into input using \link[glue]{glue}.} \item{.open}{The opening delimiter.} \item{.close}{The closing delimiter.} \item{.envir}{Environment used for \code{glue} interpolation. Defaults to \code{parent.frame()}.} } \value{ A message object. } \description{ Add an HTML body to a message object. } \examples{ # Inline HTML message. envelope() \%>\% html("Hello!") # Read HTML message from a file. htmlfile <- tempfile(fileext = ".html") cat("

Hello!

\n", file = htmlfile) envelope() \%>\% html(htmlfile) # You can pass a vector of character. Components will be separated by a # "\n". envelope() \%>\% html(c("Hello", "

World!

")) # You can also pass a tagList from {htmltools}. if (requireNamespace("htmltools", quietly = TRUE)) { library(htmltools) envelope() \%>\% html(tagList(h2("Hello"), p("World!"))) } } \seealso{ \code{\link{text}}, \code{\link{render}} } emayili/man/compare.Rd0000644000176200001440000000061314132315227014373 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{compare} \alias{compare} \title{Compare vectors} \usage{ compare(lhs, rhs) } \arguments{ \item{lhs}{LHS of operation.} \item{rhs}{RHS of operation.} } \value{ A Boolean value. } \description{ Returns \code{TRUE} wherever elements are the same (including \code{NA}), and \code{FALSE} everywhere else. } emayili/man/as.character.vctrs_address.Rd0000644000176200001440000000071614132315227020154 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/address.R \name{as.character.vctrs_address} \alias{as.character.vctrs_address} \title{Convert address object to character} \usage{ \method{as.character}{vctrs_address}(x, ...) } \arguments{ \item{x}{A vector of \code{address} objects.} \item{...}{Further arguments passed to or from other methods.} } \value{ A character vector. } \description{ Convert address object to character } emayili/man/parties.Rd0000644000176200001440000000101514132315227014411 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/parties.R \name{parties} \alias{parties} \title{Extract sender and recipient(s)} \usage{ parties(msg) } \arguments{ \item{msg}{A message object.} } \value{ A tibble. } \description{ Extract sender and recipient(s) } \examples{ email <- envelope() \%>\% from("Gerald ") \%>\% to(c("bob@gmail.com", "alice@yahoo.com")) \%>\% cc("Craig < craig@gmail.com>") \%>\% bcc(" Erin ") parties(email) } emayili/DESCRIPTION0000644000176200001440000000466614155611241013425 0ustar liggesusersType: Package Package: emayili Title: Send Email Messages Version: 0.7.0 Authors@R: c(person(given = c("Andrew", "B."), family = "Collier", role = c("aut", "cre", "cph"), email = "andrew@fathomdata.dev"), person(given = "Matt", family = "Dennis", role = "ctb", email = "matt@fathomdata.dev"), person(given = "Antoine", family = "Bichat", role = "ctb", email = "a.bichat@yahoo.fr", comment = c(ORCID = "0000-0001-6599-7081")), person(given = "Daniel", family = "Fahey", role = "ctb", email = "dpfahey@gmail.com"), person(given = c("Johann", "R."), family = "Kleinbub", role = "ctb", email = "johann.kleinbub@gmail.com"), person(given = c("Panagiotis"), family = "Moulos", role = "ctb", email = "pmoulos@gmail.com"), person(given = "Swechhya", family = "Bista", role = "ctb", email = "swechhyabista@gmail.com"), person(given = "Colin", family = "Fay", role = "ctb", email = "contact@colinfay.me", comment = c(ORCID = "0000-0001-7343-1846"))) Description: A light, simple tool for sending emails with minimal dependencies. URL: https://datawookie.github.io/emayili/ BugReports: https://github.com/datawookie/emayili/issues License: GPL-3 Language: en-GB Imports: base64enc, commonmark, curl (>= 4.0), digest, dplyr, glue, htmltools, httr, logger, magrittr, mime, purrr, rmarkdown, stringi, stringr, tidyr, urltools, vctrs, xfun, xml2 Suggests: cld2, cld3, gpg, here, memoise, testthat (>= 2.1.0), roxygen2, showtext, Microsoft365R SystemRequirements: The function render() requires Pandoc (http://pandoc.org). To use PGP/GnuPG encryption requires gpg. Encoding: UTF-8 RoxygenNote: 7.1.2 KeepSource: true NeedsCompilation: no Packaged: 2021-12-12 15:39:07 UTC; wookie Author: Andrew B. Collier [aut, cre, cph], Matt Dennis [ctb], Antoine Bichat [ctb] (), Daniel Fahey [ctb], Johann R. Kleinbub [ctb], Panagiotis Moulos [ctb], Swechhya Bista [ctb], Colin Fay [ctb] () Maintainer: Andrew B. Collier Repository: CRAN Date/Publication: 2021-12-13 09:30:09 UTC emayili/tests/0000755000176200001440000000000014132315227013045 5ustar liggesusersemayili/tests/testthat/0000755000176200001440000000000014155611241014705 5ustar liggesusersemayili/tests/testthat/test-message.R0000644000176200001440000000100514154176563017441 0ustar liggesuserstest_that("all header fields", { msg <- envelope() %>% subject("Test message") %>% to("frank@yahoo.co.uk") %>% cc("bob@gmail.com") %>% bcc("alice@yahoo.com") %>% from("olivia@google.com") %>% sender("olivia@gov.uk") expect_match(headers(msg), "Date: +.*\r\nX-Mailer: +\\{emayili\\}-[0-9]+\\.[0-9]+\\.[0-9]+\r\nMIME-Version: +1\\.0\r\nSubject: +Test message\r\nTo: +frank@yahoo.co.uk\r\nCc: +bob@gmail.com\r\nBcc: +alice@yahoo.com\r\nFrom: +olivia@google.com\r\nSender: +olivia@gov.uk") }) emayili/tests/testthat/test-body.R0000644000176200001440000000522014154176563016755 0ustar liggesuserstest_that("text: only single message body", { expect_error(envelope() %>% text("

foo

"), NA) expect_error(envelope() %>% text(c("

foo

", "

bar

"))) }) test_that("list_to_char: tagList & vec of html are casted to character", { expect_equal( list_to_char(c("Hello!", "

World

")), "Hello!\n

World

" ) skip_if_not_installed("htmltools") expect_equal( list_to_char( htmltools::tagList( htmltools::h2("this"), htmltools::p("That") ) ), "

this

\n

That

" ) }) test_that("html: tagList & vec of html are cast to character", { msg <- envelope() %>% html(c("Hello!", "

World

")) expect_true( grepl( "Hello!", msg$parts[[1]]$content ) ) expect_true( grepl( "

World

", msg$parts[[1]]$content ) ) skip_if_not_installed("htmltools") msg <- envelope() %>% html( htmltools::tagList( htmltools::h2("Hello"), htmltools::p("World") ) ) expect_true( grepl( "

Hello

", msg$parts[[1]]$content ) ) expect_true( grepl( "

World

", msg$parts[[1]]$content ) ) }) test_that("html: HTML from file", { expect_true( grepl( HTMLCONTENT, envelope() %>% html(HTMLPATH, encoding = NULL) %>% as.character() ) ) }) test_that("disable interpolation", { expect_match( envelope() %>% text("Hello {{name}}!", interpolate = FALSE) %>% as.character(), "Hello \\{\\{name\\}\\}!" ) }) test_that("interpolate from environment", { variables <- list(name = "Alice") expect_match( envelope() %>% text("Hello {{name}}!", .envir = variables) %>% as.character(), "Hello Alice!" ) expect_match( envelope() %>% html("

Hello {{name}}!

", .envir = variables) %>% as.character(), "Hello Alice!" ) }) test_that("interpolation delimeters", { name <- "Alice" expect_match( envelope() %>% text("Hello <>!", .open = "<<", .close = ">>") %>% as.character(), "Hello Alice!" ) }) test_that("toggle visibility", { options(envelope.invisible = FALSE) expect_visible(envelope() %>% text("Hello!")) options(envelope.invisible = TRUE) expect_invisible(envelope() %>% text("Hello!")) }) test_that("html: inject CSS", { expect_match( envelope() %>% html("

foo

", css_files = CSSPATH) %>% as.character(), COLOUR_GLAUCOUS ) }) test_that("Content-Type header", { expect_match( envelope() %>% text("Hello!") %>% as.character(), "Content-Type: +text/plain;[[:space:]]+charset=utf-8;[[:space:]]+format=flowed" ) }) emayili/tests/testthat/test-address.R0000644000176200001440000000603414154176563017451 0ustar liggesuserstest_that("create address", { expect_equal( address( c("gerry@gmail.com", "alice@yahoo.com", "jim@aol.com"), c("Gerald", "Alice", NA) ), c( address("gerry@gmail.com", "Gerald"), address("alice@yahoo.com", "Alice"), address("jim@aol.com") ) ) }) test_that("create address from local and domain", { expect_equal( address(local = c("alice", "erin"), domain = "yahoo.com"), address(c("alice@yahoo.com", "erin@yahoo.com")) ) }) test_that("mandatory arguments", { expect_error(address()) expect_error(address(NA, "Bob")) expect_error(address(local = "alice")) expect_error(address(domain = "yahoo.com")) expect_error(address("alice@yahoo.com", local = "alice")) expect_error(address("alice@yahoo.com", domain = "yahoo.com")) }) test_that("convert to character", { expect_equal(as.character(address("gerry@gmail.com")), "gerry@gmail.com") expect_equal(as.character( address("gerry@gmail.com", "Gerald")), "Gerald ") }) test_that("normalise", { expect_equal( as.character(address(" gerry@gmail.com ", normalise = FALSE)), " gerry@gmail.com " ) expect_equal(as.character(address(" gerry @ gmail.com ")), "gerry@gmail.com") expect_equal( as.character(address(" gerry @ gmail.com ", " Gerald ")), "Gerald " ) }) test_that("parse address", { expect_equal(as.address(" Gerald < gerry@gmail.com > "), address("gerry@gmail.com", "Gerald")) }) test_that("normalise", { expect_equal(as.address(" Gerald < gerry@gmail.com >"), address("gerry@gmail.com", "Gerald")) }) test_that("full type of vector", { expect_equal(vec_ptype_full(address("alice@yahoo.com", "Alice")), "address") }) test_that("abbreviated type of vector", { expect_equal(vec_ptype_abbr(address("alice@yahoo.com", "Alice")), "addr") }) test_that("print address", { expect_output(print(address("alice@yahoo.com", "Alice")), "Alice ") }) test_that("address operators", { expect_true(address("alice@yahoo.com", "Alice") == address("alice@yahoo.com", "Alice")) expect_true(address("alice@yahoo.com", "Alice") == "Alice ") expect_true(address("alice@yahoo.com", "Alice") == address("alice@yahoo.com", "Gerald")) expect_true(address("alice@yahoo.com", "Alice") != address("bob@gmail.com", "Bob")) expect_true(address("alice@yahoo.com", "Alice") != address("gerry@gmail.com", "Alice")) # # Undefined operation. # expect_error(address("alice@yahoo.com", "Alice") / address("gerry@gmail.com", "Alice")) }) test_that("split address list", { addr_list <- address( c("gerry@gmail.com", "alice@yahoo.com", "jim@aol.com"), c("Gerald", NA, NA) ) expect_equal( as.address(c("Gerald ", "alice@yahoo.com", "jim@aol.com")), addr_list ) expect_equal( as.address("Gerald , alice@yahoo.com, jim@aol.com"), addr_list ) expect_equal( as.address(c("Gerald ", "alice@yahoo.com, jim@aol.com")), addr_list ) }) emayili/tests/testthat/test-header-receipt.R0000644000176200001440000000061314154176563020702 0ustar liggesuserstest_that("read receipt", { expect_error(envelope() %>% request_receipt_read()) msg <- envelope() %>% from("olivia@google.com") %>% request_receipt_read() expect_match(headers(msg), "Return-Receipt-To: +olivia@google.com") expect_match(headers(msg), "Disposition-Notification-To: +olivia@google.com") expect_match(headers(msg), "X-Confirm-Reading-To: +olivia@google.com") }) emayili/tests/testthat/test-subject.R0000644000176200001440000000070714154176563017464 0ustar liggesuserstest_that("subject: set/get", { msg <- envelope() %>% subject("Test message") expect_equal(subject(msg), "Test message") }) test_that("interpolate", { name <- "Alice" variables <- list(name = name) expect_equal( envelope() %>% subject("Hello {{name}}!") %>% subject(), "Hello Alice!" ) expect_equal( envelope() %>% subject("Hello {{name}}!", .envir = variables) %>% subject(), "Hello Alice!" ) }) emayili/tests/testthat/setup.R0000644000176200001440000000740214155412563016201 0ustar liggesuserssuppressPackageStartupMessages({ library(here) library(logger) library(dplyr) require(htmltools, quietly = TRUE) }) log_threshold(ERROR) # Generate random folder name. # rndchar <- function(n = 24) { stringi::stri_rand_strings(1, n) } TXTPATH <- tempfile(fileext = ".txt") CSSPATH <- tempfile(fileext = ".css") HTMLPATH <- "hello.html" RMD_TEMPLATE <- "vignette.Rmd" # This file comes from https://bit.ly/2P4LUO8 (cat poster on WikiPedia). # JPGPATH <- system.file("cats.jpg", package = "emayili", mustWork = TRUE) # If have not installed yet. if (!file.exists(JPGPATH)) JPGPATH <- here("inst", "cats.jpg") TXTCONTENT <- "Some random text." PLAIN_MARKDOWN <- "## Section\n[link](https://www.google.com)" PLAIN_MARKDOWN_INTERPOLATE <- "Hi {{name}}!" COLOUR_GLAUCOUS = "#6082b6" # Start with a blank slate. # source("teardown-files.R", local = TRUE) # The , sep = "" prevents it from writing an "\n" at the end of the line. writeLines(TXTCONTENT, TXTPATH, sep = "") TXTCONTENT_ENCODED <- emayili:::mime_base64encode(charToRaw(TXTCONTENT)) writeLines( paste0("body {color: ", COLOUR_GLAUCOUS," !important;}"), CSSPATH ) HTMLCONTENT <- "

Hello there, stranger!

" # writeLines(HTMLCONTENT, HTMLPATH) SUBJECT <- rndchar(36) # SERVER ----------------------------------------------------------------------- # Using fake SMTP server. # # - https://mailtrap.io/ # - https://www.smtpbucket.com/ # SMTP_SERVER = "mail.smtpbucket.com" SMTP_PORT = 8025 SMTP_USERNAME_GMAIL <- Sys.getenv("GMAIL_USERNAME", NA) SMTP_PASSWORD_GMAIL <- Sys.getenv("GMAIL_PASSWORD", NA) SMTP_PASSWORD_SENDGRID <- Sys.getenv("SENDGRID_API_KEY") SMTP_USERNAME_MAILGUN <- Sys.getenv("MAILGUN_SMTP_USERNAME") SMTP_PASSWORD_MAILGUN <- Sys.getenv("MAILGUN_SMTP_PASSWORD") SMTP_USERNAME_SENDINBLUE <- Sys.getenv("SENDINBLUE_SMTP_USERNAME") SMTP_PASSWORD_SENDINBLUE <- Sys.getenv("SENDINBLUE_SMTP_PASSWORD") SMTP_USERNAME_MAILERSEND <- Sys.getenv("MAILERSEND_SMTP_USERNAME") SMTP_PASSWORD_MAILERSEND <- Sys.getenv("MAILERSEND_SMTP_PASSWORD") EMAIL_FROM <- ifelse(is.na(SMTP_USERNAME_GMAIL), "alice@gmail.com", SMTP_USERNAME_GMAIL) EMAIL_TO <- ifelse(is.na(SMTP_USERNAME_GMAIL), "bob@yahoo.com", SMTP_USERNAME_GMAIL) smtp <- server( host = SMTP_SERVER, port = SMTP_PORT ) smtp_verbose <- server( host = SMTP_SERVER, port = SMTP_PORT, username = SMTP_USERNAME_GMAIL ) smtp_insecure <- server( host = SMTP_SERVER, port = SMTP_PORT, username = SMTP_USERNAME_GMAIL, insecure = TRUE ) if (is.na(SMTP_USERNAME_GMAIL) || is.na(SMTP_PASSWORD_GMAIL)) { smtp_gmail <- NA } else { smtp_gmail <- gmail( username = SMTP_USERNAME_GMAIL, password = SMTP_PASSWORD_GMAIL ) } smtp_sendgrid <- sendgrid( password = SMTP_PASSWORD_SENDGRID ) smtp_mailgun <- mailgun( username = SMTP_USERNAME_MAILGUN, password = SMTP_PASSWORD_MAILGUN ) smtp_sendinblue <- sendinblue( username = SMTP_USERNAME_SENDINBLUE, password = SMTP_PASSWORD_SENDINBLUE ) smtp_mailersend <- mailersend( username = SMTP_USERNAME_MAILERSEND, password = SMTP_PASSWORD_MAILERSEND ) # R MARKDOWN FILE -------------------------------------------------------------- # Create an Rmd document from template. rmarkdown::draft( RMD_TEMPLATE, template = "html_vignette", package = "rmarkdown", edit = FALSE ) # UTILITY ---------------------------------------------------------------------- skip_if_neither_installed <- function(paks) { are_there <- vapply( paks, requireNamespace, FUN.VALUE = logical(1), quietly = TRUE ) if (!any(are_there)) { testthat::skip( sprintf( "None of {%s} are installed", paste(paks, collapse = "} or {") ) ) } } with_tz <- function(code) { old_tz <- Sys.getenv("TZ") on.exit( Sys.setenv("TZ" = old_tz) ) Sys.setenv(TZ = "UTC") force(code) } emayili/tests/testthat/test-header-various.R0000644000176200001440000000474614154176563020752 0ustar liggesuserstest_that("invalid levels", { expect_error(envelope %>% sensitivity("none"), "Invalid") }) test_that("set sensitivity", { expect_true(is.null(priority(envelope()))) expect_equal(envelope() %>% sensitivity("personal") %>% sensitivity(), "personal") }) test_that("set expires & reply-by", { with_tz({ expect_match( envelope() %>% expires("2030-01-01 13:25:00", "UTC") %>% expires(), "Tue, 01 Jan 2030 13:25:00 \\+0000 \\(UTC|GMT\\)" ) expect_match( envelope() %>% replyby("2021-12-25 06:00:00", "GMT") %>% replyby(), "Sat, 25 Dec 2021 06:00:00 \\+0000 \\(UTC|GMT\\)" ) }) }) test_that("in-reply-to & references", { expect_match( envelope() %>% inreplyto("<6163c08e.1c69fb81.65b78.183c@mx.google.com>") %>% as.character(), "In-Reply-To: +<6163c08e.1c69fb81.65b78.183c@mx.google.com>" ) expect_match( envelope() %>% subject("Test") %>% inreplyto("<6163c08e.1c69fb81.65b78.183c@mx.google.com>", "AW: ") %>% as.character(), "Subject: +AW: Test" ) expect_match( envelope() %>% references("<6163c08e.1c69fb81.65b78.183c@mx.google.com>") %>% as.character(), "References: +<6163c08e.1c69fb81.65b78.183c@mx.google.com>" ) expect_match( envelope() %>% subject("Test") %>% references("<6163c08e.1c69fb81.65b78.183c@mx.google.com>", "AW: ") %>% as.character(), "Subject: +AW: Test" ) }) test_that("return-path", { expect_equal( envelope() %>% return_path("bob@gmail.com") %>% return_path(), address("bob@gmail.com") ) }) test_that("comments", { msg <- envelope() %>% comments("Test message") expect_equal(comments(msg), "Test message") expect_match( envelope() %>% comments("Test comments") %>% as.character(), "Test comments" ) }) test_that("keywords", { msg <- envelope() %>% keywords("Test keyword") expect_equal(keywords(msg), "Test keyword") expect_match( envelope() %>% keywords("Test keyword") %>% as.character(), "Test keyword" ) msg <- envelope() %>% keywords("Test keyword1, Test keyword2") expect_equal(keywords(msg), "Test keyword1, Test keyword2") expect_match( envelope() %>% keywords("Test keyword1, Test keyword2") %>% as.character(), "Test keyword1,[[:space:]]+Test keyword2" ) msg <- envelope() %>% keywords(c("Test keyword1, Test keyword2")) expect_equal(keywords(msg), "Test keyword1, Test keyword2") expect_match( envelope() %>% keywords(c("Test keyword1, Test keyword2")) %>% as.character(), "Test keyword1,[[:space:]]+Test keyword2" ) }) emayili/tests/testthat/test-display.R0000644000176200001440000000036414154176563017471 0ustar liggesuserstest_that("returns display name", { expect_equal(display(as.address("Gerald ")), "Gerald") }) test_that("returns NA when no display name present", { expect_equal(display(as.address("gerry@gmail.com")), NA_character_) }) emayili/tests/testthat/test-header-precedence.R0000644000176200001440000000113314154176563021342 0ustar liggesuserstest_that("invalid levels", { expect_error(envelope %>% priority("none"), "Invalid") expect_error(envelope %>% importance("urgent"), "Invalid") }) test_that("set priority", { expect_true(is.null(priority(envelope()))) expect_equal(envelope() %>% priority("urgent") %>% priority(), "urgent") expect_equal(envelope(priority = "urgent") %>% priority(), "urgent") }) test_that("set importance", { expect_true(is.null(importance(envelope()))) expect_equal(envelope() %>% importance("high") %>% importance(), "high") expect_equal(envelope(importance = "high") %>% importance(), "high") }) emayili/tests/testthat/test-encoding.R0000644000176200001440000000407314154176563017613 0ustar liggesusers# Example text from https://en.wikipedia.org/wiki/Quoted-printable. # encoded <- "J'interdis aux marchands de vanter trop leurs marchandises. Car ils se font=\n vite p=C3=A9dagogues et t'enseignent comme but ce qui n'est par essence qu=\n'un moyen, et te trompant ainsi sur la route =C3=A0 suivre les voil=C3=A0 b=\nient=C3=B4t qui te d=C3=A9gradent, car si leur musique est vulgaire ils te =\nfabriquent pour te la vendre une =C3=A2me vulgaire." decoded <- "J'interdis aux marchands de vanter trop leurs marchandises. Car ils se font vite pédagogues et t'enseignent comme but ce qui n'est par essence qu'un moyen, et te trompant ainsi sur la route à suivre les voilà bientôt qui te dégradent, car si leur musique est vulgaire ils te fabriquent pour te la vendre une âme vulgaire." # Can generate other test text at https://www.webatic.com/quoted-printable-convertor. test_that("(en|de)code text", { local_reproducible_output(unicode = TRUE) expect_equal(qp_encode(decoded), encoded) expect_equal(qp_decode(encoded), decoded) }) test_that("(en|de)code '='", { expect_equal(qp_encode("= =="), "=3D =3D=3D") expect_equal(qp_decode("=3D =3D=3D"), "= ==") }) test_that("don't break lines in token", { expect_equal(qp_encode("========================="), "=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D") expect_equal(qp_encode(" ========================="), " =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=\n=3D") expect_equal(qp_encode(" ========================="), " =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=\n=3D") expect_equal(qp_encode(" ========================="), " =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=\n=3D") expect_equal(qp_encode(" ========================="), " =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=\n=3D=3D") }) test_that("unicode", { local_reproducible_output(unicode = TRUE) expect_equal(Encoding(qp_decode("=E3=83=84")), Encoding("ツ")) expect_equal(qp_decode("=E3=83=84"), "ツ") }) emayili/tests/testthat/test-header.R0000644000176200001440000000101614154176563017247 0ustar liggesuserstest_that("print header", { expect_output( print(header("Header", "value")), "Header: +value" ) }) test_that("header append", { # Don't append. expect_equal( envelope() %>% header_set("Header", "first") %>% header_set("Header", "second") %>% header_get("Header"), "second" ) # Do append. expect_equal( envelope() %>% header_set("Header", "first") %>% header_set("Header", "second", append = TRUE) %>% header_get("Header"), c("first", "second") ) }) emayili/tests/testthat/test-compatibility-microsoft365r.R0000644000176200001440000000241614155404046023307 0ustar liggesusersskip_on_ci() skip_on_cran() skip_if_not( require(Microsoft365R, quietly=TRUE), "{Microsoft365R} tests skipped (package not available)." ) # To run these tests you'll need to first authenticate. This is done by running # the following command in an interactive session. # # It might also help to login to a Microsoft account (https://login.live.com/). outlook <- get_personal_outlook() from_addr <- "anne@example.com" to_addr <- "bob@example.com" cc_addr <- "jane@example.com" bcc_addr <- "rex@example.com" msg <- envelope( to = to_addr, cc = cc_addr, bcc = bcc_addr, subject = SUBJECT, html = HTMLCONTENT ) msg_outlook <- outlook$create_email(msg) test_that("body is correct", { expect_identical(msg_outlook$properties$body$contentType, "html") expect_true(grepl(HTMLCONTENT, msg_outlook$properties$body$content)) }) test_that("subject is correct", { expect_identical(msg_outlook$properties$subject, SUBJECT) }) test_that("parties are correct", { expect_identical(msg_outlook$properties$toRecipients[[1]]$emailAddress$address, as.character(to(msg))) expect_identical(msg_outlook$properties$ccRecipients[[1]]$emailAddress$address, as.character(cc(msg))) expect_identical(msg_outlook$properties$bccRecipients[[1]]$emailAddress$address, as.character(bcc(msg))) }) emayili/tests/testthat/test-address-parts.R0000644000176200001440000000033014154176563020571 0ustar liggesuserstest_that("returns local part of email address", { expect_equal(local("alice@example.com"), "alice") }) test_that("returns domain of email address", { expect_equal(domain("alice@example.com"), "example.com") }) emayili/tests/testthat/test-sanitise.R0000644000176200001440000000026214154176563017640 0ustar liggesuserstest_that("remove comments", { expect_equal(sanitise("alice@yahoo.com(comment)"), "alice@yahoo.com") expect_equal(sanitise("alice(comment)@yahoo.com"), "alice@yahoo.com") }) emayili/tests/testthat/test-locale.R0000644000176200001440000000205314155162547017256 0ustar liggesusersskip_on_os(os = c("mac", "windows", "solaris")) # To ensure that these locales are supported you might need to run this in the # shell: # # sudo locale-gen es_ES es_ES.utf8 # sudo update-locale check_render <- function(locale, unicode, txt) { test_that(locale, { # Write text to file in a UTF8 locale. This ensures that the accented # characters are rendered correctly to the file on disk. Sys.setlocale("LC_ALL", "en_US.UTF-8") md <- tempfile() cat(txt, file = md) # Change to other locale. Sys.setlocale("LC_ALL", locale) local_reproducible_output(unicode = unicode) # Render from string. expect_match(envelope() %>% render(txt) %>% as.character(), txt) # Render from file. expect_match(envelope() %>% render(md) %>% as.character(), txt) }) } # Get current locale. # CURRENT_LOCALE = Sys.getlocale("LC_CTYPE") # Spanish # ES_TXT <- "Desde el comité organizador" # check_render("es_ES.utf8", TRUE, ES_TXT) check_render("Spanish", FALSE, ES_TXT) # Restore locale. # Sys.setlocale("LC_ALL", CURRENT_LOCALE) emayili/tests/testthat/test-header-language.R0000644000176200001440000000154014154176563021032 0ustar liggesuserstest_that("content-language field", { expect_match( envelope() %>% text("Hello!", language = "en-GB") %>% as.character(), "Content-Language: +en-GB" ) expect_match( envelope() %>% html(HTMLCONTENT, language = "en-GB") %>% as.character(), "Content-Language: +en-GB" ) expect_match( envelope() %>% render(RMD_TEMPLATE, language = "en-GB", include_css = FALSE) %>% as.character(), "Content-Language: +en-GB" ) }) test_that("detect language", { skip_if_neither_installed(c("cld3", "cld2")) expect_no_match( envelope() %>% text("Hello!", language = FALSE) %>% as.character(), "Content-Language:" ) expect_match( envelope() %>% html( "

Hij heeft de klok horen luiden maar weet niet waar de klepel hangt.

", language = TRUE ) %>% as.character(), "Content-Language: +nl" ) }) emayili/tests/testthat/test-server.R0000644000176200001440000000475314154176563017340 0ustar liggesusersmsg <- envelope() %>% from(EMAIL_FROM) %>% to(EMAIL_TO) msg_no_recipient <- envelope() %>% from(EMAIL_FROM) msg_no_sender <- envelope() %>% to(EMAIL_TO) test_that("server type", { expect_type(smtp, "closure") expect_type(smtp_sendgrid, "closure") expect_type(smtp_mailgun, "closure") expect_type(smtp_sendinblue, "closure") expect_type(smtp_mailersend, "closure") skip_if(suppressWarnings(is.na(smtp_gmail))) expect_type(smtp_gmail, "closure") }) test_that("error if sender missing", { skip_on_cran() expect_error(smtp(msg_no_sender), "Must specify who the email is from.") }) test_that("error if recipient missing", { skip_on_cran() expect_error(smtp(msg_no_recipient), "Must specify at least one email recipient.") }) test_that("sends text message", { msg <- msg %>% subject("Text body") %>% text("Hello, World!") skip_on_cran() expect_error(smtp(msg), NA) }) test_that("sends message with insecure = TRUE", { skip_on_cran() expect_error(smtp_insecure(msg), NA) }) test_that("sends with SSL", { skip_on_cran() skip_on_ci() skip_if(suppressWarnings(is.na(smtp_gmail))) expect_error(smtp_gmail(msg %>% subject("{emayili} test")), NA) }) test_that("sends HTML message", { msg <- msg %>% subject("HTML body") %>% html("

Hello, World! You can also underline text.

") skip_on_cran() expect_error(smtp(msg), NA) }) test_that("sends message with text attachment", { msg <- msg %>% attachment(TXTPATH) skip_on_cran() expect_error(smtp(msg %>% subject("{emayili} test: Text attachment")), NA) }) test_that("sends message with image attachment", { msg <- msg %>% attachment(JPGPATH) skip_on_cran() expect_error(smtp(msg %>% subject("{emayili} test: Image attachment")), NA) }) test_that("sends message with image attachment (using CID)", { msg <- msg %>% html('') %>% attachment(JPGPATH, cid = "r-logo", type = "image/jpg") skip_on_cran() expect_error(smtp(msg %>% subject("{emayili} test: Image attachment (using CID)")), NA) }) test_that("verbose output", { skip_on_cran() expect_match( capture.output(smtp(msg, verbose = TRUE), type = "message") %>% paste(collapse = "\n"), "250 Message accepted", fixed = TRUE ) expect_length(capture.output(smtp(msg), type = "message"), 0) }) test_that("replace bare line feeds", { msg <- envelope() %>% render("Hello!") expect_false(as.character(msg) %>% str_detect(REGEX_BARE_LINEFEED)) }) emayili/tests/testthat/test-parties.R0000644000176200001440000000060714154176563017473 0ustar liggesuserstest_that("generate parties table", { msg <- envelope() %>% to(address("gerry@gmail.com", "Gerald")) %>% from("jim@aol.com") %>% cc("alice@yahoo.com", "bob@yahoo.co.uk") %>% bcc("Erin ") addresses <- parties(msg) expect_type(addresses, "list") expect_equal( names(addresses), c("type", "address", "display", "raw", "local", "domain") ) }) emayili/tests/testthat/test-envelope.R0000644000176200001440000000354314154176563017643 0ustar liggesuserstest_that("envelope print() output", { msg <- envelope() %>% subject("Test message") expect_output(print(msg), "Date: +.*\nSubject: +Test message") }) test_that("class envelope", { object <- envelope() expect_equal(class(object), "envelope") }) test_that("recipient address", { recipient <- envelope(to = "bob@gmail.com") expect_equal(recipient$headers$To$values[[1]], address("bob@gmail.com")) }) test_that("sender address", { sender <- envelope(from = "bob@gmail.com") expect_equal(sender$headers$From$values[[1]], address("bob@gmail.com")) }) test_that("maximum one sender address", { expect_error(envelope(from = c("bob@gmail.com", "anne@example.com"))) expect_error(envelope() %>% from(c("bob@gmail.com", "anne@example.com"))) }) test_that("cc", { cc <- envelope(cc = "bob@gmail.com") expect_equal(cc$headers$Cc$values[[1]], address("bob@gmail.com")) }) test_that("bcc", { bcc <- envelope(bcc = "bob@gmail.com") expect_equal(bcc$headers$Bcc$values[[1]], address("bob@gmail.com")) }) test_that("reply to", { reply <- envelope(reply = "bob@gmail.com") expect_equal(reply$header[["Reply-To"]]$values[[1]], address("bob@gmail.com")) }) test_that("subject", { subject <- envelope(subject = "Email Subject") expect_equal(subject$header$Subject$values[[1]], "Email Subject") }) test_that("body text", { email_text <- envelope(text = "foo") expect_equal(email_text$parts[[1]]$content, "foo") }) test_that("body html", { html <- envelope(html = "

foo

") expect_match(html$parts[[1]]$content, "

foo

") }) test_that("append another body", { msg <- envelope() %>% text("Hello!") %>% html("

Goodbye!

") expect_equal(length(msg$parts), 2) }) test_that("parts are not nested", { msg <- envelope() %>% text("Hello!") %>% html(HTMLPATH) %>% attachment(JPGPATH) expect_false(is.nested(msg$parts)) }) emayili/tests/testthat/test-encrypt.R0000644000176200001440000001056314155162547017510 0ustar liggesusersskip_if_not_installed("gpg") # # Encryption tests currently don't work with macOS on GitHub Actions. # skip_on_os(os = "mac") library(gpg) # - If running locally then use random home directory for keyring. # - Don't do this on CI because GPG doesn't work on macOS with non-default # home folder. # if (Sys.getenv("CI") == "") { message("Use random home folder for GPG.") gpg_restart(home = tempdir(), silent = TRUE) } # gpg_keygen(name = "Alice", email = "alice@yahoo.com") gpg_keygen(name = "Bob", email = "bob@gmail.com") gpg_keygen(name = "Jim", email = "jim@aol.com") # # The keys should all be RSA. # stopifnot(all(gpg_list_keys() %>% pull(algo) %in% c("RSA", "EdDSA"))) BEGIN_PGP_MESSAGE = "-----BEGIN PGP MESSAGE-----" END_PGP_MESSAGE = "-----END PGP MESSAGE-----" BEGIN_PGP_SIGNATURE = "-----BEGIN PGP SIGNATURE-----" END_PGP_SIGNATURE = "-----END PGP SIGNATURE-----" BEGIN_PGP_PUBLIC_KEY_BLOCK = "-----BEGIN PGP PUBLIC KEY BLOCK-----" END_PGP_PUBLIC_KEY_BLOCK = "-----END PGP PUBLIC KEY BLOCK-----" test_that("sign/encrypt empty message", { msg <- envelope( to = "alice@yahoo.com", from = "bob@gmail.com", encrypt = TRUE, sign = TRUE ) expect_error(as.character(msg)) }) test_that("sign", { msg <- envelope( to = "alice@yahoo.com", from = "bob@gmail.com", sign = TRUE ) %>% text(TXTCONTENT) expect_match(as.character(msg), BEGIN_PGP_SIGNATURE) expect_match(as.character(msg), END_PGP_SIGNATURE) expect_no_match(as.character(msg), BEGIN_PGP_MESSAGE) expect_no_match(as.character(msg), END_PGP_MESSAGE) expect_no_match(as.character(msg), BEGIN_PGP_PUBLIC_KEY_BLOCK) expect_no_match(as.character(msg), END_PGP_PUBLIC_KEY_BLOCK) }) test_that("encrypt", { msg <- envelope( to = "alice@yahoo.com", from = "bob@gmail.com", encrypt = TRUE ) %>% text(TXTCONTENT) expect_match(as.character(msg), BEGIN_PGP_MESSAGE) expect_match(as.character(msg), END_PGP_MESSAGE) expect_no_match(as.character(msg), BEGIN_PGP_SIGNATURE) expect_no_match(as.character(msg), END_PGP_SIGNATURE) expect_no_match(as.character(msg), BEGIN_PGP_PUBLIC_KEY_BLOCK) expect_no_match(as.character(msg), END_PGP_PUBLIC_KEY_BLOCK) }) test_that("sign & encrypt", { msg <- envelope( to = "alice@yahoo.com", from = "bob@gmail.com", sign = TRUE, encrypt = TRUE ) %>% text(TXTCONTENT) expect_match(as.character(msg), BEGIN_PGP_MESSAGE) expect_match(as.character(msg), END_PGP_MESSAGE) expect_no_match(as.character(msg), BEGIN_PGP_SIGNATURE) expect_no_match(as.character(msg), END_PGP_SIGNATURE) expect_no_match(as.character(msg), BEGIN_PGP_PUBLIC_KEY_BLOCK) expect_no_match(as.character(msg), END_PGP_PUBLIC_KEY_BLOCK) }) test_that("public key", { msg <- envelope( to = "alice@yahoo.com", from = "bob@gmail.com", public_key = TRUE ) %>% text(TXTCONTENT) expect_match(as.character(msg), BEGIN_PGP_PUBLIC_KEY_BLOCK) expect_match(as.character(msg), END_PGP_PUBLIC_KEY_BLOCK) expect_no_match(as.character(msg), BEGIN_PGP_SIGNATURE) expect_no_match(as.character(msg), END_PGP_SIGNATURE) expect_no_match(as.character(msg), BEGIN_PGP_MESSAGE) expect_no_match(as.character(msg), END_PGP_MESSAGE) }) test_that("fail without sender or recipients", { expect_error( envelope(to = "alice@yahoo.com") %>% encrypt() %>% as.character(), "without sender" ) expect_error( envelope(from = "bob@gmail.com") %>% encrypt() %>% as.character(), "without recipients" ) }) test_that("missing public keys", { # Missing sender key. expect_error( envelope(to = "alice@yahoo.com", from = "tim@gmail.com") %>% encrypt() %>% as.character(), "missing keys", ignore.case = TRUE ) # Missing recipient key. expect_error( envelope(to = "jenny@yahoo.com", from = "bob@gmail.com") %>% encrypt() %>% as.character(), "missing keys", ignore.case = TRUE ) }) test_that("sign with/without body", { nobody <- envelope(to = "alice@yahoo.com", from = "bob@gmail.com") body <- nobody %>% text("Hello!") # With public key. expect_error(nobody %>% encrypt(FALSE, TRUE, TRUE) %>% as.character(), NA) expect_error(body %>% encrypt(FALSE, TRUE, TRUE) %>% as.character(), NA) # Without public key. expect_error(nobody %>% encrypt(FALSE, TRUE, FALSE) %>% as.character(), "empty message") expect_error(body %>% encrypt(FALSE, TRUE, FALSE) %>% as.character(), NA) }) emayili/tests/testthat/test-mime.R0000644000176200001440000000530014155162547016744 0ustar liggesuserstest_that("children must be MIME", { expect_error(MIME(children = list(1)), ERROR_NOT_MIME_OBJECT) expect_error(append.MIME(MIME(), 1), ERROR_NOT_MIME_OBJECT) expect_error(prepend.MIME(MIME(), 1), ERROR_NOT_MIME_OBJECT) }) test_that("create multipart/mixed", { expect_equal(class(multipart_mixed()), c("multipart_mixed", "MIME")) }) test_that("convert single child to list", { expect_type(MIME(children = text_plain("Hello!"))$children, "list") }) test_that("missing disposition", { mime_txt <- other(TXTPATH, disposition = NA) mime_jpg <- other(JPGPATH, disposition = NA) expect_match(mime_txt$type, "^text/plain") expect_match(mime_jpg$type, "^image/jpeg") expect_match(mime_txt$disposition, "^inline") expect_match(mime_jpg$disposition, "^attachment") }) test_that("print", { mime_txt <- emayili:::other(TXTPATH, disposition = NA) expect_output( print.MIME(mime_txt), as.character.MIME(mime_txt) %>% str_replace_all("\r\n", "\n"), fixed = TRUE ) }) test_that("squish", { expect_match( text_html( "

Hello!

", squish = TRUE )$content, "

Hello!

" ) }) test_that("header fields", { mime_txt <- other(TXTPATH, disposition = NA) expect_match(as.character.MIME(mime_txt), "Content-Type: +text/plain;[[:space:]]+name=\"[^.]+\\.txt\"\r\nContent-Disposition: +inline;[[:space:]]+filename=\"[^.]+\\.txt\"\r\nContent-Transfer-Encoding: +base64\r\nX-Attachment-Id: +.*\nContent-ID: +<[^>]+>\r\n") }) test_that("valid encoding", { expect_error(MIME(encoding = "klingon") %>% as.character.MIME()) }) test_that("length", { expect_equal(length(MIME()), 1) }) test_that("base64 encoding & MD5 checksum", { mime_txt <- emayili:::other(TXTPATH, disposition = NA) expect_match( as.character.MIME(mime_txt), TXTCONTENT_ENCODED ) expect_match( as.character.MIME(mime_txt), "Content-MD5: +7r/PnFfETLz0CFCESunVvA==", fixed = FALSE ) }) test_that("order doesn't matter", { expect_error( envelope() %>% text("Hello!") %>% attachment(TXTPATH) %>% as.character(), NA ) expect_error( envelope() %>% attachment(TXTPATH) %>% text("Hello!") %>% as.character(), NA ) }) test_that("MD5 checksum", { mime_jpg <- other(JPGPATH, disposition = NA) expect_match( as.character.MIME(mime_jpg), "Content-MD5: +0KTj0bnhRWCUK4N7LnvNmA==" ) }) test_that("order doesn't matter", { expect_error( envelope() %>% text("Hello!") %>% attachment(TXTPATH) %>% as.character(), NA ) expect_error( envelope() %>% attachment(TXTPATH) %>% text("Hello!") %>% as.character(), NA ) }) test_that("invalid hash algorithm", { expect_error(multipart_signed(micalg = "pgp-sha0")) }) emayili/tests/testthat/test-utils.R0000644000176200001440000000337514155162547017167 0ustar liggesuserstest_that("drape line feeds", { expect_equal(drape_linefeed("foo\nbar"), "foo\r\nbar") expect_equal(drape_linefeed("foo\r\nbar"), "foo\r\nbar") expect_equal(drape_linefeed("foo\n\n\nbar"), "foo\r\n\r\n\r\nbar") }) test_that("remove CSS comments", { expect_equal(css_remove_comment("/*! * Bootstrap v3.3.5 */"), "") expect_equal(css_remove_comment("/* * Bootstrap v3.3.5 */"), "") expect_equal( css_remove_comment("/* * Bootstrap v3.3.5 */ p {} /*! normalize.css */"), " p {} " ) }) test_that("enclose angle brackets", { expect_equal(wrap_angle_brackets("") expect_equal(wrap_angle_brackets("foo>"), "") expect_equal(wrap_angle_brackets(""), "") expect_equal(wrap_angle_brackets("foo"), "") }) test_that("smtp server URL", { expect_equal(smtp_url("smtp.gmail.com", 465, protocol = NA), "smtps://smtp.gmail.com:465/") expect_equal(smtp_url("smtp.gmail.com", 587, protocol = NA), "smtp://smtp.gmail.com:587/") expect_equal(smtp_url("smtp.gmail.com", 465, protocol = "smtp"), "smtp://smtp.gmail.com:465/") expect_equal(smtp_url("smtp.gmail.com", 587, protocol = "smtps"), "smtps://smtp.gmail.com:587/") expect_equal(smtp_url("smtp://smtp.gmail.com", 465, protocol = "smtps"), "smtp://smtp.gmail.com:465/") expect_equal(smtp_url("smtps://smtp.gmail.com", 587, protocol = "smtp"), "smtps://smtp.gmail.com:587/") expect_error(emayili:::smtp_url("smtp.gmail.com", 443, protocol = "http")) }) test_that("base64 encoding with non-file input", { expect_equal(emayili:::mime_base64encode("Hello!"), "SGVsbG8h") expect_equal(emayili:::mime_base64encode(42), "Kg==") }) test_that("compare", { expect_false(all(compare(c(1, 2, 3, 4), c(1, 2, 3, NA)))) expect_true(all(compare(c(1, 2, 3, NA), c(1, 2, 3, NA)))) }) emayili/tests/testthat/test-compliant.R0000644000176200001440000000141214154176563020005 0ustar liggesuserstest_that("compliant", { expect_true(compliant("alice@example.com")) expect_true(compliant("al.ice@example.com")) }) test_that("not compliant", { expect_false(compliant("alice?example.com")) # More than one consecutive ".". expect_false(compliant("al..ice@example.com")) # Start or end with ".". expect_false(compliant(".alice@example.com")) expect_false(compliant("alice.@example.com")) expect_false(compliant("alice.@example.com")) expect_false(compliant("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX@gmail.com")) expect_false(compliant("al ice@example.com")) # Missing '@'. expect_false(compliant("alice.yahoo.com")) }) test_that("not compliant raises error", { expect_error(compliant("alice?example.com", error = TRUE)) }) emayili/tests/testthat/test-attachment.R0000644000176200001440000000107214154176563020151 0ustar liggesuserstest_that("attachment: set", { msg <- envelope() %>% attachment(TXTPATH) expect_match(as.character(msg), TXTCONTENT_ENCODED) }) test_that("attachment: specify CID", { cid <- "histogram" msg <- envelope() %>% attachment(JPGPATH, cid = cid) expect_true( grepl( mime_base64encode(JPGPATH), as.character(msg), fixed = TRUE ) ) expect_equal(msg$parts[[1]]$cid, cid) }) test_that("attachment: number of files", { msg <- envelope() expect_error(msg %>% attachment()) expect_error(msg %>% attachment(c(TXTPATH, JPGPATH))) }) emayili/tests/testthat/teardown-files.R0000644000176200001440000000005314153335452017756 0ustar liggesusersunlink(c(TXTPATH, HTMLPATH, RMD_TEMPLATE)) emayili/tests/testthat.R0000644000176200001440000000005114132315227015024 0ustar liggesuserslibrary(testthat) test_check("emayili") emayili/R/0000755000176200001440000000000014155162570012112 5ustar liggesusersemayili/R/body.R0000644000176200001440000000742314155162570013200 0ustar liggesuserscheck_message_body <- function(content) { if (length(content) > 1) { stop("Only a single message body allowed.", call. = FALSE) } } #' Add a text body to a message. #' #' Add \code{text/plain} content to a message. #' #' The \code{text/plain} format is #' described in \href{https://www.ietf.org/rfc/rfc2646.txt}{RFC 2646}. #' #' Uses \code{glue::glue()} to evaluate expressions enclosed in brackets as R code. #' #' @inheritParams mime-parameters #' @param msg A message object. #' #' @return A message object. #' @seealso \code{\link{html}}, \code{\link{render}} #' #' @export #' @examples #' msg <- envelope() %>% text("Hello!") #' #' # Using {glue} interpolation. #' # #' name <- "Alice" #' msg <- envelope() %>% text("Hello {name}.") #' #' print(msg, details = TRUE) #' #' # Disable {glue} interpolation. #' # #' msg <- envelope() %>% text("This is a set: {1, 2, 3}.", interpolate = FALSE) text <- function( msg, content, disposition = "inline", charset = "utf-8", encoding = "7bit", language = FALSE, interpolate = TRUE, .open = "{{", .close = "}}", .envir = NULL ) { check_message_body(content) if (is.null(.envir)) { .envir <- parent.frame() } else { .envir <- list2env(.envir) } if (interpolate) content <- glue(content, .open = .open, .close = .close, .envir = .envir) body <- text_plain(content, disposition, charset, encoding, language) msg <- append(msg, body) if (get_option_invisible()) invisible(msg) else msg # nocov } #' Transform a (tag)list to a character string #' #' @param content Element to transform. #' #' @return If the content is a list, a tagList or a tag, #' a character vector. Otherwise, it will return the #' input unchanged. #' #' @noRd #' @examples #' list_to_char(list("Hello", "

World!

")) #' library(htmltools) #' list_to_char(tagList(h2("Hello"), p("World!"))) list_to_char <- function(content) { if ( # We do the change if the element is a # tag or a tag.list inherits(content, "shiny.tag.list") | inherits(content, "shiny.tag") ) { content <- as.character(content) } # Then if we have a list, we collapse it to # a character vector if (length(content) > 1) { content <- paste(content, collapse = "\n") } content } #' Add an HTML body to a message object. #' #' @inheritParams text #' @param css_files Extra CSS files. #' @return A message object. #' @seealso \code{\link{text}}, \code{\link{render}} #' @export #' @examples #' # Inline HTML message. #' envelope() %>% html("Hello!") #' #' # Read HTML message from a file. #' htmlfile <- tempfile(fileext = ".html") #' cat("

Hello!

\n", file = htmlfile) #' envelope() %>% html(htmlfile) #' #' # You can pass a vector of character. Components will be separated by a #' # "\n". #' envelope() %>% html(c("Hello", "

World!

")) #' #' # You can also pass a tagList from {htmltools}. #' if (requireNamespace("htmltools", quietly = TRUE)) { #' library(htmltools) #' envelope() %>% html(tagList(h2("Hello"), p("World!"))) #' } html <- function( msg, content, disposition = "inline", charset = "utf-8", encoding = "quoted-printable", css_files = c(), language = FALSE, interpolate = TRUE, .open = "{{", .close = "}}", .envir = NULL ) { content <- list_to_char(content) # Check if it's a file. # if (file.exists(content)) { content <- paste(readLines(content), collapse = "\n") } if (is.null(.envir)) { .envir <- parent.frame() } else { .envir <- list2env(.envir) } if (interpolate) content <- glue(content, .open = .open, .close = .close, .envir = .envir) body <- text_html( content, disposition, charset, encoding, css = read_text(css_files), language = language ) msg <- append(msg, body) if (get_option_invisible()) invisible(msg) else msg # nocov } emayili/R/mime.R0000644000176200001440000003165314153553110013164 0ustar liggesusersERROR_NOT_MIME_OBJECT <- "Child is not a MIME object." # CONSTRUCTOR ----------------------------------------------------------------- is.mime <- function(x) { "MIME" %in% class(x) } #' Parameters for MIME functions #' #' These are parameters which occur commonly across functions for components of #' a MIME document. #' #' @name mime-parameters #' #' @param content A string of message content. #' @param disposition Should the content be displayed inline or as an #' attachment? Valid options are \code{"inline"} and \code{"attachment"}. If #' set to \code{NA} then will guess appropriate value. #' @param charset What character set is used. Most often either \code{"UTF-8"} #' or \code{"ISO-8859-1"}. #' @param encoding How content is transformed to ASCII. Options are #' \code{"7bit"}, \code{"quoted-printable"} and \code{"base64"}. Use \code{NA} #' or \code{NULL} for no (or "identity") encoding. #' @param language Langauge of content. If \code{FALSE} then will not include #' language field. If \code{TRUE} then will attempt to auto-detect language. #' Otherwise will use the specified language. #' @param description Description of content. #' @param name Name used when downloading file. #' @param filename Path to a file. #' @param boundary Boundary string. #' @param type The MIME type of the content. #' @param children List of child MIME objects. #' @param interpolate Whether or not to interpolate into input using \link[glue]{glue}. #' @param .open The opening delimiter. #' @param .close The closing delimiter. #' @param .envir Environment used for \code{glue} interpolation. Defaults to \code{parent.frame()}. NULL #' Create a MIME object #' #' ``` #' MIME #' ├── multipart/mixed #' ├── multipart/related #' ├── text/plain #' ├── text/html #' └── other #' ``` #' #' @section MIME Multipart Types: #' #' There are a number of options for multipart messages: #' #' \itemize{ #' \item{\code{multipart/mixed} — }{Used for sending content with multiple independent parts either inline or as attachments. Each part can have different \code{Content-Type}.} #' \item{\code{multipart/alternative} — }{Used when each part of the message is an "alternative" version of the same content. The order of the parts is important: preferred and/or more complex formats should be found towards the end. #' #' \emph{Example:} A message with both plain text and HTML versions.} #' \item{\code{multipart/digest} — }{Used to send multiple plain text messages.} #' \item{\code{multipart/related} — }{Used when each part of the the message represents a component of the complete message. #' #' \emph{Example:} A web page with images.} #' \item{\code{multipart/signed} — }{Used when a message has a digital signature attached.} #' \item{\code{multipart/encrypted} — }{Used for a message with encrypted content.} #' } #' #' A nice illustration of how some of these relate can be found at \url{https://stackoverflow.com/a/40420648/633961}. #' #' @noRd #' @return A MIME object. MIME <- function( content = NULL, disposition = NA, protocol = NA, charset = NA, encoding = NA, language = NA, description = NA, name = NA, filename = NA, format = NA, boundary = hexkey(), type = NA, children = list() ) { # If just a single child, convert to list. if (!all(class(children) == c("list"))) children <- list(children) # Check that all children are MIME. for (child in children) { if (!is.mime(child)) stop(ERROR_NOT_MIME_OBJECT) } structure( list( content = content, disposition = disposition, protocol = protocol, charset = charset, encoding = encoding, language = language, description = description, name = name, filename = filename, format = format, boundary = boundary, children = children, type = type ), class = "MIME" ) } multipart_related <- function(...) { structure( c( MIME(...), list() ), class = c("multipart_related", "MIME") ) } multipart_mixed <- function(...) { structure( c( MIME(...), list() ), class = c("multipart_mixed", "MIME") ) } #' Create \code{multipart/encrypted} MIME object #' #' @noRd #' #' @inheritParams MIME multipart_encrypted <- function( content, ... ) { structure( c( MIME( "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156).", protocol = "application/pgp-encrypted", ... ), list() ), class = c("multipart_encrypted", "MIME") ) } #' Create \code{multipart/signed} MIME object #' #' @noRd #' #' @inheritParams MIME #' @param micalg Message Integrity Check ALGorithm. Valid options are: #' \code{"pgp-sha256"}, \code{"pgp-md5"}, \code{"pgp-sha1"}, #' \code{"pgp-ripemd160"}, \code{"pgp-md2"}, \code{"pgp-tiger192"}, and #' \code{"pgp-haval-5-160"}. multipart_signed <- function( micalg = "pgp-sha256", ... ) { if (!(micalg %in% LEVELS_MICALG)) stop('Invalid micalg: "{micalg}".') structure( c( MIME( "This is an OpenPGP/MIME signed message (RFC 4880 and 3156).", protocol = "application/pgp-signature", type = c("multipart/signed", 'micalg="{micalg}"'), ... ), list() ), class = c("multipart_signed", "MIME") ) } application_pgp_encrypted <- function( content = "Version: 1", ... ) { structure( c( MIME( content, type = "application/pgp-encrypted", description = "PGP/MIME version identification", boundary = NA, ... ), list() ), class = c("application_pgp_encrypted", "MIME") ) } application_pgp_signature <- function( content, ... ) { structure( c( MIME( content, type = "application/pgp-signature", description = "OpenPGP digital signature", name = "signature.asc", filename = "signature.asc", disposition = "attachment", boundary = NA, ... ), list() ), class = c("application_pgp_signature", "MIME") ) } application_pgp_keys <- function( content, ... ) { structure( c( MIME( content, type = "application/pgp-keys", description = "OpenPGP public key", name = "keys.asc", filename = "keys.asc", disposition = "attachment", encoding = "quoted-printable", boundary = NA, ... ), list() ), class = c("application_pgp_keys", "MIME") ) } application_octet_stream <- function( content, disposition = "inline", filename, ... ) { structure( c( MIME( content, disposition = disposition, type = "application/octet-stream", description = "OpenPGP encrypted message", name = filename, filename = filename, boundary = NA, ... ), list() ), class = c("application_octet_stream", "MIME") ) } text_plain <- function( content, disposition = "inline", charset = "utf-8", encoding = "7bit", ... ) { structure( c( MIME(content, disposition, NA, charset, encoding, format = "flowed", boundary = NA, ...), list() ), class = c("text_plain", "MIME") ) } #' Create \code{text/html} MIME object #' #' @noRd #' #' @inheritParams MIME #' @param squish Whether to remove whitespace outside of tags. #' @param ... Further arguments passed to or from other methods. #' #' @return A \code{text_html} object derived from \code{MIME}. text_html <- function( content, disposition = "inline", charset = "utf-8", encoding = NA, squish = FALSE, css = NA, ... ) { # Clean up content. # if (squish) { content <- html_squish(content) } content <- read_html(content) if (length(css) && !all(is.na(css) | css == "")) { css <- css %>% unlist() %>% str_c(collapse = "\n") %>% css_remove_comment() %>% str_squish() # Add (can be missing if rendering Plain Markdown). if (is.na(xml_find_first(content, "//head"))) { xml_add_sibling( xml_find_first(content, "//body"), "head", .where = "before" ) } # Write consolidated CSS to single