<commit_before>package arn

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/aerogo/http/client"
	"github.com/animenotifier/arn/autocorrect"
	"github.com/animenotifier/arn/validate"
	"github.com/animenotifier/ffxiv"
	"github.com/animenotifier/osu"
	gravatar "github.com/ungerik/go-gravatar"
)

var setNickMutex sync.Mutex
var setEmailMutex sync.Mutex

// User is a registered person.
type User struct {
	ID           string       `json:"id"`
	Nick         string       `json:"nick" editable:"true"`
	FirstName    string       `json:"firstName" private:"true"`
	LastName     string       `json:"lastName" private:"true"`
	Email        string       `json:"email" editable:"true" private:"true"`
	Role         string       `json:"role"`
	Registered   string       `json:"registered"`
	LastLogin    string       `json:"lastLogin" private:"true"`
	LastSeen     string       `json:"lastSeen" private:"true"`
	ProExpires   string       `json:"proExpires" editable:"true"`
	Gender       string       `json:"gender" private:"true"`
	Language     string       `json:"language"`
	Tagline      string       `json:"tagline" editable:"true"`
	Introduction string       `json:"introduction" editable:"true" type:"textarea"`
	Website      string       `json:"website" editable:"true"`
	IP           string       `json:"ip" private:"true"`
	UserAgent    string       `json:"agent" private:"true"`
	Balance      int          `json:"balance" private:"true"`
	Avatar       UserAvatar   `json:"avatar"`
	Cover        UserCover    `json:"cover"`
	AgeRange     UserAgeRange `json:"ageRange" private:"true"`
	Accounts     UserAccounts `json:"accounts" private:"true"`
	Browser      UserBrowser  `json:"browser" private:"true"`
	OS           UserOS       `json:"os" private:"true"`
	Location     *Location    `json:"location" private:"true"`

	// user.Email = ""
	// user.Gender = ""
	// user.FirstName = ""
	// user.LastName = ""
	// user.IP = ""
	// user.UserAgent = ""
	// user.LastLogin = ""
	// user.LastSeen = ""
	// user.Accounts.Facebook.ID = ""
	// user.Accounts.Google.ID = ""
	// user.Accounts.Twitter.ID = ""
	// user.AgeRange = UserAgeRange{}
	// user.Location = &Location{}
	// user.Browser = UserBrowser{}
	// user.OS = UserOS{}
}

// NewUser creates an empty user object with a unique ID.
func NewUser() *User {
	user := &User{
		ID: GenerateID("User"),

		// Avoid nil value fields
		Location: &Location{},
	}

	return user
}

// RegisterUser registers a new user in the database and sets up all the required references.
func RegisterUser(user *User) {
	user.Registered = DateTimeUTC()
	user.LastLogin = user.Registered
	user.LastSeen = user.Registered

	// Save nick in NickToUser table
	DB.Set("NickToUser", user.Nick, &NickToUser{
		Nick:   user.Nick,
		UserID: user.ID,
	})

	// Save email in EmailToUser table
	if user.Email != "" {
		DB.Set("EmailToUser", user.Email, &EmailToUser{
			Email:  user.Email,
			UserID: user.ID,
		})
	}

	// Create default settings
	NewSettings(user).Save()

	// Add empty anime list
	DB.Set("AnimeList", user.ID, &AnimeList{
		UserID: user.ID,
		Items:  []*AnimeListItem{},
	})

	// Add empty inventory
	NewInventory(user.ID).Save()

	// Add draft index
	NewDraftIndex(user.ID).Save()

	// Add empty push subscriptions
	DB.Set("PushSubscriptions", user.ID, &PushSubscriptions{
		UserID: user.ID,
		Items:  []*PushSubscription{},
	})

	// Add empty follow list
	NewUserFollows(user.ID).Save()

	// Add empty notifications list
	NewUserNotifications(user.ID).Save()

	// Fetch gravatar
	if user.Email != "" {
		gravatarURL := gravatar.Url(user.Email) + "?s=" + fmt.Sprint(AvatarMaxSize) + "&d=404&r=pg"
		gravatarURL = strings.Replace(gravatarURL, "http://", "https://", 1)

		response, err := client.Get(gravatarURL).End()

		if err == nil && response.StatusCode() == http.StatusOK {
			data := response.Bytes()
			user.SetAvatarBytes(data)
		}
	}
}

// SendNotification accepts a PushNotification and generates a new Notification object.
// The notification is then sent to all registered push devices.
func (user *User) SendNotification(pushNotification *PushNotification) {
	// Don't ever send notifications in development mode
	if IsDevelopment() && user.ID != "4J6qpK1ve" {
		return
	}

	// Save notification in database
	notification := NewNotification(user.ID, pushNotification)
	notification.Save()

	userNotifications := user.Notifications()
	userNotifications.Add(notification.ID)
	userNotifications.Save()

	// Send push notification
	subs := user.PushSubscriptions()
	expired := []*PushSubscription{}

	for _, sub := range subs.Items {
		resp, err := sub.SendNotification(pushNotification)

		if resp != nil && resp.StatusCode == http.StatusGone {
			expired = append(expired, sub)
			continue
		}

		// Print errors
		if err != nil {
			fmt.Println(err)
			continue
		}

		// Print bad status codes
		if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
			body, _ := ioutil.ReadAll(resp.Body)
			fmt.Println(resp.StatusCode, string(body))
			continue
		}

		sub.LastSuccess = DateTimeUTC()
	}

	// Remove expired items
	if len(expired) > 0 {
		for _, sub := range expired {
			subs.Remove(sub.ID())
		}
	}

	// Save changes
	subs.Save()
}

// RealName returns the real name of the user.
func (user *User) RealName() string {
	if user.LastName == "" {
		return user.FirstName
	}

	if user.FirstName == "" {
		return user.LastName
	}

	return user.FirstName + " " + user.LastName
}

// RegisteredTime returns the time the user registered his account.
func (user *User) RegisteredTime() time.Time {
	reg, _ := time.Parse(time.RFC3339, user.Registered)
	return reg
}

// LastSeenTime returns the time the user was last seen on the site.
func (user *User) LastSeenTime() time.Time {
	lastSeen, _ := time.Parse(time.RFC3339, user.LastSeen)
	return lastSeen
}

// IsActive tells you whether the user is active.
func (user *User) IsActive() bool {
	lastSeen, _ := time.Parse(time.RFC3339, user.LastSeen)
	twoWeeksAgo := time.Now().Add(-14 * 24 * time.Hour)

	if lastSeen.Unix() < twoWeeksAgo.Unix() {
		return false
	}

	// if !user.AnimeList().HasItemsWithStatus(AnimeListStatusWatching) {
	// 	return false
	// }

	return true
}

// IsPro returns whether the user is a PRO user or not.
func (user *User) IsPro() bool {
	if user.ProExpires == "" {
		return false
	}

	return DateTimeUTC() < user.ProExpires
}

// ExtendProDuration extends the PRO account duration by the given duration.
func (user *User) ExtendProDuration(duration time.Duration) {
	now := time.Now().UTC()
	expires, _ := time.Parse(time.RFC3339, user.ProExpires)

	// If the user never had a PRO account yet,
	// or if it already expired,
	// use the current time as the start time.
	if user.ProExpires == "" || now.Unix() > expires.Unix() {
		expires = now
	}

	user.ProExpires = expires.Add(duration).Format(time.RFC3339)
}

// TimeSinceRegistered returns the duration since the user registered his account.
func (user *User) TimeSinceRegistered() time.Duration {
	registered, _ := time.Parse(time.RFC3339, user.Registered)
	return time.Since(registered)
}

// HasNick returns whether the user has a custom nickname.
func (user *User) HasNick() bool {
	return !strings.HasPrefix(user.Nick, "g") && !strings.HasPrefix(user.Nick, "fb") && !strings.HasPrefix(user.Nick, "t") && user.Nick != ""
}

// WebsiteURL adds https:// to the URL.
func (user *User) WebsiteURL() string {
	return "https://" + user.WebsiteShortURL()
}

// WebsiteShortURL returns the user website without the protocol.
func (user *User) WebsiteShortURL() string {
	return strings.Replace(strings.Replace(user.Website, "https://", "", 1), "http://", "", 1)
}

// Link returns the URI to the user page.
func (user *User) Link() string {
	return "/+" + user.Nick
}

// HasAvatar tells you whether the user has an avatar or not.
func (user *User) HasAvatar() bool {
	return user.Avatar.Extension != ""
}

// AvatarLink returns the URL to the user avatar.
// Expects "small" (50 x 50) or "large" (560 x 560) for the size parameter.
func (user *User) AvatarLink(size string) string {
	if user.HasAvatar() {
		return fmt.Sprintf("//%s/images/avatars/%s/%s%s?%v", MediaHost, size, user.ID, user.Avatar.Extension, user.Avatar.LastModified)
	}

	return fmt.Sprintf("//%s/images/elements/no-avatar.svg", MediaHost)
}

// CoverLink ...
func (user *User) CoverLink(size string) string {
	if user.Cover.Extension != "" {
		return fmt.Sprintf("//%s/images/covers/%s/%s%s?%v", MediaHost, size, user.ID, user.Cover.Extension, user.Cover.LastModified)
	}

	return "/images/elements/default-cover.jpg"
}

// Gravatar returns the URL to the gravatar if an email has been registered.
func (user *User) Gravatar() string {
	if user.Email == "" {
		return ""
	}

	return gravatar.SecureUrl(user.Email) + "?s=" + fmt.Sprint(AvatarMaxSize)
}

// EditorScore returns the editor score.
func (user *User) EditorScore() int {
	ignoreDifferences := FilterIgnoreAnimeDifferences(func(entry *IgnoreAnimeDifference) bool {
		return entry.CreatedBy == user.ID
	})

	score := len(ignoreDifferences) * IgnoreAnimeDifferenceEditorScore

	logEntries := FilterEditLogEntries(func(entry *EditLogEntry) bool {
		return entry.UserID == user.ID
	})

	for _, entry := range logEntries {
		score += entry.EditorScore()
	}

	return score
}

// ActivateItemEffect activates an item in the user inventory by the given item ID.
func (user *User) ActivateItemEffect(itemID string) error {
	month := 30 * 24 * time.Hour

	switch itemID {
	case "pro-account-1":
		user.ExtendProDuration(1 * month)
		user.Save()
		return nil

	case "pro-account-3":
		user.ExtendProDuration(3 * month)
		user.Save()
		return nil

	case "pro-account-6":
		user.ExtendProDuration(6 * month)
		user.Save()
		return nil

	case "pro-account-12":
		user.ExtendProDuration(12 * month)
		user.Save()
		return nil

	case "pro-account-24":
		user.ExtendProDuration(24 * month)
		user.Save()
		return nil

	default:
		return errors.New("Can't activate unknown item: " + itemID)
	}
}

// SetNick changes the user's nickname safely.
func (user *User) SetNick(newName string) error {
	setNickMutex.Lock()
	defer setNickMutex.Unlock()

	newName = autocorrect.UserNick(newName)

	if !validate.Nick(newName) {
		return errors.New("Invalid nickname")
	}

	if newName == user.Nick {
		return nil
	}

	// Make sure the nickname doesn't exist already
	_, err := GetUserByNick(newName)

	// If there was no error: the username exists.
	// If "not found" is not included in the error message it's a different error type.
	if err == nil || !strings.Contains(err.Error(), "not found") {
		return errors.New("Username '" + newName + "' is taken already")
	}

	user.ForceSetNick(newName)
	return nil
}

// ForceSetNick forces a nickname overwrite.
func (user *User) ForceSetNick(newName string) {
	// Delete old nick reference
	DB.Delete("NickToUser", user.Nick)

	// Set new nick
	user.Nick = newName

	DB.Set("NickToUser", user.Nick, &NickToUser{
		Nick:   user.Nick,
		UserID: user.ID,
	})
}

