diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 5c82269ff29781b69d8f6d547d1c88b9222add69..2fce8c079a5635a71ed54100f4a2ef9b528c8476 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -48,6 +48,14 @@ class UsersController < ApplicationController
         else
           flash[:error] = I18n.t 'users.update.language_not_changed'
         end
+      elsif u[:email]
+        @user.unconfirmed_email = u[:email]
+        if @user.save
+          @user.mail_confirm_email
+          flash[:notice] = I18n.t 'users.update.unconfirmed_email_changed'
+        else
+          flash[:error] = I18n.t 'users.update.unconfirmed_email_not_changed'
+        end
       end
     end
 
@@ -126,4 +134,13 @@ class UsersController < ApplicationController
     tar_path = PhotoMover::move_photos(current_user)
     send_data( File.open(tar_path).read, :filename => "#{current_user.id}.tar" )
   end
+
+  def confirm_email
+    if current_user.confirm_email(params[:token])
+      flash[:notice] = I18n.t('users.confirm_email.email_confirmed', :email => current_user.email)
+    elsif current_user.unconfirmed_email.present?
+      flash[:error] = I18n.t('users.confirm_email.email_not_confirmed')
+    end
+    redirect_to edit_user_path
+  end
 end
diff --git a/app/mailers/notifier.rb b/app/mailers/notifier.rb
index 99d9e2b1e1a8d469c71b0723ec51e899b6c08beb..a1b58452a74d6b4559cae9e46c2d31849e6e8bda 100644
--- a/app/mailers/notifier.rb
+++ b/app/mailers/notifier.rb
@@ -113,6 +113,16 @@ class Notifier < ActionMailer::Base
     end
   end
 
+  def confirm_email(receiver_id)
+    @receiver = User.find_by_id(receiver_id)
+
+    I18n.with_locale(@receiver.language) do
+      mail(:to => "\"#{@receiver.name}\" <#{@receiver.unconfirmed_email}>",
+           :subject => I18n.t('notifier.confirm_email.subject', :unconfirmed_email => @receiver.unconfirmed_email),
+           :host => AppConfig[:pod_uri].host)
+    end
+  end
+
   private
   def log_mail recipient_id, sender_id, type
     log_string = "event=mail mail_type=#{type} recipient_id=#{recipient_id} sender_id=#{sender_id}"
diff --git a/app/models/jobs/mail_confirm_email.rb b/app/models/jobs/mail_confirm_email.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6c8bc3248057994a68a37cd3b67b9c6db860edec
--- /dev/null
+++ b/app/models/jobs/mail_confirm_email.rb
@@ -0,0 +1,8 @@
+module Job
+  class MailConfirmEmail < Base
+    @queue = :mail
+    def self.perform_delegate(user_id)
+      Notifier.confirm_email(user_id).deliver
+    end
+  end
+end
\ No newline at end of file
diff --git a/app/models/user.rb b/app/models/user.rb
index f8c2ecf47b5fc03f3aa4b0920b86452e2ed05b4f..d75b91f4d840e35da414616a86e812e52b6359d9 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -24,6 +24,7 @@ class User < ActiveRecord::Base
   validates_format_of :username, :with => /\A[A-Za-z0-9_]+\z/
   validates_length_of :username, :maximum => 32
   validates_inclusion_of :language, :in => AVAILABLE_LANGUAGE_CODES
+  validates_format_of :unconfirmed_email, :with  => Devise.email_regexp, :allow_blank => true
 
   validates_presence_of :person, :unless => proc {|user| user.invitation_token.present?}
   validates_associated :person
@@ -48,6 +49,7 @@ class User < ActiveRecord::Base
   before_save do
     person.save if person && person.changed?
   end
+  before_save :guard_unconfirmed_email
 
   attr_accessible :getting_started, :password, :password_confirmation, :language, :disable_mail
 
@@ -101,6 +103,12 @@ class User < ActiveRecord::Base
     true
   end
 
+  def confirm_email(token)
+    return false if token.blank? || token != confirm_email_token
+    self.email = unconfirmed_email
+    save
+  end
+
   ######### Aspects ######################
 
   def move_contact(person, to_aspect, from_aspect)
@@ -214,6 +222,12 @@ class User < ActiveRecord::Base
     end
   end
 
+  def mail_confirm_email
+    return false if unconfirmed_email.blank?
+    Resque.enqueue(Job::MailConfirmEmail, id)
+    true 
+  end
+
   ######### Posts and Such ###############
   def retract(target)
     if target.respond_to?(:relayable?) && target.relayable?
