Package: netbase
Version: 4.19
Severity: critical
Tags: patch
Justification: causes serious data loss

When I upgrade ipopd an unrelated (custom-made) entry in my inetd.conf
is being deleted.  The entry is

    pop3 stream tcp nowait nobody /usr/sbin/tcpd /usr/sbin/stunnel -c -r 
mail.argon.org:995

and it gets deleted when ipopd.postrm runs

    update-inetd --remove pop3s

due to a bug in DebianNet.  In tracing this down I found and fixed these
problems in DebianNet.pm:

    - add_service(), remove_service(), and scan_entries() weren't properly
      anchoring the service search, so they could all match where they
      shouldn't have.

    - There's no need to set the umask before doing a chmod() because
      chmod() isn't affected by the umask.  Additionally, leaving the
      process with a 0 umask is not safe.  I fixed this by removing the
      umask() call.  (A better fix would be to locally set a umask
      before calling open() and not do the chmod() at all, but I wanted
      to keep my changes minimal.)

    - The module causes some use of initialized value warnings.

Here's a demonstration of all these problems, and my fix for them.
After that is the patch.  I'll also attach these files so you can get
at them without having to cut/paste them out of the main part of this
message.

-------------------------------------------------------------------------------

$ cat do-test
#!/usr/bin/perl -w
use strict;

use DebianNet ();
print "using $INC{'DebianNet.pm'}\n";

$DebianNet::inetdcf = "test.conf";
$DebianNet::verbose = 1;

my $base = "stream tcp nowait nobody file cmd";

# There's no need for DebianNet to set the umask because it doesn't affect
# chmod.  (Really it should be setting umask() before the open() instead
# of doing the chmod(), but I wanted to keep my changes minimal.)
umask 0777;

print "various uninit warnings\n";
DebianNet::add_service          "warn-add       $base";
DebianNet::enable_service       "warn-enable";
DebianNet::disable_service      "warn-disable";
DebianNet::remove_service       "warn-rm";

print "scan_entries() doesn't properly anchor search\n";
DebianNet::disable_service      "test-scan";

print "add_service() doesn't properly anchor search\n";
DebianNet::add_service("test-add $base", "TEST");

print "remove_service() doesn't properly anchor search\n";
DebianNet::remove_service("arg");
$ cat test.conf.orig
#:TEST:

#<off># warn-enable     stream tcp nowait nobody file cmd
warn-disable            stream tcp nowait nobody file cmd
warn-rm                 stream tcp nowait nobody file cmd

# This shouldn't be seen by add_service() because the #<off># isn't at
# the start of the line.

# x #<off># test-add stream tcp nowait nobody file cmd

# This shouldn't be removed by remove_service("arg").

test-rm         stream tcp nowait nobody file cmd arg

# These shouldn't be found by scan_entries('test-scan').

test-scan-1     stream tcp nowait nobody file cmd arg
test-scan-2     stream tcp nowait nobody file cmd arg
$ cp test.conf.orig test.conf; \
  perl do-test; \
  diff -u test.conf.orig test.conf
using /usr/share/perl5/DebianNet.pm
various uninit warnings
Use of uninitialized value in scalar chomp at /usr/share/perl5/DebianNet.pm 
line 25.
Processing service `warn-add' ... added
Use of uninitialized value in regexp compilation at 
/usr/share/perl5/DebianNet.pm line 199, <ICREAD> line 3.
Processing service `warn-enable' ... enabled
Use of uninitialized value in regexp compilation at 
/usr/share/perl5/DebianNet.pm line 236, <ICREAD> line 4.
Use of uninitialized value in regexp compilation at 
/usr/share/perl5/DebianNet.pm line 174, <ICREAD> line 4.
Processing service `warn-disable' ... disabled
Use of uninitialized value in regexp compilation at 
/usr/share/perl5/DebianNet.pm line 236, <ICREAD> line 5.
Removing line: `warn-rm                 stream tcp nowait nobody file cmd'
scan_entries() doesn't properly anchor search
Use of uninitialized value in regexp compilation at 
/usr/share/perl5/DebianNet.pm line 236, <ICREAD> line 17.
Use of uninitialized value in regexp compilation at 
/usr/share/perl5/DebianNet.pm line 236, <ICREAD> line 18.