// SetEmail changes the user's email safely.
func (user *User) SetEmail(newEmail string) error {
	setEmailMutex.Lock()
	defer setEmailMutex.Unlock()

	if !validate.Email(newEmail) {
		return errors.New("Invalid email address")
	}

	// Delete old email reference
	DB.Delete("EmailToUser", user.Email)

	// Set new email
	user.Email = newEmail

	DB.Set("EmailToUser", user.Email, &EmailToUser{
		Email:  user.Email,
		UserID: user.ID,
	})

	return nil
}

// RefreshOsuInfo refreshes a user's Osu information.
func (user *User) RefreshOsuInfo() error {
	if user.Accounts.Osu.Nick == "" {
		return nil
	}

	osu, err := osu.GetUser(user.Accounts.Osu.Nick)

	if err != nil {
		return err
	}

	user.Accounts.Osu.PP, _ = strconv.ParseFloat(osu.PPRaw, 64)
	user.Accounts.Osu.Level, _ = strconv.ParseFloat(osu.Level, 64)
	user.Accounts.Osu.Accuracy, _ = strconv.ParseFloat(osu.Accuracy, 64)

	return nil
}

// RefreshFFXIVInfo refreshes a user's FFXIV information.
func (user *User) RefreshFFXIVInfo() error {
	if user.Accounts.FinalFantasyXIV.Nick == "" || user.Accounts.FinalFantasyXIV.Server == "" {
		return nil
	}

	characterID, err := ffxiv.GetCharacterID(user.Accounts.FinalFantasyXIV.Nick, user.Accounts.FinalFantasyXIV.Server)

	if err != nil {
		return err
	}

	character, err := ffxiv.GetCharacter(characterID)

	if err != nil {
		return err
	}

	user.Accounts.FinalFantasyXIV.Class = character.Class
	user.Accounts.FinalFantasyXIV.Level = character.Level
	user.Accounts.FinalFantasyXIV.ItemLevel = character.ItemLevel

	return nil
}
<commit_msg>Updated activity condition<commit_after>package arn

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/aerogo/http/client"
	"github.com/animenotifier/arn/autocorrect"
	"github.com/animenotifier/arn/validate"
	"github.com/animenotifier/ffxiv"
	"github.com/animenotifier/osu"
	gravatar "github.com/ungerik/go-gravatar"
)

var setNickMutex sync.Mutex
var setEmailMutex sync.Mutex

// User is a registered person.
type User struct {
	ID           string       `json:"id"`
	Nick         string       `json:"nick" editable:"true"`
	FirstName    string       `json:"firstName" private:"true"`
	LastName     string       `json:"lastName" private:"true"`
	Email        string       `json:"email" editable:"true" private:"true"`
	Role         string       `json:"role"`
	Registered   string       `json:"registered"`
	LastLogin    string       `json:"lastLogin" private:"true"`
	LastSeen     string       `json:"lastSeen" private:"true"`
	ProExpires   string       `json:"proExpires" editable:"true"`
	Gender       string       `json:"gender" private:"true"`
	Language     string       `json:"language"`
	Tagline      string       `json:"tagline" editable:"true"`
	Introduction string       `json:"introduction" editable:"true" type:"textarea"`
	Website      string       `json:"website" editable:"true"`
	IP           string       `json:"ip" private:"true"`
	UserAgent    string       `json:"agent" private:"true"`
	Balance      int          `json:"balance" private:"true"`
	Avatar       UserAvatar   `json:"avatar"`
	Cover        UserCover    `json:"cover"`
	AgeRange     UserAgeRange `json:"ageRange" private:"true"`
	Accounts     UserAccounts `json:"accounts" private:"true"`
	Browser      UserBrowser  `json:"browser" private:"true"`
	OS           UserOS       `json:"os" private:"true"`
	Location     *Location    `json:"location" private:"true"`

	// user.Email = ""
	// user.Gender = ""
	// user.FirstName = ""
	// user.LastName = ""
	// user.IP = ""
	// user.UserAgent = ""
	// user.LastLogin = ""
	// user.LastSeen = ""
	// user.Accounts.Facebook.ID = ""
	// user.Accounts.Google.ID = ""
	// user.Accounts.Twitter.ID = ""
	// user.AgeRange = UserAgeRange{}
	// user.Location = &Location{}
	// user.Browser = UserBrowser{}
	// user.OS = UserOS{}
}

// NewUser creates an empty user object with a unique ID.
func NewUser() *User {
	user := &User{
		ID: GenerateID("User"),

		// Avoid nil value fields
		Location: &Location{},
	}

	return user
}

// RegisterUser registers a new user in the database and sets up all the required references.
func RegisterUser(user *User) {
	user.Registered = DateTimeUTC()
	user.LastLogin = user.Registered
	user.LastSeen = user.Registered

	// Save nick in NickToUser table
	DB.Set("NickToUser", user.Nick, &NickToUser{
		Nick:   user.Nick,
		UserID: user.ID,
	})

	// Save email in EmailToUser table
	if user.Email != "" {
		DB.Set("EmailToUser", user.Email, &EmailToUser{
			Email:  user.Email,
			UserID: user.ID,
		})
	}

	// Create default settings
	NewSettings(user).Save()

	// Add empty anime list
	DB.Set("AnimeList", user.ID, &AnimeList{
		UserID: user.ID,
		Items:  []*AnimeListItem{},
	})

	// Add empty inventory
	NewInventory(user.ID).Save()

	// Add draft index
	NewDraftIndex(user.ID).Save()

	// Add empty push subscriptions
	DB.Set("PushSubscriptions", user.ID, &PushSubscriptions{
		UserID: user.ID,
		Items:  []*PushSubscription{},
	})

	// Add empty follow list
	NewUserFollows(user.ID).Save()

	// Add empty notifications list
	NewUserNotifications(user.ID).Save()

	// Fetch gravatar
	if user.Email != "" {
		gravatarURL := gravatar.Url(user.Email) + "?s=" + fmt.Sprint(AvatarMaxSize) + "&d=404&r=pg"
		gravatarURL = strings.Replace(gravatarURL, "http://", "https://", 1)

		response, err := client.Get(gravatarURL).End()

		if err == nil && response.StatusCode() == http.StatusOK {
			data := response.Bytes()
			user.SetAvatarBytes(data)
		}
	}
}

// SendNotification accepts a PushNotification and generates a new Notification object.
// The notification is then sent to all registered push devices.
func (user *User) SendNotification(pushNotification *PushNotification) {
	// Don't ever send notifications in development mode
	if IsDevelopment() && user.ID != "4J6qpK1ve" {
		return
	}

	// Save notification in database
	notification := NewNotification(user.ID, pushNotification)
	notification.Save()

	userNotifications := user.Notifications()
	userNotifications.Add(notification.ID)
	userNotifications.Save()

	// Send push notification
	subs := user.PushSubscriptions()
	expired := []*PushSubscription{}

	for _, sub := range subs.Items {
		resp, err := sub.SendNotification(pushNotification)

		if resp != nil && resp.StatusCode == http.StatusGone {
			expired = append(expired, sub)
			continue
		}

		// Print errors
		if err != nil {
			fmt.Println(err)
			continue
		}

		// Print bad status codes
		if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
			body, _ := ioutil.ReadAll(resp.Body)
			fmt.Println(resp.StatusCode, string(body))
			continue
		}

		sub.LastSuccess = DateTimeUTC()
	}

	// Remove expired items
	if len(expired) > 0 {
		for _, sub := range expired {
			subs.Remove(sub.ID())
		}
	}

	// Save changes
	subs.Save()
}

// RealName returns the real name of the user.
func (user *User) RealName() string {
	if user.LastName == "" {
		return user.FirstName
	}

	if user.FirstName == "" {
		return user.LastName
	}

	return user.FirstName + " " + user.LastName
}

// RegisteredTime returns the time the user registered his account.
func (user *User) RegisteredTime() time.Time {
	reg, _ := time.Parse(time.RFC3339, user.Registered)
	return reg
}

// LastSeenTime returns the time the user was last seen on the site.
func (user *User) LastSeenTime() time.Time {
	lastSeen, _ := time.Parse(time.RFC3339, user.LastSeen)
	return lastSeen
}

// IsActive tells you whether the user is active.
func (user *User) IsActive() bool {
	lastSeen, _ := time.Parse(time.RFC3339, user.LastSeen)
	twoWeeksAgo := time.Now().Add(-14 * 24 * time.Hour)

	if lastSeen.Unix() < twoWeeksAgo.Unix() {
		return false
	}

	if len(user.AnimeList().Items) == 0 {
		return false
	}

	return true
}

// IsPro returns whether the user is a PRO user or not.
func (user *User) IsPro() bool {
	if user.ProExpires == "" {
		return false
	}

	return DateTimeUTC() < user.ProExpires
}

// ExtendProDuration extends the PRO account duration by the given duration.
func (user *User) ExtendProDuration(duration time.Duration) {
	now := time.Now().UTC()
	expires, _ := time.Parse(time.RFC3339, user.ProExpires)

	// If the user never had a PRO account yet,
	// or if it already expired,
	// use the current time as the start time.
	if user.ProExpires == "" || now.Unix() > expires.Unix() {
		expires = now
	}

	user.ProExpires = expires.Add(duration).Format(time.RFC3339)
}

// TimeSinceRegistered returns the duration since the user registered his account.
func (user *User) TimeSinceRegistered() time.Duration {
	registered, _ := time.Parse(time.RFC3339, user.Registered)
	return time.Since(registered)
}

// HasNick returns whether the user has a custom nickname.
func (user *User) HasNick() bool {
	return !strings.HasPrefix(user.Nick, "g") && !strings.HasPrefix(user.Nick, "fb") && !strings.HasPrefix(user.Nick, "t") && user.Nick != ""
}

// WebsiteURL adds https:// to the URL.
func (user *User) WebsiteURL() string {
	return "https://" + user.WebsiteShortURL()
}

// WebsiteShortURL returns the user website without the protocol.
func (user *User) WebsiteShortURL() string {
	return strings.Replace(strings.Replace(user.Website, "https://", "", 1), "http://", "", 1)
}

// Link returns the URI to the user page.
func (user *User) Link() string {
	return "/+" + user.Nick
}

// HasAvatar tells you whether the user has an avatar or not.
func (user *User) HasAvatar() bool {
	return user.Avatar.Extension != ""
}

// AvatarLink returns the URL to the user avatar.
// Expects "small" (50 x 50) or "large" (560 x 560) for the size parameter.
func (user *User) AvatarLink(size string) string {
	if user.HasAvatar() {
		return fmt.Sprintf("//%s/images/avatars/%s/%s%s?%v", MediaHost, size, user.ID, user.Avatar.Extension, user.Avatar.LastModified)
	}

	return fmt.Sprintf("//%s/images/elements/no-avatar.svg", MediaHost)
}

// CoverLink ...
func (user *User) CoverLink(size string) string {
	if user.Cover.Extension != "" {
		return fmt.Sprintf("//%s/images/covers/%s/%s%s?%v", MediaHost, size, user.ID, user.Cover.Extension, user.Cover.LastModified)
	}

	return "/images/elements/default-cover.jpg"
}

// Gravatar returns the URL to the gravatar if an email has been registered.
func (user *User) Gravatar() string {
	if user.Email == "" {
		return ""
	}

	return gravatar.SecureUrl(user.Email) + "?s=" + fmt.Sprint(AvatarMaxSize)
}

// EditorScore returns the editor score.
func (user *User) EditorScore() int {
	ignoreDifferences := FilterIgnoreAnimeDifferences(func(entry *IgnoreAnimeDifference) bool {
		return entry.CreatedBy == user.ID
	})

	score := len(ignoreDifferences) * IgnoreAnimeDifferenceEditorScore

	logEntries := FilterEditLogEntries(func(entry *EditLogEntry) bool {
		return entry.UserID == user.ID
	})

	for _, entry := range logEntries {
		score += entry.EditorScore()
	}

	return score
}