@@ -365,4 +379,12 @@ class User < ActiveRecord::Base
   def remove_mentions
     Mention.where( :person_id => self.person.id).delete_all
   end
+
+  def guard_unconfirmed_email
+    self.unconfirmed_email = nil if unconfirmed_email.blank? || unconfirmed_email == email
+    
+    if unconfirmed_email_changed?
+      self.confirm_email_token = unconfirmed_email ? ActiveSupport::SecureRandom.hex(15) : nil
+    end
+  end
 end
diff --git a/app/views/notifier/confirm_email.html.haml b/app/views/notifier/confirm_email.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..eb4f905e2a6077de687476b0fbe189e34deb0258
--- /dev/null
+++ b/app/views/notifier/confirm_email.html.haml
@@ -0,0 +1,6 @@
+%p
+  = t('notifier.hello', :name => @receiver.profile.first_name)
+%p
+  != t('notifier.confirm_email.click_link', :unconfirmed_email => @receiver.unconfirmed_email)
+  %br
+  = link_to confirm_email_url(:token => @receiver.confirm_email_token), confirm_email_url(:token => @receiver.confirm_email_token)
diff --git a/app/views/notifier/confirm_email.text.haml b/app/views/notifier/confirm_email.text.haml
new file mode 100644
index 0000000000000000000000000000000000000000..8ea9b8fef2d2dc03da454420c6fed73d9b056f85
--- /dev/null
+++ b/app/views/notifier/confirm_email.text.haml
@@ -0,0 +1,4 @@
+!= t('notifier.hello', :name => @receiver.profile.first_name)
+
+!= t('notifier.confirm_email.click_link', :unconfirmed_email => @receiver.unconfirmed_email)
+!= confirm_email_url(:token => @receiver.confirm_email_token)
diff --git a/app/views/users/edit.html.haml b/app/views/users/edit.html.haml
index 4b0fee89a35e688fce08e6f1d6242ddf09582ca1..ee3f8ecc4d575e3c69926d137d78985d592fe3f5 100644
--- a/app/views/users/edit.html.haml
+++ b/app/views/users/edit.html.haml
@@ -19,8 +19,15 @@
   .span-5.last
     %h3
       = t('.your_email')
-    %p
-      = current_user.email
+    = form_for 'user', :url => user_path, :html => { :method => :put } do |f|
+      = f.error_messages
+      %p
+        = f.text_field :email, :value => @user.unconfirmed_email || @user.email
+        = f.submit t('.change_email')
+    %br
+    - if @user.unconfirmed_email.present?
+      %p= t('.email_awaiting_confirmation', :email => @user.email, :unconfirmed_email => @user.unconfirmed_email)
+    %br
 
   %br
   %br
diff --git a/config/locales/diaspora/en.yml b/config/locales/diaspora/en.yml
index cf1a843d48815bab7d86df9432dc9d8a84a8e335..b08d091269585713628a1e3f4f4e932a6755ace8 100644
--- a/config/locales/diaspora/en.yml
+++ b/config/locales/diaspora/en.yml
@@ -420,7 +420,9 @@ en:
     liked:
         liked: "%{name} just liked your post"
         view_post: "View post >"
-
+    confirm_email:
+        subject: "Please activate your new e-mail address %{unconfirmed_email}"
+        click_link: "To activate your new e-mail address %{unconfirmed_email}, please click this link:"
   people:
     zero: "no people"
     one: "1 person"
@@ -723,6 +725,7 @@ en:
       close_account: "Close Account"
       change_language: "Change Language"
       change_password: "Change Password"
+      change_email: "Change E-Mail"
       new_password: "New Password"
       current_password: "Current password"
       download_xml: "download my xml"
@@ -738,6 +741,7 @@ en:
       private_message: "...you receive a private message?"
       liked: "...someone likes your post?"
       change: "Change"
+      email_awaiting_confirmation: "We have sent you an activation link to %{unconfirmed_email}. Till you follow this link and activate the new address, we will continue to use your original address %{email}."
     destroy: "Your account has been locked.  It may take 20 minutes for us to finish closing your account.  Thank you for trying Diaspora."
     getting_started:
       welcome: "Welcome to Diaspora!"
@@ -761,8 +765,13 @@ en:
       language_changed: "Language Changed"
       language_not_changed: "Language Change Failed"
       email_notifications_changed: "Email notifications changed"
