Email Files to Yourself (Drop Folder → Send)

February 20, 2026 NEW

A tiny email files to myself tool you can host on your own site. Upload a file, add an optional note, and it emails you either an attachment (small files) or a download link (bigger files).

Why it’s useful: You’re on a device where logging into cloud storage is annoying, blocked, or you just want a dead-simple “send to me” button. This gives you a private dropbox you control.

What it does:

  • Upload a file from any browser.
  • Emails you:
    • Attachment for small files (configurable limit), or
    • Secure download link for larger files.
  • No database — stores uploads in a folder.
  • Optional basic-auth gate (simple protection).

Install:

  1. Create: /tools/send-to-me/
  2. Create writable folder: /tools/send-to-me/_up/
  3. Save the script below as: /tools/send-to-me/index.php
  4. Edit $TO_EMAIL and the password near the top.
  5. Visit: /tools/send-to-me/
<?php
declare(strict_types=1);

/**
 * Email Files to Yourself — One File (No DB)
 * /tools/send-to-me/index.php
 *
 * Upload a file and email yourself an attachment (small) or a download link (large).
 *
 * NOTE:
 * - This uses PHP mail(). If your host blocks mail(), use SMTP via PHPMailer instead.
 * - Keep this private (basic auth + random folder path recommended).
 */

header('X-Content-Type-Options: nosniff');
header('Referrer-Policy: strict-origin-when-cross-origin');

// ----------------- CONFIG -----------------
$TO_EMAIL   = 'you@example.com';   // <-- change
$FROM_EMAIL = 'noreply@yourdomain.com'; // <-- change to a domain you control
$TO_NAME    = 'Me';
$SUBJECT    = 'File drop';

// Attachment threshold (bytes). Above this, send a link instead.
$ATTACH_MAX = 2_500_000; // ~2.5MB

// Upload rules
$UPLOAD_DIR = __DIR__ . '/_up';
$MAX_BYTES  = 25_000_000; // 25MB
$ALLOWED_EXT = ['jpg','jpeg','png','gif','webp','pdf','txt','zip','mp4','mov','webm','csv','json','doc','docx'];

// Link settings (for large files)
$LINK_TTL_HOURS = 72; // expires in 3 days
$SECRET_SALT    = 'CHANGE_THIS_LONG_RANDOM_STRING'; // <-- change
$BASE_URL       = ''; // leave blank to auto-detect

// Optional basic auth (recommended)
$AUTH_USER = 'admin';
$AUTH_PASS = 'CHANGE_ME_PASS';
// ----------------- /CONFIG -----------------

function h(string $s): string { return htmlspecialchars($s, ENT_QUOTES, 'UTF-8'); }

function is_https(): bool {
  if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') return true;
  if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && stripos((string)$_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0) return true;
  return false;
}

function detect_base_url(): string {
  $proto = is_https() ? 'https://' : 'http://';
  $host  = $_SERVER['HTTP_HOST'] ?? '';
  $path  = $_SERVER['SCRIPT_NAME'] ?? '';
  return $proto . $host . $path;
}

function ensure_dir(string $dir): bool {
  return is_dir($dir) || @mkdir($dir, 0755, true);
}

function slug_name(string $s): string {
  $s = trim($s);
  $s = preg_replace('/[^\w\.\-]+/u', '_', $s) ?? $s;
  $s = preg_replace('/_+/', '_', $s) ?? $s;
  return trim($s, '_');
}

function sign_token(string $id, int $exp, string $salt): string {
  return hash_hmac('sha256', $id . '|' . $exp, $salt);
}

function build_link(string $id, int $exp, string $sig): string {
  $base = $GLOBALS['BASE_URL'];
  if ($base === '') $base = detect_base_url();
  return $base . '?dl=1&id=' . rawurlencode($id) . '&exp=' . $exp . '&sig=' . rawurlencode($sig);
}

function send_mail_plain(string $to, string $subject, string $body, string $fromEmail, string $fromName): bool {
  $headers = [];
  $headers[] = 'From: ' . $fromName . ' <' . $fromEmail . '>';
  $headers[] = 'MIME-Version: 1.0';
  $headers[] = 'Content-Type: text/plain; charset=UTF-8';
  return @mail($to, $subject, $body, implode("\r\n", $headers));
}

