]> git.treefish.org Git - fex.git/blobdiff - cgi-bin/fup
Original release 20160328
[fex.git] / cgi-bin / fup
index 97624a081db94e732440bc30c4ac116d63234a1d..2a799acd3293d2d2cf7768dbfdc2762ae4664f04 100755 (executable)
@@ -10,6 +10,7 @@
 
 BEGIN { ($ENV{PERLINIT}||'') =~ /(.+)/s and eval $1 }
 
+use utf8;
 use Encode;
 use Fcntl              qw':flock :seek :mode';
 use IO::Handle;
@@ -57,6 +58,7 @@ my @header;           # HTTP entity header
 my $fileid;            # file ID
 my $captive;
 my $muser;             # main user fur sub or group user
+my %specific;          # upload specific KEEP and AUTODELETE parameters
 
 # load common code, local config: $FEXLIB/fex.ph
 require "$FEXLIB/fex.pp";
@@ -108,7 +110,8 @@ if ($addto) {
   my %to;
   foreach $to (@to) { $to{$to} = 1 }
   push @to,$addto unless $to{$addto};
-  if ($submit and @to == 1) { $addto = '' }
+  # user has submitted with [select from your address book] ?
+  # if ($submit and @to == 1) { $addto = '' }
 }
 
 $to = join(',',@to);
@@ -151,7 +154,7 @@ if ($from =~ /^anonymous@/ and
   $id = $rid = $anonymous = 'anonymous';
   if ($to =~ /^anonymous/) {
     @to = ($to);
-    $autodelete{$to} = $autodelete = 'NO';
+    $autodelete{$to} = $autodelete = $specific{'autodelete'}||'NO';
   }
   $nomail = $anonymous;
 }
