o k`@sddlZddlZddlZddlZddlZddlmZddlmZm Z m Z m Z ddl m Z ddlmZddlmZddlmZmZmZddlmZmZdd lmZmZmZmZmZdd lm Z m!Z!m"Z"m#Z#dd l$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z,dd l-m.Z.dd l/m0Z0ddl1m2Z2m3Z3ddl4m5Z5ddl6m7Z7m8Z8m9Z9m:Z:ddl;mm?Z?gdZ@dZAdZBeCdejDZEdaFejGZHeHjIddZJddZKddZLddZMd@dd ZNeCd!ZOd"d#ZPd$d%ZQd&d'ZRdAd(d)ZSd*d+ZTd,d-ZUGd.d/d/eVZWGd0d1d1e ZXGd2d3d3ejGZYGd4d5d5eVZZGd6d7d7e Z[Gd8d9d9e Z\Gd:d;d;e Z]Gdd?d?e Z_dS)BN)policy)BASE64QPSHORTESTCharset)Header)Address) EmailMessage) formatdate parseaddr getaddresses)PopenPIPE) BoolOptionConfigurationError IntOptionOptionOrderedExtensionsOption) ComponentExtensionPoint TracError implements) get_target_idIEmailAddressResolverIEmailDecorator IEmailSenderINotificationDistributorINotificationFormatterINotificationSubscriberNotificationSystem)lazy) close_fds)time_now to_utimestamp)tag)CRLFexception_to_unicodefix_eol to_unicode)_tag_)get_session_attribute)AlwaysEmailSubscriberEMAIL_LOOKALIKE_PATTERNEmailDistributorFromAuthorEmailDecorator MAXHEADERLENRecipientMatcherSendmailEmailSenderSessionEmailResolverSmtpEmailSendercreate_charset create_headercreate_message_idcreate_mime_multipartcreate_mime_textget_message_addressesget_from_author set_headerLzJ[a-zA-Z0-9.'+_-]+@(?:[a-zA-Z0-9_-]+\.)+[a-zA-Z](?:[-a-zA-Z\d]*[a-zA-Z\d])?z=\?[^?]+\?[bq]\?[^?]+\?=i)max_line_lengthcCs~t}d|_d|_d|_d|_|}|dkrt|_t|_|S|dvr+t |_t |_|S|dkr7t |_d|_|St t d|d)aCreate an appropriate email charset for the given encoding. Valid options are 'base64' for Base64 encoding, 'qp' for Quoted-Printable, and 'none' for no encoding, in which case emails will be sent as 7bit if the content is all ASCII, or 8bit otherwise. utf-8base64)qpquoted-printablenoneNz1Invalid email encoding setting: %(mime_encoding)s) mime_encoding) r input_charsetoutput_charset input_codec output_codeclowerrheader_encoding body_encodingrrrr))rDcharsetprefrN8/usr/lib/python3/dist-packages/trac/notification/mail.pyr5Ms* r5cCstt|d}|dkrttdd}t|ttfr|\}}t|ts(t|}|s,|Sd}t |sKz| d}Wn t yCYnwt |d|d}|sXt | |j||d}t|}|rn|ddd d }d ||f}|S) aVCreate an email Header. The `key` is always a string and will be converted to the appropriate `charset`. The `value` can either be a string or a two-element tuple where the first item is the name and the second item is the email address. See `set_header()` for a helper that sets a header directly on a message.  zHeader length is too shortNascii) maxlinelen\z\\"z\"z "%s" <%s>)r0lenrr) isinstancetupleliststrr(_mime_encoding_researchencodeUnicodeEncodeErrorrrHreplace)keyvaluerL maxlengthemailheadertmprNrNrOr6is6      r6cCst|tr d|fg}g}|D]G}t|tr||qt|tr%d}|}nt|ttfr1|\}}ntd|d|vrD|dd\}}n|}d}|tt|pOd||q|S)zCreate address header instance to pass to `set_header`. The `addresses` is a list or an iterable of addresses. The item can either be `str`, a `(name, address)` tuple or a `(None, address)`. NzUnrecognized item %r@) rWrZrappendrYrX ValueErrorrsplit_replace_encoded_words) addresseslitemnameaddrusernamedomainrNrNrOcreate_address_headers&       rtcCsz|dur t|}n"t|ttfrt|g}nt|tr|}n |dur%d}ntt|}||vr7|||dS|||<dS)aCreate and add or replace a header in a `EmailMessage`. The `key` is always a string. The `value` can either be `None`, a string or a two-element tuple where the first item is the name and the second item is the email address. The `addresses` can either be a list or an iterable of a two-element tuple. When the `addresses` is given, the `value` will be ignored. The `charset` is no longer used. Example:: set_header(my_message, 'From', ('Trac', 'noreply@ourcompany.com')) set_header(my_message, 'To', addresses=[('Foo', 'foo@example.org'), ('Bar', 'bar@example.org')]) Nrh)rtrWrYrXrrlrZreplace_header)messager`rarLrmrdrNrNrOr<s     r<z=\?cCs|rtd|}|S)uUReplace '=?' with '=​?' to avoid decoding encoded-words by `EmailMessage`. u=​?)_encoded_words_resub)textrNrNrOrls rlcCsTt}|dkr |n|dkr|n|dkr|ntd|d|d<|S)aCreate a multipart email message. The `subtype` is a string that describes the type of multipart message you are defining. You should pick one that is defined by the email standards. The function does not check if the `subtype` is valid. The most common examples are: * `related` infers that each part is in an integrated whole, like images that are embedded in a html part. * `alternative` infers that the message contains different formats and the client can choose which to display based on capabilities and user preferences, such as a text/html with an alternative text/plain. related alternativemixedz@subtype must be one of ('related', 'multipart', 'mixed'), not %r1.0 MIME-Version)r make_relatedmake_alternative make_mixedrj)subtypemsgrNrNrOr8s   r8cCsXt|tr t|d}tdtdi|j}t|durtnt }d|d<|j |||d|S)a{Create a `EmailMessage` that can be added to an email message. :param body: a string with the body of the message. :param format: each text has a EmailMessage, like `text/plain`. The supertype is always `text`, so in the `format` parameter you pass the subtype, like `plain` or `html`. :param charset: should be created using `create_charset()`. r?r@rBNr}r~)rcte) rWbytesrZrrgetrKr _policy_8bit_policy_default set_content)bodyformatrLrrrNrNrOr9s r9c Cs|j|t|g}|dur||ddddd|D}t|j}zt|}Wn t t d|d| ||| d d d} d t ||| fS) a:Generate a predictable, but sufficiently unique message ID. In case you want to set the "Message ID" header, this convenience function will generate one by running a hash algorithm over a number of properties. :param env: the `Environment` :param targetid: a string that identifies the target, like `NotificationEvent.target` :param from_email: the email address that the message is sent from :param time: a Python `datetime` :param more: a string that contains additional information that makes this message unique NrRignore.css,|]}t|tr |nt|dVqdS)r?N)rWrrZr]).0rorNrNrO %s   z$create_message_id..zUnknown hash type '%(type)s')typerfrgz <%03d.%s@%s>) project_urlr#rir]joinrmessage_id_hashhashlibnewrr)updatefindrV hexdigest) envtargetid from_emailtimemoreitemssource hash_typehhostrNrNrOr7s    r7cCstdd||dDS)Ncss|]}t|VqdSN)rZ)rrdrNrNrOr4sz(get_message_addresses..rN)r get_all)rvrprNrNrOr:3sr:cCs8|jrt|jrt|}||j}|r|SdSdSdS)aGet the author name and email from a given `event`. The `event` parameter should be of the type `NotificationEvent`. If you only have the username of a Trac user, you should instead use the `RecipientMatcher` to find the user's details. The method returns a tuple that contains the name and email address of the user. For example: `('developer', 'developer@ourcompany.com')`. This tuple can be parsed by `set_header()`. N)authorrsmtp_from_authorr1match_from_author)reventmatcherfrom_rNrNrOr;7s  r;c@s^eZdZdZedZddZeddZ eddZ ed d Z d d Z d dZ ddZdS)r1zWMatches user names and email addresses. :param env: The `trac.env.Enviroment` z^[-A-Za-z0-9!*+/=_.]+$cCs||_t}t||_|jj}|r-|d\}}|g}|dd|Dd|d|f}t d|tj |_ t d|tj |_ t dd|jjD|_dS) Nrfcss|]}t|VqdSr)reescaperxrNrNrOrXsz,RecipientMatcher.__init__..z %s@(?:%s)|z ?$z(.*)\s+<\s*(%s)\s*>$css|]}|VqdSr)rIrrNrNrOr]s)rr-r notify_sysadmit_domains_listsplitextendrrcompile IGNORECASE shortaddr_re longaddr_resetignore_domains_listignore_domains)selfraddrfmt admit_domainslocalfmt domainfmtdomainsrNrNrO__init__Ps   zRecipientMatcher.__init__cC|jjSr)ruse_short_addrrrNrNrOr`zRecipientMatcher.use_short_addrcCrr)rsmtp_default_domainrrNrNrOrdrz$RecipientMatcher.smtp_default_domaincCs|jjddS)NT)as_dict)rget_known_usersrrNrNrOusershszRecipientMatcher.userscCsD|sdS|j|}|r ||ddd}||jvr dSdS)agCheck if an email address is valid. This method checks against the list of domains that are to be ignored, which is controlled by the `ignore_domains_list` configuration option. :param address: the address to validate :return: `True` if it is a valid email address that is not in the ignore list. FrfrgNT)rmatchrrIr)raddressrrsrNrNrOis_emaills   zRecipientMatcher.is_emailcCs|r|dkrdS||jvr|}d}|j|dpdp|}nd}d}|}|j|rN|jr5|||fS|jrDd||jf}|||fS|jjd|dS|j |}|r\| d}n |j |}|ri| d}| |sx|jjd |dS|||fS) aConvenience function to check for an email address The parameter `address` can either be a valid user name, or an email address. The method first checks if the parameter is a valid user name. If so, it will look up the address. If there is no match, the function will check if it is a valid email address. :return: A tuple with a session id, a `1` or `0` to indicate whether the user is authenticated, and the matched address. Returns `None` when `address` does not match a valid user, nor a valid email address. When `address` is an email address, the sid will be `None` and the authentication parameter will always be `0` anonymousNrgrhr%s@%szEmail address w/o domain: %srPzInvalid email address: %s) rstrip nodomaddr_rerrrrlogdebugrgrouprr)rrsidauthmorNrNrOmatch_recipients6           z RecipientMatcher.match_recipientcCs|r|}||}|sdS|\}}}|sdSd}|r*|r*||jvr*|j|d}|s9|j|}|r9|d}|r?||fS|S)zFind a name and email address for a specific user :param author: The username that you want to query. :return: On success, a two-item tuple is returned, with the real name and the email address of the user. Nrrg)rrrrrr)rr recipientr authenticatedr from_namerrNrNrOrs     z"RecipientMatcher.match_from_authorN)__name__ __module__ __qualname____doc__rrrrr rrrrrrrNrNrNrOr1Is     1r1c@steZdZdZeeeeZee Z e dde ddddZ eddd d d Zd d ZddZddZddZddZdS)r.z*Distributes notification events as emails. notificationemail_address_resolversr3FzComma separated list of email resolver components in the order they will be called. If an email address is resolved, the remaining resolvers will not be called. )include_missingdoczdefault_format.email text/plainz1Default format to distribute email notifications.)rcCst|jdd|_dS)NrrD)r5configr_charsetrrNrNrOrs  zEmailDistributor.__init__ccs dVdSNrcrNrrNrNrO transportss zEmailDistributor.transportscCs|dkrdS|jdds|jd|jjdSi}|jD]}||D] \}}||jkr1|||<q$q|sC|j d|jj||jdS|jd|jj||jd |t |j }t |j } t| j} i} |D]\} } }}||vr~|jd|jj|||jqf| r|s|jD]}|| | pd}|r|jd |jj|| | |jjnq| r| r|s| }| jr| js|r|j|rd || jf}|s|jd |jj| | qf||s| jr|j|r| |t|| r| r| | vr| | | |qf| jr| |qf|jd |jj|| | qfi}g}|D]I\}}|| vr*|d kr*qz ||||||<Wqtyc}z |jd|jj|j|||jt|dd| |WYd}~qd}~ww|rd |vr|D]}| d t!| "|dqn| D]G\}}|jd|jj|d ||#||}|rt|}t$|| @}t$|| }|%|||||q|jd|jj|j|d |qdS)Nrcr smtp_enabledz,%s skipped because smtp_enabled set to falsez%s No formats found for %s %szG%s has found the following formats capable of handling '%s' of '%s': %sz, z$%s format %s not available for %s %sz.%s found the address '%s' for '%s [%s]' via %srz.%s was unable to find an address for '%s [%s]'z2%s was unable to use an address '%s' for '%s [%s]'rz:%s caught exception while formatting %s to %s for %s: %s%sT tracebackrNz"%s is sending event as '%s' to: %sz%%s cannot send event '%s' as '%s': %s)&rgetboolrr __class__r formattersget_supported_stylesrealmerrorrr1rrrsmtp_always_cc_list resolversget_address_for_sessionrrrrr setdefaultadddiscard use_public_ccrr Exceptionwarningr&rirpop_create_messagesorted_do_send)r transport recipientsrformatsfstylerrr always_ccrmrrrqfmtresolveroutputsfailed formattereaddrsrvcc_addrs bcc_addrsrNrNrO distributes                          zEmailDistributor.distributec Cs||vrdStd}|d\}}t||||j}|dkr;d|vr;t|dd|j}td}|||||}|||S)Nrz/rplainr{)r8rr9rattach) rrrrvmaintyper preferredryr{rNrNrOrJs    z EmailDistributor._create_messagecCs@t|j}|j}|jp|jj}|j} |js2|jr2|r%d|vr%d||jf}| r2d| vr2d| |jf} i} d|jj| d<|jj| d<|jj| d<|jj | d<|j | d<d | d <d | d <t |j t tfrmd tt|j } nt|j } t|j| |d|j d} |jdkr| | d<nt|j| ||j|j d| d<| | d<| | d<t| d<d| d<| D] \} }t|| |qt|d|r||fn||rt|d|d|rt|d|dt|d| gd|jD] }||||jqtt|d\}}t}dD]}tt||d}|ddt |Dq|d=|!|t ||"dS)NrfrzTrac %s, by Edgewall SoftwarezX-MailerzX-Trac-VersionzX-Trac-ProjectzX-URLz X-Trac-Realmbulk Precedencezauto-generatedzAuto-Submitted,)rcreatedz Message-IDz In-Reply-To ReferencesDatezundisclosed-recipients: ;ToFromCcrmBcczReply-To)rrrrNcss|] \}}|r|VqdSrrN)rrprqrNrNrOrs z,EmailDistributor._do_send..)#rr smtp_fromsmtp_from_name project_name smtp_replytorr trac_versionrrrWtargetrYrXrmaprr7categoryrr rr< decoratorsdecorate_messagerr rZrrrr send_emailas_bytes)rrrrvr r rrrr!headersrrootidkv decoratorr from_addrto_addrsrpvaluesrNrNrOrZsx                zEmailDistributor._do_sendN)rrrrrrrrrrr&rrrrdefault_formatrrr rrrNrNrNrOr.s& c r.csBeZdZd fdd Zd fdd Zd ddZedd ZZS) SMTPWithUnicodeCredsFixupNctj|d}||SN) challenge)super auth_plain_patch_encode_methodrr6rvrrNrOr8 z$SMTPWithUnicodeCredsFixup.auth_plaincr4r5)r7 auth_loginr9r:r<rNrOr>r=z$SMTPWithUnicodeCredsFixup.auth_logincCs:|durdS|jdt|jd|d}||S)N r?md5)userhmacHMACpasswordr]rr9r:rNrNrO auth_cram_md5s z'SMTPWithUnicodeCredsFixup.auth_cram_md5cCs,z|dW|Styt|YSwNrR)r]r^_SMTPAuthResponse)clsrarNrNrOr9s   z.SMTPWithUnicodeCredsFixup._patch_encode_methodr) rrrr8r>rE classmethodr9 __classcell__rNrNr<rOr3s  r3c@s"eZdZdZddZd ddZdS) rGresponsecCs ||_dSrrK)rrLrNrNrOrs z_SMTPAuthResponse.__init__r?strictcCs |jdS)Nr?)rLr])rencodingerrorsrNrNrOr]s z_SMTPAuthResponse.encodeN)r?rM)rrr __slots__rr]rNrNrNrOrGsrGc@sreZdZdZeeeddddZeddddZ edd d d Z edd d d Z e ddddZ eddZddZdS)r4z+E-mail sender connecting to an SMTP server.r smtp_server localhostz4SMTP server hostname to use for email notifications. smtp_portz/SMTP server port to use for email notification. smtp_userrhz-Username for authenticating with SMTP server. smtp_passwordz-Password for authenticating with SMTP server.use_tlsfalsez,Use SSL/TLS to send notifications over SMTP.cCs<z|j|jfD]}|dqWtjStytYSwrF)rUrVr]r^r3smtplibSMTP)rrarNrNrO smtp_classs  zSmtpEmailSender.smtp_classc Cst|t}|jd|j|j|z ||j|jt}Wn tj j y;}zt t dt |tdtddd}~ww|jazz]|jr[|d|jvrSt td|||jrf||j|jt}||||t|}|dkr|jd ||jrd dl }z|Wn|jyYnw|Wn$tjyty}z|j d |j|jt|d d d}~wwW| dS| w)Nz0Sending notification through SMTP at %s:%d to %sziSMTP server connection error (%(error)s). Please modify %(option1)s or %(option2)s in your configuration.z[notification] smtp_serverz[notification] smtp_port)roption1option2starttlsz+TLS enabled but server does not support TLSz4Slow mail submission (%.2f s), check your mail setuprzCException caught while sending notification through SMTP at %s:%d%sTr)!r'r%rinforQrSr[local_hostnamerYsocketrrr*r(r$coderWehloesmtp_featuresr)r^rUloginrVr"sendmailrquitsslerror SMTPExceptionrr&close) rr/rrvserverr starttrbrNrNrOsendsp         zSmtpEmailSender.sendN)rrrrrrrrQrrSrUrVrrWr r[rorNrNrNrOr4s*  r4c@s.eZdZdZeeeddddZddZdS) r2z9E-mail sender using a locally-installed sendmail program.r sendmail_pathrgznPath to the sendmail executable. The sendmail program must accept the `-i` and `-f` options. c Cst|tj}|jd|j||jdd|g|}|jd|z t|dtttt d}Wnt yE}zt t dt |tdd d}~ww||\}}|jsR|r^td |j||fdS) Nz1Sending notification through sendmail at %s to %sz-iz-fzSendmail command line: %s)bufsizestdinstdoutstderrr!zKSendmail error (%(error)s). Please modify %(option)s in your configuration.z[notification] sendmail_path)roptionz,Sendmail failed with (%s, %s), command: '%s')r'oslineseprr`rprr rr!OSErrorrr*r(r$rc communicate returncoderr) rr/rrvcmdlinechildr outerrrNrNrOro&s2    zSendmailEmailSender.sendN) rrrrrrrrprorNrNrNrOr2s r2c@ eZdZdZeeddZdS)r3z;Gets the email address from the user preferences / session.cCst|j||dSr)r+r)rrrrNrNrOrBz,SessionEmailResolver.get_address_for_sessionN)rrrrrrrrNrNrNrOr3=s r3c@s@eZdZdZeeddZddZddZdd Z d d Z d S) r,zImplement a policy to -always- send an email to a certain address. Controlled via the smtp_always_cc and smtp_always_bcc option in the notification section of trac.ini. c cs\t|j}|jj}d}d}|D]}||}|r+|\}} }|d|| |||dfVqdS)Nrrcalways)r1rrr_get_address_listr) rrrklassrpriorityrrrrrNrNrOmatchesOs     zAlwaysEmailSubscriber.matchescCsdSrrNrrNrNrO description[z!AlwaysEmailSubscriber.descriptioncCdS)NFrNrrNrNrOrequires_authentication^rz-AlwaysEmailSubscriber.requires_authenticationcCr)NrNrNrrNrNrOdefault_subscriptionsarz+AlwaysEmailSubscriber.default_subscriptionscs.|jdfdd}t|dt|dBS)Nrcsj|dddS)N)rr?F)sep keep_empty)getlist)rpsectionrNrOrfrz8AlwaysEmailSubscriber._get_address_list..getlistsmtp_always_ccsmtp_always_bcc)rr)rrrNrrOrds    z'AlwaysEmailSubscriber._get_address_listN) rrrrrrrrrrrrNrNrNrOr,Fs  r,c@r)r/zImplement a policy to use the author of the event as the sender in notification emails. Controlled via the smtp_from_author option in the notification section of trac.ini. cCs(t|j|}|rt|d|gddSdS)Nrr)r;rr<)rrrvrLrrNrNrOr'vs z)FromAuthorEmailDecorator.decorate_messageN)rrrrrrr'rNrNrNrOr/ls r/)NNNr)`rrBrwrrYrcr email.charsetrrrr email.headerremail.headerregistryr email.messager email.utilsr r r subprocessr r trac.configrrrrr trac.corerrrrtrac.notification.apirrrrrrrr trac.utilr trac.util.compatr!trac.util.datefmtr"r#trac.util.htmlr$trac.util.textr%r&r'r(trac.util.translationr)r*trac.web.sessionr+__all__r0r-rrr[rarZrclonerr5r6rtr<rwrlr8r9r7r:r;objectr1r.r3rGr4r2r3r,r/rNrNrNrOsh    (     ,  $   Q [" &