// ActivateItemEffect activates an item in the user inventory by the given item ID.
func (user *User) ActivateItemEffect(itemID string) error {
	month := 30 * 24 * time.Hour

	switch itemID {
	case "pro-account-1":
		user.ExtendProDuration(1 * month)
		user.Save()
		return nil

	case "pro-account-3":
		user.ExtendProDuration(3 * month)
		user.Save()
		return nil

	case "pro-account-6":
		user.ExtendProDuration(6 * month)
		user.Save()
		return nil

	case "pro-account-12":
		user.ExtendProDuration(12 * month)
		user.Save()
		return nil

	case "pro-account-24":
		user.ExtendProDuration(24 * month)
		user.Save()
		return nil

	default:
		return errors.New("Can't activate unknown item: " + itemID)
	}
}

// SetNick changes the user's nickname safely.
func (user *User) SetNick(newName string) error {
	setNickMutex.Lock()
	defer setNickMutex.Unlock()

	newName = autocorrect.UserNick(newName)

	if !validate.Nick(newName) {
		return errors.New("Invalid nickname")
	}

	if newName == user.Nick {
		return nil
	}

	// Make sure the nickname doesn't exist already
	_, err := GetUserByNick(newName)

	// If there was no error: the username exists.
	// If "not found" is not included in the error message it's a different error type.
	if err == nil || !strings.Contains(err.Error(), "not found") {
		return errors.New("Username '" + newName + "' is taken already")
	}

	user.ForceSetNick(newName)
	return nil
}

// ForceSetNick forces a nickname overwrite.
func (user *User) ForceSetNick(newName string) {
	// Delete old nick reference
	DB.Delete("NickToUser", user.Nick)

	// Set new nick
	user.Nick = newName

	DB.Set("NickToUser", user.Nick, &NickToUser{
		Nick:   user.Nick,
		UserID: user.ID,
	})
}

// SetEmail changes the user's email safely.
func (user *User) SetEmail(newEmail string) error {
	setEmailMutex.Lock()
	defer setEmailMutex.Unlock()

	if !validate.Email(newEmail) {
		return errors.New("Invalid email address")
	}

	// Delete old email reference
	DB.Delete("EmailToUser", user.Email)

	// Set new email
	user.Email = newEmail

	DB.Set("EmailToUser", user.Email, &EmailToUser{
		Email:  user.Email,
		UserID: user.ID,
	})

	return nil
}

// RefreshOsuInfo refreshes a user's Osu information.
func (user *User) RefreshOsuInfo() error {
	if user.Accounts.Osu.Nick == "" {
		return nil
	}

	osu, err := osu.GetUser(user.Accounts.Osu.Nick)

	if err != nil {
		return err
	}

	user.Accounts.Osu.PP, _ = strconv.ParseFloat(osu.PPRaw, 64)
	user.Accounts.Osu.Level, _ = strconv.ParseFloat(osu.Level, 64)
	user.Accounts.Osu.Accuracy, _ = strconv.ParseFloat(osu.Accuracy, 64)

	return nil
}

// RefreshFFXIVInfo refreshes a user's FFXIV information.
func (user *User) RefreshFFXIVInfo() error {
	if user.Accounts.FinalFantasyXIV.Nick == "" || user.Accounts.FinalFantasyXIV.Server == "" {
		return nil
	}

	characterID, err := ffxiv.GetCharacterID(user.Accounts.FinalFantasyXIV.Nick, user.Accounts.FinalFantasyXIV.Server)

	if err != nil {
		return err
	}

	character, err := ffxiv.GetCharacter(characterID)

	if err != nil {
		return err
	}

	user.Accounts.FinalFantasyXIV.Class = character.Class
	user.Accounts.FinalFantasyXIV.Level = character.Level
	user.Accounts.FinalFantasyXIV.ItemLevel = character.ItemLevel

	return nil
}
<|endoftext|>


