Virtual domain alias and LDAP

classic Classic list List threaded Threaded
16 messages Options
Reply | Threaded
Open this post in threaded view
|

Virtual domain alias and LDAP

Raphael Berlamont
Hello list,

I'm trying to do some domain alias. For example, I have multiple user
for @domain.ext ...
I now have a new domain : @newsdomain.ext, and I would like to have all
mail addressed to this domain redirected to @domain.ext.

Without ldap, my virtual table looked like this :
@newdomain.ext @domain.ext

But now, with LDAP, I'm quite lost. I setup those parameters in main.cf :
=====
virtual_mailbox_domains = ldap:/etc/postfix/virtual_mailbox_domains-ldap.cf
virtual_mailbox_maps = ldap:/etc/postfix/virtual_mailbox_maps-ldap.cf
virtual_alias_maps = ldap:/etc/postfix/virtual_alias_maps-ldap.cf,
ldap:/etc/postfix/virtual_mailinglist_maps-ldap.cf
virtual_alias_domains = ldap:/etc/postfix/virtual_alias_domains-ldap.cf
=====

When I query newdomain.ext with the file pointed by
virtual_alias_domains, I do have an answer :
---
# postmap -q newdomain.ext ldap:/etc/postfix/virtual_alias_domains-ldap.cf
domain.ext
---

This domain is owned by this local postfix :
---
# postmap -q domain.ext ldap:/etc/postfix/virtual_mailbox_domains-ldap.cf
domain.ext
---

And when I query the user with the file pointed by virtual_alias_maps :
---
# postmap -q [hidden email]
ldap:/etc/postfix/virtual_alias_maps-ldap.cf
[hidden email]
---

So I have an answer. Then, finally I ask for the location of the mailbox :
---
# postmap -q [hidden email]
ldap:/etc/postfix/virtual_mailbox_maps-ldap.cf
domain.ext/final/raphael.berlamont/
---

With this configuration, postfix refuse mails for
[hidden email] :
---
# telnet 127.0.0.1 25
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
220 smtp.domain.ext ESMTP Postfix (Debian/GNU)
HELO toto
250 smtp.domain.ext
MAIL FROM: [hidden email]
250 2.1.0 Ok
RCPT TO: [hidden email]
550 5.1.1 <[hidden email]>: Recipient address rejected:
User unknown in virtual alias table
quit
221 2.0.0 Bye
Connection closed by foreign host.
---

To sum up : mails addressed to [hidden email] works
fine... and [hidden email] don't.

What have I done wrong?

--
Raphael Berlamont.
Reply | Threaded
Open this post in threaded view
|

Re: Virtual domain alias and LDAP

Victor Duchovni
On Mon, Jun 09, 2008 at 02:59:30PM +0200, Raphael Berlamont wrote:

> Hello list,
>
> I'm trying to do some domain alias. For example, I have multiple user
> for @domain.ext ...
> I now have a new domain : @newsdomain.ext, and I would like to have all
> mail addressed to this domain redirected to @domain.ext.
>
> Without ldap, my virtual table looked like this :
> @newdomain.ext @domain.ext
>
> But now, with LDAP, I'm quite lost. I setup those parameters in main.cf :
> =====
> virtual_mailbox_domains = ldap:/etc/postfix/virtual_mailbox_domains-ldap.cf
> virtual_mailbox_maps = ldap:/etc/postfix/virtual_mailbox_maps-ldap.cf
> virtual_alias_maps = ldap:/etc/postfix/virtual_alias_maps-ldap.cf,
> ldap:/etc/postfix/virtual_mailinglist_maps-ldap.cf
> virtual_alias_domains = ldap:/etc/postfix/virtual_alias_domains-ldap.cf
> =====
>
> When I query newdomain.ext with the file pointed by
> virtual_alias_domains, I do have an answer :
> ---
> # postmap -q newdomain.ext ldap:/etc/postfix/virtual_alias_domains-ldap.cf
> domain.ext

You are confused, the value returned from virtual_alias_domains tables
is not used by Postfix. So long as some value is returned, the domain is
virtual. Now your virtual_alias_maps table needs to rewrite addresses of
the form "[hidden email]" (please do use real domains or example.com,
example.net, ...) to the real mailbox.

--
        Viktor.

