chiark / gitweb /
Online registration now sends email. The confirmation URL doesn't
authorrjk@greenend.org.uk <>
Sat, 29 Dec 2007 18:54:16 +0000 (18:54 +0000)
committerrjk@greenend.org.uk <>
Sat, 29 Dec 2007 18:54:16 +0000 (18:54 +0000)
work yet.  The mail comes from mail_sender which MUST be configured.

sendmail() sends mail via the SMTP server specified in the config
file.  The default is localhost.  inputline() is extended to cope with
CRLF-flavored protocols.

local_hostname() returns the FQDN of the local host (assuming you
didn't screw up your /etc/hosts).

mime_encode_text() and mime_to_qp() prepare text/* messages for
sending in mail.

14 files changed:
lib/Makefile.am
lib/configuration.c
lib/configuration.h
lib/hostname.c [new file with mode: 0644]
lib/hostname.h [new file with mode: 0644]
lib/inputline.c
lib/inputline.h
lib/mime.c
lib/mime.h
lib/sendmail.c [new file with mode: 0644]
lib/sendmail.h [new file with mode: 0644]
lib/test.c
server/dcgi.c
templates/login.html

index 9b8493736d8c7b9b8332974fb8d1c84ad0c20a86..d658f862adfe3401ff9a554c147cf5dc7bfa737f 100644 (file)
@@ -40,6 +40,7 @@ libdisorder_a_SOURCES=charset.c charset.h             \
        hash.c hash.h                                   \
        heap.h                                          \
        hex.c hex.h                                     \
+       hostname.c hostname.h                           \
        ifreq.c ifreq.h                                 \
        inputline.c inputline.h                         \
        kvp.c kvp.h                                     \
@@ -56,6 +57,7 @@ libdisorder_a_SOURCES=charset.c charset.h             \
        rights.c queue-rights.c rights.h                \
        rtp.h                                           \
        selection.c selection.h                         \
+       sendmail.c sendmail.h                           \
        signame.c signame.h                             \
        sink.c sink.h                                   \
        speaker-protocol.c speaker-protocol.h           \
index bb30c2f33f0aaf2c20d87aa61b314bf9c36a6a8a..e5dd120757ce536761c2dd0defe5a3f2d39d50c1 100644 (file)
@@ -928,6 +928,7 @@ static const struct conf conf[] = {
   { C(home),             &type_string,           validate_isabspath },
   { C(listen),           &type_stringlist,       validate_port },
   { C(lock),             &type_boolean,          validate_any },
+  { C(mail_sender),      &type_string,           validate_any },
   { C(mixer),            &type_string,           validate_ischr },
   { C(multicast_loop),   &type_boolean,          validate_any },
   { C(multicast_ttl),    &type_integer,          validate_non_negative },
@@ -948,6 +949,7 @@ static const struct conf conf[] = {
   { C(scratch),          &type_string_accum,     validate_isreg },
   { C(short_display),    &type_integer,          validate_positive },
   { C(signal),           &type_signal,           validate_any },
+  { C(smtp_server),      &type_string,           validate_any },
   { C(sox_generation),   &type_integer,          validate_non_negative },
   { C(speaker_backend),  &type_backend,          validate_any },
   { C(speaker_command),  &type_string,           validate_any },
@@ -1147,6 +1149,7 @@ static struct config *config_default(void) {
   c->dbversion = 2;
   c->cookie_login_lifetime = 86400;
   c->cookie_key_lifetime = 86400 * 7;
+  c->smtp_server = xstrdup("127.0.0.1");
   if(config_set(&cs, (int)NDEFAULT_STOPWORDS, (char **)default_stopwords))
     exit(1);
   return c;
index abb95a6b6f6abb325cd42f2be97ffc8c26d40c4c..d212a7ac80d017ba350561f2c614416875c011c4 100644 (file)
@@ -255,6 +255,12 @@ struct config {
 
   /** @brief Default rights for a new user */
   char *default_rights;
+
+  /** @brief SMTP server for sending mail */
+  char *smtp_server;
+
+  /** @brief Origin address for outbound mail */
+  char *mail_sender;
   
   /* derived values: */
   int nparts;                          /* number of distinct name parts */
diff --git a/lib/hostname.c b/lib/hostname.c
new file mode 100644 (file)
index 0000000..536bb8e
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2007 Richard Kettlewell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <config.h>
+#include "types.h"
+
+#include <sys/utsname.h>
+#include <errno.h>
+#include <netdb.h>
+
+#include "mem.h"
+#include "hostname.h"
+#include "log.h"
+
+static const char *hostname;
+
+/** @brief Return the local hostname
+ * @return Hostname
+ */
+const char *local_hostname(void) {
+  if(!hostname) {
+    struct utsname u;
+    struct hostent *he;
+
+    if(uname(&u) < 0)
+      fatal(errno, "error calling uname");
+    if(!(he = gethostbyname(u.nodename)))
+      fatal(0, "cannot resolve '%s'", u.nodename);
+    hostname = xstrdup(he->h_name);
+  }
+  return hostname;
+}
+
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/lib/hostname.h b/lib/hostname.h
new file mode 100644 (file)
index 0000000..89bfeaa
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2007 Richard Kettlewell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef HOSTNAME_H
+#define HOSTNAME_H
+
+const char *local_hostname(void);
+
+#endif /* HOSTNAME_H */
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index 3202effe575923f4c758ee93a993087d7e5f5482..855910f2a8f11e7afd74b18349feaae06d6493e6 100644 (file)
  * @param tag Used in error messages
  * @param fp Stream to read from
  * @param lp Where to store newly allocated string
- * @param newline Newline character
+ * @param newline Newline character or @ref CRLF
  * @return 0 on success, -1 on error or eof.
  *
  * The newline is not included in the string.  If the last line of a
  * stream does not have a newline then that line is still returned.
  *
+ * If @p newline is @ref CRLF then the line is terminated by CR LF,
+ * not by a single newline character.  The CRLF is still not included
+ * in the string in this case.
+ *
  * @p *lp is only set if the return value was 0.
  */
 int inputline(const char *tag, FILE *fp, char **lp, int newline) {
@@ -52,8 +56,14 @@ int inputline(const char *tag, FILE *fp, char **lp, int newline) {
 
   dynstr_init(&d);
   while((ch = getc(fp)),
-       (!ferror(fp) && !feof(fp) && ch != newline))
+       (!ferror(fp) && !feof(fp) && ch != newline)) {
     dynstr_append(&d, ch);
+    if(newline == CRLF && d.nvec >= 2
+       && d.vec[d.nvec - 2] == 0x0D && d.vec[d.nvec - 1] == 0x0A) {
+      d.nvec -= 2;
+      break;
+    }
+  }
   if(ferror(fp)) {
     error(errno, "error reading %s", tag);
     return -1;
index 57d397599438554bf46a4e5d75c4d5b7b7f00579..937b7671510bb17ca7106337689099296a154c60 100644 (file)
@@ -26,6 +26,9 @@ int inputline(const char *tag, FILE *fp, char **lp, int newline);
  * them (excluding @newline@) via @lp@.  Return 0 on success, -1 on
  * error/eof. */
 
+/** @brief Magic @p newline value to make inputline() insist on CRLF */
+#define CRLF 0x100
+
 #endif /* INPUTLINE_H */
 
 /*
index 9883a3c5d41e95d086f1774028a3a1b301a2decb..422e1f5a419c90efb303b4476e10f68c40547eb4 100644 (file)
@@ -566,6 +566,97 @@ char *quote822(const char *s, int force) {
   return d->vec;
 }
 
+/** @brief Return true if @p ptr points at trailing space */
+static int is_trailing_space(const char *ptr) {
+  if(*ptr == ' ' || *ptr == '\t') {
+    while(*ptr == ' ' || *ptr == '\t')
+      ++ptr;
+    return *ptr == '\n' || *ptr == 0;
+  } else
+    return 0;
+}
+
+/** @brief Encoding text as quoted-printable
+ * @param text String to encode
+ * @return Encoded string
+ *
+ * See <a href="http://tools.ietf.org/html/rfc2045#section-6.7">RFC2045
+ * s6.7</a>.
+ */
+char *mime_to_qp(const char *text) {
+  struct dynstr d[1];
+  int linelength = 0;                  /* length of current line */
+  char buffer[10];
+
+  dynstr_init(d);
+  /* The rules are:
+   * 1. Anything except newline can be replaced with =%02X
+   * 2. Newline, 33-60 and 62-126 stand for themselves (i.e. not '=')
+   * 3. Non-trailing space/tab stand for themselves.
+   * 4. Output lines are limited to 76 chars, with =<newline> being used
+   *    as a soft line break
+   * 5. Newlines aren't counted towards the 76 char limit.
+   */
+  while(*text) {
+    const int c = (unsigned char)*text;
+    if(c == '\n') {
+      /* Newline stands as itself */
+      dynstr_append(d, '\n');
+      linelength = 0;
+    } else if((c >= 33 && c <= 126 && c != '=')
+             || ((c == ' ' || c == '\t')
+                 && !is_trailing_space(text))) {
+      /* Things that can stand for themselves  */
+      dynstr_append(d, c);
+      ++linelength;
+    } else {
+      /* Anything else that needs encoding */
+      snprintf(buffer, sizeof buffer, "=%02X", c);
+      dynstr_append_string(d, buffer);
+      linelength += 3;
+    }
+    ++text;
+    if(linelength > 73 && *text && *text != '\n') {
+      /* Next character might overflow 76 character limit if encoded, so we
+       * insert a soft break */
+      dynstr_append_string(d, "=\n");
+      linelength = 0;
+    }
+  }
+  /* Ensure there is a final newline */
+  if(linelength)
+    dynstr_append(d, '\n');
+  /* That's all */
+  dynstr_terminate(d);
+  return d->vec;
+}
+
+/** @brief Encode text
+ * @param text Underlying UTF-8 text
+ * @param charsetp Where to store charset string
+ * @param encodingp Where to store encoding string
+ * @return Encoded text (might be @ref text)
+ */
+const char *mime_encode_text(const char *text,
+                            const char **charsetp,
+                            const char **encodingp) {
+  const char *ptr;
+
+  /* See if there are in fact any non-ASCII characters */
+  for(ptr = text; *ptr; ++ptr)
+    if((unsigned char)*ptr >= 128)
+      break;
+  if(!*ptr) {
+    /* Plain old ASCII, no encoding required */
+    *charsetp = "us-ascii";
+    *encodingp = "7bit";
+    return text;
+  }
+  *charsetp = "utf-8";
+  *encodingp = "quoted-printable";
+  return mime_to_qp(text);
+}
+
 /*
 Local Variables:
 c-basic-offset:2
index 0fa8aeabf46a71baf0da9ca62b4a835633fa6bf2..b863f4abb6695f8d9cdeaed4ec3ec74edcea3533 100644 (file)
@@ -95,6 +95,10 @@ int parse_cookie(const char *s,
 const struct cookie *find_cookie(const struct cookiedata *cd,
                                 const char *name);
 char *quote822(const char *s, int force);
+char *mime_to_qp(const char *text);
+const char *mime_encode_text(const char *text,
+                            const char **charsetp,
+                            const char **encodingp);
 
 #endif /* MIME_H */
 
diff --git a/lib/sendmail.c b/lib/sendmail.c
new file mode 100644 (file)
index 0000000..3ecfc40
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2007 Richard Kettlewell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <config.h>
+#include "types.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <gcrypt.h>
+
+#include "syscalls.h"
+#include "log.h"
+#include "configuration.h"
+#include "inputline.h"
+#include "addr.h"
+#include "hostname.h"
+#include "sendmail.h"
+#include "base64.h"
+
+/** @brief Get a server response
+ * @param tag Server name
+ * @param in Input stream
+ * @return Response code 0-999 or -1 on error
+ */
+static int getresponse(const char *tag, FILE *in) {
+  char *line;
+
+  while(!inputline(tag, in, &line, CRLF)) {
+    if(line[0] >= '0' && line[0] <= '9'
+       && line[1] >= '0' && line[1] <= '9'
+       && line[2] >= '0' && line[2] <= '9') {
+      const int rc = 10 * (10 * line[0] + line[1]) + line[2] - 111 * '0';
+      if(rc >= 400 && rc <= 599)
+       error(0, "%s: %s", tag, line);
+      if(line[3] != '-') {
+       return rc;
+      }
+      /* else go round for further response lines */
+    } else {
+      error(0, "%s: malformed response: %s", tag, line);
+      return -1;
+    }
+  }
+  if(ferror(in))
+    error(errno, "%s: read error", tag);
+  else
+    error(0, "%s: server closed connection", tag);
+  return -1;
+}
+
+/** @brief Send a command to the server
+ * @param tag Server name
+ * @param out stream to send commands to
+ * @param fmt Format string
+ * @return 0 on success, non-0 on error
+ */
+static int sendcommand(const char *tag, FILE *out, const char *fmt, ...) {
+  va_list ap;
+  int rc;
+
+  va_start(ap, fmt);
+  rc = vfprintf(out, fmt, ap);
+  va_end(ap);
+  if(rc >= 0)
+    rc = fputs("\r\n", out);
+  if(rc >= 0)
+    rc = fflush(out);
+  if(rc >= 0)
+    return 0;
+  error(errno, "%s: write error", tag);
+  return -1;
+}
+
+/** @brief Send a mail message
+ * @param tag Server name
+ * @param in Stream to read responses from
+ * @param out Stream to send commands to
+ * @param sender Sender address (can be "")
+ * @param pubsender Visible sender address (must not be "")
+ * @param recipient Recipient address
+ * @param subject Subject string
+ * @param encoding Body encoding
+ * @param body_type Content-type of body
+ * @param body Text of body (encoded, but \n for newline)
+ * @return 0 on success, non-0 on error
+ */
+static int sendmailfp(const char *tag, FILE *in, FILE *out,
+                     const char *sender,
+                     const char *pubsender,
+                     const char *recipient,
+                     const char *subject,
+                     const char *encoding,
+                     const char *content_type,
+                     const char *body) {
+  int rc, sol = 1;
+  const char *ptr;
+  uint8_t idbuf[20];
+  char *id;
+
+  gcry_create_nonce(idbuf, sizeof idbuf);
+  id = mime_to_base64(idbuf, sizeof idbuf);
+  if((rc = getresponse(tag, in)) / 100 != 2)
+    return -1;
+  if(sendcommand(tag, out, "HELO %s", local_hostname()))
+    return -1;
+  if((rc = getresponse(tag, in)) / 100 != 2)
+    return -1;
+  if(sendcommand(tag, out, "MAIL FROM:<%s>", sender))
+    return -1;
+  if((rc = getresponse(tag, in)) / 100 != 2)
+    return -1;
+  if(sendcommand(tag, out, "RCPT TO:<%s>", recipient))
+    return -1;
+  if((rc = getresponse(tag, in)) / 100 != 2)
+    return -1;
+  if(sendcommand(tag, out, "DATA", sender))
+    return -1;
+  if((rc = getresponse(tag, in)) / 100 != 3)
+    return -1;
+  if(fprintf(out, "From: %s\r\n", pubsender) < 0
+     || fprintf(out, "To: %s\r\n", recipient) < 0
+     || fprintf(out, "Subject: %s\r\n", subject) < 0
+     || fprintf(out, "Message-ID: <%s@%s>\r\n", id, local_hostname()) < 0
+     || fprintf(out, "MIME-Version: 1.0\r\n") < 0
+     || fprintf(out, "Content-Type: %s\r\n", content_type) < 0
+     || fprintf(out, "Content-Transfer-Encoding: %s\r\n", encoding) < 0
+     || fprintf(out, "\r\n") < 0) {
+  write_error:
+    error(errno, "%s: write error", tag);
+    return -1;
+  }
+  for(ptr = body; *ptr; ++ptr) {
+    if(sol && *ptr == '.')
+      if(fputc('.', out) < 0)
+       goto write_error;
+    if(*ptr == '\n') {      
+      if(fputc('\r', out) < 0)
+       goto write_error;
+      sol = 1;
+    } else
+      sol = 0;
+    if(fputc(*ptr, out) < 0)
+      goto write_error;
+  }
+  if(!sol)
+    if(fputs("\r\n", out) < 0)
+      goto write_error;
+  if(fprintf(out, ".\r\n") < 0
+     || fflush(out) < 0)
+    goto write_error;
+  if((rc = getresponse(tag, in)) / 100 != 2)
+    return -1;
+  return 0;
+}
+
+/** @brief Send a mail message
+ * @param sender Sender address (can be "")
+ * @param pubsender Visible sender address (must not be "")
+ * @param recipient Recipient address
+ * @param subject Subject string
+ * @param encoding Body encoding
+ * @param content_type Content-type of body
+ * @param body Text of body (encoded, but \n for newline)
+ * @return 0 on success, non-0 on error
+ *
+ * See mime_encode_text() for encoding of text bodies.
+ */
+int sendmail(const char *sender,
+            const char *pubsender,
+            const char *recipient,
+            const char *subject,
+            const char *encoding,
+            const char *content_type,
+            const char *body) {
+  struct stringlist a;
+  char *s[2];
+  struct addrinfo *ai;
+  char *tag;
+  int fdin, fdout, rc;
+  FILE *in, *out;
+   
+  static const struct addrinfo pref = {
+    0,
+    PF_INET,
+    SOCK_STREAM,
+    IPPROTO_TCP,
+    0,
+    0,
+    0,
+    0
+  };
+
+  /* Find the SMTP server */
+  a.n = 2;
+  a.s = s;
+  s[0] = config->smtp_server;
+  s[1] = (char *)"smtp";
+  if(!(ai = get_address(&a, &pref, &tag)))
+    return -1;
+  fdin = xsocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+  if(connect(fdin, ai->ai_addr, ai->ai_addrlen) < 0) {
+    error(errno, "error connecting to %s", tag);
+    xclose(fdin);
+    return -1;
+  }
+  if((fdout = dup(fdin)) < 0)
+    fatal(errno, "error calling dup2");
+  if(!(in = fdopen(fdin, "rb")))
+    fatal(errno, "error calling fdopen");
+  if(!(out = fdopen(fdout, "wb")))
+    fatal(errno, "error calling fdopen");
+  rc = sendmailfp(tag, in, out, sender, pubsender, recipient, subject,
+                 encoding, content_type, body);
+  fclose(in);
+  fclose(out);
+  return rc;
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/lib/sendmail.h b/lib/sendmail.h
new file mode 100644 (file)
index 0000000..7849ef6
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2007 Richard Kettlewell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef SENDMAIL_H
+#define SENDMAIL_H
+
+int sendmail(const char *sender,
+            const char *pubsender,
+            const char *recipient,
+            const char *subject,
+            const char *encoding,
+            const char *content_type,
+            const char *body);
+
+#endif /* SENDMAIL_H */
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index 61b63fd4a206ecd40423f49ac8bfaeec5fb49dd3..27651ec1cedf84e9b6db03c779f4209e87f21b11 100644 (file)
@@ -454,6 +454,16 @@ static void test_mime(void) {
   check_string(mime_qp("x =\r\ny"), "x y");
   check_string(mime_qp("x = \r\ny"), "x y");
 
+  check_string(mime_to_qp(""), "");
+  check_string(mime_to_qp("foobar\n"), "foobar\n");
+  check_string(mime_to_qp("foobar \n"), "foobar=20\n");
+  check_string(mime_to_qp("foobar\t\n"), "foobar=09\n"); 
+  check_string(mime_to_qp("foobar \t \n"), "foobar=20=09=20\n");
+  check_string(mime_to_qp(" foo=bar"), " foo=3Dbar\n");
+  check_string(mime_to_qp("copyright \xC2\xA9"), "copyright =C2=A9\n");
+  check_string(mime_to_qp("foo\nbar\nbaz\n"), "foo\nbar\nbaz\n");
+  check_string(mime_to_qp("wibble wobble wibble wobble wibble wobble wibble wobble wibble wobble wibble"), "wibble wobble wibble wobble wibble wobble wibble wobble wibble wobble wibb=\nle\n");
   /* from RFC2045 */
   check_string(mime_qp("Now's the time =\r\n"
 "for all folk to come=\r\n"
index a4f774a3cb555bdd76dc0502894b4cae4c36b616..d8783305c1563b754e3387780abe2e897d99872c 100644 (file)
@@ -56,6 +56,7 @@
 #include "dcgi.h"
 #include "url.h"
 #include "mime.h"
+#include "sendmail.h"
 
 char *login_cookie;
 
@@ -497,7 +498,8 @@ static void act_logout(cgi_sink *output,
 static void act_register(cgi_sink *output,
                         dcgi_state *ds) {
   const char *username, *password, *email;
-  char *confirm;
+  char *confirm, *content_type;
+  const char *text, *encoding, *charset;
 
   username = cgi_get("username");
   password = cgi_get("password");
@@ -530,6 +532,18 @@ static void act_register(cgi_sink *output,
     expand_template(ds, output, "login");
     return;
   }
+  /* Send the user a mail */
+  /* TODO templatize this */
+  byte_xasprintf((char **)&text,
+                "Welcome to DisOrder.  To active your login, please visit this URL:\n"
+                "\n"
+                "  %s?confirm=%s\n", config->url, confirm);
+  if(!(text = mime_encode_text(text, &charset, &encoding)))
+    fatal(0, "cannot encode email");
+  byte_xasprintf(&content_type, "text/plain;charset=%s",
+                quote822(charset, 0));
+  sendmail("", config->mail_sender, email, "Welcome to DisOrder",
+          encoding, content_type, text); /* TODO error checking  */
   /* We'll go back to the login page with a suitable message */
   cgi_set_option("registered", "registeredok");
   expand_template(ds, output, "login");
index 16811f8949e000aa24f1ff53460ba0846fd54515..6c1d0b9a5402a5ce4cdac60d28aa64fc3aa05ced 100644 (file)
@@ -95,7 +95,7 @@ USA
        </tr>
        <tr>
          <td>@label:login.password@</td>
-         <td><input class=password name=changepassword type=password value=""
+         <td><input class=password name=password type=password value=""
                     size=32></td>
          <td>
            <button class=register name=action type=submit value=register>