<commit_before>/*
                      adbs - adb with serial number

         Copyright (c) 2012 Soichiro Kashima. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

func main() {
	// Check adb's availability
	if !hasAdb() {
		fmt.Printf(`'adb' command not found.
The 'adbs' tool uses adb(Android Debug Bridge).
Please install the Android SDK and add
the directory which has adb command to
the environment variable 'PATH'.
`)
		os.Exit(1)
	}

	// Get args and show usage
	serial := flag.String("s", "", "Serial number(forward match)")
	needHelp := flag.Bool("h", false, "Show this message")
	flag.Parse()
	if *needHelp {
		fmt.Printf(`Usage: adbs [[OPTIONS] ADB_COMMAND|-h]
  -h            - Show this help
  OPTIONS:
    -s SERIAL   - Serial number of target device.
                  You don't need to input complete serial number.
                  Just part of it is okay. (forward match)
  ADB_COMMAND   - command string to pass to the device.
`)
		os.Exit(1)
	}

	var matched string

	// Get device serial from input
	if *serial == "" {
		c := exec.Command("adb", "devices")
		stdout, err := c.StdoutPipe()
		if err != nil {
			fmt.Println("Failed to check devices")
		}
		c.Start()
		r := bufio.NewReader(stdout)
		candidates := []string{}
		err = nil
		for i := 1; err == nil; {
			var line []byte
			line, _, err = r.ReadLine()
			sline := string(line)
			if strings.HasPrefix(sline, "List of devices attached") {
				continue
			}
			if sline == "" {
				continue
			}
			candidateSerial := regexp.MustCompile("^[0-9a-zA-Z]+").FindString(sline)
			candidates = append(candidates, candidateSerial)
			i++
		}
		if len(candidates) == 0 {
			fmt.Println("No device attached")
			os.Exit(1)
		} else if len(candidates) == 1 {
			// This is the only device attached
			matched = candidates[0]
		} else {
			for i := range candidates {
				fmt.Printf("[%d] %s\n", i+1, candidates[i])
			}
			var input int
			fmt.Printf("Device to execute command: ")
			fmt.Scanf("%d", &input)
			if 1 <= input && input <= len(candidates) {
				matched = candidates[input-1]
				fmt.Printf("Specified: %s\n", matched)
			} else {
				fmt.Printf("Invalid number: %d\n", input)
				os.Exit(1)
			}
		}
		if matched == "" {
			fmt.Println("Serial not specified")
			os.Exit(1)
		}
	} else {
		// Find specified device
		c := exec.Command("adb", "devices")
		stdout, err := c.StdoutPipe()
		if err != nil {
			fmt.Println("Failed to check devices")
		}
		c.Start()
		r := bufio.NewReader(stdout)
		err = nil
		candidates := []string{}
		for err == nil {
			var line []byte
			line, _, err = r.ReadLine()
			sline := string(line)
			if strings.HasPrefix(sline, "List of devices attached") {
				continue
			}
			if sline == "" {
				continue
			}
			regex := regexp.MustCompile("^" + *serial + "[0-9a-zA-Z]*")
			candidateMatched := regex.FindString(sline)
			if candidateMatched != "" {
				candidates = append(candidates, candidateMatched)
			}
		}
		if len(candidates) == 0 {
			fmt.Println("Specified device not found\n")
			os.Exit(1)
		} else if 1 < len(candidates) {
			fmt.Println("Multiple candidate devices found:")
			for i := range candidates {
				fmt.Printf("[%d] %s\n", i+1, candidates[i])
			}
			os.Exit(1)
		}
		matched = candidates[0]
		fmt.Printf("adbs: serial: %s\n", matched)
	}

	// Give adb command to device
	args := retrieveRestArgs()
	c := exec.Command("adb", "-s", matched, args)
	c.Stdout = os.Stdout
	c.Stderr = os.Stderr
	c.Stdin = os.Stdin
	c.Run()
}

func hasAdb() bool {
	path, err := exec.LookPath("adb")
	return path != "" && err == nil
}

func retrieveRestArgs() string {
	arrayArgs := flag.Args()
	allArgs := ""
	for i := range arrayArgs {
		allArgs = allArgs + arrayArgs[i]
	}
	return allArgs
}
<commit_msg>Fixed a bug that serial numbers with IP address and port (e.g. Genymotion device) are ignored.<commit_after>/*
                      adbs - adb with serial number

         Copyright (c) 2012 Soichiro Kashima. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

func main() {
	// Check adb's availability
	if !hasAdb() {
		fmt.Printf(`'adb' command not found.
The 'adbs' tool uses adb(Android Debug Bridge).
Please install the Android SDK and add
the directory which has adb command to
the environment variable 'PATH'.
`)
		os.Exit(1)
	}

	// Get args and show usage
	serial := flag.String("s", "", "Serial number(forward match)")
	needHelp := flag.Bool("h", false, "Show this message")
	flag.Parse()
	if *needHelp {
		fmt.Printf(`Usage: adbs [[OPTIONS] ADB_COMMAND|-h]
  -h            - Show this help
  OPTIONS:
    -s SERIAL   - Serial number of target device.
                  You don't need to input complete serial number.
                  Just part of it is okay. (forward match)
  ADB_COMMAND   - command string to pass to the device.
`)
		os.Exit(1)
	}

	var matched string

	// Get device serial from input
	if *serial == "" {
		c := exec.Command("adb", "devices")
		stdout, err := c.StdoutPipe()
		if err != nil {
			fmt.Println("Failed to check devices")
		}
		c.Start()
		r := bufio.NewReader(stdout)
		candidates := []string{}
		err = nil
		for i := 1; err == nil; {
			var line []byte
			line, _, err = r.ReadLine()
			sline := string(line)
			if strings.HasPrefix(sline, "List of devices attached") {
				continue
			}
			if sline == "" {
				continue
			}
			candidateSerial := regexp.MustCompile("^[0-9a-zA-Z\\.:]+").FindString(sline)
			candidates = append(candidates, candidateSerial)
			i++
		}
		if len(candidates) == 0 {
			fmt.Println("No device attached")
			os.Exit(1)
		} else if len(candidates) == 1 {
			// This is the only device attached
			matched = candidates[0]
		} else {
			for i := range candidates {
				fmt.Printf("[%d] %s\n", i+1, candidates[i])
			}
			var input int
			fmt.Printf("Device to execute command: ")
			fmt.Scanf("%d", &input)
			if 1 <= input && input <= len(candidates) {
				matched = candidates[input-1]
				fmt.Printf("Specified: %s\n", matched)
			} else {
				fmt.Printf("Invalid number: %d\n", input)
				os.Exit(1)
			}
		}
		if matched == "" {
			fmt.Println("Serial not specified")
			os.Exit(1)
		}
	} else {
		// Find specified device
		c := exec.Command("adb", "devices")
		stdout, err := c.StdoutPipe()
		if err != nil {
			fmt.Println("Failed to check devices")
		}
		c.Start()
		r := bufio.NewReader(stdout)
		err = nil
		candidates := []string{}
		for err == nil {
			var line []byte
			line, _, err = r.ReadLine()
			sline := string(line)
			if strings.HasPrefix(sline, "List of devices attached") {
				continue
			}
			if sline == "" {
				continue
			}
			regex := regexp.MustCompile("^" + *serial + "[0-9a-zA-Z\\.:]*")
			candidateMatched := regex.FindString(sline)
			if candidateMatched != "" {
				candidates = append(candidates, candidateMatched)
			}
		}
		if len(candidates) == 0 {
			fmt.Println("Specified device not found\n")
			os.Exit(1)
		} else if 1 < len(candidates) {
			fmt.Println("Multiple candidate devices found:")
			for i := range candidates {
				fmt.Printf("[%d] %s\n", i+1, candidates[i])
			}
			os.Exit(1)
		}
		matched = candidates[0]
		fmt.Printf("adbs: serial: %s\n", matched)
	}

	// Give adb command to device
	args := retrieveRestArgs()
	c := exec.Command("adb", "-s", matched, args)
	c.Stdout = os.Stdout
	c.Stderr = os.Stderr
	c.Stdin = os.Stdin
	c.Run()
}

func hasAdb() bool {
	path, err := exec.LookPath("adb")
	return path != "" && err == nil
}

func retrieveRestArgs() string {
	arrayArgs := flag.Args()
	allArgs := ""
	for i := range arrayArgs {
		allArgs = allArgs + arrayArgs[i]
	}
	return allArgs
}
<|endoftext|>


<commit_before>package jwd

import (
	"math"
	"strings"
)

// According to this tool: http://www.csun.edu/english/edit_distance.php
// parameter order should have no difference in the result. Therefore,
// to avoid panicing later on, we will order the strings according to
// their length.
func order(s1, s2 string) (string, string) {
	if strings.Count(s1, "")-1 <= strings.Count(s2, "")-1 {
		return s1, s2
	} else {
		return s2, s1
	}

}

// Calculates Jaro-Winkler distance of two strings. The function lowercases and sorts the parameters
// so that that the longest string is evaluated against the shorter one.
func Calculate(s1, s2 string) float64 {

	s1, s2 = order(strings.ToLower(s1), strings.ToLower(s2))

	// This avoids the function to return NaN.
	if strings.Count(s1, "") == 1 || strings.Count(s2, "") == 1 {
		return float64(0)
	}

	// m as `matching characters`
	// t as `transposition`
	// l as `the length of common prefix at the start of the string up to a maximum of 4 characters`. Not relevant in Jaro distance.
	// See more: https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance
	m := 0
	t := 0
	l := 0

	window := math.Floor(float64(math.Max(float64(len(s1)), float64(len(s2)))/2) - 1)

	//debug:
	//fmt.Println("s1 param:", s1, "s2 param:", s2)
	//fmt.Println("Match window:", window, "s1:", len(s1), "s2:", len(s2))

	for i := 0; i < len(s1); i++ {
		// exact match
		if s1[i] == s2[i] {
			m++
			if i == l && i < 4 {
				l++
			}
		} else {
			if strings.Contains(s2, string(s1[i])) {
				// The character is considered matching if the amount of characters between the occurances in s1 and s2
				// (here `gap`) is less than match window `window`
				gap := strings.Index(s2, string(s1[i])) - strings.Index(s1, string(s1[i]))
				if gap < int(window) {
					m++
					// Somewhere in here is an error, which causes slight (max 0.04 from what I've come across, compared
					// with http://www.csun.edu/english/edit_distance.php) variations in some answers (for example:
					// dicksonx and dixonsdsgese).
					// As far as I understood, transposition is only count when character exists in the string and in
					// reach of window. This loop checks whether the end of the `s2` string contains characters which
					// exist in `s1` string.
					// Editing the for loop to the following:
					//
					// 		for k := i; k < len(s1)-i; k++ {
					// 			if strings.Index(s2, string(s1[k])) < i {
					// 				fmt.Println(string(s1[k]), string(s1[i]))
					// 				t++
					// 			}
					// 		}
					//
					// Will yield same results as the libary I took example of:
					// 	https://github.com/NaturalNode/natural/blob/master/lib/natural/distance/jaro-winkler_distance.js
					// However, then the answer will be different from the ones in Wikipedia at:
					// 	https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance
					// It's a mystery to me which one is right and which one is wrong, but I'll bet on the accuracy of
					// http://www.csun.edu/english/edit_distance.php and therefore will remain the loop as following to
					// provide as accurate answer as I can.
					for k := i; k < len(s1); k++ {
						if strings.Index(s2, string(s1[k])) < i {
							t++
						}
					}
				}
			}
		}
	}

	distance := (float64(m)/float64(len(s1)) + float64(m)/float64(len(s2)) + (float64(m)-math.Floor(float64(t)/float64(2)))/float64(m)) / float64(3)
	jwd := distance + (float64(l) * float64(0.1) * (float64(1) - distance))

	//debug:
	//fmt.Println("transpositions:",tf, "m:",m)
	//fmt.Println("distance:",distance, "l:", l)
	//fmt.Println("JWD:", jwd)

	return jwd

}
<commit_msg>algorithm: fix transposition and match window counting<commit_after>package jwd

import (
	"math"
	"strings"
)

// According to this tool: http://www.csun.edu/english/edit_distance.php
// parameter order should have no difference in the result. Therefore,
// to avoid panicing later on, we will order the strings according to
// their length.
func order(s1, s2 string) (string, string) {
	if strings.Count(s1, "")-1 <= strings.Count(s2, "")-1 {
		return s1, s2
	} else {
		return s2, s1
	}

}

// Calculates Jaro-Winkler distance of two strings. The function lowercases and sorts the parameters
// so that that the longest string is evaluated against the shorter one.
func Calculate(s1, s2 string) float64 {

	s1, s2 = order(strings.ToLower(s1), strings.ToLower(s2))

	// This avoids the function to return NaN.
	if strings.Count(s1, "") == 1 || strings.Count(s2, "") == 1 {
		return float64(0)
	}

	// m as `matching characters`
	// t as `transposition`
	// l as `the length of common prefix at the start of the string up to a maximum of 4 characters`.
	// See more: https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance
	m := 0
	t := 0
	l := 0

	window := math.Floor(float64(math.Max(float64(len(s1)), float64(len(s2)))/2) - 1)

	//debug:
	//fmt.Println("s1:", s1, "s2:", s2)
	//fmt.Println("Match window:", window)
	//fmt.Println("len(s1):", len(s1), "len(s2):", len(s2))

	for i := 0; i < len(s1); i++ {
		// Exact match
		if s1[i] == s2[i] {
			m++
			// Common prefix limitter
			if i == l && i < 4 {
				l++
			}
		} else {
			if strings.Contains(s2, string(s1[i])) {
				// The character is also considered matching if the amount of characters between the occurances in s1 and s2
				// is less than match window
				gap := strings.Index(s2, string(s1[i])) - strings.Index(s1, string(s1[i]))
				if gap <= int(window) {
					m++
					// Check if transposition is in reach of window
					for k := i; k < len(s1); k++ {
						if strings.Index(s2, string(s1[k])) <= i {
							t++
						}
					}
				}
			}
		}
	}

	distance := (float64(m)/float64(len(s1)) + float64(m)/float64(len(s2)) + (float64(m)-math.Floor(float64(t)/float64(2)))/float64(m)) / float64(3)
	jwd := distance + (float64(l) * float64(0.1) * (float64(1) - distance))

	//debug:
	//fmt.Println("- transpositions:", t)
	//fmt.Println("- matches:", m)
	//fmt.Println("- l:", l)
	//fmt.Println(jwd)

	return jwd

}
<|endoftext|>


<commit_before>package cfclient

import (
	"encoding/json"
	"io/ioutil"
	"log"
)

type AppResponse struct {
	Count     int           `json:"total_results"`
	Pages     int           `json:"total_pages"`
	Resources []AppResource `json:"resources"`
}

type AppResource struct {
	Meta   Meta `json:"metadata"`
	Entity App  `json:"entity"`
}

type App struct {
	Guid        string            `json:"guid"`
	Name        string            `json:"name"`
	Environment map[string]string `json:"environment_json"`
	SpaceURL    string            `json:"space_url"`
	SpaceData   SpaceResource     `json:"space"`
	c           *Client
}

func (a *App) Space() Space {
	var spaceResource SpaceResource
	r := a.c.newRequest("GET", a.SpaceURL)
	resp, err := a.c.doRequest(r)
	if err != nil {
		log.Printf("Error requesting space %v", err)
	}
	resBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Error reading space request %v", resBody)
	}

	err = json.Unmarshal(resBody, &spaceResource)
	if err != nil {
		log.Printf("Error unmarshaling space %v", err)
	}
	spaceResource.Entity.Guid = spaceResource.Meta.Guid
	spaceResource.Entity.c = a.c
	return spaceResource.Entity
}

func (c *Client) ListApps() []App {
	var apps []App
	var appResp AppResponse
	r := c.newRequest("GET", "/v2/apps?inline-relations-depth=2")
	resp, err := c.doRequest(r)

	if err != nil {
		log.Printf("Error requesting apps %v", err)
	}
	resBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Error reading app request %v", resBody)
	}

	err = json.Unmarshal(resBody, &appResp)
	if err != nil {
		log.Printf("Error unmarshaling app %v", err)
	}
	for _, app := range appResp.Resources {
		app.Entity.Guid = app.Meta.Guid
		app.Entity.SpaceData.Entity.Guid = app.Entity.SpaceData.Meta.Guid
		app.Entity.SpaceData.Entity.OrgData.Entity.Guid = app.Entity.SpaceData.Entity.OrgData.Meta.Guid
		app.Entity.c = c
		apps = append(apps, app.Entity)
	}
	return apps
}

func (c *Client) AppByGuid(guid string) App {
	var appResource AppResource
	r := c.newRequest("GET", "/v2/apps/"+guid+"?inline-relations-depth=2")
	resp, err := c.doRequest(r)
	if err != nil {
		log.Printf("Error requesting apps %v", err)
	}
	resBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Error reading app request %v", resBody)
	}

	err = json.Unmarshal(resBody, &appResource)
	if err != nil {
		log.Printf("Error unmarshaling app %v", err)
	}
	appResource.Entity.Guid = appResource.Meta.Guid
	appResource.Entity.SpaceData.Entity.Guid = appResource.Entity.SpaceData.Meta.Guid
	appResource.Entity.SpaceData.Entity.OrgData.Entity.Guid = appResource.Entity.SpaceData.Entity.OrgData.Meta.Guid
	appResource.Entity.c = c
	return appResource.Entity
}
<commit_msg>Fix failing test.<commit_after>package cfclient

import (
	"encoding/json"
	"io/ioutil"
	"log"
)

type AppResponse struct {
	Count     int           `json:"total_results"`
	Pages     int           `json:"total_pages"`
	Resources []AppResource `json:"resources"`
}

type AppResource struct {
	Meta   Meta `json:"metadata"`
	Entity App  `json:"entity"`
}

type App struct {
	Guid        string                 `json:"guid"`
	Name        string                 `json:"name"`
	Environment map[string]interface{} `json:"environment_json"`
	SpaceURL    string                 `json:"space_url"`
	SpaceData   SpaceResource          `json:"space"`
	c           *Client
}

func (a *App) Space() Space {
	var spaceResource SpaceResource
	r := a.c.newRequest("GET", a.SpaceURL)
	resp, err := a.c.doRequest(r)
	if err != nil {
		log.Printf("Error requesting space %v", err)
	}
	resBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Error reading space request %v", resBody)
	}

	err = json.Unmarshal(resBody, &spaceResource)
	if err != nil {
		log.Printf("Error unmarshaling space %v", err)
	}
	spaceResource.Entity.Guid = spaceResource.Meta.Guid
	spaceResource.Entity.c = a.c
	return spaceResource.Entity
}

func (c *Client) ListApps() []App {
	var apps []App
	var appResp AppResponse
	r := c.newRequest("GET", "/v2/apps?inline-relations-depth=2")
	resp, err := c.doRequest(r)

	if err != nil {
		log.Printf("Error requesting apps %v", err)
	}
	resBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Error reading app request %v", resBody)
	}

	err = json.Unmarshal(resBody, &appResp)
	if err != nil {
		log.Printf("Error unmarshaling app %v", err)
	}
	for _, app := range appResp.Resources {
		app.Entity.Guid = app.Meta.Guid
		app.Entity.SpaceData.Entity.Guid = app.Entity.SpaceData.Meta.Guid
		app.Entity.SpaceData.Entity.OrgData.Entity.Guid = app.Entity.SpaceData.Entity.OrgData.Meta.Guid
		app.Entity.c = c
		apps = append(apps, app.Entity)
	}
	return apps
}

func (c *Client) AppByGuid(guid string) App {
	var appResource AppResource
	r := c.newRequest("GET", "/v2/apps/"+guid+"?inline-relations-depth=2")
	resp, err := c.doRequest(r)
	if err != nil {
		log.Printf("Error requesting apps %v", err)
	}
	resBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Error reading app request %v", resBody)
	}

	err = json.Unmarshal(resBody, &appResource)
	if err != nil {
		log.Printf("Error unmarshaling app %v", err)
	}
	appResource.Entity.Guid = appResource.Meta.Guid
	appResource.Entity.SpaceData.Entity.Guid = appResource.Entity.SpaceData.Meta.Guid
	appResource.Entity.SpaceData.Entity.OrgData.Entity.Guid = appResource.Entity.SpaceData.Entity.OrgData.Meta.Guid
	appResource.Entity.c = c
	return appResource.Entity
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"path"
	"path/filepath"

	"github.com/jweslley/procker"
)

type App struct {
	server
	dir       string
	env       []string
	processes map[string]string
	process   procker.Process
}

func (a *App) Start() error {
	if a.Started() {
		return fmt.Errorf("bam: %s already started", a.Name())
	}

	a.process = a.buildProcess()
	return a.process.Start()
}

func (a *App) Stop() error {
	if !a.Started() {
		return fmt.Errorf("bam: %s not started", a.Name())
	}

	err := a.process.Stop(1000)
	a.process = nil
	return err
}

func (a *App) Started() bool {
	return a.process != nil && a.process.Running()
}

func (a *App) buildProcess() procker.Process {
	port, _ := FreePort()
	a.port = port
	p := []procker.Process{}
	for name, command := range a.processes {
		prefix := fmt.Sprintf("[%s:%s] ", a.Name(), name)
		process := procker.NewProcess(
			command,
			a.dir,
			append(a.env, fmt.Sprintf("PORT=%d", port)),
			procker.NewPrefixedWriter(os.Stdout, prefix),
			procker.NewPrefixedWriter(os.Stderr, prefix))
		p = append(p, process)
	}
	return procker.NewProcessGroup(p...)
}

func LoadApps(dir string) []*App {
	apps := []*App{}
	procfiles, _ := filepath.Glob(fmt.Sprintf("%s/*/Procfile", dir))
	for _, p := range procfiles {
		app, err := newApp(p)
		if err != nil {
			log.Printf("Unable to load application %s. Error: %s\n", p, err.Error())
		} else {
			apps = append(apps, app)
		}
	}
	return apps
}