Disclaimer: off-list followups get on-list replies or get ignored.
Please do not ignore the "Reply-To" header.

To unsubscribe from the postfix-users list, visit
http://www.postfix.org/lists.html or click the link below:
<mailto:[hidden email]?body=unsubscribe%20postfix-users>

If my response solves your problem, the best way to thank me is to not
send an "it worked, thanks" follow-up. If you must respond, please put
"It worked, thanks" in the "Subject" so I can delete these quickly.
Reply | Threaded
Open this post in threaded view
|

Re: Virtual domain alias and LDAP

Angelos Karageorgiou
In reply to this post by Raphael Berlamont
you will need the canonical maps section like so

recipient_canonical_maps = ldap:/etc/postfix/ldap-aliasdomains.cf
or such , I have implementet this by modifying my ldap schema and
adding another attribute I call DomainAliasedTo

like so
=====
server_host = ldap://ldap01:389 ldap://ldap02:389
search_base = ou=domains,o=vivodi
query_filter =
(&(ou=%d)(objectClass=organizationalUnit)(objectClass=qmailUser))
result_attribute = DomainAliasedTo
result_format = %U@%s
scope = sub
version = 3



Raphael Berlamont wrote:

> Hello list,
>
> I'm trying to do some domain alias. For example, I have multiple user
> for @domain.ext ...
> I now have a new domain : @newsdomain.ext, and I would like to have all
> mail addressed to this domain redirected to @domain.ext.
>
> Without ldap, my virtual table looked like this :
> @newdomain.ext @domain.ext
>
> But now, with LDAP, I'm quite lost. I setup those parameters in main.cf :
> =====
> virtual_mailbox_domains = ldap:/etc/postfix/virtual_mailbox_domains-ldap.cf
> virtual_mailbox_maps = ldap:/etc/postfix/virtual_mailbox_maps-ldap.cf
> virtual_alias_maps = ldap:/etc/postfix/virtual_alias_maps-ldap.cf,
> ldap:/etc/postfix/virtual_mailinglist_maps-ldap.cf
> virtual_alias_domains = ldap:/etc/postfix/virtual_alias_domains-ldap.cf
> =====