function send_mail_with_attachment(string $to, string $subject, string $body, string $fromEmail, string $fromName, string $filePath, string $fileName): bool {
  $boundary = 'b_' . bin2hex(random_bytes(12));
  $headers = [];
  $headers[] = 'From: ' . $fromName . ' <' . $fromEmail . '>';
  $headers[] = 'MIME-Version: 1.0';
  $headers[] = 'Content-Type: multipart/mixed; boundary="'.$boundary.'"';

  $msg  = "--{$boundary}\r\n";
  $msg .= "Content-Type: text/plain; charset=UTF-8\r\n\r\n";
  $msg .= $body . "\r\n\r\n";

  $bin = @file_get_contents($filePath);
  if ($bin === false) return false;

  $b64 = chunk_split(base64_encode($bin));

  $msg .= "--{$boundary}\r\n";
  $msg .= "Content-Type: application/octet-stream; name=\"".addslashes($fileName)."\"\r\n";
  $msg .= "Content-Transfer-Encoding: base64\r\n";
  $msg .= "Content-Disposition: attachment; filename=\"".addslashes($fileName)."\"\r\n\r\n";
  $msg .= $b64 . "\r\n";
  $msg .= "--{$boundary}--\r\n";

  return @mail($to, $subject, $msg, implode("\r\n", $headers));
}

// -------- Basic auth gate --------
if ($AUTH_USER !== '' && $AUTH_PASS !== '') {
  $u = $_SERVER['PHP_AUTH_USER'] ?? '';
  $p = $_SERVER['PHP_AUTH_PW'] ?? '';
  if ($u !== $AUTH_USER || $p !== $AUTH_PASS) {
    header('WWW-Authenticate: Basic realm="Send To Me"');
    http_response_code(401);
    echo "Auth required.";
    exit;
  }
}

ensure_dir($UPLOAD_DIR);

// -------- Download handler (for link emails) --------
if (isset($_GET['dl'], $_GET['id'], $_GET['exp'], $_GET['sig'])) {
  $id  = (string)$_GET['id'];
  $exp = (int)$_GET['exp'];
  $sig = (string)$_GET['sig'];

  if ($exp < time()) { http_response_code(410); echo "Link expired."; exit; }

  $good = sign_token($id, $exp, $SECRET_SALT);
  if (!hash_equals($good, $sig)) { http_response_code(403); echo "Bad signature."; exit; }

  $metaFile = $UPLOAD_DIR . '/' . $id . '.json';
  $meta = is_file($metaFile) ? json_decode((string)file_get_contents($metaFile), true) : null;
  if (!is_array($meta)) { http_response_code(404); echo "Not found."; exit; }

  $path = $UPLOAD_DIR . '/' . ($meta['stored'] ?? '');
  if (!is_file($path)) { http_response_code(404); echo "Missing file."; exit; }

  $orig = (string)($meta['name'] ?? 'download.bin');
  header('Content-Type: application/octet-stream');
  header('Content-Disposition: attachment; filename="'.str_replace('"','', $orig).'"');
  header('Content-Length: ' . (string)filesize($path));
  readfile($path);
  exit;
}

