<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -114,10 +114,13 @@ class AccountController &lt; ApplicationController
       email          = user[:email]
       reset_password = User.reset_password(email,username)
       if reset_password[0]
-        Notifier.deliver_forgot_password_email(username,reset_password[1],reset_password[2])
-        flash[:notice]=&quot;A new password has been emailed to you.&quot;[:new_password_emailed]    
+        new_password=reset_password[1]
+        user=reset_password[2]
+        email=user.email
+        Notifier.deliver_forgot_password_email(new_password,user)
+        flash.now[:notice]=&quot;A new password has been emailed to you at #{email}.&quot;[:new_password_emailed]    
       else
-        flash[:notice]=reset_password[1]
+        flash.now[:notice]=reset_password[1]
       end
     end
   end
@@ -182,10 +185,10 @@ class AccountController &lt; ApplicationController
   protected
   def password_authentication(username, password)
     user = User.authenticate(username,password)
-    unless user.nil?
-      successful_login(user)
+    if user[0]
+      successful_login(user[1])
     else
-      failed_login &quot;Invalid login or password&quot;[]
+      failed_login(user[1])
     end
   end
 
@@ -220,7 +223,6 @@ class AccountController &lt; ApplicationController
   def successful_login(user, new_openid_user = false)
     set_current_user(user)
     flash[:notice] = &quot;Logged in successfully&quot;[:logged_in]   
-    # TODO - user.failed_logins = 0; user.save
     # could catch the fact that they are a new openid user here and redirect somewhere else if you wanted
     if user.is_admin? &amp;&amp; ( session[:return_to].nil? || session[:return_to].empty?) # if we're an admin we STILL would love a return, thank you very much!
       redirect_to :controller =&gt; 'admin', :action =&gt; 'index', :protocol =&gt; &quot;http://&quot;
@@ -230,7 +232,6 @@ class AccountController &lt; ApplicationController
   end
 
   def failed_login(message)
-    # TODO - user.failed_logins += 1; user.save
     # TODO - send an email to an admin if user.failed_logins &gt; 10 # Smells like a dictionary attack!
     flash[:warning] = message
     redirect_to :action =&gt; 'login'</diff>
      <filename>app/controllers/account_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,11 +3,11 @@ class Notifier &lt; ActionMailer::Base
   @@from = $WEBSITE_EMAIL_FROM_ADDRESS
   @@test_recipient = &quot;junk@example.com&quot; # testing only if needed
 
-  def forgot_password_email(username,password,email)
+  def forgot_password_email(new_password,user)
     subject     &quot;EOL Forgot Password&quot;
-    recipients  email
+    recipients  user.email
     from        @@from
-    body        :username =&gt; username,:password=&gt;password
+    body        :user =&gt; user,:new_password=&gt;new_password
   end
   
   def agent_forgot_password_email(agent, new_password)</diff>
      <filename>app/models/notifier.rb</filename>
    </modified>
    <modified>
      <diff>@@ -21,7 +21,7 @@ class User &lt; ActiveRecord::Base
   before_save {|obj| obj.credentials = '' if obj.credentials.nil?}   # TODO Move this into the check_curator_status before_save method
 
   validates_presence_of   :username, :if =&gt; :not_openid?
-  validates_length_of     :username, :within =&gt; 4..16, :if =&gt; :not_openid?
+  validates_length_of     :username, :within =&gt; 4..32, :if =&gt; :not_openid?
   validates_length_of     :entered_password, :within =&gt; 4..16, :if =&gt; :not_openid?, :on =&gt; :create
   
   validates_presence_of   :given_name
@@ -143,23 +143,46 @@ class User &lt; ActiveRecord::Base
   end
   
   def self.authenticate(username,password)
-    user = User.find_by_username_and_active(username,true)
 