angelos.vcf (744 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Virtual domain alias and LDAP

Raphael Berlamont
In reply to this post by Victor Duchovni
Victor Duchovni a écrit :

> On Mon, Jun 09, 2008 at 02:59:30PM +0200, Raphael Berlamont wrote:
>
>  
>> Hello list,
>>
>> [...]
>>    
>
> You are confused, the value returned from virtual_alias_domains tables
> is not used by Postfix. So long as some value is returned, the domain is
> virtual.
MMmm Now I'm completely lost with the difference beetween
virtual_alias_domains and virtual_mailbox_domains. I thought, while
reading postconf(5), that virtual_mailbox_domains was here just to check
that the domain was handle by the "virtual" process of this postfix, and
virtual_alias_domains was here to do some aliasing stuffs....

>  Now your virtual_alias_maps table needs to rewrite addresses of
> the form "[hidden email]" (please do use real domains or example.com,
> example.net, ...) to the real mailbox

Ok, I tried to do this :
in my main.cf:
virtual_alias_maps = ldap:/etc/postfix/virtual_alias_maps-ldap.cf,
ldap:/etc/postfix/virtual_mailinglist_maps-ldap.cf,
ldap:/etc/postfix/virtual_alias_map-domains-ldap.cf

and if I query the file  :
---
# postmap -q newdomain.ext
ldap:/etc/postfix/virtual_alias_map-domains-ldap.cf
domain.ext
---

But still, my postfix refuse mail for [hidden email], arguying :
550 5.1.1 <[hidden email]>: Recipient address rejected:
User unknown in virtual alias table

And if I use the original mail (@domain.ext) everything is fine.

I think I missed something somewhere...

--
Raphael Berlamont
Reply | Threaded
Open this post in threaded view
|

Re: [Resolved] Virtual domain alias and LDAP

Raphael Berlamont
In reply to this post by Angelos Karageorgiou
Angelos Karageorgiou a écrit :

> you will need the canonical maps section like so
>
> recipient_canonical_maps = ldap:/etc/postfix/ldap-aliasdomains.cf
> or such , I have implementet this by modifying my ldap schema and
> adding another attribute I call DomainAliasedTo
>
> like so
> =====
> server_host = ldap://ldap01:389 ldap://ldap02:389
> search_base = ou=domains,o=vivodi
> query_filter =
> (&(ou=%d)(objectClass=organizationalUnit)(objectClass=qmailUser))
> result_attribute = DomainAliasedTo
> result_format = %U@%s
> scope = sub
> version = 3

This is working perfectly.

Thx Angelos!

--
Raphael Berlamont
Reply | Threaded
Open this post in threaded view
|

Re: [Resolved] Virtual domain alias and LDAP

Victor Duchovni
On Mon, Jun 09, 2008 at 06:21:44PM +0200, Raphael Berlamont wrote:

> Angelos Karageorgiou a ??crit :
> >you will need the canonical maps section like so
> >
> >recipient_canonical_maps = ldap:/etc/postfix/ldap-aliasdomains.cf
> >or such , I have implementet this by modifying my ldap schema and
> >adding another attribute I call DomainAliasedTo
> >
> >like so
> >=====
> >server_host = ldap://ldap01:389 ldap://ldap02:389
> >search_base = ou=domains,o=vivodi
> >query_filter =
> >(&(ou=%d)(objectClass=organizationalUnit)(objectClass=qmailUser))
> >result_attribute = DomainAliasedTo
> >result_format = %U@%s
> >scope = sub
> >version = 3
>
> This is working perfectly.

This is a wild-card alias, and breaks recipient validation. Don't
use rewriting tables that match non-existent users.

--
        Viktor.

Disclaimer: off-list followups get on-list replies or get ignored.
Please do not ignore the "Reply-To" header.

To unsubscribe from the postfix-users list, visit
http://www.postfix.org/lists.html or click the link below:
<mailto:[hidden email]?body=unsubscribe%20postfix-users>

If my response solves your problem, the best way to thank me is to not
send an "it worked, thanks" follow-up. If you must respond, please put
"It worked, thanks" in the "Subject" so I can delete these quickly.
Reply | Threaded
Open this post in threaded view
|

Re: [Not-Resolved] Virtual domain alias and LDAP

Raphael Berlamont
Le lundi 09 juin 2008 à 12:29 -0400, Victor Duchovni a écrit :
> On Mon, Jun 09, 2008 at 06:21:44PM +0200, Raphael Berlamont wrote:
> > This is working perfectly.
>
> This is a wild-card alias, and breaks recipient validation. Don't
> use rewriting tables that match non-existent users.

Ok, so where can I found some valid infos on how to proceed then?

As a remminder, my wish is to accept mail for [hidden email],
and automaticaly redirect it to [hidden email], without having to
go in every user account and add a new alias like
[hidden email].

Maybe it's not possible?

--
Raphael Berlamont

Reply | Threaded
Open this post in threaded view
|

Re: [Not-Resolved] Virtual domain alias and LDAP

Victor Duchovni
On Mon, Jun 09, 2008 at 09:26:50PM +0200, Raphael Berlamont wrote:

> Le lundi 09 juin 2008 ?? 12:29 -0400, Victor Duchovni a ??crit :
> > On Mon, Jun 09, 2008 at 06:21:44PM +0200, Raphael Berlamont wrote:
> > > This is working perfectly.
> >
> > This is a wild-card alias, and breaks recipient validation. Don't
> > use rewriting tables that match non-existent users.
>
> Ok, so where can I found some valid infos on how to proceed then?
>
> As a remminder, my wish is to accept mail for [hidden email],
> and automaticaly redirect it to [hidden email], without having to
> go in every user account and add a new alias like
> [hidden email].
>
> Maybe it's not possible?

It is possible, but the alias approach is actually better in the long-term.
You can search the archives, I've posted the solution before.

--
        Viktor.

Disclaimer: off-list followups get on-list replies or get ignored.
Please do not ignore the "Reply-To" header.

To unsubscribe from the postfix-users list, visit
http://www.postfix.org/lists.html or click the link below:
<mailto:[hidden email]?body=unsubscribe%20postfix-users>

If my response solves your problem, the best way to thank me is to not
send an "it worked, thanks" follow-up. If you must respond, please put
"It worked, thanks" in the "Subject" so I can delete these quickly.
Reply | Threaded
Open this post in threaded view
|

Re: [Resolved] Virtual domain alias and LDAP

Angelos Karageorgiou
In reply to this post by Victor Duchovni
Victor Duchovni wrote:

> On Mon, Jun 09, 2008 at 06:21:44PM +0200, Raphael Berlamont wrote:
>
>  
>> Angelos Karageorgiou a ??crit :
>>    
>>> you will need the canonical maps section like so
>>>
>>> recipient_canonical_maps = ldap:/etc/postfix/ldap-aliasdomains.cf
>>> or such , I have implementet this by modifying my ldap schema and
>>> adding another attribute I call DomainAliasedTo
>>>
>>> like so
>>> =====
>>> server_host = ldap://ldap01:389 ldap://ldap02:389
>>> search_base = ou=domains,o=vivodi
>>> query_filter =
>>> (&(ou=%d)(objectClass=organizationalUnit)(objectClass=qmailUser))
>>> result_attribute = DomainAliasedTo
>>> result_format = %U@%s
>>> scope = sub
>>> version = 3
>>>      
>> This is working perfectly.
>>    
>
> This is a wild-card alias, and breaks recipient validation. Don't
> use rewriting tables that match non-existent users.
>
>  

Nope the return value

result_format = %U@%s

will bounce non existent users on the rewritten address


Reply | Threaded
Open this post in threaded view
|

Re: [Resolved] Virtual domain alias and LDAP

Victor Duchovni
On Tue, Jun 10, 2008 at 08:31:56AM +0300, Angelos Karageorgiou wrote:

> >>>query_filter =
> >>>(&(ou=%d)(objectClass=organizationalUnit)(objectClass=qmailUser))
> >>>result_attribute = DomainAliasedTo
> >>>result_format = %U@%s
> >>>scope = sub
> >>>version = 3
> >>>      
> >>This is working perfectly.
>
> >This is a wild-card alias, and breaks recipient validation. Don't
> >use rewriting tables that match non-existent users.
>
> Nope the return value
>
> result_format = %U@%s
>
> will bounce non existent users on the rewritten address

Nope, it will *bounce* them, rather than reject at SMTP time, which
does indeed break SMTP recipient *validation* and turns the server into
a backscatter source. DO NOT do this. The rewriting table query filter
only verifies the recipient domain, not the user id.

--
        Viktor.

Disclaimer: off-list followups get on-list replies or get ignored.
Please do not ignore the "Reply-To" header.

To unsubscribe from the postfix-users list, visit
http://www.postfix.org/lists.html or click the link below:
<mailto:[hidden email]?body=unsubscribe%20postfix-users>

If my response solves your problem, the best way to thank me is to not
send an "it worked, thanks" follow-up. If you must respond, please put
"It worked, thanks" in the "Subject" so I can delete these quickly.
Reply | Threaded
Open this post in threaded view
|

Re: [Resolved] Virtual domain alias and LDAP

Angelos Karageorgiou
Victor Duchovni wrote:
> Nope, it will *bounce* them, rather than reject at SMTP time, which
> does indeed break SMTP recipient *validation* and turns the server into
> a backscatter source. DO NOT do this. The rewriting table query filter
> only verifies the recipient domain, not the user id.
>

Ah jeez you are right Viktor, so  I am trying this out instead

virtual_alias_maps      =   ldap:/etc/postfix/ldap-virtual-vacation.cf,
                             ldap:/etc/postfix/ldap-forwarding.cf,
                             ldap:/etc/postfix/ldap-virtual.cf,
                             ldap:/etc/postfix/ldap-catchall.cf,
                             ldap:/etc/postfix/ldap-aliasdomains.cf




as the last step to alias expansion
where
+ cat /etc/postfix/ldap-virtual-vacation.cf
bind=no
domain=hash:/etc/postfix/domains.hash
server_host = ldap://ldap01:389 ldap://ldap02:389
search_base = ou=%d,ou=domains,o=top
query_filter =
(&(|(mail=%s)(mailAlternateAddress=%s))(VacationStatus=active))
result_attribute = mail
result_format = %u+%d@autoreply,%u@%d
scope = sub
version = 3



+ cat /etc/postfix/ldap-forwarding.cf
domain=hash:/etc/postfix/domains.hash
bind=no
server_host = ldap://ldap01:389 ldap://ldap02:389
search_base = ou=%d,ou=domains,o=top
query_filter = (|(mail=%s)(mailalternateaddress=%s))
result_attribute = mailForwardingAddress
scope = one
version = 3

+ cat /etc/postfix/ldap-virtual.cf
bind=no
domain=hash:/etc/postfix/domains.hash
server_host = ldap://ldap01:389 ldap://ldap02:389
search_base = ou=%d,ou=domains,o=top
query_filter = (|(mail=%s)(mailAlternateAddress=%s))
result_attribute = mail
scope = sub
version = 3

+ cat /etc/postfix/ldap-catchall.cf
domain=hash:/etc/postfix/domains.hash
bind=no
server_host = ldap://ldap01:389 ldap://ldap02:389
search_base = ou=%d,ou=domains,o=top
query_filter = (mailalternateaddress=catchall@%d)
result_attribute = mail
scope = sub
version = 3


+ cat /etc/postfix/ldap-aliasdomains.cf
bind=no
server_host = ldap://ldap01:389 ldap://ldap02:389
search_base = ou=domains,o=top
query_filter =
(&(ou=%d)(objectClass=organizationalUnit)(objectClass=qmailUser))
special_result_attribute = mail
scope = sub
version = 3




The steps are
vacation <- forwarding <- virtual,alias <- catchall <- domain aliasing

angelos.vcf (543 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [Resolved] Virtual domain alias and LDAP

Victor Duchovni
On Wed, Jun 11, 2008 at 04:13:15PM +0300, Angelos Karageorgiou wrote:

> Ah jeez you are right Viktor, so  I am trying this out instead
>
> virtual_alias_maps      =   ldap:/etc/postfix/ldap-virtual-vacation.cf,
>                             ldap:/etc/postfix/ldap-forwarding.cf,
>                             ldap:/etc/postfix/ldap-virtual.cf,
>                             ldap:/etc/postfix/ldap-catchall.cf,
>                             ldap:/etc/postfix/ldap-aliasdomains.cf
>

You are still confused, this is NOT a lookup *chain*, where failure
to match early in the chain results in no match. This is a list of
tables to try iteratively, and if any table is a wildcard-lookup,
you still have backscatter. Each and every table must validate
the full address if it returns a result.

> + cat /etc/postfix/ldap-aliasdomains.cf
> bind=no
> server_host = ldap://ldap01:389 ldap://ldap02:389
> search_base = ou=domains,o=top
> query_filter =
> (&(ou=%d)(objectClass=organizationalUnit)(objectClass=qmailUser))
> special_result_attribute = mail
> scope = sub
> version = 3
>

This done does not. In it far from clear what this one is supposed
to do.

--
        Viktor.

Disclaimer: off-list followups get on-list replies or get ignored.
Please do not ignore the "Reply-To" header.

To unsubscribe from the postfix-users list, visit
http://www.postfix.org/lists.html or click the link below:
<mailto:[hidden email]?body=unsubscribe%20postfix-users>

If my response solves your problem, the best way to thank me is to not
send an "it worked, thanks" follow-up. If you must respond, please put
"It worked, thanks" in the "Subject" so I can delete these quickly.
Reply | Threaded
Open this post in threaded view
|

Re: [Resolved] Virtual domain alias and LDAP

Angelos Karageorgiou
Victor Duchovni wrote:
> You are still confused, this is NOT a lookup *chain*, where failure
> to match early in the chain results in no match. This is a list of
> tables to try iteratively, and if any table is a wildcard-lookup,
> you still have backscatter. Each and every table must validate
> the full address if it returns a result.
>
>  
Well and good, any pointers? I cannot call a lookup to a table from
another table ....
Reply | Threaded
Open this post in threaded view
|

Re: [Resolved] Virtual domain alias and LDAP

Victor Duchovni
On Wed, Jun 11, 2008 at 06:13:52PM +0300, Angelos Karageorgiou wrote:

> Well and good, any pointers? I cannot call a lookup to a table from
> another table ....

If you want to rewrite example.com addresses to example.net addresses,
you should do this only when the corresponding example.net address
exists. Do do that, the query must use the address localpart to
check for the existence of the target mailbox. The simplest case is:

    domain              = example.com
    query               = mail=%[hidden email]
    result_attribute    = mail

You can elaborate on this idea as necessary.

--
        Viktor.

Disclaimer: off-list followups get on-list replies or get ignored.
Please do not ignore the "Reply-To" header.

To unsubscribe from the postfix-users list, visit
http://www.postfix.org/lists.html or click the link below:
<mailto:[hidden email]?body=unsubscribe%20postfix-users>

If my response solves your problem, the best way to thank me is to not
send an "it worked, thanks" follow-up. If you must respond, please put
"It worked, thanks" in the "Subject" so I can delete these quickly.
Reply | Threaded
Open this post in threaded view
|

Re: [Resolved] Virtual domain alias and LDAP

Angelos Karageorgiou
Victor Duchovni wrote:

> On Wed, Jun 11, 2008 at 06:13:52PM +0300, Angelos Karageorgiou wrote:
>
>> Well and good, any pointers? I cannot call a lookup to a table from
>> another table ....
>
> If you want to rewrite example.com addresses to example.net addresses,
> you should do this only when the corresponding example.net address
> exists. Do do that, the query must use the address localpart to
> check for the existence of the target mailbox. The simplest case is:
>
>     domain              = example.com
>     query               = mail=%[hidden email]
>     result_attribute    = mail
>
> You can elaborate on this idea as necessary.
>
Sorry for being a bit thick here, but, I understand that the absolute
best way to handle this is to list all "MailAlternateAddress"es in the
ldif entry of a user like so

mail=[hidden email]
mailalternateaddress=[hidden email]
mailalternateaddress=[hidden email]
mailalternateaddress=[hidden email]

Now that is fine and dandy UNTIL one tries to script a provisioning
system around this.

So for all of us users of postfix a domain to domain translation saves
us a lot of grief. Could you pretty please with sugar on top allow
for such behaviour?


P.S. I do not run a single domain, I run an ISP, many to many domains
mapped via ldap queries, that is why I would love a recursive call to a
lookup table.

Best regards, and thank you for your patience
Angelos

angelos.vcf (543 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [Resolved] Virtual domain alias and LDAP

Victor Duchovni
On Thu, Jun 12, 2008 at 10:00:22AM +0300, Angelos Karageorgiou wrote:

> Sorry for being a bit thick here, but, I understand that the absolute
> best way to handle this is to list all "MailAlternateAddress"es in the
> ldif entry of a user like so
>
> mail=[hidden email]
> mailalternateaddress=[hidden email]
> mailalternateaddress=[hidden email]
> mailalternateaddress=[hidden email]
>
> Now that is fine and dandy UNTIL one tries to script a provisioning
> system around this.

This is how my mailbox is provisioned:

    mail: [hidden email]
    mailalternateaddress;ext: [hidden email]
    mailalternateaddress;ext: [hidden email]
    mailalternateaddress;ext: [hidden email]
    mailalternateaddress;ext: [hidden email]
    mailalternateaddress;int: [hidden email]
    maildrop: [hidden email]

The ";ext" mailalternate address is accepted from outside and inside,
while the ";int" address is accepted only from internal clients.

> So for all of us users of postfix a domain to domain translation saves
> us a lot of grief. Could you pretty please with sugar on top allow
> for such behaviour?

Sadly, this is a major architectural change. It is unlikely to happen in
the Postfix 2.X series (2.6 in Q1 09). If there is some day a Postfix 3.0
that revisits how address rewriting is done, and allows the SMTP server
to validate rewritten addresses, implements chained tables, ... then
this becomes viable. Don't hold your breath.

Today, you can use a policy service call-out to implement recipient
validation in code of your choice, and then allow Postfix to make
wild-card replacements, because the validation is done in the policy
server. If this is more palatable than per-user lists of valid addresses,
you can take the policy-service-for-validation approach.

--
        Viktor.

Disclaimer: off-list followups get on-list replies or get ignored.
Please do not ignore the "Reply-To" header.

To unsubscribe from the postfix-users list, visit
http://www.postfix.org/lists.html or click the link below:
<mailto:[hidden email]?body=unsubscribe%20postfix-users>

If my response solves your problem, the best way to thank me is to not
send an "it worked, thanks" follow-up. If you must respond, please put
"It worked, thanks" in the "Subject" so I can delete these quickly.