func newApp(procfile string) (*App, error) {
	processes, err := parseProfile(procfile)
	if err != nil {
		log.Printf("Unable to load Procfile %s: %s\n", procfile, err)
		return nil, err
	}

	dir := path.Dir(procfile)
	name := path.Base(dir)
	envFile := path.Join(dir, ".env")
	env, err := parseEnv(envFile)
	if err != nil {
		log.Printf("Unable to load env file %s: %s\n", envFile, err)
		env = []string{}
	}

	a := &App{dir: dir}
	a.name = name
	a.env = env
	a.processes = processes
	return a, nil
}

func parseProfile(filepath string) (map[string]string, error) {
	file, err := os.Open(filepath)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	processes, err := procker.ParseProcfile(file)
	if err != nil {
		return nil, err
	}
	return processes, nil
}

func parseEnv(filepath string) ([]string, error) {
	file, err := os.Open(filepath)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	env, err := procker.ParseEnv(file)
	if err != nil {
		return nil, err
	}
	return env, nil
}
<commit_msg>update procker<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"path"
	"path/filepath"

	"github.com/jweslley/procker"
)

type App struct {
	server
	dir       string
	env       []string
	processes map[string]string
	process   procker.Process
}

func (a *App) Start() error {
	if a.Started() {
		return fmt.Errorf("bam: %s already started", a.Name())
	}

	a.process = a.buildProcess()
	return a.process.Start()
}

func (a *App) Stop() error {
	if !a.Started() {
		return fmt.Errorf("bam: %s not started", a.Name())
	}

	err := a.process.Stop(1000)
	a.process = nil
	return err
}

func (a *App) Started() bool {
	return a.process != nil && a.process.Running()
}

func (a *App) buildProcess() procker.Process {
	port, _ := FreePort()
	a.port = port
	p := []procker.Process{}
	for name, command := range a.processes {
		prefix := fmt.Sprintf("[%s:%s] ", a.Name(), name)
		process := &procker.SysProcess{
			Command: command,
			Dir:     a.dir,
			Env:     append(a.env, fmt.Sprintf("PORT=%d", port)),
			Stdout:  procker.NewPrefixedWriter(os.Stdout, prefix),
			Stderr:  procker.NewPrefixedWriter(os.Stderr, prefix),
		}
		p = append(p, process)
	}
	return procker.NewProcessGroup(p...)
}

func LoadApps(dir string) []*App {
	apps := []*App{}
	procfiles, _ := filepath.Glob(fmt.Sprintf("%s/*/Procfile", dir))
	for _, p := range procfiles {
		app, err := newApp(p)
		if err != nil {
			log.Printf("Unable to load application %s. Error: %s\n", p, err.Error())
		} else {
			apps = append(apps, app)
		}
	}
	return apps
}

func newApp(procfile string) (*App, error) {
	processes, err := parseProfile(procfile)
	if err != nil {
		log.Printf("Unable to load Procfile %s: %s\n", procfile, err)
		return nil, err
	}

	dir := path.Dir(procfile)
	name := path.Base(dir)
	envFile := path.Join(dir, ".env")
	env, err := parseEnv(envFile)
	if err != nil {
		log.Printf("Unable to load env file %s: %s\n", envFile, err)
		env = []string{}
	}

	a := &App{dir: dir}
	a.name = name
	a.env = env
	a.processes = processes
	return a, nil
}

func parseProfile(filepath string) (map[string]string, error) {
	file, err := os.Open(filepath)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	processes, err := procker.ParseProcfile(file)
	if err != nil {
		return nil, err
	}
	return processes, nil
}

func parseEnv(filepath string) ([]string, error) {
	file, err := os.Open(filepath)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	env, err := procker.ParseEnv(file)
	if err != nil {
		return nil, err
	}
	return env, nil
}
<|endoftext|>


<commit_before>package goauth

import (
    "errors"
    "net/http"
    "code.google.com/p/go.crypto/bcrypt"
    "github.com/gorilla/sessions"
    "github.com/gorilla/context"
)

type UserData struct {
    Username string
    Hash []byte
    Email string
}

type Authorizer struct {
    Users map[string]UserData
    cookiejar *sessions.CookieStore
    backend AuthBackend
}

type AuthBackend interface {
    LoadAuth() (a Authorizer, err error)
    SaveAuth(a Authorizer) (err error)
}

func (a Authorizer) addMessage(rw http.ResponseWriter, req *http.Request, message string) {
    message_session, _ := a.cookiejar.Get(req, "messages")
    defer message_session.Save(req, rw)
    message_session.AddFlash(message)
}

func (a Authorizer) goBack(rw http.ResponseWriter, req *http.Request) {
    redirect_session, _ := a.cookiejar.Get(req, "redirects");
    defer redirect_session.Save(req, rw)
    redirect_session.Flashes()
    redirect_session.AddFlash(req.URL.Path)
}

func NewAuthorizer(backend AuthBackend, key []byte) (a Authorizer) {
    a, err := backend.LoadAuth()
    if err != nil {
        panic(err.Error)
    }
    if a.Users == nil {
        a.Users = make(map[string]UserData)
    }
    a.cookiejar = sessions.NewCookieStore([]byte(key))
    a.backend = backend
    return a
}

func (a Authorizer) Login(rw http.ResponseWriter, req *http.Request, u string, p string, dest string) error {
    session, _ := a.cookiejar.Get(req, "auth")
    if session.Values["username"] != nil {
        return errors.New("Already authenticated.")
    }
    if user, ok := a.Users[u]; !ok {
        a.addMessage(rw, req, "Invalid username or password.")
        return errors.New("User not found.")
    } else {
        verify := bcrypt.CompareHashAndPassword(user.Hash, []byte(u + p))
        if verify != nil {
            a.addMessage(rw, req, "Invalid username or password.")
            return errors.New("Password doesn't match.")
        }
    }
    session.Values["username"] = u
    session.Save(req, rw)

    redirect_session, _ := a.cookiejar.Get(req, "redirects")
    if flashes := redirect_session.Flashes(); len(flashes) > 0 {
        dest = flashes[0].(string)
    }
    http.Redirect(rw, req, dest, http.StatusSeeOther)
    return nil
}

func (a Authorizer) Register(rw http.ResponseWriter, req *http.Request, u string, p string, e string) error {
    if _, ok := a.Users[u]; ok {
        a.addMessage(rw, req, "Username has been taken.")
        return errors.New("User already exists.")
    }

    hash, err := bcrypt.GenerateFromPassword([]byte(u + p), 8)
    if err != nil {
        return errors.New("Couldn't save password: " + err.Error())
    }
    user := (UserData{u, hash, e})

    a.Users[u] = user

    a.backend.SaveAuth(a)

    if err != nil {
        a.addMessage(rw, req, err.Error())
    }
    return nil
}

func (a Authorizer) Authorize(rw http.ResponseWriter, req *http.Request, redirectWithMessage bool) error {
    auth_session, err := a.cookiejar.Get(req, "auth")
    if err != nil {
        if redirectWithMessage {
            a.goBack(rw, req)
        }
        return errors.New("New authorization session. Possible restart of server.")
    }
    if auth_session.IsNew {
        if redirectWithMessage {
            a.goBack(rw, req)
            a.addMessage(rw, req, "Log in to do that.")
        }
        return errors.New("No session existed.")
    }
    username := auth_session.Values["username"]
    if !auth_session.IsNew && username != nil {
        if _, ok := a.Users[username.(string)]; !ok {
            auth_session.Options.MaxAge = -1 // kill the cookie
            auth_session.Save(req, rw)
            if redirectWithMessage {
                a.goBack(rw, req)
                a.addMessage(rw, req, "Log in to do that.")
            }
            return errors.New("User not found.")
        }
    }
    if username == nil {
        if redirectWithMessage {
            a.goBack(rw, req)
            a.addMessage(rw, req, "Log in to do that.")
        }
        return errors.New("User not logged in.")
    }
    context.Set(req, "username", username)
    return nil
}

func (a Authorizer) Logout(rw http.ResponseWriter, req *http.Request) error {
    session, _ := a.cookiejar.Get(req, "auth")
    defer session.Save(req, rw)

    session.Options.MaxAge = -1 // kill the cookie
    a.addMessage(rw, req, "Logged out.")
    return nil
}

func (a Authorizer) Messages(rw http.ResponseWriter, req *http.Request) []string {
    session, _ := a.cookiejar.Get(req, "messages")
    flashes := session.Flashes()
    session.Save(req, rw)
    var messages []string
    for _, val := range flashes {
        messages = append(messages, val.(string))
    }
    return messages
}
<commit_msg>Started documenting.<commit_after>package goauth

import (
    "errors"
    "net/http"
    "code.google.com/p/go.crypto/bcrypt"
    "github.com/gorilla/sessions"
    "github.com/gorilla/context"
)

// UserData represents a single user. It contains the users username and email
// as well as a has of their username and password.
type UserData struct {
    Username string
    Email string
    Hash []byte
}

// An Authorizer structure contains a list of users, the store of user session
// cookies, and a reference to a backend storage system.
type Authorizer struct {
    Users map[string]UserData
    cookiejar *sessions.CookieStore
    backend AuthBackend
}

// A type can be used as a backend if it implements the AuthBackend interface.
type AuthBackend interface {
    LoadAuth() (a Authorizer, err error)
    SaveAuth(a Authorizer) (err error)
}

// Helper function to add a user directed message to a message queue.
func (a Authorizer) addMessage(rw http.ResponseWriter, req *http.Request, message string) {
    message_session, _ := a.cookiejar.Get(req, "messages")
    defer message_session.Save(req, rw)
    message_session.AddFlash(message)
}

// Helper function to save a redirect to the page a user tried to visit before
// logging in.
func (a Authorizer) goBack(rw http.ResponseWriter, req *http.Request) {
    redirect_session, _ := a.cookiejar.Get(req, "redirects");
    defer redirect_session.Save(req, rw)
    redirect_session.Flashes()
    redirect_session.AddFlash(req.URL.Path)
}