-    # if we don't have any matching username for an active account, return nothing
-    return nil if user.blank?
+    # try username first
+    user = User.find_by_username_and_active(username,true)
+    if !user.blank? &amp;&amp; user.hashed_password==User.hash_password(password) 
+      user.reset_login_attempts # found a matching username and password matched!
+      return true,user
+    elsif !user.blank?  # found a matching username, but password didn't match!
+      user.invalid_login_attempt
+      return false,&quot;Invalid login or password&quot;[]
+    end
+    
+    # no match with username, next try email address, which is not necessarily unique in database
+    users=User.find_all_by_email_and_active(username,true)
+    return false,&quot;Invalid login or password&quot;[] if users.blank? # no email match either, returning nothing
+
+    users.each do |u| # check all users with matching email addresses to see if one of them matches the password
+      if u.hashed_password==User.hash_password(password) 
+        u.reset_login_attempts # found a match with email and password
+        return true,u
+      else
+        u.invalid_login_attempt # log the bad attempt for this user!
+      end
+    end
     
-    # if we have a matching username active account, confirm the password too
-    if user.hashed_password==User.hash_password(password)
-      user.update_attributes(:failed_login_attempts=&gt;0) # reset the user's failed login attempts
-      return user
+    if users.size &gt; 1 
+      return false,&quot;The email address is not unique - you must enter a username&quot;[] # more than 1 email address with no matching passwords
     else
-      # user matched but not password, so log a failed login attempt and return nothing
-      user.update_attributes(:failed_login_attempts=&gt;user.failed_login_attempts+1) 
-      return nil
+      return false,&quot;Invalid login or password&quot;[]  # no matches yet again :(
     end
     
   end
 
+  def reset_login_attempts
+    self.update_attributes(:failed_login_attempts=&gt;0) # reset the user's failed login attempts
+  end
+  
+  def invalid_login_attempt
+   self.update_attributes(:failed_login_attempts=&gt;self.failed_login_attempts+1)
+  end
+  
   # I wanted to centralize this call, so we can quickly change from one kind of hashing to another.
   def self.hash_password(raw)
     Digest::MD5.hexdigest(raw)
@@ -191,7 +214,7 @@ class User &lt; ActiveRecord::Base
       8.times { new_password &lt;&lt; chars[rand(chars.size)] }
       new_guy[0].password = new_password
       if new_guy[0].save
-        return true, new_password, new_guy[0].email
+        return true, new_password, new_guy[0]
       else
         return false, &quot;Sorry, a problem occurred updating your account - please try again later.&quot;[:problem_updating_account]      
       end  </diff>
      <filename>app/models/user.rb</filename>
    </modified>
    <modified>
      <diff>@@ -13,7 +13,7 @@
             &lt;br /&gt;
  
 
-                &lt;p&gt;&lt;%= &quot;Enter the email address OR username (or both) you registered with to retrieve your password&quot;[:forgot_password_message] %&gt;:&lt;/p&gt;
+                &lt;p&gt;&lt;%= &quot;Enter the email address OR username (or both) you registered with to reset your password&quot;[:forgot_password_message] %&gt;:&lt;/p&gt;
                 &lt;% form_for :user do |form| -%&gt;
                 &lt;fieldset&gt;
 </diff>
      <filename>app/views/account/forgot_password.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -24,14 +24,14 @@
                 &lt;% form_for :user, :url =&gt; {:action=&gt;'authenticate'} do |form| -%&gt;
                 &lt;fieldset&gt;
    
-                    &lt;label for=&quot;user_username&quot;&gt;&lt;%= &quot;Username&quot;[] %&gt;&lt;/label&gt;
+                    &lt;label for=&quot;user_username&quot;&gt;&lt;%= &quot;Username&quot;[] %&gt; 	&lt;%=&quot;or&quot;[]%&gt; &lt;%= &quot;Email Address&quot;[] %&gt;:&lt;/label&gt;
                     &lt;%= form.text_field 'username', :size=&gt;'16',:maxlength=&gt;'30' %&gt;
-
-                    &lt;label for=&quot;user_password&quot;&gt;&lt;%= &quot;Password&quot;[] %&gt;&lt;/label&gt;
+                    &lt;br /&gt;&lt;br /&gt;
+                    &lt;label for=&quot;user_password&quot;&gt;&lt;%= &quot;Password&quot;[] %&gt;:&lt;/label&gt;
                     &lt;%= form.password_field 'password', :size=&gt;'16',:maxlength=&gt;'30' %&gt;
