|  | @@ -42,6 +42,8 @@ var (
 | 
	
		
			
				|  |  |  	ErrUserNotExist          = errors.New("User does not exist")
 | 
	
		
			
				|  |  |  	ErrUserNotKeyOwner       = errors.New("User does not the owner of public key")
 | 
	
		
			
				|  |  |  	ErrEmailAlreadyUsed      = errors.New("E-mail already used")
 | 
	
		
			
				|  |  | +	ErrEmailNotExist         = errors.New("E-mail does not exist")
 | 
	
		
			
				|  |  | +	ErrEmailNotActivated     = errors.New("E-mail address has not been activated")
 | 
	
		
			
				|  |  |  	ErrUserNameIllegal       = errors.New("User name contains illegal characters")
 | 
	
		
			
				|  |  |  	ErrLoginSourceNotExist   = errors.New("Login source does not exist")
 | 
	
		
			
				|  |  |  	ErrLoginSourceNotActived = errors.New("Login source is not actived")
 | 
	
	
		
			
				|  | @@ -50,10 +52,11 @@ var (
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // User represents the object of individual and member of organization.
 | 
	
		
			
				|  |  |  type User struct {
 | 
	
		
			
				|  |  | -	Id          int64
 | 
	
		
			
				|  |  | -	LowerName   string `xorm:"UNIQUE NOT NULL"`
 | 
	
		
			
				|  |  | -	Name        string `xorm:"UNIQUE NOT NULL"`
 | 
	
		
			
				|  |  | -	FullName    string
 | 
	
		
			
				|  |  | +	Id        int64
 | 
	
		
			
				|  |  | +	LowerName string `xorm:"UNIQUE NOT NULL"`
 | 
	
		
			
				|  |  | +	Name      string `xorm:"UNIQUE NOT NULL"`
 | 
	
		
			
				|  |  | +	FullName  string
 | 
	
		
			
				|  |  | +	// Email is the primary email address (to be used for communication).
 | 
	
		
			
				|  |  |  	Email       string `xorm:"UNIQUE(s) NOT NULL"`
 | 
	
		
			
				|  |  |  	Passwd      string `xorm:"NOT NULL"`
 | 
	
		
			
				|  |  |  	LoginType   LoginType
 | 
	
	
		
			
				|  | @@ -93,6 +96,16 @@ type User struct {
 | 
	
		
			
				|  |  |  	Members     []*User `xorm:"-"`
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +// EmailAdresses is the list of all email addresses of a user. Can contain the
 | 
	
		
			
				|  |  | +// primary email address, but is not obligatory
 | 
	
		
			
				|  |  | +type EmailAddress struct {
 | 
	
		
			
				|  |  | +	Id          int64
 | 
	
		
			
				|  |  | +	Uid         int64  `xorm:"INDEX NOT NULL"`
 | 
	
		
			
				|  |  | +	Email       string `xorm:"UNIQUE NOT NULL"`
 | 
	
		
			
				|  |  | +	IsActivated bool
 | 
	
		
			
				|  |  | +	IsPrimary   bool `xorm:"-"`
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  // DashboardLink returns the user dashboard page link.
 | 
	
		
			
				|  |  |  func (u *User) DashboardLink() string {
 | 
	
		
			
				|  |  |  	if u.IsOrganization() {
 | 
	
	
		
			
				|  | @@ -248,6 +261,9 @@ func IsEmailUsed(email string) (bool, error) {
 | 
	
		
			
				|  |  |  	if len(email) == 0 {
 | 
	
		
			
				|  |  |  		return false, nil
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | +	if has, err := x.Get(&EmailAddress{Email: email}); has || err != nil {
 | 
	
		
			
				|  |  | +		return has, err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  |  	return x.Get(&User{Email: email})
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -355,6 +371,25 @@ func VerifyUserActiveCode(code string) (user *User) {
 | 
	
		
			
				|  |  |  	return nil
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +// verify active code when active account
 | 
	
		
			
				|  |  | +func VerifyActiveEmailCode(code, email string) *EmailAddress {
 | 
	
		
			
				|  |  | +	minutes := setting.Service.ActiveCodeLives
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if user := getVerifyUser(code); user != nil {
 | 
	
		
			
				|  |  | +		// time limit code
 | 
	
		
			
				|  |  | +		prefix := code[:base.TimeLimitCodeLength]
 | 
	
		
			
				|  |  | +		data := com.ToStr(user.Id) + email + user.LowerName + user.Passwd + user.Rands
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		if base.VerifyTimeLimitCode(data, minutes, prefix) {
 | 
	
		
			
				|  |  | +			emailAddress := &EmailAddress{Email: email}
 | 
	
		
			
				|  |  | +			if has, _ := x.Get(emailAddress); has {
 | 
	
		
			
				|  |  | +				return emailAddress
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	return nil
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  // ChangeUserName changes all corresponding setting from old user name to new one.
 | 
	
		
			
				|  |  |  func ChangeUserName(u *User, newUserName string) (err error) {
 | 
	
		
			
				|  |  |  	if !IsLegalName(newUserName) {
 | 
	
	
		
			
				|  | @@ -488,6 +523,10 @@ func DeleteUser(u *User) error {
 | 
	
		
			
				|  |  |  	if _, err = x.Delete(&Access{UserName: u.LowerName}); err != nil {
 | 
	
		
			
				|  |  |  		return err
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | +	// Delete all alternative email addresses
 | 
	
		
			
				|  |  | +	if _, err = x.Delete(&EmailAddress{Uid: u.Id}); err != nil {
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  |  	// Delete all SSH keys.
 | 
	
		
			
				|  |  |  	keys := make([]*PublicKey, 0, 10)
 | 
	
		
			
				|  |  |  	if err = x.Find(&keys, &PublicKey{OwnerId: u.Id}); err != nil {
 | 
	
	
		
			
				|  | @@ -508,9 +547,12 @@ func DeleteUser(u *User) error {
 | 
	
		
			
				|  |  |  	return err
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -// DeleteInactivateUsers deletes all inactivate users.
 | 
	
		
			
				|  |  | +// DeleteInactivateUsers deletes all inactivate users and email addresses.
 | 
	
		
			
				|  |  |  func DeleteInactivateUsers() error {
 | 
	
		
			
				|  |  |  	_, err := x.Where("is_active=?", false).Delete(new(User))
 | 
	
		
			
				|  |  | +	if err == nil {
 | 
	
		
			
				|  |  | +		_, err = x.Where("is_activated=?", false).Delete(new(EmailAddress))
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  |  	return err
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -584,6 +626,117 @@ func GetUserIdsByNames(names []string) []int64 {
 | 
	
		
			
				|  |  |  	return ids
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +// Get all email addresses
 | 
	
		
			
				|  |  | +func GetEmailAddresses(uid int64) ([]*EmailAddress, error) {
 | 
	
		
			
				|  |  | +	emails := make([]*EmailAddress, 0, 5)
 | 
	
		
			
				|  |  | +	err := x.Where("owner_id=?", uid).Find(&emails)
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return nil, err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	u, err := GetUserById(uid)
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return nil, err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	isPrimaryFound := false
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	for _, email := range emails {
 | 
	
		
			
				|  |  | +		if email.Email == u.Email {
 | 
	
		
			
				|  |  | +			isPrimaryFound = true
 | 
	
		
			
				|  |  | +			email.IsPrimary = true
 | 
	
		
			
				|  |  | +		} else {
 | 
	
		
			
				|  |  | +			email.IsPrimary = false
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	// We alway want the primary email address displayed, even if it's not in
 | 
	
		
			
				|  |  | +	// the emailaddress table (yet)
 | 
	
		
			
				|  |  | +	if !isPrimaryFound {
 | 
	
		
			
				|  |  | +		emails = append(emails, &EmailAddress{Email: u.Email, IsActivated: true, IsPrimary: true})
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	return emails, nil
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +func AddEmailAddress(email *EmailAddress) error {
 | 
	
		
			
				|  |  | +	used, err := IsEmailUsed(email.Email)
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	} else if used {
 | 
	
		
			
				|  |  | +		return ErrEmailAlreadyUsed
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	_, err = x.Insert(email)
 | 
	
		
			
				|  |  | +	return err
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +func (email *EmailAddress) Activate() error {
 | 
	
		
			
				|  |  | +	email.IsActivated = true
 | 
	
		
			
				|  |  | +	if _, err := x.Id(email.Id).AllCols().Update(email); err != nil {
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if user, err := GetUserById(email.Uid); err != nil {
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	} else {
 | 
	
		
			
				|  |  | +		user.Rands = GetUserSalt()
 | 
	
		
			
				|  |  | +		return UpdateUser(user)
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +func DeleteEmailAddress(email *EmailAddress) error {
 | 
	
		
			
				|  |  | +	has, err := x.Get(email)
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	} else if !has {
 | 
	
		
			
				|  |  | +		return ErrEmailNotExist
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if _, err = x.Delete(email); err != nil {
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return nil
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +func MakeEmailPrimary(email *EmailAddress) error {
 | 
	
		
			
				|  |  | +	has, err := x.Get(email)
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	} else if !has {
 | 
	
		
			
				|  |  | +		return ErrEmailNotExist
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if !email.IsActivated {
 | 
	
		
			
				|  |  | +		return ErrEmailNotActivated
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	user := &User{Id: email.Uid}
 | 
	
		
			
				|  |  | +	has, err = x.Get(user)
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	} else if !has {
 | 
	
		
			
				|  |  | +		return ErrUserNotExist
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	// Make sure the former primary email doesn't disappear
 | 
	
		
			
				|  |  | +	former_primary_email := &EmailAddress{Email: user.Email}
 | 
	
		
			
				|  |  | +	has, err = x.Get(former_primary_email)
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	} else if !has {
 | 
	
		
			
				|  |  | +		former_primary_email.Uid = user.Id
 | 
	
		
			
				|  |  | +		former_primary_email.IsActivated = user.IsActive
 | 
	
		
			
				|  |  | +		x.Insert(former_primary_email)
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	user.Email = email.Email
 | 
	
		
			
				|  |  | +	_, err = x.Id(user.Id).AllCols().Update(user)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return err
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  // UserCommit represents a commit with validation of user.
 | 
	
		
			
				|  |  |  type UserCommit struct {
 | 
	
		
			
				|  |  |  	User *User
 | 
	
	
		
			
				|  | @@ -629,14 +782,27 @@ func GetUserByEmail(email string) (*User, error) {
 | 
	
		
			
				|  |  |  	if len(email) == 0 {
 | 
	
		
			
				|  |  |  		return nil, ErrUserNotExist
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | +	// First try to find the user by primary email
 | 
	
		
			
				|  |  |  	user := &User{Email: strings.ToLower(email)}
 | 
	
		
			
				|  |  |  	has, err := x.Get(user)
 | 
	
		
			
				|  |  |  	if err != nil {
 | 
	
		
			
				|  |  |  		return nil, err
 | 
	
		
			
				|  |  | -	} else if !has {
 | 
	
		
			
				|  |  | -		return nil, ErrUserNotExist
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | -	return user, nil
 | 
	
		
			
				|  |  | +	if has {
 | 
	
		
			
				|  |  | +		return user, nil
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	// Otherwise, check in alternative list for activated email addresses
 | 
	
		
			
				|  |  | +	emailAddress := &EmailAddress{Email: strings.ToLower(email), IsActivated: true}
 | 
	
		
			
				|  |  | +	has, err = x.Get(emailAddress)
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return nil, err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	if has {
 | 
	
		
			
				|  |  | +		return GetUserById(emailAddress.Uid)
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return nil, ErrUserNotExist
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // SearchUserByName returns given number of users whose name contains keyword.
 |