// Given an AuthBackend and a cookie store key, returns a new Authorizer.
// If the key changes, logged in users will need to reauthenticate.
func NewAuthorizer(backend AuthBackend, key []byte) (a Authorizer) {
    a, err := backend.LoadAuth()
    if err != nil {
        panic(err.Error)
    }
    if a.Users == nil {
        a.Users = make(map[string]UserData)
    }
    a.cookiejar = sessions.NewCookieStore([]byte(key))
    a.backend = backend
    return a
}

// Log a user in. They will be redirected to faildest with an invalid username
// or password, and to the last location an authorization redirect was
// triggered (if found) on success. A message will be added to the session on
// failure with the reason
func (a Authorizer) Login(rw http.ResponseWriter, req *http.Request, u string, p string, faildest string) error {
    session, _ := a.cookiejar.Get(req, "auth")
    if session.Values["username"] != nil {
        return errors.New("Already authenticated.")
    }
    if user, ok := a.Users[u]; !ok {
        a.addMessage(rw, req, "Invalid username or password.")
        return errors.New("User not found.")
    } else {
        verify := bcrypt.CompareHashAndPassword(user.Hash, []byte(u + p))
        if verify != nil {
            a.addMessage(rw, req, "Invalid username or password.")
            return errors.New("Password doesn't match.")
        }
    }
    session.Values["username"] = u
    session.Save(req, rw)

    redirect_session, _ := a.cookiejar.Get(req, "redirects")
    if flashes := redirect_session.Flashes(); len(flashes) > 0 {
        faildest = flashes[0].(string)
    }
    http.Redirect(rw, req, faildest, http.StatusSeeOther)
    return nil
}

// Register and save a new user.
func (a Authorizer) Register(rw http.ResponseWriter, req *http.Request, u string, p string, e string) error {
    if _, ok := a.Users[u]; ok {
        a.addMessage(rw, req, "Username has been taken.")
        return errors.New("User already exists.")
    }

    hash, err := bcrypt.GenerateFromPassword([]byte(u + p), 8)
    if err != nil {
        return errors.New("Couldn't save password: " + err.Error())
    }
    user := (UserData{u, hash, e})

    a.Users[u] = user

    a.backend.SaveAuth(a)

    if err != nil {
        a.addMessage(rw, req, err.Error())
    }
    return nil
}

func (a Authorizer) Authorize(rw http.ResponseWriter, req *http.Request, redirectWithMessage bool) error {
    auth_session, err := a.cookiejar.Get(req, "auth")
    if err != nil {
        if redirectWithMessage {
            a.goBack(rw, req)
        }
        return errors.New("New authorization session. Possible restart of server.")
    }
    if auth_session.IsNew {
        if redirectWithMessage {
            a.goBack(rw, req)
            a.addMessage(rw, req, "Log in to do that.")
        }
        return errors.New("No session existed.")
    }
    username := auth_session.Values["username"]
    if !auth_session.IsNew && username != nil {
        if _, ok := a.Users[username.(string)]; !ok {
            auth_session.Options.MaxAge = -1 // kill the cookie
            auth_session.Save(req, rw)
            if redirectWithMessage {
                a.goBack(rw, req)
                a.addMessage(rw, req, "Log in to do that.")
            }
            return errors.New("User not found.")
        }
    }
    if username == nil {
        if redirectWithMessage {
            a.goBack(rw, req)
            a.addMessage(rw, req, "Log in to do that.")
        }
        return errors.New("User not logged in.")
    }
    context.Set(req, "username", username)
    return nil
}

func (a Authorizer) Logout(rw http.ResponseWriter, req *http.Request) error {
    session, _ := a.cookiejar.Get(req, "auth")
    defer session.Save(req, rw)

    session.Options.MaxAge = -1 // kill the cookie
    a.addMessage(rw, req, "Logged out.")
    return nil
}

func (a Authorizer) Messages(rw http.ResponseWriter, req *http.Request) []string {
    session, _ := a.cookiejar.Get(req, "messages")
    flashes := session.Flashes()
    session.Save(req, rw)
    var messages []string
    for _, val := range flashes {
        messages = append(messages, val.(string))
    }
    return messages
}
<|endoftext|>


<commit_before>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main
import (
	"fmt"
	"encoding/json"
	"net/http"
	"log"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"strings"
)

type AccessToken struct {
 	Token  string
 	Expiry int64
}

func googleOAuth2Config(domain string) *oauth2.Config {
	appConf := configuration()
	conf := &oauth2.Config{
 		ClientID:     appConf.GoogleClientID,
		ClientSecret: appConf.GoogleClientSecret,
 		RedirectURL:  "postmessage",
		Scopes:       []string{"https://www.googleapis.com/auth/plus.profile.emails.read"},
		Endpoint: google.Endpoint,
 	}
	return conf
}

func readHttpBody(response *http.Response) string {
	fmt.Println("Reading body")
 	bodyBuffer := make([]byte, 5000)
 	var str string
 	count, err := response.Body.Read(bodyBuffer)
 	for ; count > 0; count, err = response.Body.Read(bodyBuffer) {
 		if err != nil {

 		}
 		str += string(bodyBuffer[:count])
 	}
 	return str
 }

func oauth2callback(w http.ResponseWriter, r *http.Request) {
	code := r.FormValue("code")
	fmt.Println("oauth2callback - url: " + r.URL.RawQuery)
	fmt.Println("oauth2callback - code: " + code)

	newAccount := r.FormValue("new_account")
 	conf := googleOAuth2Config(domain(r))
	tok, err := conf.Exchange(oauth2.NoContext, code)
	if err != nil {
		log.Fatal(err)
	}
	client := conf.Client(oauth2.NoContext, tok)
 	response, err := client.Get("https://www.googleapis.com/plus/v1/people/me")
 	// handle err. You need to change this into something more robust
 	// such as redirect back to home page with error message
 	if err != nil {
 		w.Write([]byte(err.Error()))
 	}
 	str := readHttpBody(response)
	type Email struct {
		Value string
		Type string
	}
	type OAuth2Response struct {
		Kind string
		Emails []Email
	}
	dec := json.NewDecoder(strings.NewReader(str))
	var m OAuth2Response
	if err := dec.Decode(&m); err != nil {
		log.Fatal(err)
	}
	for _, v := range m.Emails {
		fmt.Println("email (value, type): " + v.Value + ", " + v.Type)
	}
	email := m.Emails[0].Value
	if newAccount == "true" {
		fmt.Println("NEW ACCOUNT")
		dbCreate(email)
		dbInsert(email, "#1")
	}
	w.Write([]byte(email))
}

<commit_msg>Minor changes to oauth2callback<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main
import (
	"fmt"
	"encoding/json"
	"net/http"
	"log"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"strings"
)

type AccessToken struct {
 	Token  string
 	Expiry int64
}

func googleOAuth2Config(domain string) *oauth2.Config {
	appConf := configuration()
	conf := &oauth2.Config{
 		ClientID:     appConf.GoogleClientID,
		ClientSecret: appConf.GoogleClientSecret,
 		RedirectURL:  "postmessage",
		Scopes:       []string{"https://www.googleapis.com/auth/plus.profile.emails.read"},
		Endpoint: google.Endpoint,
 	}
	return conf
}

func readHttpBody(response *http.Response) string {
	fmt.Println("Reading body")
 	bodyBuffer := make([]byte, 5000)
 	var str string
 	count, err := response.Body.Read(bodyBuffer)
 	for ; count > 0; count, err = response.Body.Read(bodyBuffer) {
 		if err != nil {

 		}
 		str += string(bodyBuffer[:count])
 	}
 	return str
 }

func oauth2callback(w http.ResponseWriter, r *http.Request) {
	code := r.FormValue("code")
	fmt.Println("oauth2callback - url: " + r.URL.RawQuery)
	fmt.Println("oauth2callback - code: " + code)

	newAccount := r.FormValue("new_account")
 	conf := googleOAuth2Config(domain(r))
	tok, err := conf.Exchange(oauth2.NoContext, code)
	if err != nil {
		log.Fatal(err)
	}
	client := conf.Client(oauth2.NoContext, tok)
 	response, err := client.Get("https://www.googleapis.com/plus/v1/people/me")
 	// handle err. You need to change this into something more robust
 	// such as redirect back to home page with error message
 	if err != nil {
 		w.Write([]byte(err.Error()))
 	}
 	str := readHttpBody(response)
	type Email struct {
		Value string
		Type string
	}
	type OAuth2Response struct {
		Kind string
		Emails []Email
	}
	dec := json.NewDecoder(strings.NewReader(str))
	var m OAuth2Response
	if err := dec.Decode(&m); err != nil {
		log.Fatal(err)
	}
	for _, v := range m.Emails {
		fmt.Println("email (value, type): " + v.Value + ", " + v.Type)
	}
	email := "dummy@dummy.com"
	if len(m.Emails) != 1 {
		fmt.Println("NO VALID EMAIL OR TOO MANY")

	} else {
		email = m.Emails[0].Value
	}
	if newAccount == "true" {
		fmt.Println("NEW ACCOUNT")
		dbCreate(email)
		dbInsert(email, "#1")
	}
	w.Write([]byte(email))
}

<|endoftext|>


<commit_before>package fire

import (
	"errors"
	"reflect"

	"github.com/asaskevich/govalidator"
	"github.com/manyminds/api2go/jsonapi"
	"gopkg.in/mgo.v2/bson"
)

// Base is the base for every fire model.
type Base struct {
	DocID bson.ObjectId `json:"-" bson:"_id,omitempty"`

	model interface{}
	meta  *Meta
}

// ID returns the models id.
func (b *Base) ID() bson.ObjectId {
	return b.DocID
}

// Get returns the value of the given field.
//
// Note: Get will return the value of the first field that has a matching Name,
// JSONName, or BSONName and will panic if no field can be found.
func (b *Base) Get(name string) interface{} {
	for _, field := range b.meta.Fields {
		if field.JSONName == name || field.BSONName == name || field.Name == name {
			// read value from model struct
			field := reflect.ValueOf(b.model).Elem().Field(field.index)
			return field.Interface()
		}
	}

	panic("Missing field " + name + " on " + b.meta.SingularName)
}

// Set will set given field to the the passed valued.
//
// Note: Set will set the value of the first field that has a matching Name,
// JSONName, or BSONName and will panic if no field can been found. The method
// will also panic if the type of the field and the passed value do not match.
func (b *Base) Set(name string, value interface{}) {
	for _, field := range b.meta.Fields {
		if field.JSONName == name || field.BSONName == name || field.Name == name {
			// set the value on model struct
			reflect.ValueOf(b.model).Elem().Field(field.index).Set(reflect.ValueOf(value))
			return
		}
	}

	panic("Missing field " + name + " on " + b.meta.SingularName)
}

// Validate validates the model based on the `valid:""` struct tags.
func (b *Base) Validate(fresh bool) error {
	// validate id
	if !b.DocID.Valid() {
		return errors.New("Invalid id")
	}

	// validate parent model
	_, err := govalidator.ValidateStruct(b.model)
	if err != nil {
		return err
	}

	return nil
}

// Meta returns the models Meta structure.
func (b *Base) Meta() *Meta {
	return b.meta
}

func (b *Base) initialize(model Model) {
	b.model = model

	// set id if missing
	if !b.DocID.Valid() {
		b.DocID = bson.NewObjectId()
	}

	// assign meta
	b.meta = NewMeta(model)
}

/* api2go.jsonapi interface */

// GetName returns the plural name of the Model.
//
// This methods is required by https://godoc.org/github.com/manyminds/api2go/jsonapi#EntityNamer.
func (b *Base) GetName() string {
	return b.meta.PluralName
}

// GetID returns the id of the Model.
//
// This methods is required by https://godoc.org/github.com/manyminds/api2go/jsonapi#MarshalIdentifier.
func (b *Base) GetID() string {
	return b.DocID.Hex()
}