-				&lt;br /&gt;			
-				or
-
+				&lt;br /&gt;&lt;br /&gt;			
+				&lt;%=&quot;or&quot;[]%&gt;
+        &lt;br /&gt;
         &lt;div id=&quot;openid&quot;&gt;
    				  &lt;label for=&quot;openid_url&quot;&gt;&lt;%= &quot;OpenID&quot;[] %&gt;&lt;/label&gt;
             &lt;%= text_field_tag 'openid_url', {},{:size=&gt;100,:maxlength=&gt;250,:class=&gt;'openid'} %&gt; </diff>
      <filename>app/views/account/login.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -33,7 +33,9 @@ function check_passwords() {
 	&lt;div id=&quot;page-content&quot; class=&quot;clearfix&quot;&gt;
 	    &lt;!-- center page content --&gt;
     	&lt;div id=&quot;full-page-content&quot;&gt;	
-    &lt;%= eol_lang_error_messages_for :user %&gt;
+    	
+      	  &lt;%=eol_lang_error_messages_for :user if request.post?%&gt;
+
     	    &lt;br /&gt;
 
               &lt;% form_for :user, :html=&gt;{:class=&gt;&quot;warning-warn invalid-invalid styleLabelOnErr&quot;} do |f| -%&gt;
@@ -41,9 +43,9 @@ function check_passwords() {
                     &lt;legend&gt;&lt;%= &quot;Account Information&quot;[] %&gt;&lt;/legend&gt;
 
 
-                    &lt;label&gt;Username:&lt;/label&gt; 
-	                 	&lt;!-- user_username ^[a-zA-Z0-9\-_]{4,16}$ false true //--&gt;
-	     					    &lt;%= f.text_field :username, {:maxlength=&gt;16, :onblur=&gt;'JavaScript:check_username();'} %&gt;
+                    &lt;label&gt;Username:  (4 - 32 characters)&lt;/label&gt; 
+	                 	&lt;!-- user_username ^[a-zA-Z0-9\-_]{4,32}$ false true //--&gt;
+	     					    &lt;%= f.text_field :username, {:maxlength=&gt;32, :onblur=&gt;'JavaScript:check_username();'} %&gt;
        						 &lt;span class=&quot;error&quot; id=&quot;username_warn&quot;&gt;&lt;/span&gt;
 					
 					&lt;% if @user.openid? %&gt;
@@ -53,7 +55,7 @@ function check_passwords() {
 
 					&lt;% else %&gt;
 			      &lt;p&gt;&lt;%=&quot;Only enter a new password if you wish to change it.&quot;[:only_enter_new_password_if_needed]%&gt;&lt;/p&gt;
-              	&lt;label for=&quot;password&quot;&gt;&lt;%=&quot;New Password&quot;[]%&gt;&lt;/label&gt;
+              	&lt;label for=&quot;password&quot;&gt;&lt;%=&quot;New Password&quot;[]%&gt;  (4 - 16 characters)&lt;/label&gt;
               	&lt;%= f.password_field :entered_password,{:maxlength=&gt;16} %&gt;
 
               	&lt;label for=&quot;password_confirmation&quot;&gt;&lt;%=&quot;Confirm New Password&quot;[]%&gt;&lt;/label&gt;</diff>
      <filename>app/views/account/profile.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -51,9 +51,9 @@
 	
 						&lt;span class=&quot;error&quot; id=&quot;verification_warn&quot;&gt;&lt;strong&gt;&lt;%=@verification_did_not_match%&gt;&lt;/strong&gt;&lt;/span&gt;
 					
-	                    &lt;label for=&quot;user_username&quot; title=&quot;&lt;%= &quot;Please enter your username&quot;[] %&gt;&quot;&gt;&lt;%= &quot;Username&quot;[] %&gt; (4 - 16 characters)&lt;span title=&quot;required&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;warn&quot;&gt;(&lt;%= &quot;Required&quot;[] %&gt;)&lt;/span&gt;&lt;/label&gt;
-	                 	&lt;!-- user_username ^[a-zA-Z0-9\-_]{4,16}$ false true //--&gt;
-        					    &lt;%= f.text_field :username, {:maxlength=&gt;16, :onblur=&gt;'JavaScript:check_username();'} %&gt;
+	                    &lt;label for=&quot;user_username&quot; title=&quot;&lt;%= &quot;Please enter your username&quot;[] %&gt;&quot;&gt;&lt;%= &quot;Username&quot;[] %&gt; (4 - 32 characters)&lt;span title=&quot;required&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;warn&quot;&gt;(&lt;%= &quot;Required&quot;[] %&gt;)&lt;/span&gt;&lt;/label&gt;
+	                 	&lt;!-- user_username ^[a-zA-Z0-9\-_]{4,32}$ false true //--&gt;
+        					    &lt;%= f.text_field :username, {:maxlength=&gt;32, :onblur=&gt;'JavaScript:check_username();'} %&gt;
         						 &lt;span class=&quot;error&quot; id=&quot;username_warn&quot;&gt;&lt;/span&gt;
 						 
 	                    &lt;label for=&quot;user_entered_password&quot; title=&quot;&lt;%= &quot;Please enter a password&quot;[] %&gt;&quot;&gt;&lt;%= &quot;Password&quot;[] %&gt; (4 - 16 characters)&lt;span title=&quot;required&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;warn&quot;&gt;(&lt;%= &quot;Required&quot;[] %&gt;)&lt;/span&gt;&lt;/label&gt;</diff>
      <filename>app/views/account/signup.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -1,11 +1,10 @@
-	&lt;%= eol_lang_error_messages_for :user %&gt;
-					
+
 	&lt;br /&gt;
 	
 	&lt;table&gt;	
 	  &lt;tr&gt;
 			&lt;td&gt;&lt;strong&gt;Username:&lt;/strong&gt;&lt;/td&gt;
-			&lt;td&gt;&lt;%=f.text_field :username,{:size=&gt;'50',:maxlength=&gt;'16'}%&gt;&lt;/td&gt;
+			&lt;td&gt;&lt;%=f.text_field :username,{:size=&gt;'50',:maxlength=&gt;'32'}%&gt;&lt;/td&gt;
 		&lt;/tr&gt;
 		&lt;% if @user.openid?%&gt;
   		&lt;tr&gt;</diff>
      <filename>app/views/administrator/user/_form.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -2,5 +2,6 @@ You have indicated that you have forgotten your password.   A new password has b
 for you and is shown below.  Please log into the website at http://www.eol.org/login and then visit your profile page at http://www.eol.org/profile to
 change your password.
 
-Username:   &lt;%=@username%&gt;
-Password:   &lt;%=@password%&gt;
\ No newline at end of file
+Username:   &lt;%=@user.username%&gt;
+Email:      &lt;%=@user.email%&gt;
+Password:   &lt;%=@new_password%&gt;
\ No newline at end of file</diff>
      <filename>app/views/notifier/forgot_password_email.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -64,7 +64,7 @@ ActionController::Routing::Routes.draw do |map|
 
   map.external_link 'external_link',:controller=&gt;'application', :action=&gt;'external_link'
 
-  map.root :controller =&gt; 'content'
+  map.root :controller =&gt; 'content', :action=&gt;'index'
 
   map.search 'search', :controller =&gt; 'taxa', :action =&gt; 'search'
   map.connect 'search/:id', :controller =&gt; 'taxa', :action =&gt; 'search'
@@ -82,7 +82,16 @@ ActionController::Routing::Routes.draw do |map|
 
   map.connect '/taxon_concepts/:taxon_concept_id/comments/', :controller =&gt; 'comments', :action =&gt; 'index', :conditions =&gt; {:method =&gt; :get}
   map.connect '/taxon_concepts/:taxon_concept_id/comments/', :controller =&gt; 'comments', :action =&gt; 'create', :conditions =&gt; {:method =&gt; :post}
+
+  map.admin 'admin', :controller=&gt;'admin', :action=&gt;'index'
+  map.admin 'content_partner', :controller=&gt;'content_partner', :action=&gt;'index'
+  
+  # this represents a URL with just a random namestring -- send to search page (e.g. www.eol.org/animalia)
+  map.connect ':id', :controller=&gt;'taxa', :action=&gt;'search'
+ 
   # Install the default routes as the lowest priority.
   map.connect ':controller/:action/:id'
   map.connect ':controller/:action/:id.:format'  
+  
+
 end</diff>
      <filename>config/routes.rb</filename>
    </modified>
    <modified>
      <diff>@@ -342,8 +342,8 @@ forgot_password_message: Enter the email address or username (or both) you regis
 could_not_locate_account: Sorry, but we could not locate your account.
 #Sorry, but your email address is not unique - you must also specify a username.
 must_specify_username_too: Sorry, but your email address is not unique - you must also specify a username.
-# A new password has been emailed to you.
-new_password_emailed: A new password has been emailed to you.
+# A new password has been emailed to you at {email}.
+new_password_emailed: A new password has been emailed to you at {email}.
 #Sorry, a problem occurred updating your account - please try again later.
 problem_updating_account: Sorry, a problem occurred updating your account - please try again later.
 taxon_group_of_interest: Taxon group of interest</diff>
      <filename>lang/de.yml</filename>
    </modified>
    <modified>
      <diff>@@ -346,8 +346,8 @@ forgot_password_message: Enter the email address or username (or both) you regis
 could_not_locate_account: Sorry, but we could not locate your account.
 #Sorry, but your email address is not unique - you must also specify a username.
 must_specify_username_too: Sorry, but your email address is not unique - you must also specify a username.
-# A new password has been emailed to you.
-new_password_emailed: A new password has been emailed to you.
+# A new password has been emailed to you at {email}.
+new_password_emailed: A new password has been emailed to you at {email}..
 #Sorry, a problem occurred updating your account - please try again later.
 problem_updating_account: Sorry, a problem occurred updating your account - please try again later.
 taxon_group_of_interest: Taxon group of interest</diff>
      <filename>lang/fr.yml</filename>
    </modified>
    <modified>
      <diff>@@ -346,8 +346,8 @@ forgot_password_message: Enter the email address or username (or both) you regis
 could_not_locate_account: Sorry, but we could not locate your account.
 #Sorry, but your email address is not unique - you must also specify a username.
 must_specify_username_too: Sorry, but your email address is not unique - you must also specify a username.
-# A new password has been emailed to you.
-new_password_emailed: &#1053;&#1086;&#1074;&#1099;&#1081; &#1087;&#1072;&#1088;&#1086;&#1083;&#1100; &#1073;&#1099;&#1083; &#1086;&#1090;&#1087;&#1088;&#1072;&#1074;&#1083;&#1077;&#1085; &#1085;&#1072; &#1074;&#1072;&#1096; e-mail &#1072;&#1076;&#1088;&#1077;&#1089;.
+# A new password has been emailed to you at {email}..
+new_password_emailed: &#1053;&#1086;&#1074;&#1099;&#1081; &#1087;&#1072;&#1088;&#1086;&#1083;&#1100; &#1073;&#1099;&#1083; &#1086;&#1090;&#1087;&#1088;&#1072;&#1074;&#1083;&#1077;&#1085; &#1085;&#1072; &#1074;&#1072;&#1096; e-mail &#1072;&#1076;&#1088;&#1077;&#1089; {email}.
 #Sorry, a problem occurred updating your account - please try again later.
 problem_updating_account: Sorry, a problem occurred updating your account - please try again later.
 taxon_group_of_interest: Taxon group of interest</diff>
      <filename>lang/ru.yml</filename>
    </modified>
    <modified>
      <diff>@@ -396,8 +396,8 @@ forgot_password_message: Enter the email address or username (or both) you regis
 could_not_locate_account: Sorry, but we could not locate your account.
 #Sorry, but your email address is not unique - you must also specify a username.
 must_specify_username_too: Sorry, but your email address is not unique - you must also specify a username.
-# A new password has been emailed to you.
-new_password_emailed: A new password has been emailed to you.
+# A new password has been emailed to you at {email}.
+new_password_emailed: A new password has been emailed to you at {email}.
 #Sorry, a problem occurred updating your account - please try again later.
 problem_updating_account: Sorry, a problem occurred updating your account - please try again later.
 taxon_group_of_interest: Taxon group of interest</diff>
      <filename>lang/translation_template.yml</filename>
    </modified>
    <modified>
      <diff>@@ -7,16 +7,35 @@ describe User do
     @user.should_not be_a_new_record
   end
 
-  it 'should authenticate existing user with correct password' do
-    User.authenticate( @user.username, @user.password ).id.should == @user.id
+  it 'should authenticate existing user with correct password, returning true and user back' do
+    success,user=User.authenticate( @user.username, @user.password )
+    success.should be_true
+    user.id.should == @user.id
+  end
+
+  it 'should authenticate existing user with correct email address and password, returning true and user back' do
+    success,user=User.authenticate( @user.email, @user.password )
+    success.should be_true
+    user.id.should == @user.id  
   end
 
-  it 'should return nil for non-existing user' do
-    User.authenticate('idontexistATALL', @user.password).should be_nil
+  it 'should authenticate existing user with a duplicate email address, assuming password is correct, returning true and user back' do
+    user2 = User.gen :username =&gt; 'SpongeBob', :password =&gt; 'squarepants', :email=&gt; @user.email
+    success,user=User.authenticate( user2.email, user2.password )
+    success.should be_true
+    user.id.should == user2.id  
+  end
+      
+  it 'should return false as first return value for non-existing user' do
+    success,message=User.authenticate('idontexistATALL', @user.password)
+    success.should be_false
+    message.should == 'Invalid login or password'    
   end
 
-  it 'should return nil for user with incorrect password' do
-    User.authenticate(@user.username, 'totally wrong password').should be_nil
+  it 'should return false as first return value for user with incorrect password' do
+    success,message=User.authenticate(@user.username, 'totally wrong password')
+    success.should be_false
+    message.should == 'Invalid login or password'
   end
 
   it 'should fail to change password if the account is not found' do
@@ -44,11 +63,11 @@ describe User do
     # TODO this API is very smelly.  User#reset_password returns different kinds of
     #      results depending on whether the reset succeeded or failed.  very frustrating.
     #      this would be a good candicate for refactoring.
-    success, password, email = User.reset_password @user.email, ''
+    success, password, user = User.reset_password @user.email, ''
     
     success.should be_true
     password.should_not == @user.password
-    email.should == @user.email
+    user.email.should == @user.email
 
     # confirm things really changed properly, in the database
     User.find(@user.id).hashed_password.should == User.hash_password(password)
@@ -56,11 +75,11 @@ describe User do
   end
 
   it 'should be able to reset the password on an account if both email address and username are entered' do
-    success, password, email = User.reset_password @user.email, @user.username
+    success, password, user = User.reset_password @user.email, @user.username
 
     success.should be_true
     password.should_not == @user_password
-    email.should == @user.email
+    user.email.should == @user.email
 
     User.find(@user.id).hashed_password.should == User.hash_password(password)
     User.find(@user.id).hashed_password.should_not == @user.hashed_password</diff>
      <filename>spec/models/user_spec.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>5fc90294b0460c28435bee91b7d057ff8bf2d5e4</id>
    </parent>
  </parents>
  <author>
    <name>peter</name>
    <email>peter@78829999-583a-0410-bd01-8a9b849fd409</email>
  </author>
  <url>http://github.com/eol/eol/commit/85439ecb9b1ab9c64b6e2239150d2077110382bb</url>
  <id>85439ecb9b1ab9c64b6e2239150d2077110382bb</id>
  <committed-date>2009-05-28T11:17:08-07:00</committed-date>
  <authored-date>2009-05-28T11:17:08-07:00</authored-date>
  <message>allow users to login with their email address as well as their usernames; fix some small bugs with the forgot password email not sending them their usernames in the body of the email; add default routing for taxa friendly URLs (like www.eol.org/animalia)


git-svn-id: file:///data/subversion/eol/trunk@610 78829999-583a-0410-bd01-8a9b849fd409</message>
  <tree>409eacb1ac4b998ce2574e225c390d663d639e69</tree>
  <committer>
    <name>peter</name>
    <email>peter@78829999-583a-0410-bd01-8a9b849fd409</email>
  </committer>
</commit>