WARNING!!!!!! test.conf contains multiple entries for
the `test-scan' service. You're about to disable these entries.
Do you want to continue? [n]

Ok, I'll stop ...
add_service() doesn't properly anchor search
remove_service() doesn't properly anchor search
Removing line: `# This shouldn't be removed by remove_service("arg").'
Removing line: `test-rm         stream tcp nowait nobody file cmd arg'
Removing line: `test-scan-1     stream tcp nowait nobody file cmd arg'
Removing line: `test-scan-2     stream tcp nowait nobody file cmd arg'
--- test.conf.orig      2005-02-24 12:21:19.000000000 -0500
+++ test.conf   2005-02-24 12:39:51.000000000 -0500
@@ -1,19 +1,15 @@
 #:TEST:

-#<off># warn-enable     stream tcp nowait nobody file cmd
-warn-disable            stream tcp nowait nobody file cmd
-warn-rm                 stream tcp nowait nobody file cmd
+warn-enable     stream tcp nowait nobody file cmd
+#<off># warn-disable            stream tcp nowait nobody file cmd

 # This shouldn't be seen by add_service() because the #<off># isn't at
 # the start of the line.

 # x #<off># test-add stream tcp nowait nobody file cmd

-# This shouldn't be removed by remove_service("arg").

-test-rm         stream tcp nowait nobody file cmd arg

 # These shouldn't be found by scan_entries('test-scan').

-test-scan-1     stream tcp nowait nobody file cmd arg
-test-scan-2     stream tcp nowait nobody file cmd arg
+warn-add       stream tcp nowait nobody file cmd
$ cp test.conf.orig test.conf; \
  perl -I. do-test; \
  diff -u test.conf.orig test.conf
using DebianNet.pm
various uninit warnings
Processing service `warn-add' ... added
Processing service `warn-enable' ... enabled
Processing service `warn-disable' ... disabled
Removing line: `warn-rm                 stream tcp nowait nobody file cmd'
scan_entries() doesn't properly anchor search
add_service() doesn't properly anchor search
Processing service `test-add' ... added
remove_service() doesn't properly anchor search
--- test.conf.orig      2005-02-24 12:21:19.000000000 -0500
+++ test.conf   2005-02-24 12:39:57.000000000 -0500
@@ -1,8 +1,8 @@
 #:TEST:
+test-add stream tcp nowait nobody file cmd

-#<off># warn-enable     stream tcp nowait nobody file cmd
-warn-disable            stream tcp nowait nobody file cmd
-warn-rm                 stream tcp nowait nobody file cmd
+warn-enable     stream tcp nowait nobody file cmd
+#<off># warn-disable            stream tcp nowait nobody file cmd

 # This shouldn't be seen by add_service() because the #<off># isn't at
 # the start of the line.
@@ -17,3 +17,4 @@

 test-scan-1     stream tcp nowait nobody file cmd arg
 test-scan-2     stream tcp nowait nobody file cmd arg
+warn-add       stream tcp nowait nobody file cmd
$ _

-------------------------------------------------------------------------------

--- /usr/share/perl5/DebianNet.pm       2004-10-31 11:29:34.000000000 -0500
+++ DebianNet.pm        2005-02-24 12:16:13.000000000 -0500
@@ -22,8 +22,12 @@
     local($newentry, $group) = @_;
     local($service, $searchentry, @inetd, $inetdconf, $found, $success);
     unless (defined($newentry)) { return(-1) };
-    chomp($newentry); chomp($group);
-    $group = "OTHER" unless (defined($group));
+    chomp($newentry);
+    if (defined $group) {
+        chomp $group;
+    } else {
+        $group = "OTHER";
+    }
     $group =~ tr/a-z/A-Z/;
     $newentry =~ s/\\t/\t/g;
     ($service = $newentry) =~ s/(\W*\w+)\s+.*/$1/;