// SetID sets the id of the Model.
//
// This methods is required by https://godoc.org/github.com/manyminds/api2go/jsonapi#UnmarshalIdentifier.
func (b *Base) SetID(id string) error {
	if len(id) == 0 {
		b.DocID = bson.NewObjectId()
		return nil
	}

	if !bson.IsObjectIdHex(id) {
		return errors.New("Invalid id")
	}

	b.DocID = bson.ObjectIdHex(id)
	return nil
}

// GetReferences returns a list of the available references.
//
// This methods is required by https://godoc.org/github.com/manyminds/api2go/jsonapi#MarshalReferences.
func (b *Base) GetReferences() []jsonapi.Reference {
	// prepare result
	var refs []jsonapi.Reference

	// add to one, to many and has many relationships
	for _, field := range b.meta.Fields {
		if field.ToOne || field.ToMany || field.HasMany {
			refs = append(refs, jsonapi.Reference{
				Type:        field.RelType,
				Name:        field.RelName,
				IsNotLoaded: true,
			})
		}
	}

	return refs
}

// GetReferencedIDs returns list of references ids.s
//
// This methods is required by https://godoc.org/github.com/manyminds/api2go/jsonapi#MarshalLinkedRelations.
func (b *Base) GetReferencedIDs() []jsonapi.ReferenceID {
	// prepare result
	var ids []jsonapi.ReferenceID

	// add to one relationships
	for _, field := range b.meta.Fields {
		if field.ToOne {
			// get struct field
			structField := reflect.ValueOf(b.model).Elem().Field(field.index)

			// prepare id
			var id string

			// check if field is optional
			if field.Optional {
				// continue if id is not set
				if structField.IsNil() {
					continue
				}

				// get id
				id = structField.Interface().(*bson.ObjectId).Hex()
			} else {
				// get id
				id = structField.Interface().(bson.ObjectId).Hex()
			}

			// append reference id
			ids = append(ids, jsonapi.ReferenceID{
				ID:   id,
				Type: field.RelType,
				Name: field.RelName,
			})
		}

		if field.ToMany {
			// get struct field
			structField := reflect.ValueOf(b.model).Elem().Field(field.index)

			// get ids
			for i := 0; i < structField.Len(); i++ {
				// read slice value
				id := structField.Index(i).Interface().(bson.ObjectId).Hex()

				// append reference id
				ids = append(ids, jsonapi.ReferenceID{
					ID:   id,
					Type: field.RelType,
					Name: field.RelName,
				})
			}
		}
	}

	return ids
}

// SetToOneReferenceID sets a reference to the passed id.
//
// This methods is required by https://godoc.org/github.com/manyminds/api2go/jsonapi#UnmarshalToOneRelations.
func (b *Base) SetToOneReferenceID(name, id string) error {
	// check object id
	if !bson.IsObjectIdHex(id) {
		return errors.New("Invalid id")
	}

	for _, field := range b.meta.Fields {
		if field.ToOne && field.RelName == name {
			// get struct field
			structField := reflect.ValueOf(b.model).Elem().Field(field.index)

			// create id
			oid := bson.ObjectIdHex(id)

			// check if optional
			if field.Optional {
				structField.Set(reflect.ValueOf(&oid))
			} else {
				structField.Set(reflect.ValueOf(oid))
			}

			return nil
		}
	}

	return errors.New("Missing relationship " + name)
}

// SetToManyReferenceIDs sets references to the passed ids.
//
// This methods is required by https://godoc.org/github.com/manyminds/api2go/jsonapi#UnmarshalToOneRelations.
func (b *Base) SetToManyReferenceIDs(name string, ids []string) error {
	// check object ids
	for _, id := range ids {
		if !bson.IsObjectIdHex(id) {
			return errors.New("Invalid id")
		}
	}

	for _, field := range b.meta.Fields {
		if field.ToMany && field.RelName == name {
			// get struct field
			structField := reflect.ValueOf(b.model).Elem().Field(field.index)

			// append ids
			for _, id := range ids {
				// create id
				oid := bson.ObjectIdHex(id)

				// append id
				structField.Set(reflect.Append(structField, reflect.ValueOf(oid)))
			}

			return nil
		}
	}

	return errors.New("Missing relationship " + name)
}
<commit_msg>only define a has many relationship "as not loaded"<commit_after>package fire

import (
	"errors"
	"reflect"

	"github.com/asaskevich/govalidator"
	"github.com/manyminds/api2go/jsonapi"
	"gopkg.in/mgo.v2/bson"
)

// Base is the base for every fire model.
type Base struct {
	DocID bson.ObjectId `json:"-" bson:"_id,omitempty"`

	model interface{}
	meta  *Meta
}

// ID returns the models id.
func (b *Base) ID() bson.ObjectId {
	return b.DocID
}

// Get returns the value of the given field.
//
// Note: Get will return the value of the first field that has a matching Name,
// JSONName, or BSONName and will panic if no field can be found.
func (b *Base) Get(name string) interface{} {
	for _, field := range b.meta.Fields {
		if field.JSONName == name || field.BSONName == name || field.Name == name {
			// read value from model struct
			field := reflect.ValueOf(b.model).Elem().Field(field.index)
			return field.Interface()
		}
	}

	panic("Missing field " + name + " on " + b.meta.SingularName)
}

// Set will set given field to the the passed valued.
//
// Note: Set will set the value of the first field that has a matching Name,
// JSONName, or BSONName and will panic if no field can been found. The method
// will also panic if the type of the field and the passed value do not match.
func (b *Base) Set(name string, value interface{}) {
	for _, field := range b.meta.Fields {
		if field.JSONName == name || field.BSONName == name || field.Name == name {
			// set the value on model struct
			reflect.ValueOf(b.model).Elem().Field(field.index).Set(reflect.ValueOf(value))
			return
		}
	}

	panic("Missing field " + name + " on " + b.meta.SingularName)
}

// Validate validates the model based on the `valid:""` struct tags.
func (b *Base) Validate(fresh bool) error {
	// validate id
	if !b.DocID.Valid() {
		return errors.New("Invalid id")
	}

	// validate parent model
	_, err := govalidator.ValidateStruct(b.model)
	if err != nil {
		return err
	}

	return nil
}

// Meta returns the models Meta structure.
func (b *Base) Meta() *Meta {
	return b.meta
}

func (b *Base) initialize(model Model) {
	b.model = model

	// set id if missing
	if !b.DocID.Valid() {
		b.DocID = bson.NewObjectId()
	}

	// assign meta
	b.meta = NewMeta(model)
}

/* api2go.jsonapi interface */

// GetName returns the plural name of the Model.
//
// This methods is required by https://godoc.org/github.com/manyminds/api2go/jsonapi#EntityNamer.
func (b *Base) GetName() string {
	return b.meta.PluralName
}

// GetID returns the id of the Model.
//
// This methods is required by https://godoc.org/github.com/manyminds/api2go/jsonapi#MarshalIdentifier.
func (b *Base) GetID() string {
	return b.DocID.Hex()
}

// SetID sets the id of the Model.
//
// This methods is required by https://godoc.org/github.com/manyminds/api2go/jsonapi#UnmarshalIdentifier.
func (b *Base) SetID(id string) error {
	if len(id) == 0 {
		b.DocID = bson.NewObjectId()
		return nil
	}

	if !bson.IsObjectIdHex(id) {
		return errors.New("Invalid id")
	}

	b.DocID = bson.ObjectIdHex(id)
	return nil
}

// GetReferences returns a list of the available references.
//
// This methods is required by https://godoc.org/github.com/manyminds/api2go/jsonapi#MarshalReferences.
func (b *Base) GetReferences() []jsonapi.Reference {
	// prepare result
	var refs []jsonapi.Reference

	// add to one, to many and has many relationships
	for _, field := range b.meta.Fields {
		if field.ToOne || field.ToMany {
			refs = append(refs, jsonapi.Reference{
				Type: field.RelType,
				Name: field.RelName,
			})
		} else if field.HasMany {
			refs = append(refs, jsonapi.Reference{
				Type:        field.RelType,
				Name:        field.RelName,
				IsNotLoaded: true,
			})
		}
	}

	return refs
}

// GetReferencedIDs returns list of references ids.
//
// This methods is required by https://godoc.org/github.com/manyminds/api2go/jsonapi#MarshalLinkedRelations.
func (b *Base) GetReferencedIDs() []jsonapi.ReferenceID {
	// prepare result
	var ids []jsonapi.ReferenceID

	// add to one relationships
	for _, field := range b.meta.Fields {
		if field.ToOne {
			// get struct field
			structField := reflect.ValueOf(b.model).Elem().Field(field.index)

			// prepare id
			var id string

			// check if field is optional
			if field.Optional {
				// continue if id is not set
				if structField.IsNil() {
					continue
				}

				// get id
				id = structField.Interface().(*bson.ObjectId).Hex()
			} else {
				// get id
				id = structField.Interface().(bson.ObjectId).Hex()
			}

			// append reference id
			ids = append(ids, jsonapi.ReferenceID{
				ID:   id,
				Type: field.RelType,
				Name: field.RelName,
			})
		}

		if field.ToMany {
			// get struct field
			structField := reflect.ValueOf(b.model).Elem().Field(field.index)

			// get ids
			for i := 0; i < structField.Len(); i++ {
				// read slice value
				id := structField.Index(i).Interface().(bson.ObjectId).Hex()

				// append reference id
				ids = append(ids, jsonapi.ReferenceID{
					ID:   id,
					Type: field.RelType,
					Name: field.RelName,
				})
			}
		}
	}

	return ids
}

// SetToOneReferenceID sets a reference to the passed id.
//
// This methods is required by https://godoc.org/github.com/manyminds/api2go/jsonapi#UnmarshalToOneRelations.
func (b *Base) SetToOneReferenceID(name, id string) error {
	// check object id
	if !bson.IsObjectIdHex(id) {
		return errors.New("Invalid id")
	}

	for _, field := range b.meta.Fields {
		if field.ToOne && field.RelName == name {
			// get struct field
			structField := reflect.ValueOf(b.model).Elem().Field(field.index)

			// create id
			oid := bson.ObjectIdHex(id)

			// check if optional
			if field.Optional {
				structField.Set(reflect.ValueOf(&oid))
			} else {
				structField.Set(reflect.ValueOf(oid))
			}

			return nil
		}
	}

	return errors.New("Missing relationship " + name)
}

// SetToManyReferenceIDs sets references to the passed ids.
//
// This methods is required by https://godoc.org/github.com/manyminds/api2go/jsonapi#UnmarshalToOneRelations.
func (b *Base) SetToManyReferenceIDs(name string, ids []string) error {
	// check object ids
	for _, id := range ids {
		if !bson.IsObjectIdHex(id) {
			return errors.New("Invalid id")
		}
	}

	for _, field := range b.meta.Fields {
		if field.ToMany && field.RelName == name {
			// get struct field
			structField := reflect.ValueOf(b.model).Elem().Field(field.index)

			// append ids
			for _, id := range ids {
				// create id
				oid := bson.ObjectIdHex(id)

				// append id
				structField.Set(reflect.Append(structField, reflect.ValueOf(oid)))
			}

			return nil
		}
	}

	return errors.New("Missing relationship " + name)
}
<|endoftext|>


<commit_before>package search

import (
    "encoding/xml"
    "io/ioutil"
    "net/http"
    "net/url"
    "h12.me/socks"
    "strconv"
)

type Item struct {
    Title       string `xml:"title"`
    Description string `xml:"description"`
    Link        string `xml:"link"`
    PubDate     string `xml:"pubDate"`
}

type SearchResult struct {
    XMLName xml.Name `xml:"rss"`
    Channel struct {
        Title       string `xml:"title"`
        Link        string `xml:"link"`
        Description string `xml:"description"`
        Items       []Item `xml:"item"`
    } `xml:"channel"`
}

type Options struct {
    Query string
    Count int
    Tor bool
}

var globalUa string = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36"