// -------- Upload handler --------
$okMsg = '';
$errMsg = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  $note = trim((string)($_POST['note'] ?? ''));
  $f = $_FILES['file'] ?? null;

  if (!is_array($f) || (int)($f['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
    $errMsg = 'Upload failed.';
  } else {
    $size = (int)($f['size'] ?? 0);
    if ($size <= 0 || $size > $MAX_BYTES) $errMsg = 'File too large (max ' . (int)round($MAX_BYTES/1024/1024) . 'MB).';

    $orig = (string)($f['name'] ?? 'file');
    $ext = strtolower(pathinfo($orig, PATHINFO_EXTENSION));
    if ($errMsg === '' && $ext !== '' && !in_array($ext, $ALLOWED_EXT, true)) $errMsg = 'File type not allowed.';

    if ($errMsg === '') {
      $id = gmdate('Ymd_His') . '_' . bin2hex(random_bytes(6));
      $safeOrig = slug_name($orig);
      if ($safeOrig === '') $safeOrig = 'file.' . ($ext ?: 'bin');

      $stored = $id . '_' . $safeOrig;
      $dest = $UPLOAD_DIR . '/' . $stored;

      if (!@move_uploaded_file((string)$f['tmp_name'], $dest)) {
        $errMsg = 'Could not save file (permissions?).';
      } else {
        // Save meta for link downloads
        $meta = [
          'id' => $id,
          'name' => $orig,
          'stored' => $stored,
          'size' => $size,
          'note' => $note,
          'createdAt' => gmdate('c'),
        ];
        @file_put_contents($UPLOAD_DIR . '/' . $id . '.json', json_encode($meta, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES), LOCK_EX);

        $body = "New file drop\n\n";
        $body .= "File: {$orig}\n";
        $body .= "Size: " . round($size/1024, 1) . " KB\n";
        if ($note !== '') $body .= "\nNote:\n{$note}\n";

        $sent = false;

        if ($size <= $ATTACH_MAX) {
          $sent = send_mail_with_attachment($TO_EMAIL, $SUBJECT, $body, $FROM_EMAIL, $TO_NAME, $dest, $orig);
          if (!$sent) {
            // fallback to link
            $exp = time() + ($LINK_TTL_HOURS * 3600);
            $sig = sign_token($id, $exp, $SECRET_SALT);
            $link = build_link($id, $exp, $sig);
            $body .= "\n\n(Attachment failed; sending link instead)\n";
            $body .= "Download link (expires in {$LINK_TTL_HOURS}h):\n{$link}\n";
            $sent = send_mail_plain($TO_EMAIL, $SUBJECT, $body, $FROM_EMAIL, $TO_NAME);
          }
        } else {
          $exp = time() + ($LINK_TTL_HOURS * 3600);
          $sig = sign_token($id, $exp, $SECRET_SALT);
          $link = build_link($id, $exp, $sig);
          $body .= "\n\nDownload link (expires in {$LINK_TTL_HOURS}h):\n{$link}\n";
          $sent = send_mail_plain($TO_EMAIL, $SUBJECT, $body, $FROM_EMAIL, $TO_NAME);
        }

        if ($sent) $okMsg = 'Sent! Check your inbox.';
        else $errMsg = 'Mail failed. Your host may block PHP mail().';
      }
    }
  }
}

?><!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Email Files to Yourself</title>
<meta name="description" content="Upload a file and email it to yourself as an attachment (small) or a secure download link (large). Lightweight, no database." />
<meta name="robots" content="index,follow" />
<style>
  :root{
    --bg:#0b0f16; --panel:#111a26; --panel2:#0c1420;
    --text:#e8eef8; --muted:rgba(232,238,248,.72);
    --line:rgba(255,255,255,.10); --accent:#2a8cff;
    --shadow:0 18px 55px rgba(0,0,0,.45); --r:16px;
    --ok:#7CFF9A; --bad:#FF7C7C;
  }
  *{box-sizing:border-box}
  body{margin:0;background:var(--bg);color:var(--text);font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif}
  .wrap{max-width:760px;margin:0 auto;padding:18px}
  .card{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);padding:14px;margin:12px 0;box-shadow:var(--shadow)}
  .pill{display:inline-block;padding:4px 10px;border-radius:999px;background:rgba(42,140,255,.14);border:1px solid rgba(42,140,255,.25);font-weight:900;font-size:12px}
  .muted{color:var(--muted)}
  input,textarea{
    width:100%;padding:10px 12px;border-radius:12px;border:1px solid var(--line);
    background:var(--panel2);color:var(--text);outline:none;
    font:700 13px/1.2 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
  }
  textarea{min-height:120px;resize:vertical;font-weight:650;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;line-height:1.45;border-radius:14px}
  .btn{
    display:inline-flex;align-items:center;justify-content:center;gap:8px;
    border:0;border-radius:12px;padding:10px 12px;background:var(--accent);
    color:#06101a;font-weight:950;cursor:pointer;
  }
  .ok{color:var(--ok);font-weight:950}
  .bad{color:var(--bad);font-weight:950}
  .hr{height:1px;background:var(--line);margin:12px 0}
  .small{font-size:12px}
</style>
</head>
<body>
<div class="wrap">

  <div class="card">
    <div class="pill">Utility</div>
    <h1 style="margin:10px 0 6px;font-size:22px">Email Files to Yourself</h1>
    <div class="muted">Upload a file → get it emailed as an attachment (small) or a secure link (large).</div>

    <?php if ($okMsg): ?><div class="hr"></div><div class="ok"><?php echo h($okMsg); ?></div><?php endif; ?>
    <?php if ($errMsg): ?><div class="hr"></div><div class="bad"><?php echo h($errMsg); ?></div><?php endif; ?>
  </div>

  <div class="card">
    <form method="post" enctype="multipart/form-data" style="display:grid;gap:10px">
      <div>
        <div class="muted small" style="margin-bottom:6px">Choose a file</div>
        <input type="file" name="file" required />
        <div class="muted small" style="margin-top:6px">
          Max upload: <b><?php echo (int)round($MAX_BYTES/1024/1024); ?>MB</b> •
          Attach up to: <b><?php echo (int)round($ATTACH_MAX/1024/1024); ?>MB</b>
        </div>
      </div>

      <div>
        <div class="muted small" style="margin-bottom:6px">Optional note</div>
        <textarea name="note" placeholder="Anything you want to remember about this file..."></textarea>
      </div>

      <button class="btn" type="submit">📨 Send to me</button>
    </form>
  </div>

  <div class="card">
    <b>Security tips</b>
    <div class="muted small" style="margin-top:8px;line-height:1.55">
      <ul style="margin:0;padding-left:18px">
        <li>Keep it behind Basic Auth (already included).</li>
        <li>Change <span class="muted">ADMIN</span> credentials + <code>$SECRET_SALT</code>.</li>
        <li>Consider placing this tool in a non-obvious path (<code>/tools/_drop_9f3a/</code>).</li>
        <li>Limit allowed file types if you only need a few.</li>
      </ul>
    </div>
  </div>

</div>
</body>
</html>

Comments (0)

No comments yet — be the first.

← Back to all scripts