+      unconfirmed_email_changed: "E-Mail Changed. Needs activation."
+      unconfirmed_email_not_changed: "E-Mail Change Failed"
     public:
       does_not_exist: "User %{username} does not exist!"
+    confirm_email:
+      email_confirmed: "E-Mail %{email} activated"
+      email_not_confirmed: "E-Mail could not be activated. Wrong link?"
 
   webfinger:
     fetch_failed: "failed to fetch webfinger profile for %{profile_url}"
diff --git a/config/routes.rb b/config/routes.rb
index bca90dd5c596403cea5404243e31ad3ac3c46d13..9b792f21e4966bae7a3db079f019e6fa12ea6926 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -68,6 +68,7 @@ Diaspora::Application.routes.draw do
     get 'public/:username'          => :public,          :as => 'users_public'
     match 'getting_started'         => :getting_started, :as => 'getting_started'
     get 'getting_started_completed' => :getting_started_completed
+    get 'confirm_email/:token'      => :confirm_email,   :as => 'confirm_email'
   end
 
   # This is a hack to overide a route created by devise.
diff --git a/db/migrate/20110601083310_add_unconfirmed_email_to_users.rb b/db/migrate/20110601083310_add_unconfirmed_email_to_users.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9c295e557006d9b8d5d68bbbac3c9339002272db
--- /dev/null
+++ b/db/migrate/20110601083310_add_unconfirmed_email_to_users.rb
@@ -0,0 +1,9 @@
+class AddUnconfirmedEmailToUsers < ActiveRecord::Migration
+  def self.up
+    add_column :users, :unconfirmed_email, :string, :default => nil, :null => true
+  end
+
+  def self.down
+    remove_column :users, :unconfirmed_email
+  end
+end
diff --git a/db/migrate/20110601091059_add_confirm_email_token_to_users.rb b/db/migrate/20110601091059_add_confirm_email_token_to_users.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4c71e97b2368a77c5b315939b418a3f30baeca5c
--- /dev/null
+++ b/db/migrate/20110601091059_add_confirm_email_token_to_users.rb
@@ -0,0 +1,9 @@
+class AddConfirmEmailTokenToUsers < ActiveRecord::Migration
+  def self.up
+    add_column :users, :confirm_email_token, :string, :limit => 30
+  end
+
+  def self.down
+    remove_column :users, :confirm_email_token
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index da2eb61fc25f4a53c388eefef8f10e0fafab5483..a8ddf606d9a5c91dc230d253915d623992d19898 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -391,6 +391,8 @@ ActiveRecord::Schema.define(:version => 20110707234802) do
     t.integer  "invited_by_id"
     t.string   "invited_by_type"
     t.string   "authentication_token",   :limit => 30
+    t.string   "unconfirmed_email"
+    t.string   "confirm_email_token",    :limit => 30
     t.datetime "locked_at"
   end
 
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index 440809bfee2e320136e723521bc94e1763c245ee..9c4090967796810ee110c2f2b907ab41b2b764f0 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -89,6 +89,41 @@ describe UsersController do
       end
     end
 
+    describe 'email' do
+      before do
+        Resque.stub!(:enqueue)
+      end
+
+      it 'allow the user to change his (unconfirmed) email' do
+        put(:update, :id => @user.id, :user => { :email => "my@newemail.com"})
+        @user.reload
+        @user.unconfirmed_email.should eql("my@newemail.com")
+      end
+
+      it 'informs the user about success' do
+        put(:update, :id => @user.id, :user => { :email => "my@newemail.com"})
+        request.flash[:notice].should eql(I18n.t('users.update.unconfirmed_email_changed'))
+        request.flash[:error].should be_blank
+      end
+
+      it 'informs the user about failure' do
+        put(:update, :id => @user.id, :user => { :email => "my@newemailcom"})
+        request.flash[:error].should eql(I18n.t('users.update.unconfirmed_email_not_changed'))
+        request.flash[:notice].should be_blank
+      end
+
+      it 'allow the user to change his (unconfirmed) email to blank (= abort confirmation)' do
+        put(:update, :id => @user.id, :user => { :email => ""})
+        @user.reload
+        @user.unconfirmed_email.should eql(nil)
+      end
+
+      it 'sends out activation email on success' do
+        Resque.should_receive(:enqueue).with(Job::MailConfirmEmail, @user.id).once
+        put(:update, :id => @user.id, :user => { :email => "my@newemail.com"})
+      end
+    end
+
     describe 'email settings' do
       it 'lets the user turn off mail' do
         par = {:id => @user.id, :user => {:email_preferences => {'mentioned' => 'true'}}}