@@ -41,7 +45,7 @@
     if (open(INETDCONF,"$inetdcf")) {
         @inetd=<INETDCONF>;
         close(INETDCONF);
-        if (grep(m/$sep$sservice\s+/,@inetd)) {
+        if (grep(m/^$sep$sservice\s+/,@inetd)) {
             &enable_service($sservice);
         } else {
             if (grep(m/^$sservice\s+/,@inetd)) {
@@ -84,7 +88,7 @@
 
             rename("$inetdcf.new","$inetdcf") ||
                 die "Error installing new $inetdcf: $!\n";
-            umask(000); chmod(0644, "$inetdcf");
+            chmod(0644, "$inetdcf");
 
             &wakeup_inetd;
         }
@@ -131,7 +135,7 @@
     open(ICREAD, "$inetdcf");
     RLOOP: while(<ICREAD>) {
         chomp;
-        unless (/$service\b/) {
+        unless (/^$service\s+/) {
             print ICWRITE "$_\n";
         } else {
             &printv("Removing line: \`$_'\n");
@@ -142,7 +146,7 @@
 
     rename("$inetdcf.new", "$inetdcf") ||
         die "Error installing new $inetdcf: $!\n";
-    umask(000); chmod(0644, "$inetdcf");
+    chmod(0644, "$inetdcf");
 
     &wakeup_inetd;
     return(1);
@@ -151,6 +155,7 @@
 sub disable_service {
     my($service, $pattern) = @_;
     unless (defined($service)) { return(-1) };
+    unless (defined($pattern)) { $pattern = ''; }
     chomp($service);
 
     if ((&scan_entries("$service", $pattern) > 1) and (not defined($multi))) {
@@ -182,7 +187,7 @@
 
     rename("$inetdcf.new","$inetdcf") ||
         die "Error installing new $inetdcf: $!\n";
-    umask(000); chmod(0644, "$inetdcf");
+    chmod(0644, "$inetdcf");
 
     &wakeup_inetd;
     return(1);
@@ -191,6 +196,7 @@
 sub enable_service {
     my($service, $pattern) = @_;
     unless (defined($service)) { return(-1) };
+    unless (defined($pattern)) { $pattern = ''; }
     chomp($service);
     open(ICWRITE, ">$inetdcf.new") || die "Error creating new $inetdcf: $!\n";
     open(ICREAD, "$inetdcf");
@@ -207,7 +213,7 @@
 
     rename("$inetdcf.new","$inetdcf") ||
         die "Error installing new $inetdcf: $!\n";
-    umask(000); chmod(0644, "$inetdcf");
+    chmod(0644, "$inetdcf");
 
     &wakeup_inetd;
     return(1);
@@ -229,11 +235,12 @@
 
 sub scan_entries {
     my ($service, $pattern) = @_;
+    unless (defined($pattern)) { $pattern = ''; }
     my $counter = 0;
 
     open(ICREAD, "$inetdcf");
     SLOOP: while (<ICREAD>) {
-        $counter++ if (/^$service\b/ and /$pattern/);
+        $counter++ if (/^$service\s+/ and /$pattern/);
     }
     close(ICREAD);
     return($counter);

-------------------------------------------------------------------------------

-- System Information:
Debian Release: 3.1
  APT prefers testing
  APT policy: (900, 'testing'), (700, 'unstable')
Architecture: i386 (i686)
Kernel: Linux 2.6.8-jones.1
Locale: LANG=en_US, LC_CTYPE=en_US (charmap=ISO-8859-1)

Versions of packages netbase depends on:
ii  debconf                       1.4.30.11  Debian configuration management sy
ii  ifupdown                      0.6.4-4.10 High level tools to configure netw
ii  netkit-inetd                  0.10-10    The Internet Superserver
ii  netkit-ping [ping]            0.10-10    The ping utility from netkit
ii  tcpd                          7.6.dbs-6  Wietse Venema's TCP wrapper utilit

-- debconf information excluded

-- 
Roderick Schertler
[EMAIL PROTECTED]
--- /usr/share/perl5/DebianNet.pm       2004-10-31 11:29:34.000000000 -0500
+++ DebianNet.pm        2005-02-24 12:16:13.000000000 -0500
@@ -22,8 +22,12 @@
     local($newentry, $group) = @_;
     local($service, $searchentry, @inetd, $inetdconf, $found, $success);
     unless (defined($newentry)) { return(-1) };
-    chomp($newentry); chomp($group);
-    $group = "OTHER" unless (defined($group));
+    chomp($newentry);
+    if (defined $group) {
+        chomp $group;
+    } else {
+        $group = "OTHER";
+    }
     $group =~ tr/a-z/A-Z/;
     $newentry =~ s/\\t/\t/g;
     ($service = $newentry) =~ s/(\W*\w+)\s+.*/$1/;
@@ -41,7 +45,7 @@
     if (open(INETDCONF,"$inetdcf")) {
         @inetd=<INETDCONF>;
         close(INETDCONF);
-        if (grep(m/$sep$sservice\s+/,@inetd)) {
+        if (grep(m/^$sep$sservice\s+/,@inetd)) {
             &enable_service($sservice);
         } else {
             if (grep(m/^$sservice\s+/,@inetd)) {
@@ -84,7 +88,7 @@
 
             rename("$inetdcf.new","$inetdcf") ||
                 die "Error installing new $inetdcf: $!\n";
-            umask(000); chmod(0644, "$inetdcf");
+            chmod(0644, "$inetdcf");
 
             &wakeup_inetd;
         }
@@ -131,7 +135,7 @@
     open(ICREAD, "$inetdcf");
     RLOOP: while(<ICREAD>) {
         chomp;
-        unless (/$service\b/) {
+        unless (/^$service\s+/) {
             print ICWRITE "$_\n";
         } else {
             &printv("Removing line: \`$_'\n");
@@ -142,7 +146,7 @@
 
     rename("$inetdcf.new", "$inetdcf") ||
         die "Error installing new $inetdcf: $!\n";
-    umask(000); chmod(0644, "$inetdcf");
+    chmod(0644, "$inetdcf");
 
     &wakeup_inetd;
     return(1);
@@ -151,6 +155,7 @@
 sub disable_service {
     my($service, $pattern) = @_;
     unless (defined($service)) { return(-1) };
+    unless (defined($pattern)) { $pattern = ''; }
     chomp($service);
 
     if ((&scan_entries("$service", $pattern) > 1) and (not defined($multi))) {
@@ -182,7 +187,7 @@
 
     rename("$inetdcf.new","$inetdcf") ||
         die "Error installing new $inetdcf: $!\n";
-    umask(000); chmod(0644, "$inetdcf");
+    chmod(0644, "$inetdcf");
 
     &wakeup_inetd;
     return(1);
@@ -191,6 +196,7 @@
 sub enable_service {
     my($service, $pattern) = @_;
     unless (defined($service)) { return(-1) };
+    unless (defined($pattern)) { $pattern = ''; }
     chomp($service);
     open(ICWRITE, ">$inetdcf.new") || die "Error creating new $inetdcf: $!\n";
     open(ICREAD, "$inetdcf");
@@ -207,7 +213,7 @@
 
     rename("$inetdcf.new","$inetdcf") ||
         die "Error installing new $inetdcf: $!\n";
-    umask(000); chmod(0644, "$inetdcf");
+    chmod(0644, "$inetdcf");
 
     &wakeup_inetd;
     return(1);
@@ -229,11 +235,12 @@
 
 sub scan_entries {
     my ($service, $pattern) = @_;
+    unless (defined($pattern)) { $pattern = ''; }
     my $counter = 0;
 
     open(ICREAD, "$inetdcf");
     SLOOP: while (<ICREAD>) {
-        $counter++ if (/^$service\b/ and /$pattern/);
+        $counter++ if (/^$service\s+/ and /$pattern/);
     }
     close(ICREAD);
     return($counter);

Attachment: do-test
Description: Perl program

#:TEST:

#<off># warn-enable     stream tcp nowait nobody file cmd
warn-disable            stream tcp nowait nobody file cmd
warn-rm                 stream tcp nowait nobody file cmd

# This shouldn't be seen by add_service() because the #<off># isn't at
# the start of the line.

# x #<off># test-add stream tcp nowait nobody file cmd

# This shouldn't be removed by remove_service("arg").

test-rm         stream tcp nowait nobody file cmd arg

# These shouldn't be found by scan_entries('test-scan').

test-scan-1     stream tcp nowait nobody file cmd arg
test-scan-2     stream tcp nowait nobody file cmd arg
# DebianNet.pm: a perl module to add entries to the /etc/inetd.conf file
#
# Copyright (C) 1995, 1996 Peter Tobias <[EMAIL PROTECTED]>
#                          Ian Jackson <[EMAIL PROTECTED]>
#
#
# DebianNet::add_service($newentry, $group);
# DebianNet::disable_service($service, $pattern);
# DebianNet::enable_service($service, $pattern);
# DebianNet::remove_service($entry);
#

package DebianNet;

require 5.000;

$inetdcf="/etc/inetd.conf";
$sep = "#<off># ";
$version = "1.11";

sub add_service {
    local($newentry, $group) = @_;
    local($service, $searchentry, @inetd, $inetdconf, $found, $success);
    unless (defined($newentry)) { return(-1) };
    chomp($newentry);
    if (defined $group) {
        chomp $group;
    } else {
        $group = "OTHER";
    }
    $group =~ tr/a-z/A-Z/;
    $newentry =~ s/\\t/\t/g;
    ($service = $newentry) =~ s/(\W*\w+)\s+.*/$1/;
    ($sservice = $service) =~ s/^#([A-Za-z].*)/$1/;
    ($searchentry = $newentry) =~ s/^$sep//;
    $searchentry =~ s/^#([A-Za-z].*)/$1/;

    # strip parameter from entry (e.g. -s /tftpboot)
    # example:          service dgram udp     wait    root    /tcpd /prg   -s 
/tftpboot";
    $searchentry =~ s/^(\w\S+\W+\w+\W+\w\S+\W+\w\S+\W+\w\S+\W+\S+\W+\S+).*/$1/;
    $searchentry =~ s/[ \t]+/ /g;
    $searchentry =~ s/ /\\s+/g;
    $searchentry =~ [EMAIL PROTECTED]/\S+\\s\+/[EMAIL PROTECTED]@g;

    if (open(INETDCONF,"$inetdcf")) {
        @inetd=<INETDCONF>;
        close(INETDCONF);
        if (grep(m/^$sep$sservice\s+/,@inetd)) {
            &enable_service($sservice);
        } else {
            if (grep(m/^$sservice\s+/,@inetd)) {
                if (grep(m/^$sservice\s+/,@inetd) > 1) {
                    &inetde("There are several entries for $sservice in 
$inetdcf\n");
                } elsif (!grep(m:^#?.*$searchentry.*:, @inetd)) {
                    print"\nTrying to add the following entry:\n\n 
$newentry\n\n";
                    &inetde("There is already an entry for $sservice in 
$inetdcf,
but I don't recognise it.  Here is what it looks like:\n
 ".join(' ',grep(m/^$sservice\s+/,@inetd)));
                }
            } elsif (grep(m/^#\s*$sservice\s+/, @inetd) >= 1 or
              (($service =~ s/^#//) and grep(m/^$service\s+/, @inetd)>=1)) {
                &printv("Processing service \`$service' ... not changed\n");
            } else {
                &printv("Processing service \`$sservice' ... added\n");
                $inetdconf=1;
            }
        }
        if ($inetdconf) {
            open(ICWRITE, ">$inetdcf.new") || die "Error creating new $inetdcf: 
$!\n";
            open(ICREAD, "$inetdcf");
            while(<ICREAD>) {
                chomp;
                if (/^#:$group:/) {
                    $found = 1;
                };
                if ($found and !(/[a-zA-Z#]/)) {
                    print (ICWRITE "$newentry\n") || die "Error writing new 
$inetdcf: $!\n";
                    $found = 0;
                    $success = 1;
                }
                print ICWRITE "$_\n";
            }
            close(ICREAD);
            unless ($success) {
                print (ICWRITE "$newentry\n") || die "Error writing new 
$inetdcf: $!\n";
            }
            close(ICWRITE) || die "Error closing new inetd.conf: $!\n";

            rename("$inetdcf.new","$inetdcf") ||
                die "Error installing new $inetdcf: $!\n";
            chmod(0644, "$inetdcf");

            &wakeup_inetd;
        }
    }

    sub inetde {
        my($response);
        do {
            print @_,
"\nDo you want to ignore this potential problem and continue, or would
you rather not do so now ?  Continue?  (n/y) ";
            $!=0; defined($response=<STDIN>) || die "netconfig: EOF/error on 
stdin: $!\n";
        } while ($response !~ m/^\s*[yn]?\s*$/i);
        return(1) if($response =~ m/y/i);
        exit(1);
    }
    return(1);
}

sub remove_service {
    my($service) = @_;
    unless(defined($service)) { return(-1) };
    chomp($service);
    if($service eq "") {
         print "DebianNet::remove_service called with empty argument\n";
         return(-1);
    }

    if ((&scan_entries("$service") > 1) and (not defined($multi))) {
        print "\nWARNING!!!!!! $inetdcf contains multiple entries for \n";
        print "the \`$service' service. You're about to remove these 
entries.\n";
        print "Do you want to continue? [n] ";
        if (<STDIN> =~ /^[^y]/i) {
            print "\nOk, I'll stop ...\n";
            return(1);
        } else {
            if ($want_continue == 0) {
                print "\nOk, I'll continue ...\n";
            }
        }
    }

    open(ICWRITE, ">$inetdcf.new") || die "Error creating $inetdcf.new";
    open(ICREAD, "$inetdcf");
    RLOOP: while(<ICREAD>) {
        chomp;
        unless (/^$service\s+/) {
            print ICWRITE "$_\n";
        } else {
            &printv("Removing line: \`$_'\n");
        }
    }
    close(ICREAD);
    close(ICWRITE);

    rename("$inetdcf.new", "$inetdcf") ||
        die "Error installing new $inetdcf: $!\n";
    chmod(0644, "$inetdcf");

    &wakeup_inetd;
    return(1);
}

sub disable_service {
    my($service, $pattern) = @_;
    unless (defined($service)) { return(-1) };
    unless (defined($pattern)) { $pattern = ''; }
    chomp($service);

    if ((&scan_entries("$service", $pattern) > 1) and (not defined($multi))) {
        print "\nWARNING!!!!!! $inetdcf contains multiple entries for \n";
        print "the \`$service' service. You're about to disable these 
entries.\n";
        print "Do you want to continue? [n] ";
        if (<STDIN> =~ /^[^y]/i) {
            print "\nOk, I'll stop ...\n";
            return(1);
        } else {
            if ($want_continue == 0) {
                print "\nOk, I'll continue ...\n";
            }
        }
    }

    open(ICWRITE, ">$inetdcf.new") || die "Error creating new $inetdcf: $!\n";
    open(ICREAD, "$inetdcf");
    DLOOP: while(<ICREAD>) {
      chomp;
      if (/^$service\s+\w+\s+/ and /$pattern/) {
          &printv("Processing service \`$service' ... disabled\n");
          $_ =~ s/^(.+)$/$sep$1/;
      }
      print ICWRITE "$_\n";
    }
    close(ICREAD);
    close(ICWRITE) || die "Error closing new inetd.conf: $!\n";

    rename("$inetdcf.new","$inetdcf") ||
        die "Error installing new $inetdcf: $!\n";
    chmod(0644, "$inetdcf");

    &wakeup_inetd;
    return(1);
}

sub enable_service {
    my($service, $pattern) = @_;
    unless (defined($service)) { return(-1) };
    unless (defined($pattern)) { $pattern = ''; }
    chomp($service);
    open(ICWRITE, ">$inetdcf.new") || die "Error creating new $inetdcf: $!\n";
    open(ICREAD, "$inetdcf");
    while(<ICREAD>) {
      chomp;
      if (/^$sep$service\s+\w+\s+/ and /$pattern/) {
          &printv("Processing service \`$service' ... enabled\n");
          $_ =~ s/^$sep//;
      }
      print ICWRITE "$_\n";
    }
    close(ICREAD);
    close(ICWRITE) || die "Error closing new inetd.conf: $!\n";

    rename("$inetdcf.new","$inetdcf") ||
        die "Error installing new $inetdcf: $!\n";
    chmod(0644, "$inetdcf");

    &wakeup_inetd;
    return(1);
}

sub wakeup_inetd {
    my($pid);
    if (open(P,"/var/run/inetd.pid")) {
        $pid=<P>;
        if (open(C,sprintf("/proc/%d/stat",$pid))) {
            $_=<C>;
            if (m/^\d+ \(inetd\)/) { kill(1,$pid); }
            close(C);
        }
        close(P);
    }
    return(1);
}

sub scan_entries {
    my ($service, $pattern) = @_;
    unless (defined($pattern)) { $pattern = ''; }
    my $counter = 0;

    open(ICREAD, "$inetdcf");
    SLOOP: while (<ICREAD>) {
        $counter++ if (/^$service\s+/ and /$pattern/);
    }
    close(ICREAD);
    return($counter);
}

sub printv {
    print @_ if (defined($verbose));
}

1;

Reply via email to