@@ -199,10 +202,9 @@ if ($from and $id and not ($gkey or $skey or $public or $okey)) {
     # set akey link for HTTP sessions
     # (need original id for consistant non-moving akey)
     if (-d $akeydir and open $idf,'<',"$from/@" and my $id = getline($idf)) {
-      $akey = untaint(md5_hex("$from:$id"));
-      mksymlink("$akeydir/$akey","../$from");
-      # show URL from fexsend
-      if ($from eq $to and $comment eq '*') {
+      # akey for webbrowser or fexsend special
+      if (not $sid or ($from eq $to and ($comment eq '*')) or $command) {
+        $akey = untaint(md5_hex("$from:$id"));
         mksymlink("$akeydir/$akey","../$from");
       }
     }
@@ -403,14 +405,14 @@ if (($from and $id and $rid eq $id or $gkey or $skey) and $command) {
                     - int((time-mtime("$file/filename"))/$DS);
         if ($comment =~ /NOMAIL/ or
            (readlink "$to/\@NOTIFICATION"||'') =~ /^no/i) {
-          printf "%8s MB [%s d] %s/%s/%s\n",
+          printf "%8s MB (%2s d) %s/%s/%s\n",
                  $size,
                  $rkeep,
                  $durl,
                  $dkey,
                  urlencode(basename($file));
         } else {
-          printf "%8s MB [%s d] <a href=\"%s\">%s</a>%s %s\n",
+          printf "%8s MB (%2s d) <a href=\"%s\">%s</a>%s %s\n",
                  $size,
                  $rkeep,
                  untaint("/fup?akey=$akey&dkey=$dkey&command=RENOTIFY"),
@@ -469,9 +471,10 @@ if (($from and $id and $rid eq $id or $gkey or $skey) and $command) {
           }
           my $rkeep = untaint(readlink "$file/keep"||$keep_default)
                       - int((time-mtime("$file/filename"))/$DS);
-          printf "%8s MB [%s d] <a href=\"%s\">%s</a>%s\n",
+          printf "%8s MB (%2s d) %s <a href=\"%s\">%s</a>%s\n",
                  $size,
                  $rkeep,
+                 stat("$file/download")?'+':'-',
                  untaint("/fup?akey=$akey&dkey=$dkey&command=FORWARD"),
                  $filename,
                  $comment?qq( "$comment"):'';
@@ -528,7 +531,7 @@ if (($from and $id and $rid eq $id or $gkey or $skey) and $command) {
                    $akey,$dkey;
             printf "[<a href=\"/fup?akey=%s&dkey=%s&command=COPY\">forward</a>] ",
                    $akey,$dkey;
-            printf "%8s MB (%s d) <a href=\"%s\">%s</a>%s\n",
+            printf "%8s MB (%2s d) <a href=\"%s\">%s</a>%s\n",
                    $size,$rkeep,$url,$filename,$comment;
           }
         }
@@ -630,11 +633,12 @@ if (($from and $id and $rid eq $id or $gkey or $skey) and $command) {
     foreach my $to (@group?@group:@to) {
       # my $options = sprintf "(autodelete=%s,keep=%s,locale=%s)",
       # readlink "$to/\@LOCALE"||$locale||$locale{$to}||$default_locale;
-      my $options = sprintf "(autodelete=%s,keep=%s,locale=%s,notification=%s)",
+      # my $options = sprintf "(autodelete=%s,keep=%s,locale=%s,notification=%s)",
+      my $options = sprintf "(autodelete=%s,keep=%s,locale=%s)",
         $autodelete{$to}||$autodelete,
         $keep{$to}||$keep_default,
-        readlink("$to/\@LOCALE")||$default_locale,
-        readlink("$to/\@NOTIFICATION")||'full';
+        readlink("$to/\@LOCALE")||$locale{$to}||$default_locale;
+        readlink("$to/\@NOTIFICATION")||'full';
       nvt_print("X-Recipient: $to $options");
     }
     nvt_print('');
@@ -796,7 +800,7 @@ unless ($file) {
   my @cookies;
   if ($logout and my $cookie = $ENV{HTTP_COOKIE}) {
     while ($cookie =~ s/(\w+key)=\w+//) {
-      push @cookies,"Set-Cookie: $1=; Max-Age=0; Discard";
+      push @cookies,"Set-Cookie: $1=x; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT";
     }
   }
 
@@ -839,6 +843,8 @@ unless ($file) {
   {
     present_locales('/fup');
 
+    # print "[$addto] [$submit] [@to]<p>\n";
+
     @ab = ("<option></option>");
 
     # select menu from server address book
@@ -848,6 +854,7 @@ unless ($file) {
         if (/(\S+)[=\s]+(\S+@[\w.-]+\S*)/) {
           $_ = "$1 &lt;$2>";
           s/,.*/,.../g;
+          s/:.*/>/;
           push @ab,"<option>$_</option>";
         }
       }
@@ -876,7 +883,7 @@ unless ($file) {
       '  <input type="hidden" name="from" value="$from">'
       '  <input type="hidden" name="id"   value="$id">'
       '  <table border="1">'
-      '    <tr><td>sender:   <td><a href="/fup/$ab64">$from</a></tr>'
+      '    <tr><td>sender:   <td><a href="/foc">$from</a></tr>'
       '    <tr title="e-mail address or alias"><td>recipient(s):'
       '        <td><input type="text" name="to" size="96" value="$to"><br>'
     ));
@@ -908,6 +915,11 @@ unless ($file) {
           foreach my $rd (@local_rdomains) {
             print "*\@$rd\n";
           }
+        } elsif (/^\@LOCAL_USERS/) {
+          foreach (glob "*/@") {
+            s:/.::;
+            print "$_\n";
+          }
         } else {
           print "$_\n";
         }
@@ -942,20 +954,20 @@ unless ($file) {
       print "Alternate Java client</a> (for files > 2 GB or sending of more than one file)\n";
     }
     print &logout;
-    if (-x "$FEXHOME/cgi-bin/login") {
-      print $info_login||$info_1;
-    }
     pq(qq(
       '<p><hr><p>'
       '<b>'
-      'Warning: the recipient must not be a mailing list, because after'
-      'download the file will be no more available!'
+      'Warning: the recipient must not be a mailing list,'
+      'because after download the file will be no more available!'
       '</b><br>'
-      'Contact <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a>'
-      'if you want to fex to a mailing list,'
+      'Contact <a href="mailto:$ENV{SERVER_ADMIN}">fexmaster</a> if you want to fex to a mailing list,'
       'he can allow multiple downloads for specific addresses.'
+      '<p>'
+      'Use a <a href="/tools.html">F*EX client</a> if you want to send more than one file or resume an interrupted upload.'
       '</body></html>'
-    ));
+      '<p>'
+   ));
+    print $info_1;
     exit;
   }
 
@@ -1052,7 +1064,7 @@ unless ($file) {
       pq(qq(
         '  <input type="hidden" name="akey" value="$akey">'
         '  <table border="1">'
-        '    <tr><td>sender:<td>$from</tr>'
+        '    <tr><td>sender:<td><a href="/foc">$from</a></tr>'
       ));
       if ($anonymous) {
         pq(qq(
@@ -1185,7 +1197,7 @@ unless ($file) {
   pq(qq(
     '<form action="/fup"'
     '      method="post"'
-    '      accept-charset="ISO-8859-1"'
+    '      accept-charset="UTF-8"'
     '      enctype="multipart/form-data">'
     '  <table>'
     '    <tr><td>sender:'
@@ -1232,7 +1244,7 @@ unless ($file) {
   # }
   print "</form>\n";
 
-  print $info_1;
+  print $info_login||$info_1;
 
   if ($debug and $debug>1) {
     print "<hr>\n<pre>\n";
@@ -1409,11 +1421,12 @@ if ($nostore) {
     my $to = $_;
     $to =~ s/:\w+=.*//; # remove options from address
     my $file = "$to/$from/$fkey";
-    my $options = sprintf "(autodelete=%s,keep=%s,locale=%s,notification=%s)",
+    # my $options = sprintf "(autodelete=%s,keep=%s,locale=%s,notification=%s)",
+    my $options = sprintf "(autodelete=%s,keep=%s,locale=%s)",
       readlink("$file/autodelete")||$autodelete,
       readlink("$file/keep")||readlink("$to/\@KEEP")||$keep_default,
-      readlink("$to/\@LOCALE")||readlink("$file/locale")||$default_locale,
-      readlink("$to/\@NOTIFICATION")||'full';
+      readlink("$to/\@LOCALE")||readlink("$file/locale")||$default_locale;
+      readlink("$to/\@NOTIFICATION")||'full';
     nvt_print("X-Recipient: $to $options");
     nvt_print("X-Location: $durl/$dkey{$to}/$fkey") unless $restricted;
   }
@@ -1502,7 +1515,13 @@ if ($okey) {
   print "&bwlimit=$bwlimit&autodelete=$autodelete&keep=$keep\">";
   print "send another file</a>\n";
   if ($http_client !~ /fexsend/ and $http_client =~ /Linux/i) {
-    print qq'<p>Hi Linux-user, try <a href="/FAQ/user.html#Why_should_I_use_a_special_F_EX_client">fexsend</a>! &#9786;<p>\n';
+    print '<p>Hi Linux-user, try ',
+          '<a href="/FAQ/user.html#Why_should_I_use_a_special_F_EX_client">',
+          "fexsend</a>! &#9786;<p>\n";
+  }
+  if ($http_client !~ /fexit/ and $http_client =~ /Windows/i) {
+    print '<p>Hi Windows-user, try <a href="/fexit.html">fexit</a>! ',
+          "&#9786;<p>\n";
   }
   print &logout;
 }
@@ -1538,11 +1557,21 @@ sub parse_request {
   # parse HTTP QUERY_STRING (parameter=value pairs)
   if ($qs) {
     foreach (split '&',$qs) {
-      if (s/^(\w+)=//) {
-        my $x = $1;
+      if (s/^(\w+)=(.*)//) {
+        my $p = uc($1);
+        my $v = $2;
         # decode URL-encoding
-        s/%([a-f0-9]{2})/chr(hex($1))/gie;
-        setparam($x,$_);
+        $v =~ s/%([a-f0-9]{2})/chr(hex($1))/gie;
+        setparam($p,$v);
+        if ($p eq 'AUTODELETE') {
+          $specific{'autodelete'} = $autodelete = $v;
+        }
+        if ($p eq 'KEEP' and /^\d+$/) {
+          $specific{'keep'} = $keep = $v;
+        }
+        # if ($p eq 'LOCALE') {
+        #   $specific{'locale'} = $locale = $v;
+        # }
       }
     }
   }
@@ -1662,27 +1691,43 @@ sub parse_request {
 
   # collect multiple addresses and check for aliases (not group)
   if (@to and "@to" !~ /^@[\w-]+$/
-      and not ($gkey or $addto or $command =~ /^LIST(RECEIVED)?$/))
-  {
-
+      and not ($gkey or $addto or $command =~ /^LIST(RECEIVED)?$/)) {
     # read address book
     if ($from and open my $AB,'<',"$from/\@ADDRESS_BOOK") {
-      my ($alias,$address,$autodelete,$locale,$keep);
+      my ($alias,$addresses,$autodelete,$locale,$keep);
       while (<$AB>) {
         s/#.*//;
         $_ = lc $_;
         if (s/^\s*(\S+)[=\s]+(\S+)//) {
-          ($alias,$address) = ($1,$2);
+          ($alias,$addresses) = ($1,$2);
+          # alias specific options?
           $autodelete = $locale = $keep = '';
           $autodelete = $1 if /autodelete=(\w+)/;
           $locale     = $1 if /locale=(\w+)/;
           $keep       = $1 if /keep=(\d+)/;
-          foreach my $address (split(",",$address)) {
-            $address .= '@'.$mdomain if $mdomain and $address !~ /@/;
+          foreach my $address (split(",",$addresses)) {
+            # alias address specific :options?
+            if ($address =~ s/(.+?):(.+)/$1/) {
+              my @options = split(':',$2);
+              $address = expand($address);
+              foreach (@options) {
+                if (/^keep=(\d+)$/i) {
+                  $alias_keep{$alias}{$address} = $1
+                }
+                if (/^autodelete=(yes|no|delay)$/i) {
+                  $alias_autodelete{$alias}{$address} = $1
+                }
+                if (/^locale=(\w+)$/i) {
+                  $alias_locale{$alias}{$address} = $1
+                }
+              }
+            } else {
+              $address = expand($address);
+            }
             push @{$ab{$alias}},$address;
-            $autodelete{$alias} = $autodelete;
-            $keep{$alias}       = $keep;
-            $locale{$alias}     = $locale;
+            $autodelete{$alias} = $autodelete if $autodelete;
+            $keep{$alias}       = $keep       if $keep;
+            $locale{$alias}     = $locale     if $locale;
           }
         }
       }
@@ -1692,46 +1737,86 @@ sub parse_request {
     # look for recipient's options and eliminate dupes
     %to = ();
     foreach my $to (my @loop = @to) {
-     # address book alias?
-      if ($to !~ /@/ and $ab{$to}) {
-        foreach my $address (my @loop = @{$ab{$to}}) {
-          $address .= '@'.$mdomain if $mdomain and $address !~ /@/;
+      # address book alias?
+      if ($to !~ /@/ and ($ab{$to} or $to =~ /(.+?):(.+)/ and $ab{$1})) {
+        my $alias = $to;
+        my @options = ();
+        $alias =~ s/:(.*)// and @options = split(':',$1);
+        if (@options) {
+          # alias with :options
+          $alias =~ s/:.*//;
+          foreach my $address (my @loop = @{$ab{$alias}}) {
+            $to{$address} = $address; # ignore dupes
+            foreach (@options) {
+              $keep{$address} = $1       if /^keep=(\d+)$/i;
+              $autodelete{$address} = $1 if /^autodelete=(yes|no|delay)$/i;
+              $locale{$address} = $1     if /^locale=(\w+)$/i;
+            }
+          }
+        }
+        foreach my $address (my @loop = @{$ab{$alias}}) {
           $to{$address} = $address; # ignore dupes
-          if ($specific{'autodelete'}) {
-            $autodelete{$address} = $specific{'autodelete'};
-          } elsif ($autodelete{$to}) {
-            $autodelete{$address} = $autodelete{$to};
-          } else {
-            $autodelete{$address} = readlink "$address/\@AUTODELETE"
-                                    || $autodelete;
+          unless ($keep{$address}) {
+            $keep{$address} = $keep{$alias} if $keep{$alias};
+            if ($specific{'keep'}) {
+              $keep{$address} = $specific{'keep'}
+            } elsif (my $keep = $alias_keep{$alias}{$address}) {
+              $keep{$address} = $keep;
+            } elsif ($keep{$alias}) {
+              $keep{$address} = $keep{$alias}
+            }
           }
-          if (my $locale = readlink "$address/\@LOCALE") {
-            $locale{$address} = $locale;
-          } elsif ($locale{$to}) {
-            $locale{$address} = $locale{$to};
-          } else {
-            $locale{$address} = $locale ;
+          unless ($autodelete{$address}) {
+            if ($specific{'autodelete'}) {
+              $autodelete{$address} = $specific{'autodelete'};
+            } elsif (my $autodelete = $alias_autodelete{$alias}{$address}) {
+              $autodelete{$address} = $keep;
+            } elsif ($autodelete{$alias}) {
+              $autodelete{$address} = $autodelete{$alias};
+            } else {
+              $autodelete{$address} = readlink "$address/\@AUTODELETE"
+                                      || $autodelete;
+            }
           }
           unless ($locale{$address}) {
-            $locale{$address} = $default_locale || 'english';
+            if (my $locale = readlink "$address/\@LOCALE") {
+              $locale{$address} = $locale;
+            } elsif ($locale{$alias}) {
+              $locale{$address} = $locale{$alias};
+            } elsif ($locale = $alias_locale{$alias}{$address}) {
+              $locale{$address} = $locale;
+            } else {
+              $locale{$address} = $::locale ;
+            }
+            $locale{$address} ||= $default_locale || 'english';
           }
-          if ($specific{'keep'}) { $keep{$address} = $specific{'keep'} }
-          elsif ($keep{$to})     { $keep{$address} = $keep{$to} }
         }
       } else {
+        # regular address, not an alias
+        if ($to =~ s/(.+?):(.+)/$1/) {
+          my @options = split(':',$2);
+          $to = expand($to);
+          foreach (@options) {
+            $keep{$to} = $1       if /^keep=(\d+)$/i;
+            $autodelete{$to} = $1 if /^autodelete=(yes|no|delay)$/i;
+            $locale{$to} = $1     if /^locale=(\w+)$/i;
+          }
+        }
         $to = expand($to);
         $to{$to} = $to; # ignore dupes
         unless ($autodelete{$to}) {
-          $autodelete{$to} = readlink "$to/\@AUTODELETE" || $autodelete;
+          $autodelete{$to} = untaint(readlink("$to/\@AUTODELETE")
+                                     ||$autodelete);
+          if ($specific{'autodelete'}) {
+            $autodelete{$to} = $specific{'autodelete'};
+          }
+        }
+        unless ($keep{$to}) {
+          $keep{$to} = $keep_default;
+          $keep{$to} = $keep                          if $keep;
+          $keep{$to} = untaint(readlink "$to/\@KEEP") if -l "$to/\@KEEP";
+          $keep{$to} = $specific{'keep'}              if $specific{'keep'};
         }
-        $autodelete{$to} = $specific{'autodelete'}  if $specific{'autodelete'};
-        $keep{$to} = $keep_default;
-        $keep{$to} = $keep                          if $keep;
-        $keep{$to} = untaint(readlink "$to/\@KEEP") if -l "$to/\@KEEP";
-        $keep{$to} = $specific{'keep'}              if $specific{'keep'};
-        # recipient specific parameters
-        $keep{$to}       = $1 if $to =~ /:keep=(\d+)/i;
-        $autodelete{$to} = $1 if $to =~ /:autodelete=(\w+)/i;
       }
       $autodelete{$to} = 'NO' if $to =~ /$amdl/; # mailing lists, etc
       if (-e "$to/\@CAPTIVE") {
@@ -2056,7 +2141,7 @@ sub get_file {
 
       if ($from eq "@to") {
         # special "fex yourself"
-        mksymlink("$filed/autodelete",'NO');
+        mksymlink("$filed/autodelete",$specific{'autodelete'}||'NO');
       } else {
         $autodelete{$to} = $autodelete unless $autodelete{$to};
         if ($autodelete{$to} =~ /^(DELAY|NO|\d+)$/i) {
@@ -2320,6 +2405,9 @@ sub check_rr {
             $ar .= '|[^\@]+\@' . $rd;
           }
           $ar .= ')';
+        } elsif (/^\@LOCAL_USERS/ and -s "$to/@") {
+          $allowed = 1;
+          last;
         } else {
           # allow wildcard *, but not regexps
           $ar = quotemeta $_;
@@ -2375,11 +2463,14 @@ sub expand {
 sub forward {
   my $file = shift;
   my ($nfile,$to,$AB);
-  my ($filename);
+  my ($filename,$keep);
   my (%to);
 
   http_die("no file data for <code>$file</code>") unless -f "$file/data";
 
+  $keep = $::keep||$keep_default;
+  if (my $mt = mtime("$file/data")) { $keep += int((time-$mt)/$DS) }
+
   if (@to) {
 
     # check recipients restriction
@@ -2413,11 +2504,11 @@ sub forward {
       }
     }
 
+    @to = keys %to;
+
     http_header('200 OK');
     print html_header($head);
 
-    @to = keys %to;
-
     foreach my $to (my @loop = @to) {
       $to =~ s/:\w+=.*//; # remove options from address
       $nfile = $file;
@@ -2444,15 +2535,15 @@ sub forward {
         close $comment;
       }
       if ($autodelete =~ /^(DELAY|NO|\d+)$/i) {
-        symlink($autodelete,"$nfile/autodelete");
-      }
-      symlink($keep||$keep_default,         "$nfile/keep");
-                    copy("$file/id",        "$nfile/id");
-                    copy("$file/ip",        "$nfile/ip");
-                    copy("$file/speed",     "$nfile/speed");
-                    copy("$file/replyto",   "$nfile/replyto");
-      $filename   = copy("$file/filename",  "$nfile/filename");
-      link               "$file/data",      "$nfile/data"
+        symlink $autodelete,"$nfile/autodelete";
+      }
+      symlink          $keep,             "$nfile/keep";
+                  copy("$file/id",        "$nfile/id");
+                  copy("$file/ip",        "$nfile/ip");
+                  copy("$file/speed",     "$nfile/speed");
+                  copy("$file/replyto",   "$nfile/replyto");
+      $filename = copy("$file/filename",  "$nfile/filename");
+      link             "$file/data",      "$nfile/data"
         or die http_die("cannot create $nfile/data - $!");
       unless ($dkey = readlink("$nfile/dkey") and -l "$dkeydir/$dkey") {
         $dkey = randstring(8);
@@ -2593,15 +2684,15 @@ sub setparam {
     $locale = $1;
   } elsif ($v eq 'REDIRECT' and $vv =~ /^([\w?=]+)$/) {
     $redirect = $1;
-  } elsif (($v eq 'KEY' or $v eq 'SKEY') and $vv =~ /^([\w:]+)$/) {
+  } elsif ($v eq 'SKEY' and $vv =~ /^([\w:]+)/) {
     $skey = $1;
     $restricted = $v;
-  } elsif ($v eq 'GKEY' and $vv =~ /^([\w:]+)$/) {
+  } elsif ($v eq 'GKEY' and $vv =~ /^([\w:]+)/) {
     $gkey = $1 unless $nomail;
     $restricted = $v;
-  } elsif ($v eq 'DKEY' and $vv =~ /^(\w+)$/) {
+  } elsif ($v eq 'DKEY' and $vv =~ /^(\w+)/) {
     $dkey = $1;
-  } elsif ($v eq 'AKEY' and $vv =~ /^(\w+)$/) {
+  } elsif ($v eq 'AKEY' and $vv =~ /^(\w+)/) {
     $akey = $1;
   } elsif ($v eq 'FROM' or $v eq 'USER') {
     $from = normalize_email($vv);
@@ -2622,6 +2713,7 @@ sub setparam {
   } elsif ($v eq 'FEXYOURSELF') {
     $submit = $vv;
     @to = ($from);
+    $specific{'autodelete'} = $autodelete = 'no';
   } elsif ($v eq 'TO') {
     # extract AUTODELETE and KEEP options
     if ($vv =~ s/[\s,]+AUTODELETE=(\w+)//i) {
@@ -2637,9 +2729,15 @@ sub setparam {
     if ($from) {
       if ($to eq '.') {
         $to = $from;
+         unless ($specific{'autodelete'}) {
+           $specific{'autodelete'} = $autodelete = 'no';
+         }
       }
       if ($to eq '//') {
         $to = $from;
+        unless ($specific{'autodelete'}) {
+          $specific{'autodelete'} = $autodelete = 'no';
+        }
         $comment = '//';
       }
     }
@@ -2693,7 +2791,7 @@ sub setparam {
     $keep = $keep_max if $keep_max and $keep > $keep_max;
     $specific{'keep'} = $keep;
   } elsif ($v eq 'TIMEOUT' and $vv =~ /^(\d+)$/) {
-    $specific{'timeout'} = $timeout = $1;
+     $specific{'timeout'} = $timeout = $1;
   }
 }
 
@@ -2799,6 +2897,11 @@ sub mail_forgotten {
 # lookup akey, skey and gkey (full and sub user and group)
 sub check_keys {
 
+  if (@to and "@to" ne '_') {
+    http_die("you cannot mix TO and SKEY URL parameters") if $skey;
+    http_die("you cannot mix TO and GKEY URL parameters") if $gkey;
+  }
+
   # only one key can be valid
   $akey = $gkey = '' if $skey;
   $akey = $skey = '' if $gkey;