anchor.diff:       password))))
   
   ;;;
  -;;; Design and querying
  +;;; DB
   ;;;
  -(defun ensure-account-design-doc ()
  -  (ensure-doc "_design/account"
  -              (mkdoc "language" "common-lisp"
  -                     "views" (mkdoc "by_account_name"
  -                                    (mkdoc "map"
  -                                           (mapfun doc "account"
  -                                             (emit (string-downcase (hashget doc "account_name"))
  -                                                   doc)))
  -                                    "by_account_name_password"
  -                                    (mkdoc "map"
  -                                           (mapfun doc "account"
  -                                             (emit (list (string-downcase (hashget doc "account_name"))
  -                                                         (hashget doc "password"))
  -                                                   doc)))
  -                                    "by_display_name"
  -                                    (mkdoc "map"
  -                                           (mapfun doc "account"
  -                                             (emit (string-downcase (hashget doc "display_name"))
  -                                                   doc)))))))
  +(defgeneric account-email (account))
  +(defgeneric account-display-name (account))
  +(defgeneric account-password (account))
   
  -(defun account-view-value (view-name key)
  -  (view-query-value "account" view-name key))
  +(defdao account ()
  +  ((id :col-type serial :reader id)
  +   (display-name :col-type text :initarg :display-name :reader account-display-name)
  +   (email :col-type text :initarg :email :reader account-email)
  +   (password :col-type text :initarg :password :reader account-password)
  +   (created-at :col-type timestamp :col-default (:now)))
  +  (:keys email)
  +  (:index 'id)
  +  (:index 'email)
  +  (:unique 'email)
  +  (:unique 'display-name))
   
  -(defun find-account-by-display-name (display-name)
  -  (account-view-value "by_display_name" display-name))
  +(defun find-account (email)
  +  (get-dao 'account (string-downcase email)))
   
  -(defun find-account (account-name)
  -  (account-view-value "by_account_name" (string-downcase account-name)))
  +(defun validate-account (email password)
  +  (with-transaction ()
  +    (when-let (account (find-account email))
  +      (let ((hashed-pass (hash-password password (account-email account))))
  +        (when (string= hashed-pass (account-password account))
  +          account)))))
   
  -(defun validate-credentials (account-name password)
  -  (when-let (account (account-view-value "by_account_name" (string-downcase account-name)))
  -    (let ((hashed-pass (hash-password password (doc-val account "_id"))))
  -      (when (string= hashed-pass (doc-val account "password"))
  -        account))))
  -
  -(defun account-name (account)
  -  (doc-val account "account_name"))
  -(defun account-display-name (account)
  -  (doc-val account "display_name"))
  +(defun display-name-exists-p (display-name)
  +  (query (:select t :from 'account :where (:= 'display-name display-name))
  +         :single))
   
   ;;;
   ;;; Creation and validation
  
positive.code: ;;; Utils
  ;;;
  
  ;;; The password confirmation scheme uses PDKDF2. CouchDB's randomly-generated document id for the
  ;;; particular account is used as a salt for the password. The two parameters should be adjusted
  ;;; according to your server's capabilities. Do some profiling and make these numbers as high as you
  ;;; can without lagging under heavy load.
  
  (defparameter *key-derivation-iterations* 1056)
  (defparameter *key-length* 32)
  
negative.code: (cl:defpackage #:sykosomatic.account
    (:use :cl :alexandria :cl-ppcre :sykosomatic.db :postmodern)
    (:export :create-account :find-account :validate-account
             :account-email :account-display-name))
  (cl:in-package #:sykosomatic.account)
  
  (declaim (optimize debug))
  