func WebSearch(options Options) (results []Item, ok bool) {
    // base url is bing
    baseURL := "http://www.bing.com/search?"
    query := url.Values{}
    query.Set("q", options.Query)
    query.Add("format", "rss")
    query.Add("go", "Submit")
    query.Add("qs", "bs")

    // count
    count := options.Count
    if count > 40 {
        count = 40
    }
    query.Add("count", strconv.Itoa(count))

    // the url is complete url
    theURL := baseURL + query.Encode()

    client := &http.Client{}
    if options.Tor {

        dialSocksProxy := socks.DialSocksProxy(socks.SOCKS5, "127.0.0.1:9050")
        client.Transport = &http.Transport{
            Dial: dialSocksProxy,
        }
    }

    req, err := http.NewRequest("GET", theURL, nil)
    if err != nil {
        return
    }
    req.Header.Set("User-Agent", globalUa)
    req.Header.Set("Referer", "http://www.bing.com/")
    req.Header.Set("Accept",  "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
    req.Header.Set("Accept-Language", "en-US")

    resp, err := client.Do(req)
    if err != nil {
        return
    }
    // if ok then defer to close body
    defer resp.Body.Close()

    // read the body
    body, err := ioutil.ReadAll(resp.Body)
    //fmt.Println(body)

    if err != nil {
        return
    }
    //fmt.Println(body)

    searchResult := SearchResult{}
    errUnmarshal := xml.Unmarshal([]byte(body), &searchResult)
    if errUnmarshal != nil {
        return
    }

    ok = true
    results = searchResult.Channel.Items
    return
}

func URLInfo(theURL string, tor bool) (info Item, ok bool) {
    options := Options{
        Query:"url:"+theURL,
        Count: 1,
        Tor: tor,
    }
    results, ok := WebSearch(options)
    if !ok {
        return
    }

    if len(results) > 0 {
        info = results[0]
        return
    }
    return
}
<commit_msg>add json tags in struct<commit_after>package search

import (
    "encoding/xml"
    "io/ioutil"
    "net/http"
    "net/url"
    "h12.me/socks"
    "strconv"
)

type Item struct {
    Title       string `xml:"title" json:"title"`
    Description string `xml:"description"  json:"description"`
    Link        string `xml:"link"  json:"link"`
    PubDate     string `xml:"pubDate  json:"pubdate"`
}

type SearchResult struct {
    XMLName xml.Name `xml:"rss"`
    Channel struct {
        Title       string `xml:"title"`
        Link        string `xml:"link"`
        Description string `xml:"description"`
        Items       []Item `xml:"item"`
    } `xml:"channel"`
}

type Options struct {
    Query string
    Count int
    Tor bool
}

var globalUa string = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36"

func WebSearch(options Options) (results []Item, ok bool) {
    // base url is bing
    baseURL := "http://www.bing.com/search?"
    query := url.Values{}
    query.Set("q", options.Query)
    query.Add("format", "rss")
    query.Add("go", "Submit")
    query.Add("qs", "bs")

    // count
    count := options.Count
    if count > 40 {
        count = 40
    }
    query.Add("count", strconv.Itoa(count))

    // the url is complete url
    theURL := baseURL + query.Encode()

    client := &http.Client{}
    if options.Tor {

        dialSocksProxy := socks.DialSocksProxy(socks.SOCKS5, "127.0.0.1:9050")
        client.Transport = &http.Transport{
            Dial: dialSocksProxy,
        }
    }

    req, err := http.NewRequest("GET", theURL, nil)
    if err != nil {
        return
    }
    req.Header.Set("User-Agent", globalUa)
    req.Header.Set("Referer", "http://www.bing.com/")
    req.Header.Set("Accept",  "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
    req.Header.Set("Accept-Language", "en-US")

    resp, err := client.Do(req)
    if err != nil {
        return
    }
    // if ok then defer to close body
    defer resp.Body.Close()

    // read the body
    body, err := ioutil.ReadAll(resp.Body)
    //fmt.Println(body)

    if err != nil {
        return
    }
    //fmt.Println(body)

    searchResult := SearchResult{}
    errUnmarshal := xml.Unmarshal([]byte(body), &searchResult)
    if errUnmarshal != nil {
        return
    }

    ok = true
    results = searchResult.Channel.Items
    return
}

func URLInfo(theURL string, tor bool) (info Item, ok bool) {
    options := Options{
        Query:"url:"+theURL,
        Count: 1,
        Tor: tor,
    }
    results, ok := WebSearch(options)
    if !ok {
        return
    }

    if len(results) > 0 {
        info = results[0]
        return
    }
    return
}
<|endoftext|>


<commit_before>package git

/*
#include <git2.h>
#include <string.h>

extern int _go_git_blob_create_fromchunks(git_oid *id,
	git_repository *repo,
	const char *hintpath,
	void *payload);

*/
import "C"
import (
	"io"
	"runtime"
	"unsafe"
)

type Blob struct {
	Object
	cast_ptr *C.git_blob
}

func (v *Blob) Size() int64 {
	return int64(C.git_blob_rawsize(v.cast_ptr))
}

func (v *Blob) Contents() []byte {
	size := C.int(C.git_blob_rawsize(v.cast_ptr))
	buffer := unsafe.Pointer(C.git_blob_rawcontent(v.cast_ptr))
	return C.GoBytes(buffer, size)
}

func (repo *Repository) CreateBlobFromBuffer(data []byte) (*Oid, error) {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var id C.git_oid
	var ptr unsafe.Pointer

	if len(data) > 0 {
		ptr = unsafe.Pointer(&data[0])
	} else {
		ptr = unsafe.Pointer(nil)
	}

	ecode := C.git_blob_create_frombuffer(&id, repo.ptr, ptr, C.size_t(len(data)))
	if ecode < 0 {
		return nil, MakeGitError(ecode)
	}
	return newOidFromC(&id), nil
}

type BlobChunkCallback func(maxLen int) ([]byte, error)

type BlobCallbackData struct {
	Callback BlobChunkCallback
	Error    error
}

//export blobChunkCb
func blobChunkCb(buffer *C.char, maxLen C.size_t, handle unsafe.Pointer) int {
	payload := pointerHandles.Get(handle)
	data, ok := payload.(*BlobCallbackData)
	if !ok {
		panic("could not retrieve blob callback data")
	}

	goBuf, err := data.Callback(int(maxLen))
	if err == io.EOF {
		return 0
	} else if err != nil {
		data.Error = err
		return -1
	}
	C.memcpy(unsafe.Pointer(buffer), unsafe.Pointer(&goBuf[0]), C.size_t(len(goBuf)))
	return len(goBuf)
}

func (repo *Repository) CreateBlobFromChunks(hintPath string, callback BlobChunkCallback) (*Oid, error) {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var chintPath *C.char = nil
	if len(hintPath) > 0 {
		chintPath = C.CString(hintPath)
		defer C.free(unsafe.Pointer(chintPath))
	}
	oid := C.git_oid{}

	payload := &BlobCallbackData{Callback: callback}
	handle := pointerHandles.Track(payload)
	defer pointerHandles.Untrack(handle)

	ecode := C._go_git_blob_create_fromchunks(&oid, repo.ptr, chintPath, handle)
	if payload.Error != nil {
		return nil, payload.Error
	}
	if ecode < 0 {
		return nil, MakeGitError(ecode)
	}
	return newOidFromC(&oid), nil
}
<commit_msg>Work around Go 1.6's CGo pointer check<commit_after>package git

/*
#include <git2.h>
#include <string.h>

extern int _go_git_blob_create_fromchunks(git_oid *id,
	git_repository *repo,
	const char *hintpath,
	void *payload);

*/
import "C"
import (
	"io"
	"runtime"
	"unsafe"
)

type Blob struct {
	Object
	cast_ptr *C.git_blob
}

func (v *Blob) Size() int64 {
	return int64(C.git_blob_rawsize(v.cast_ptr))
}

func (v *Blob) Contents() []byte {
	size := C.int(C.git_blob_rawsize(v.cast_ptr))
	buffer := unsafe.Pointer(C.git_blob_rawcontent(v.cast_ptr))
	return C.GoBytes(buffer, size)
}

func (repo *Repository) CreateBlobFromBuffer(data []byte) (*Oid, error) {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var id C.git_oid
	var size C.size_t

	// Go 1.6 added some increased checking of passing pointer to
	// C, but its check depends on its expectations of waht we
	// pass to the C function, so unless we take the address of
	// its contents at the call site itself, it can fail when
	// 'data' is a slice of a slice.
	//
	// When we're given an empty slice, create a dummy one where 0
	// isn't out of bounds.
	if len(data) > 0 {
		size = C.size_t(len(data))
	} else {
		data = []byte{0}
		size = C.size_t(0)
	}

	ecode := C.git_blob_create_frombuffer(&id, repo.ptr, unsafe.Pointer(&data[0]), size)
	if ecode < 0 {
		return nil, MakeGitError(ecode)
	}
	return newOidFromC(&id), nil
}

type BlobChunkCallback func(maxLen int) ([]byte, error)

type BlobCallbackData struct {
	Callback BlobChunkCallback
	Error    error
}

//export blobChunkCb
func blobChunkCb(buffer *C.char, maxLen C.size_t, handle unsafe.Pointer) int {
	payload := pointerHandles.Get(handle)
	data, ok := payload.(*BlobCallbackData)
	if !ok {
		panic("could not retrieve blob callback data")
	}

	goBuf, err := data.Callback(int(maxLen))
	if err == io.EOF {
		return 0
	} else if err != nil {
		data.Error = err
		return -1
	}
	C.memcpy(unsafe.Pointer(buffer), unsafe.Pointer(&goBuf[0]), C.size_t(len(goBuf)))
	return len(goBuf)
}

func (repo *Repository) CreateBlobFromChunks(hintPath string, callback BlobChunkCallback) (*Oid, error) {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var chintPath *C.char = nil
	if len(hintPath) > 0 {
		chintPath = C.CString(hintPath)
		defer C.free(unsafe.Pointer(chintPath))
	}
	oid := C.git_oid{}

	payload := &BlobCallbackData{Callback: callback}
	handle := pointerHandles.Track(payload)
	defer pointerHandles.Untrack(handle)

	ecode := C._go_git_blob_create_fromchunks(&oid, repo.ptr, chintPath, handle)
	if payload.Error != nil {
		return nil, payload.Error
	}
	if ecode < 0 {
		return nil, MakeGitError(ecode)
	}
	return newOidFromC(&oid), nil
}
<|endoftext|>


<commit_before>package goconf

func LoadConfig(filename string){
	return
}<commit_msg>Worked a lot<commit_after>package goconf

import (
	"io"
	"log"
	"strings"
	"time"
)

type config struct {
	modTime time.Time
	info    map[string]interface{}
}

type Config struct {
	name string
}

func (c *Config) Get(key string) interface{} {
	return confMapping[c.name].info[key]
}

var confMapping map[string]Config

func LoadConfig(filename string) (*Config, error) {
	if confMapping == nil {
		confMapping = make(map[string]time.Time)
		confMapping[filename] = time.Now().Add(-100) // Arbitrary time before now to force a reload
		go hotReload()
	}
	return &Config{name:filename}
}

func loadConfig(name string) (map[string]interface{}, error) {
	f, err = os.Open(name)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	split = strings.Split(name, '.')
	format := split[len(split) - 1]
	switch format {
	case "json":
		var rval interface{}
		decoder := json.NewDecoder(f)
		err := decoder.Decode(rval)
		if err != nil {
			return nil, err
		}

		return rval, nil
	default:
		return nil, fmt.Error("Invalid format: %s", format)
	}
}

func hotReload() {
	for {
		for name, conf := range confMapping {
			fi, err := os.Stat(name)
			if err != nil {
				log.Printf("Error reading file %v\n", name)
			}
			if conf.modTime.Before(fi.ModTime()) {
				conf, err := loadConfig(name)
				if err != nil {
					log.Printf("Error reading file %v\n", name)
				}
				confMapping[name] = conf
			}
		}
	}
}<|endoftext|>