@@ -138,4 +173,31 @@ describe UsersController do
       alice.reload.access_locked?.should be_true
     end
   end
+
+  describe '#confirm_email' do
+    before do
+      @user.update_attribute(:unconfirmed_email, 'my@newemail.com')
+    end
+
+    it 'redirects to to the user edit page' do
+      get 'confirm_email', :token => @user.confirm_email_token
+      response.should redirect_to edit_user_path
+    end
+
+    it 'confirms email' do
+      get 'confirm_email', :token => @user.confirm_email_token
+      @user.reload
+      @user.email.should eql('my@newemail.com')
+      request.flash[:notice].should eql(I18n.t('users.confirm_email.email_confirmed', :email => 'my@newemail.com'))
+      request.flash[:error].should be_blank
+    end
+
+    it 'does NOT confirm email with wrong token' do
+      get 'confirm_email', :token => @user.confirm_email_token.reverse
+      @user.reload
+      @user.email.should_not eql('my@newemail.com')
+      request.flash[:error].should eql(I18n.t('users.confirm_email.email_not_confirmed'))
+      request.flash[:notice].should be_blank
+    end
+  end
 end
diff --git a/spec/mailers/notifier_spec.rb b/spec/mailers/notifier_spec.rb
index f585b7841bca013cf8e112a7a4a3e23eda815cf7..f11b67ea14478a428e59d8f7b3e5058f1c690f84 100644
--- a/spec/mailers/notifier_spec.rb
+++ b/spec/mailers/notifier_spec.rb
@@ -46,7 +46,7 @@ describe Notifier do
     end
 
     it 'has the layout' do
-      
+
       mail = Notifier.single_admin("Welcome to bureaucracy!", user)
       mail.body.encoded.should match /change your notification settings/
     end
@@ -217,5 +217,33 @@ describe Notifier do
         end
       end
     end
+
+    describe ".confirm_email" do
+      before do
+        user.update_attribute(:unconfirmed_email, "my@newemail.com")
+      end
+
+      let!(:confirm_email) { Notifier.confirm_email(user.id) }
+
+      it 'goes to the right person' do
+        confirm_email.to.should == [user.unconfirmed_email]
+      end
+
+      it 'has the unconfirmed emil in the subject' do
+        confirm_email.subject.should include(user.unconfirmed_email)
+      end
+
+      it 'has the unconfirmed emil in the body' do
+        confirm_email.body.encoded.should include(user.unconfirmed_email)
+      end
+
+      it 'has the receivers name in the body' do
+        confirm_email.body.encoded.should include(user.person.profile.first_name)
+      end
+
+      it 'has the activation link in the body' do
+        confirm_email.body.encoded.should include(confirm_email_url(:token => user.confirm_email_token))
+      end
+    end
   end
 end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index f95c68d01cc5726f3f643f56398585e66fbfd1d7..b34d92e3d44e42da0f721a7475fadd8c9e5a80f9 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -114,6 +114,31 @@ describe User do
         alice.email = eve.email
         alice.should_not be_valid
       end
+      
+      it "requires a vaild email address" do
+        alice.email = "somebody@anywhere"
+        alice.should_not be_valid
+      end
+    end
+    
+    describe "of unconfirmed_email" do
+      it "unconfirmed_email address can be nil/blank" do
+        alice.unconfirmed_email = nil
+        alice.should be_valid
+        alice.unconfirmed_email = ""
+        alice.should be_valid
+      end
+
+      it "does NOT require a unique unconfirmed_email address" do
+        eve.update_attribute :unconfirmed_email, "new@email.com"
+        alice.unconfirmed_email = "new@email.com"
+        alice.should be_valid
+      end
+      
+      it "requires a vaild unconfirmed_email address" do
+        alice.unconfirmed_email = "somebody@anywhere"
+        alice.should_not be_valid
+      end
     end
 
     describe "of language" do
@@ -595,4 +620,136 @@ describe User do
       end
     end
   end
+
+  context 'change email' do
+    let(:user){ alice }
+
+    describe "#unconfirmed_email" do
+      it "is nil by default" do
+        user.unconfirmed_email.should eql(nil)
+      end
+
+      it "forces blank to nil" do
+        user.unconfirmed_email = ""
+        user.save!
+        user.unconfirmed_email.should eql(nil)
+      end
+
+      it "is ignored if it equals email" do
+        user.unconfirmed_email = user.email
+        user.save!
+        user.unconfirmed_email.should eql(nil)
+      end
+
+      it "allows change to valid new email" do
+        user.unconfirmed_email = "alice@newmail.com"
+        user.save!
+        user.unconfirmed_email.should eql("alice@newmail.com")
+      end
+    end
+
+    describe "#confirm_email_token" do
+      it "is nil by default" do
+        user.confirm_email_token.should eql(nil)
+      end
+
+      it "is autofilled when unconfirmed_email is set to new email" do
+        user.unconfirmed_email = "alice@newmail.com"
+        user.save!
+        user.confirm_email_token.should_not be_blank
+        user.confirm_email_token.size.should eql(30)
+      end
+
+      it "is set back to nil when unconfirmed_email is empty" do
+        user.unconfirmed_email = "alice@newmail.com"
+        user.save!
+        user.confirm_email_token.should_not be_blank
+        user.unconfirmed_email = nil
+        user.save!
+        user.confirm_email_token.should eql(nil)
+      end
+
+      it "generates new token on every new unconfirmed_email" do
+        user.unconfirmed_email = "alice@newmail.com"
+        user.save!
+        first_token = user.confirm_email_token
+        user.unconfirmed_email = "alice@andanotherone.com"
+        user.save!
+        user.confirm_email_token.should_not eql(first_token)
+        user.confirm_email_token.size.should eql(30)
+      end
+    end
+    
+    describe '#mail_confirm_email' do
+      it 'enqueues a mail job on user with unconfirmed email' do
+        user.update_attribute(:unconfirmed_email, "alice@newmail.com")
+        Resque.should_receive(:enqueue).with(Job::MailConfirmEmail, alice.id).once
+        alice.mail_confirm_email.should eql(true)
+      end
+
+      it 'enqueues NO mail job on user without unconfirmed email' do
+        Resque.should_not_receive(:enqueue).with(Job::MailConfirmEmail, alice.id)
+        alice.mail_confirm_email.should eql(false)
+      end
+    end
+
+    describe '#confirm_email' do
+      context 'on user with unconfirmed email' do
+        before do
+          user.update_attribute(:unconfirmed_email, "alice@newmail.com")
+        end
+
+        it 'confirms email and set the unconfirmed_email to email on valid token' do
+          user.confirm_email(user.confirm_email_token).should eql(true)
+          user.email.should eql("alice@newmail.com")
+          user.unconfirmed_email.should eql(nil)
+          user.confirm_email_token.should eql(nil)
+        end
+
+        it 'returns false and does not change anything on wrong token' do
+          user.confirm_email(user.confirm_email_token.reverse).should eql(false)
+          user.email.should_not eql("alice@newmail.com")
+          user.unconfirmed_email.should_not eql(nil)
+          user.confirm_email_token.should_not eql(nil)
+        end
+        
+        it 'returns false and does not change anything on blank token' do
+          user.confirm_email("").should eql(false)
+          user.email.should_not eql("alice@newmail.com")
+          user.unconfirmed_email.should_not eql(nil)
+          user.confirm_email_token.should_not eql(nil)
+        end
+        
+        it 'returns false and does not change anything on blank token' do
+          user.confirm_email(nil).should eql(false)
+          user.email.should_not eql("alice@newmail.com")
+          user.unconfirmed_email.should_not eql(nil)
+          user.confirm_email_token.should_not eql(nil)
+        end
+      end
+
+      context 'on user without unconfirmed email' do
+        it 'returns false and does not change anything on any token' do
+          user.confirm_email("12345"*6).should eql(false)
+          user.email.should_not eql("alice@newmail.com")
+          user.unconfirmed_email.should eql(nil)
+          user.confirm_email_token.should eql(nil)
+        end
+
+        it 'returns false and does not change anything on blank token' do
+          user.confirm_email("").should eql(false)
+          user.email.should_not eql("alice@newmail.com")
+          user.unconfirmed_email.should eql(nil)
+          user.confirm_email_token.should eql(nil)
+        end
+
+        it 'returns false and does not change anything on blank token' do
+          user.confirm_email(nil).should eql(false)
+          user.email.should_not eql("alice@newmail.com")
+          user.unconfirmed_email.should eql(nil)
+          user.confirm_email_token.should eql(nil)
+        end
+      end
+    end
+  end
 end