<commit_before>package tgbotapi

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"
)

// APIResponse is a response from the Telegram API with the result
// stored raw.
type APIResponse struct {
	Ok          bool            `json:"ok"`
	Result      json.RawMessage `json:"result"`
	ErrorCode   int             `json:"error_code"`
	Description string          `json:"description"`
}

// Update is an update response, from GetUpdates.
type Update struct {
	UpdateID           int                `json:"update_id"`
	Message            Message            `json:"message"`
	InlineQuery        InlineQuery        `json:"inline_query"`
	ChosenInlineResult ChosenInlineResult `json:"chosen_inline_result"`
}

// User is a user on Telegram.
type User struct {
	ID        int    `json:"id"`
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"` // optional
	UserName  string `json:"username"`  // optional
}

// String displays a simple text version of a user.
//
// It is normally a user's username, but falls back to a first/last
// name as available.
func (u *User) String() string {
	if u.UserName != "" {
		return u.UserName
	}

	name := u.FirstName
	if u.LastName != "" {
		name += " " + u.LastName
	}

	return name
}

// GroupChat is a group chat.
type GroupChat struct {
	ID    int    `json:"id"`
	Title string `json:"title"`
}

// Chat contains information about the place a message was sent.
type Chat struct {
	ID        int    `json:"id"`
	Type      string `json:"type"`
	Title     string `json:"title"`      // optional
	UserName  string `json:"username"`   // optional
	FirstName string `json:"first_name"` // optional
	LastName  string `json:"last_name"`  // optional
}

// IsPrivate returns if the Chat is a private conversation.
func (c *Chat) IsPrivate() bool {
	return c.Type == "private"
}

// IsGroup returns if the Chat is a group.
func (c *Chat) IsGroup() bool {
	return c.Type == "group"
}

// IsSuperGroup returns if the Chat is a supergroup.
func (c *Chat) IsSuperGroup() bool {
	return c.Type == "supergroup"
}

// IsChannel returns if the Chat is a channel.
func (c *Chat) IsChannel() bool {
	return c.Type == "channel"
}

// Message is returned by almost every request, and contains data about
// almost anything.
type Message struct {
	MessageID             int         `json:"message_id"`
	From                  User        `json:"from"` // optional
	Date                  int         `json:"date"`
	Chat                  Chat        `json:"chat"`
	ForwardFrom           User        `json:"forward_from"`            // optional
	ForwardDate           int         `json:"forward_date"`            // optional
	ReplyToMessage        *Message    `json:"reply_to_message"`        // optional
	Text                  string      `json:"text"`                    // optional
	Audio                 Audio       `json:"audio"`                   // optional
	Document              Document    `json:"document"`                // optional
	Photo                 []PhotoSize `json:"photo"`                   // optional
	Sticker               Sticker     `json:"sticker"`                 // optional
	Video                 Video       `json:"video"`                   // optional
	Voice                 Voice       `json:"voice"`                   // optional
	Caption               string      `json:"caption"`                 // optional
	Contact               Contact     `json:"contact"`                 // optional
	Location              Location    `json:"location"`                // optional
	NewChatParticipant    User        `json:"new_chat_participant"`    // optional
	LeftChatParticipant   User        `json:"left_chat_participant"`   // optional
	NewChatTitle          string      `json:"new_chat_title"`          // optional
	NewChatPhoto          []PhotoSize `json:"new_chat_photo"`          // optional
	DeleteChatPhoto       bool        `json:"delete_chat_photo"`       // optional
	GroupChatCreated      bool        `json:"group_chat_created"`      // optional
	SuperGroupChatCreated bool        `json:"supergroup_chat_created"` // optional
	ChannelChatCreated    bool        `json:"channel_chat_created"`    // optional
	MigrateToChatID       int         `json:"migrate_to_chat_id"`      // optional
	MigrateFromChatID     int         `json:"migrate_from_chat_id"`    // optional
}

// Time converts the message timestamp into a Time.
func (m *Message) Time() time.Time {
	return time.Unix(int64(m.Date), 0)
}

// IsCommand returns true if message starts with '/'.
func (m *Message) IsCommand() bool {
	return m.Text != "" && m.Text[0] == '/'
}

// Command checks if the message was a command and if it was, returns the
// command. If the Message was not a command, it returns an empty string.
//
// If the command contains the at bot syntax, it removes the bot name.
func (m *Message) Command() string {
	if !m.IsCommand() {
		return ""
	}

	command := strings.SplitN(m.Text, " ", 2)[0][1:]

	if i := strings.Index(command, "@"); i != -1 {
		command = command[:i]
	}

	return command
}

// CommandArguments checks if the message was a command and if it was,
// returns all text after the command name. If the Message was not a
// command, it returns an empty string.
func (m *Message) CommandArguments() string {
	if !m.IsCommand() {
		return ""
	}

	split := strings.SplitN(m.Text, " ", 2)
	if len(split) != 2 {
		return ""
	}

	return strings.SplitN(m.Text, " ", 2)[1]
}

// PhotoSize contains information about photos.
type PhotoSize struct {
	FileID   string `json:"file_id"`
	Width    int    `json:"width"`
	Height   int    `json:"height"`
	FileSize int    `json:"file_size"` // optional
}

// Audio contains information about audio.
type Audio struct {
	FileID    string `json:"file_id"`
	Duration  int    `json:"duration"`
	Performer string `json:"performer"` // optional
	Title     string `json:"title"`     // optional
	MimeType  string `json:"mime_type"` // optional
	FileSize  int    `json:"file_size"` // optional
}

// Document contains information about a document.
type Document struct {
	FileID    string    `json:"file_id"`
	Thumbnail PhotoSize `json:"thumb"`     // optional
	FileName  string    `json:"file_name"` // optional
	MimeType  string    `json:"mime_type"` // optional
	FileSize  int       `json:"file_size"` // optional
}

// Sticker contains information about a sticker.
type Sticker struct {
	FileID    string    `json:"file_id"`
	Width     int       `json:"width"`
	Height    int       `json:"height"`
	Thumbnail PhotoSize `json:"thumb"`     // optional
	FileSize  int       `json:"file_size"` // optional
}

// Video contains information about a video.
type Video struct {
	FileID    string    `json:"file_id"`
	Width     int       `json:"width"`
	Height    int       `json:"height"`
	Duration  int       `json:"duration"`
	Thumbnail PhotoSize `json:"thumb"`     // optional
	MimeType  string    `json:"mime_type"` // optional
	FileSize  int       `json:"file_size"` // optional
}

// Voice contains information about a voice.
type Voice struct {
	FileID   string `json:"file_id"`
	Duration int    `json:"duration"`
	MimeType string `json:"mime_type"` // optional
	FileSize int    `json:"file_size"` // optional
}

// Contact contains information about a contact.
//
// Note that LastName and UserID may be empty.
type Contact struct {
	PhoneNumber string `json:"phone_number"`
	FirstName   string `json:"first_name"`
	LastName    string `json:"last_name"` // optional
	UserID      int    `json:"user_id"`   // optional
}

// Location contains information about a place.
type Location struct {
	Longitude float32 `json:"longitude"`
	Latitude  float32 `json:"latitude"`
}

// UserProfilePhotos contains information a set of user profile photos.
type UserProfilePhotos struct {
	TotalCount int         `json:"total_count"`
	Photos     []PhotoSize `json:"photos"`
}

// File contains information about a file to download from Telegram.
type File struct {
	FileID   string `json:"file_id"`
	FileSize int    `json:"file_size"` // optional
	FilePath string `json:"file_path"` // optional
}

// Link returns a full path to the download URL for a File.
//
// It requires the Bot Token to create the link.
func (f *File) Link(token string) string {
	return fmt.Sprintf(FileEndpoint, token, f.FilePath)
}

// ReplyKeyboardMarkup allows the Bot to set a custom keyboard.
type ReplyKeyboardMarkup struct {
	Keyboard        [][]string `json:"keyboard"`
	ResizeKeyboard  bool       `json:"resize_keyboard"`   // optional
	OneTimeKeyboard bool       `json:"one_time_keyboard"` // optional
	Selective       bool       `json:"selective"`         // optional
}

// ReplyKeyboardHide allows the Bot to hide a custom keyboard.
type ReplyKeyboardHide struct {
	HideKeyboard bool `json:"hide_keyboard"`
	Selective    bool `json:"selective"` // optional
}

// ForceReply allows the Bot to have users directly reply to it without
// additional interaction.
type ForceReply struct {
	ForceReply bool `json:"force_reply"`
	Selective  bool `json:"selective"` // optional
}

// InlineQuery is a Query from Telegram for an inline request.
type InlineQuery struct {
	ID     string `json:"id"`
	From   User   `json:"from"`
	Query  string `json:"query"`
	Offset string `json:"offset"`
}

// InlineQueryResultArticle is an inline query response article.
type InlineQueryResultArticle struct {
	Type                  string `json:"type"`         // required
	ID                    string `json:"id"`           // required
	Title                 string `json:"title"`        // required
	MessageText           string `json:"message_text"` // required
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
	URL                   string `json:"url"`
	HideURL               bool   `json:"hide_url"`
	Description           string `json:"description"`
	ThumbURL              string `json:"thumb_url"`
	ThumbWidth            int    `json:"thumb_width"`
	ThumbHeight           int    `json:"thumb_height"`
}

// InlineQueryResultPhoto is an inline query response photo.
type InlineQueryResultPhoto struct {
	Type                  string `json:"type"`      // required
	ID                    string `json:"id"`        // required
	URL                   string `json:"photo_url"` // required
	MimeType              string `json:"mime_type"`
	Width                 int    `json:"photo_width"`
	Height                int    `json:"photo_height"`
	ThumbURL              string `json:"thumb_url"`
	Title                 string `json:"title"`
	Description           string `json:"description"`
	Caption               string `json:"caption"`
	MessageText           string `json:"message_text"`
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
}

// InlineQueryResultGIF is an inline query response GIF.
type InlineQueryResultGIF struct {
	Type                  string `json:"type"`    // required
	ID                    string `json:"id"`      // required
	URL                   string `json:"gif_url"` // required
	Width                 int    `json:"gif_width"`
	Height                int    `json:"gif_height"`
	ThumbURL              string `json:"thumb_url"`
	Title                 string `json:"title"`
	Caption               string `json:"caption"`
	MessageText           string `json:"message_text"`
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
}

// InlineQueryResultMPEG4GIF is an inline query response MPEG4 GIF.
type InlineQueryResultMPEG4GIF struct {
	Type                  string `json:"type"`      // required
	ID                    string `json:"id"`        // required
	URL                   string `json:"mpeg4_url"` // required
	Width                 int    `json:"mpeg4_width"`
	Height                int    `json:"mpeg4_height"`
	ThumbURL              string `json:"thumb_url"`
	Title                 string `json:"title"`
	Caption               string `json:"caption"`
	MessageText           string `json:"message_text"`
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
}

// InlineQueryResultVideo is an inline query response video.
type InlineQueryResultVideo struct {
	Type                  string `json:"type"`         // required
	ID                    string `json:"id"`           // required
	URL                   string `json:"video_url"`    // required
	MimeType              string `json:"mime_type"`    // required
	MessageText           string `json:"message_text"` // required
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
	Width                 int    `json:"video_width"`
	Height                int    `json:"video_height"`
	ThumbURL              string `json:"thumb_url"`
	Title                 string `json:"title"`
	Description           string `json:"description"`
}

// ChosenInlineResult is an inline query result chosen by a User
type ChosenInlineResult struct {
	ResultID string `json:"result_id"`
	From     User   `json:"from"`
	Query    string `json:"query"`
}
<commit_msg>Remove a stray word from UserProfilePhotos comment.<commit_after>package tgbotapi

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"
)

// APIResponse is a response from the Telegram API with the result
// stored raw.
type APIResponse struct {
	Ok          bool            `json:"ok"`
	Result      json.RawMessage `json:"result"`
	ErrorCode   int             `json:"error_code"`
	Description string          `json:"description"`
}

// Update is an update response, from GetUpdates.
type Update struct {
	UpdateID           int                `json:"update_id"`
	Message            Message            `json:"message"`
	InlineQuery        InlineQuery        `json:"inline_query"`
	ChosenInlineResult ChosenInlineResult `json:"chosen_inline_result"`
}

// User is a user on Telegram.
type User struct {
	ID        int    `json:"id"`
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"` // optional
	UserName  string `json:"username"`  // optional
}

// String displays a simple text version of a user.
//
// It is normally a user's username, but falls back to a first/last
// name as available.
func (u *User) String() string {
	if u.UserName != "" {
		return u.UserName
	}

	name := u.FirstName
	if u.LastName != "" {
		name += " " + u.LastName
	}

	return name
}

// GroupChat is a group chat.
type GroupChat struct {
	ID    int    `json:"id"`
	Title string `json:"title"`
}

// Chat contains information about the place a message was sent.
type Chat struct {
	ID        int    `json:"id"`
	Type      string `json:"type"`
	Title     string `json:"title"`      // optional
	UserName  string `json:"username"`   // optional
	FirstName string `json:"first_name"` // optional
	LastName  string `json:"last_name"`  // optional
}

// IsPrivate returns if the Chat is a private conversation.
func (c *Chat) IsPrivate() bool {
	return c.Type == "private"
}

// IsGroup returns if the Chat is a group.
func (c *Chat) IsGroup() bool {
	return c.Type == "group"
}

// IsSuperGroup returns if the Chat is a supergroup.
func (c *Chat) IsSuperGroup() bool {
	return c.Type == "supergroup"
}

// IsChannel returns if the Chat is a channel.
func (c *Chat) IsChannel() bool {
	return c.Type == "channel"
}

// Message is returned by almost every request, and contains data about
// almost anything.
type Message struct {
	MessageID             int         `json:"message_id"`
	From                  User        `json:"from"` // optional
	Date                  int         `json:"date"`
	Chat                  Chat        `json:"chat"`
	ForwardFrom           User        `json:"forward_from"`            // optional
	ForwardDate           int         `json:"forward_date"`            // optional
	ReplyToMessage        *Message    `json:"reply_to_message"`        // optional
	Text                  string      `json:"text"`                    // optional
	Audio                 Audio       `json:"audio"`                   // optional
	Document              Document    `json:"document"`                // optional
	Photo                 []PhotoSize `json:"photo"`                   // optional
	Sticker               Sticker     `json:"sticker"`                 // optional
	Video                 Video       `json:"video"`                   // optional
	Voice                 Voice       `json:"voice"`                   // optional
	Caption               string      `json:"caption"`                 // optional
	Contact               Contact     `json:"contact"`                 // optional
	Location              Location    `json:"location"`                // optional
	NewChatParticipant    User        `json:"new_chat_participant"`    // optional
	LeftChatParticipant   User        `json:"left_chat_participant"`   // optional
	NewChatTitle          string      `json:"new_chat_title"`          // optional
	NewChatPhoto          []PhotoSize `json:"new_chat_photo"`          // optional
	DeleteChatPhoto       bool        `json:"delete_chat_photo"`       // optional
	GroupChatCreated      bool        `json:"group_chat_created"`      // optional
	SuperGroupChatCreated bool        `json:"supergroup_chat_created"` // optional
	ChannelChatCreated    bool        `json:"channel_chat_created"`    // optional
	MigrateToChatID       int         `json:"migrate_to_chat_id"`      // optional
	MigrateFromChatID     int         `json:"migrate_from_chat_id"`    // optional
}

// Time converts the message timestamp into a Time.
func (m *Message) Time() time.Time {
	return time.Unix(int64(m.Date), 0)
}

// IsCommand returns true if message starts with '/'.
func (m *Message) IsCommand() bool {
	return m.Text != "" && m.Text[0] == '/'
}

// Command checks if the message was a command and if it was, returns the
// command. If the Message was not a command, it returns an empty string.
//
// If the command contains the at bot syntax, it removes the bot name.
func (m *Message) Command() string {
	if !m.IsCommand() {
		return ""
	}

	command := strings.SplitN(m.Text, " ", 2)[0][1:]

	if i := strings.Index(command, "@"); i != -1 {
		command = command[:i]
	}

	return command
}

// CommandArguments checks if the message was a command and if it was,
// returns all text after the command name. If the Message was not a
// command, it returns an empty string.
func (m *Message) CommandArguments() string {
	if !m.IsCommand() {
		return ""
	}

	split := strings.SplitN(m.Text, " ", 2)
	if len(split) != 2 {
		return ""
	}

	return strings.SplitN(m.Text, " ", 2)[1]
}

// PhotoSize contains information about photos.
type PhotoSize struct {
	FileID   string `json:"file_id"`
	Width    int    `json:"width"`
	Height   int    `json:"height"`
	FileSize int    `json:"file_size"` // optional
}

// Audio contains information about audio.
type Audio struct {
	FileID    string `json:"file_id"`
	Duration  int    `json:"duration"`
	Performer string `json:"performer"` // optional
	Title     string `json:"title"`     // optional
	MimeType  string `json:"mime_type"` // optional
	FileSize  int    `json:"file_size"` // optional
}

// Document contains information about a document.
type Document struct {
	FileID    string    `json:"file_id"`
	Thumbnail PhotoSize `json:"thumb"`     // optional
	FileName  string    `json:"file_name"` // optional
	MimeType  string    `json:"mime_type"` // optional
	FileSize  int       `json:"file_size"` // optional
}

// Sticker contains information about a sticker.
type Sticker struct {
	FileID    string    `json:"file_id"`
	Width     int       `json:"width"`
	Height    int       `json:"height"`
	Thumbnail PhotoSize `json:"thumb"`     // optional
	FileSize  int       `json:"file_size"` // optional
}

// Video contains information about a video.
type Video struct {
	FileID    string    `json:"file_id"`
	Width     int       `json:"width"`
	Height    int       `json:"height"`
	Duration  int       `json:"duration"`
	Thumbnail PhotoSize `json:"thumb"`     // optional
	MimeType  string    `json:"mime_type"` // optional
	FileSize  int       `json:"file_size"` // optional
}

// Voice contains information about a voice.
type Voice struct {
	FileID   string `json:"file_id"`
	Duration int    `json:"duration"`
	MimeType string `json:"mime_type"` // optional
	FileSize int    `json:"file_size"` // optional
}

// Contact contains information about a contact.
//
// Note that LastName and UserID may be empty.
type Contact struct {
	PhoneNumber string `json:"phone_number"`
	FirstName   string `json:"first_name"`
	LastName    string `json:"last_name"` // optional
	UserID      int    `json:"user_id"`   // optional
}

// Location contains information about a place.
type Location struct {
	Longitude float32 `json:"longitude"`
	Latitude  float32 `json:"latitude"`
}

// UserProfilePhotos contains a set of user profile photos.
type UserProfilePhotos struct {
	TotalCount int         `json:"total_count"`
	Photos     []PhotoSize `json:"photos"`
}

// File contains information about a file to download from Telegram.
type File struct {
	FileID   string `json:"file_id"`
	FileSize int    `json:"file_size"` // optional
	FilePath string `json:"file_path"` // optional
}

// Link returns a full path to the download URL for a File.
//
// It requires the Bot Token to create the link.
func (f *File) Link(token string) string {
	return fmt.Sprintf(FileEndpoint, token, f.FilePath)
}

// ReplyKeyboardMarkup allows the Bot to set a custom keyboard.
type ReplyKeyboardMarkup struct {
	Keyboard        [][]string `json:"keyboard"`
	ResizeKeyboard  bool       `json:"resize_keyboard"`   // optional
	OneTimeKeyboard bool       `json:"one_time_keyboard"` // optional
	Selective       bool       `json:"selective"`         // optional
}

// ReplyKeyboardHide allows the Bot to hide a custom keyboard.
type ReplyKeyboardHide struct {
	HideKeyboard bool `json:"hide_keyboard"`
	Selective    bool `json:"selective"` // optional
}

// ForceReply allows the Bot to have users directly reply to it without
// additional interaction.
type ForceReply struct {
	ForceReply bool `json:"force_reply"`
	Selective  bool `json:"selective"` // optional
}

// InlineQuery is a Query from Telegram for an inline request.
type InlineQuery struct {
	ID     string `json:"id"`
	From   User   `json:"from"`
	Query  string `json:"query"`
	Offset string `json:"offset"`
}

// InlineQueryResultArticle is an inline query response article.
type InlineQueryResultArticle struct {
	Type                  string `json:"type"`         // required
	ID                    string `json:"id"`           // required
	Title                 string `json:"title"`        // required
	MessageText           string `json:"message_text"` // required
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
	URL                   string `json:"url"`
	HideURL               bool   `json:"hide_url"`
	Description           string `json:"description"`
	ThumbURL              string `json:"thumb_url"`
	ThumbWidth            int    `json:"thumb_width"`
	ThumbHeight           int    `json:"thumb_height"`
}

// InlineQueryResultPhoto is an inline query response photo.
type InlineQueryResultPhoto struct {
	Type                  string `json:"type"`      // required
	ID                    string `json:"id"`        // required
	URL                   string `json:"photo_url"` // required
	MimeType              string `json:"mime_type"`
	Width                 int    `json:"photo_width"`
	Height                int    `json:"photo_height"`
	ThumbURL              string `json:"thumb_url"`
	Title                 string `json:"title"`
	Description           string `json:"description"`
	Caption               string `json:"caption"`
	MessageText           string `json:"message_text"`
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
}

// InlineQueryResultGIF is an inline query response GIF.
type InlineQueryResultGIF struct {
	Type                  string `json:"type"`    // required
	ID                    string `json:"id"`      // required
	URL                   string `json:"gif_url"` // required
	Width                 int    `json:"gif_width"`
	Height                int    `json:"gif_height"`
	ThumbURL              string `json:"thumb_url"`
	Title                 string `json:"title"`
	Caption               string `json:"caption"`
	MessageText           string `json:"message_text"`
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
}

// InlineQueryResultMPEG4GIF is an inline query response MPEG4 GIF.
type InlineQueryResultMPEG4GIF struct {
	Type                  string `json:"type"`      // required
	ID                    string `json:"id"`        // required
	URL                   string `json:"mpeg4_url"` // required
	Width                 int    `json:"mpeg4_width"`
	Height                int    `json:"mpeg4_height"`
	ThumbURL              string `json:"thumb_url"`
	Title                 string `json:"title"`
	Caption               string `json:"caption"`
	MessageText           string `json:"message_text"`
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
}

// InlineQueryResultVideo is an inline query response video.
type InlineQueryResultVideo struct {
	Type                  string `json:"type"`         // required
	ID                    string `json:"id"`           // required
	URL                   string `json:"video_url"`    // required
	MimeType              string `json:"mime_type"`    // required
	MessageText           string `json:"message_text"` // required
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
	Width                 int    `json:"video_width"`
	Height                int    `json:"video_height"`
	ThumbURL              string `json:"thumb_url"`
	Title                 string `json:"title"`
	Description           string `json:"description"`
}

// ChosenInlineResult is an inline query result chosen by a User
type ChosenInlineResult struct {
	ResultID string `json:"result_id"`
	From     User   `json:"from"`
	Query    string `json:"query"`
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015, Salesforce.com, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 * following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
 *   disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
 *   disclaimer in the documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package connection

import (
	"errors"
//	. "github.com/forcedotcom/rmux/log"
	"github.com/forcedotcom/rmux/protocol"
)

var ERR_HASHRING_DOWN = errors.New("Hash ring is down")

//An outbound connection to a redis server
//Maintains its own underlying TimedNetReadWriter, and keeps track of its DatabaseId for select() changes
type HashRing struct {
	//The connection pools that we will be hashing our connections to
	ConnectionPools []*ConnectionPool
	//The bitmask to use for all hashed queries
	BitMask uint32
	//The default connection pool
	DefaultConnectionPool *ConnectionPool
	// Whether to failover to next pool when the desired one is down
	Failover bool
}

func NewHashRing(connectionPools []*ConnectionPool, failover bool) (newHashRing *HashRing, err error) {
	newHashRing = &HashRing{}
	//The goal here is to have an even distribution of connection pools for a hash,
	//AND ensuring that the distribution stays balanced when a pool goes down
	//start out by rounding up to the nearest prime p
	poolLength := len(connectionPools)
	prime, err := newHashRing.getNextPrime(poolLength)
	if err != nil {
		return
	}
//	Debug("Making a hash ring for prime %v", prime)
	newHashRing.Failover = failover
	newHashRing.setBitMask(prime)
	newHashRing.ConnectionPools = make([]*ConnectionPool, newHashRing.BitMask+1)
//	Debug("Made a set of connection pools of size %v", len(newHashRing.ConnectionPools))

	newHashRing.distributeConnectionPools(prime, connectionPools)
	return
}

func (myHashRing *HashRing) distributeConnectionPools(prime int, connectionPools []*ConnectionPool) {
	lastTarget := 0
	for multiplier := 1; multiplier < prime; multiplier++ {
		for value := 0; value < prime; value++ {
			if multiplier*value%prime < len(connectionPools) {
				lastTarget = multiplier * value % prime
			}
			myHashRing.ConnectionPools[(multiplier-1)*prime+value] = connectionPools[lastTarget]
		}
	}

	copy(myHashRing.ConnectionPools[prime*(prime-1):], myHashRing.ConnectionPools)
	if len(myHashRing.ConnectionPools) > 0 {
		myHashRing.DefaultConnectionPool = myHashRing.ConnectionPools[0]
	}
}

func (myHashRing *HashRing) getNextPrime(poolLength int) (int, error) {
	if poolLength == 0 {
		return -1, errors.New("At least one connection pool is required")
	}
	primes := []int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101}
	for _, curPrime := range primes {
		if poolLength <= curPrime {
			return curPrime, nil
		}
	}
	return -1, errors.New("Prime list isn't big enough")
}

func (myHashRing *HashRing) setBitMask(prime int) {
	myHashRing.BitMask = 1
	var minimumSize uint32 = uint32(prime * (prime - 1))
	for minimumSize > myHashRing.BitMask {
		myHashRing.BitMask = myHashRing.BitMask << 1
	}
	myHashRing.BitMask = myHashRing.BitMask - 1
}

//Gets the connectionKey, for a to-be-multiplexed command
//Uses the bernstein hash, which is one of the fastest key-distribution algorithms out there
func (myHashRing *HashRing) GetConnectionPool(command protocol.Command) (connectionPool *ConnectionPool, err error) {
	var hash uint32 = 0
	if command.GetArgCount() > 0 {
		//The bernstein hash is one of the faster key-distribution algorithms out there, for small character keys
		//An alternate (but slower) algorithm would be to use go's built-in hash/fnv, if this proves insufficient
		for _, char := range command.GetFirstArg() {
			hash = hash<<5 + hash + uint32(char)
		}
	}

	hash = myHashRing.BitMask & hash
	targetHash := hash
	connectionPool = myHashRing.ConnectionPools[hash]

	for myHashRing.Failover && !connectionPool.IsConnected() {
		if hash == myHashRing.BitMask {
			hash = 0
		} else {
			hash = hash + 1
		}

		// If we've cycled through everything, break out
		if hash == targetHash {
			break
		}

		connectionPool = myHashRing.ConnectionPools[hash]
	}

	if !connectionPool.isConnected() {
		return nil, ERR_HASHRING_DOWN
	} else {
		return connectionPool, nil
	}
}
<commit_msg>Fix test<commit_after>/*
 * Copyright (c) 2015, Salesforce.com, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 * following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
 *   disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
 *   disclaimer in the documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package connection

import (
	"errors"
//	. "github.com/forcedotcom/rmux/log"
	"github.com/forcedotcom/rmux/protocol"
)

var ERR_HASHRING_DOWN = errors.New("Hash ring is down")

//An outbound connection to a redis server
//Maintains its own underlying TimedNetReadWriter, and keeps track of its DatabaseId for select() changes
type HashRing struct {
	//The connection pools that we will be hashing our connections to
	ConnectionPools []*ConnectionPool
	//The bitmask to use for all hashed queries
	BitMask uint32
	//The default connection pool
	DefaultConnectionPool *ConnectionPool
	// Whether to failover to next pool when the desired one is down
	Failover bool
}

func NewHashRing(connectionPools []*ConnectionPool, failover bool) (newHashRing *HashRing, err error) {
	newHashRing = &HashRing{}
	//The goal here is to have an even distribution of connection pools for a hash,
	//AND ensuring that the distribution stays balanced when a pool goes down
	//start out by rounding up to the nearest prime p
	poolLength := len(connectionPools)
	prime, err := newHashRing.getNextPrime(poolLength)
	if err != nil {
		return
	}
//	Debug("Making a hash ring for prime %v", prime)
	newHashRing.Failover = failover
	newHashRing.setBitMask(prime)
	newHashRing.ConnectionPools = make([]*ConnectionPool, newHashRing.BitMask+1)
//	Debug("Made a set of connection pools of size %v", len(newHashRing.ConnectionPools))

	newHashRing.distributeConnectionPools(prime, connectionPools)
	return
}

func (myHashRing *HashRing) distributeConnectionPools(prime int, connectionPools []*ConnectionPool) {
	lastTarget := 0
	for multiplier := 1; multiplier < prime; multiplier++ {
		for value := 0; value < prime; value++ {
			if multiplier*value%prime < len(connectionPools) {
				lastTarget = multiplier * value % prime
			}
			myHashRing.ConnectionPools[(multiplier-1)*prime+value] = connectionPools[lastTarget]
		}
	}

	copy(myHashRing.ConnectionPools[prime*(prime-1):], myHashRing.ConnectionPools)
	if len(myHashRing.ConnectionPools) > 0 {
		myHashRing.DefaultConnectionPool = myHashRing.ConnectionPools[0]
	}
}

func (myHashRing *HashRing) getNextPrime(poolLength int) (int, error) {
	if poolLength == 0 {
		return -1, errors.New("At least one connection pool is required")
	}
	primes := []int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101}
	for _, curPrime := range primes {
		if poolLength <= curPrime {
			return curPrime, nil
		}
	}
	return -1, errors.New("Prime list isn't big enough")
}

func (myHashRing *HashRing) setBitMask(prime int) {
	myHashRing.BitMask = 1
	var minimumSize uint32 = uint32(prime * (prime - 1))
	for minimumSize > myHashRing.BitMask {
		myHashRing.BitMask = myHashRing.BitMask << 1
	}
	myHashRing.BitMask = myHashRing.BitMask - 1
}

//Gets the connectionKey, for a to-be-multiplexed command
//Uses the bernstein hash, which is one of the fastest key-distribution algorithms out there
func (myHashRing *HashRing) GetConnectionPool(command protocol.Command) (connectionPool *ConnectionPool, err error) {
	var hash uint32 = 0
	if command.GetArgCount() > 0 {
		//The bernstein hash is one of the faster key-distribution algorithms out there, for small character keys
		//An alternate (but slower) algorithm would be to use go's built-in hash/fnv, if this proves insufficient
		for _, char := range command.GetFirstArg() {
			hash = hash<<5 + hash + uint32(char)
		}
	}

	hash = myHashRing.BitMask & hash
	targetHash := hash
	connectionPool = myHashRing.ConnectionPools[hash]

	for myHashRing.Failover && !connectionPool.IsConnected() {
		if hash == myHashRing.BitMask {
			hash = 0
		} else {
			hash = hash + 1
		}

		// If we've cycled through everything, break out
		if hash == targetHash {
			break
		}

		connectionPool = myHashRing.ConnectionPools[hash]
	}

	if !connectionPool.IsConnected() {
		return nil, ERR_HASHRING_DOWN
	} else {
		return connectionPool, nil
	}
}
<|endoftext|>


<commit_before>package conduit

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"testing"

	"github.com/karlseguin/typed"
)

// containsString checks whether s contains e.
func containsString(s []string, e string) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}

// call does the heavy lifting of sending a request to conduit,
// handling error responses by returning *ConduitError,
// and unmarshalling the JSON result into the specified
// result interface{}.
func call(endpointURL string, params interface{}, result interface{}) error {
	form := url.Values{}
	form.Add("output", "json")

	if params != nil {
		b, err := json.Marshal(params)
		if err != nil {
			return err
		}

		form.Add("params", string(b))

		_, isConduitConnect := params.(*pConduitConnect)
		if isConduitConnect {
			form.Add("__conduit__", "true")
		}
	}

	req, err := http.NewRequest("POST", endpointURL, strings.NewReader(form.Encode()))
	if err != nil {
		return err
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	jsonBody, err := typed.Json(body)
	if err != nil {
		return err
	}

	// parse any error conduit returned first
	if jsonBody.String("error_code") != "" {
		return &ConduitError{
			code: jsonBody.String("error_code"),
			info: jsonBody.String("error_info"),
		}
	}

	// if no error, parse the expected result
	resultBytes, err := jsonBody.ToBytes("result")
	if err != nil {
		return err
	}

	if resultBytes != nil {
		if err = json.Unmarshal(resultBytes, &result); err != nil {
			return err
		}
	}

	return nil
}

/* Test Helpers */
func expect(t *testing.T, a interface{}, b interface{}) {
	if a != b {
		t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
	}
}
<commit_msg>Also check if result != nil<commit_after>package conduit

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"testing"

	"github.com/karlseguin/typed"
)

// containsString checks whether s contains e.
func containsString(s []string, e string) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}

// call does the heavy lifting of sending a request to conduit,
// handling error responses by returning *ConduitError,
// and unmarshalling the JSON result into the specified
// result interface{}.
func call(endpointURL string, params interface{}, result interface{}) error {
	form := url.Values{}
	form.Add("output", "json")

	if params != nil {
		b, err := json.Marshal(params)
		if err != nil {
			return err
		}

		form.Add("params", string(b))

		_, isConduitConnect := params.(*pConduitConnect)
		if isConduitConnect {
			form.Add("__conduit__", "true")
		}
	}

	req, err := http.NewRequest("POST", endpointURL, strings.NewReader(form.Encode()))
	if err != nil {
		return err
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	jsonBody, err := typed.Json(body)
	if err != nil {
		return err
	}

	// parse any error conduit returned first
	if jsonBody.String("error_code") != "" {
		return &ConduitError{
			code: jsonBody.String("error_code"),
			info: jsonBody.String("error_info"),
		}
	}

	// if no error, parse the expected result
	resultBytes, err := jsonBody.ToBytes("result")
	if err != nil {
		return err
	}

	if result != nil && resultBytes != nil {
		if err = json.Unmarshal(resultBytes, &result); err != nil {
			return err
		}
	}

	return nil
}

/* Test Helpers */
func expect(t *testing.T, a interface{}, b interface{}) {
	if a != b {
		t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package errors

import (
	"bytes"
	"encoding/json"
	"regexp"
	"runtime"
	"strings"
)

var (
	reEmpty   = regexp.MustCompile(`^\s*$`)
	reInit    = regexp.MustCompile(`init·\d+$`) // main.init·1
	reClosure = regexp.MustCompile(`func·\d+$`) // main.func·001
)

// name format:
// runtime.goexit
// runtime.main
// main.init
// main.main
// main.init·1 -> main.init
// main.func·001 -> main.func
// github.com/chai2010/errors.New
// ...
func callerInfo(skip int) (name, file string, line int, ok bool) {
	pc, file, line, ok := runtime.Caller(skip)
	if !ok {
		name = "???"
		file = "???"
		line = 1
		return
	}

	name = runtime.FuncForPC(pc).Name()
	if reInit.MatchString(name) {
		name = reInit.ReplaceAllString(name, "init")
	}
	if reClosure.MatchString(name) {
		name = reClosure.ReplaceAllString(name, "func")
	}

	// Truncate file name at last file name separator.
	if idx := strings.LastIndex(file, "/"); idx >= 0 {
		file = file[idx+1:]
	} else if idx = strings.LastIndex(file, "\\"); idx >= 0 {
		file = file[idx+1:]
	}
	return
}

func jsonEncode(m interface{}) []byte {
	data, err := json.MarshalIndent(m, "", "\t")
	if err != nil {
		return nil
	}
	data = bytes.Replace(data, []byte("\n"), []byte("\r\n"), -1)
	return data
}

func jsonEncodeString(m interface{}) string {
	return string(jsonEncode(m))
}

func jsonDecode(data []byte, m interface{}) error {
	return json.Unmarshal(data, m)
}
<commit_msg>simplify json.Marshal<commit_after>// Copyright 2015 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package errors

import (
	"bytes"
	"encoding/json"
	"regexp"
	"runtime"
	"strings"
)

var (
	reEmpty   = regexp.MustCompile(`^\s*$`)
	reInit    = regexp.MustCompile(`init·\d+$`) // main.init·1
	reClosure = regexp.MustCompile(`func·\d+$`) // main.func·001
)

// name format:
// runtime.goexit
// runtime.main
// main.init
// main.main
// main.init·1 -> main.init
// main.func·001 -> main.func
// github.com/chai2010/errors.New
// ...
func callerInfo(skip int) (name, file string, line int, ok bool) {
	pc, file, line, ok := runtime.Caller(skip)
	if !ok {
		name = "???"
		file = "???"
		line = 1
		return
	}

	name = runtime.FuncForPC(pc).Name()
	if reInit.MatchString(name) {
		name = reInit.ReplaceAllString(name, "init")
	}
	if reClosure.MatchString(name) {
		name = reClosure.ReplaceAllString(name, "func")
	}

	// Truncate file name at last file name separator.
	if idx := strings.LastIndex(file, "/"); idx >= 0 {
		file = file[idx+1:]
	} else if idx = strings.LastIndex(file, "\\"); idx >= 0 {
		file = file[idx+1:]
	}
	return
}

func jsonEncode(m interface{}) []byte {
	data, err := json.Marshal(m)
	if err != nil {
		return nil
	}
	return data
}

func jsonEncodeIndent(m interface{}) []byte {
	data, err := json.MarshalIndent(m, "", "\t")
	if err != nil {
		return nil
	}
	data = bytes.Replace(data, []byte("\n"), []byte("\r\n"), -1)
	return data
}

func jsonEncodeString(m interface{}) string {
	return string(jsonEncode(m))
}

func jsonDecode(data []byte, m interface{}) error {
	return json.Unmarshal(data, m)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/samuel/go-gettext/gettext"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"path"
	"strconv"
)

func UpToDate(r *http.Request) bool {
	if r.URL.Query().Get("uptodate") == "0" {
		return false
	}
	return true
}

func Small(r *http.Request) bool {
	if len(r.URL.Query().Get("small")) > 0 {
		return true
	}
	return false
}

func Lang(r *http.Request) string {
	lang := r.URL.Query().Get("lang")
	if len(lang) == 0 {
		lang = "en_US"
	}
	return lang
}

func GetQS(q url.Values, param string, deflt int) (num int, str string) {
	str = q.Get(param)
	num, err := strconv.Atoi(str)
	if err != nil {
		num = deflt
		str = ""
	} else {
		str = fmt.Sprintf("&%s=%s", param, str)
	}
	return
}

var HaveManual = map[string]bool{
	"ar":    true,
	"zh_CN": true,
	"cs":    true,
	"nl":    true,
	"en":    true,
	"fa":    true,
	"fr":    true,
	"de":    true,
	"el":    true,
	"hu":    true,
	"it":    true,
	"ja":    true,
	"lv":    true,
	"nb":    true,
	"pl":    true,
	"pt_BR": true,
	"ru":    true,
	"es":    true,
	"sv":    true,
	"tr":    true,
}

func FuncMap(domain *gettext.Domain) template.FuncMap {
	return template.FuncMap{
		"UnEscaped": func(x string) interface{} {
			return template.HTML(x)
		},
		"UnEscapedURL": func(x string) interface{} {
			return template.URL(x)
		},
		"GetText": func(lang string, text string) string {
			return domain.GetText(lang, text)
		},
		"Equal": func(one string, two string) bool {
			return one == two
		},
		"Not": func(b bool) bool {
			return !b
		},
		"And": func(a bool, b bool) bool {
			return a && b
		},
		"UserManual": func(lang string) string {
			if _, ok := HaveManual[lang]; !ok {
				lang = "en"
			}
			return lang
		},
	}
}

var Layout *template.Template

func CompileTemplate(base string, domain *gettext.Domain, templateName string) *template.Template {
	if Layout == nil {
		Layout = template.New("")
		Layout = Layout.Funcs(FuncMap(domain))
		Layout = template.Must(Layout.ParseFiles(
			path.Join(base, "public/base.html"),
			path.Join(base, "public/torbutton.html"),
		))
	}
	l, err := Layout.Clone()
	if err != nil {
		log.Fatal(err)
	}
	return template.Must(l.ParseFiles(path.Join(base, "public/", templateName)))
}

type locale struct {
	Code string
	Name string
}

func GetLocaleList(base string) map[string]string {
	// populated from https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
	haveTranslatedNames := map[string]string{
		"ar": "العربية",
		"bs": "Bosanski jezik",
		"ca": "Català",
		"cs": "čeština",
		"cy": "Cymraeg",
		"da": "Dansk",
		"de": "Deutsch",
		"el": "ελληνικά",
		"es": "Español",
		"et": "Eesti",
		"eu": "Euskara",
		"fa": "فارسی",
		"fi": "Suomi",
		"fr": "Français",
		"gl": "Galego",
		"he": "עברית",
		"hi": "हिन्दी, हिंदी",
		"hr": "Hrvatski jezik",
		"hu": "Magyar",
		"id": "Bahasa Indonesia",
		"it": "Italiano",
		"ja": "日本語",
		"km": "មែរ",
		"kn": "ಕನ್ನಡ",
		"ko": "한국어",
		"lv": "Latviešu valoda",
		"my": "ဗမာစာ",
		"nb": "Norsk bokmål",
		"nl": "Nederlands",
		"pa": "ਪੰਜਾਬੀ",
		"pl": "Język polski",
		"pt": "Português",
		"ru": "русский язык",
		"sk": "Slovenčina",
		"sl": "Slovenski jezik",
		"sr": "српски језик",
		"sv": "Svenska",
		"th": "ไทย",
		"tr": "Türkçe",
		"uk": "українська мова",
	}

	// for all folders in locale which match a locale from https://www.transifex.com/api/2/languages/
	// use the language name unless we have an override
	webLocales, err := FetchTranslationLocales(base)
	if err != nil {
		log.Printf("Failed to get up to date language list, using fallback.")
		return haveTranslatedNames
	}

	return GetInstalledLocales(base, webLocales, haveTranslatedNames)
}

func FetchTranslationLocales(base string) (map[string]locale, error) {
	file, err := os.Open(path.Join(base, "data/langs"))
	if err != nil {
		return nil, err
	}
	defer file.Close()

	webLocales := make(map[string]locale)
	// Parse the api response into a list of possible locales
	dec := json.NewDecoder(file)
	for {
		var webList []locale
		if err = dec.Decode(&webList); err == io.EOF {
			break
		} else if err != nil {
			log.Fatal(err)
		}

		// The api returns an array, so we need to map it
		for _, l := range webList {
			webLocales[l.Code] = l
		}
	}

	return webLocales, nil
}

// Get a list of all languages installed in our locale folder with translations if available
func GetInstalledLocales(base string, webLocales map[string]locale, nameTranslations map[string]string) map[string]string {
	localFiles, err := ioutil.ReadDir(path.Join(base, "locale"))

	if err != nil {
		log.Print("No locales found in 'locale'. Try running 'make i18n'.")
		log.Fatal(err)
	}

	locales := make(map[string]string, len(localFiles))
	locales["en_US"] = "English"

	for _, f := range localFiles {
		// TODO: Ensure a language has 100% of the template file
		// Currently this is what should be on the torcheck_completed
		// branch on the translations git should be, so we don't really
		// have to check it in theory...
		code := f.Name()

		// Only accept folders which have corresponding locale
		if !f.IsDir() || webLocales[code] == (locale{}) {
			continue
		}

		// If we have a translated name for a given locale, use it
		if transName := nameTranslations[code]; transName != "" {
			locales[code] = transName
		} else {
			locales[code] = webLocales[code].Name
		}
	}

	return locales
}
<commit_msg>Regional translation names in their language<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/samuel/go-gettext/gettext"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"path"
	"strconv"
)

func UpToDate(r *http.Request) bool {
	if r.URL.Query().Get("uptodate") == "0" {
		return false
	}
	return true
}

func Small(r *http.Request) bool {
	if len(r.URL.Query().Get("small")) > 0 {
		return true
	}
	return false
}

func Lang(r *http.Request) string {
	lang := r.URL.Query().Get("lang")
	if len(lang) == 0 {
		lang = "en_US"
	}
	return lang
}

func GetQS(q url.Values, param string, deflt int) (num int, str string) {
	str = q.Get(param)
	num, err := strconv.Atoi(str)
	if err != nil {
		num = deflt
		str = ""
	} else {
		str = fmt.Sprintf("&%s=%s", param, str)
	}
	return
}

var HaveManual = map[string]bool{
	"ar":    true,
	"zh_CN": true,
	"cs":    true,
	"nl":    true,
	"en":    true,
	"fa":    true,
	"fr":    true,
	"de":    true,
	"el":    true,
	"hu":    true,
	"it":    true,
	"ja":    true,
	"lv":    true,
	"nb":    true,
	"pl":    true,
	"pt_BR": true,
	"ru":    true,
	"es":    true,
	"sv":    true,
	"tr":    true,
}

func FuncMap(domain *gettext.Domain) template.FuncMap {
	return template.FuncMap{
		"UnEscaped": func(x string) interface{} {
			return template.HTML(x)
		},
		"UnEscapedURL": func(x string) interface{} {
			return template.URL(x)
		},
		"GetText": func(lang string, text string) string {
			return domain.GetText(lang, text)
		},
		"Equal": func(one string, two string) bool {
			return one == two
		},
		"Not": func(b bool) bool {
			return !b
		},
		"And": func(a bool, b bool) bool {
			return a && b
		},
		"UserManual": func(lang string) string {
			if _, ok := HaveManual[lang]; !ok {
				lang = "en"
			}
			return lang
		},
	}
}

var Layout *template.Template

func CompileTemplate(base string, domain *gettext.Domain, templateName string) *template.Template {
	if Layout == nil {
		Layout = template.New("")
		Layout = Layout.Funcs(FuncMap(domain))
		Layout = template.Must(Layout.ParseFiles(
			path.Join(base, "public/base.html"),
			path.Join(base, "public/torbutton.html"),
		))
	}
	l, err := Layout.Clone()
	if err != nil {
		log.Fatal(err)
	}
	return template.Must(l.ParseFiles(path.Join(base, "public/", templateName)))
}

type locale struct {
	Code string
	Name string
}

func GetLocaleList(base string) map[string]string {
	// populated from https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
	// and https://sites.google.com/site/opti365/translate_codes
	haveTranslatedNames := map[string]string{
		"af":    "Afrikaans",
		"ar":    "العربية",
		"bs":    "Bosanski jezik",
		"ca":    "Català",
		"cs":    "Čeština",
		"cy":    "Cymraeg",
		"da":    "Dansk",
		"de":    "Deutsch",
		"el":    "ελληνικά",
		"eo":    "Esperanto",
		"es":    "Español",
		"es_AR": "Español (Argentina)",
		"et":    "Eesti",
		"eu":    "Euskara",
		"fa":    "فارسی",
		"fi":    "Suomi",
		"fr":    "Français",
		"fr_CA": "Français (Canadien)",
		"gl":    "Galego",
		"he":    "עברית",
		"hi":    "हिन्दी",
		"hr":    "Hrvatski jezik",
		"hr_HR": "Hrvatski jezik (Croatia)",
		"hu":    "Magyar",
		"id":    "Bahasa Indonesia",
		"it":    "Italiano",
		"ja":    "日本語",
		"km":    "មែរ",
		"kn":    "ಕನ್ನಡ",
		"ko":    "한국어",
		"ko_KR": "한국어 (South Korea)",
		"lv":    "Latviešu valoda",
		"mk":    "македонски јазик",
		"ms_MY": "Bahasa Melayu",
		"my":    "ဗမာစာ",
		"nb":    "Norsk bokmål",
		"nl":    "Nederlands",
		"nl_BE": "Vlaams",
		"pa":    "ਪੰਜਾਬੀ",
		"pl":    "Język polski",
		"pl_PL": "Język polski (Poland)",
		"pt":    "Português",
		"pt_BR": "Português do Brasil",
		"ru":    "русский язык",
		"si_LK": "සිංහල",
		"sk":    "Slovenčina",
		"sl":    "Slovenski jezik",
		"sl_SI": "Slovenski jezik (Slovenia)",
		"sr":    "српски језик",
		"sv":    "Svenska",
		"te_IN": "తెలుగు",
		"th":    "ไทย",
		"tr":    "Türkçe",
		"uk":    "українська мова",
		"zh_CN": "中文简体",
		"zh_TW": "中文繁體",
	}

	// for all folders in locale which match a locale from https://www.transifex.com/api/2/languages/
	// use the language name unless we have an override
	webLocales, err := FetchTranslationLocales(base)
	if err != nil {
		log.Printf("Failed to get up to date language list, using fallback.")
		return haveTranslatedNames
	}

	return GetInstalledLocales(base, webLocales, haveTranslatedNames)
}

func FetchTranslationLocales(base string) (map[string]locale, error) {
	file, err := os.Open(path.Join(base, "data/langs"))
	if err != nil {
		return nil, err
	}
	defer file.Close()

	webLocales := make(map[string]locale)
	// Parse the api response into a list of possible locales
	dec := json.NewDecoder(file)
	for {
		var webList []locale
		if err = dec.Decode(&webList); err == io.EOF {
			break
		} else if err != nil {
			log.Fatal(err)
		}

		// The api returns an array, so we need to map it
		for _, l := range webList {
			webLocales[l.Code] = l
		}
	}

	return webLocales, nil
}

// Get a list of all languages installed in our locale folder with translations if available
func GetInstalledLocales(base string, webLocales map[string]locale, nameTranslations map[string]string) map[string]string {
	localFiles, err := ioutil.ReadDir(path.Join(base, "locale"))

	if err != nil {
		log.Print("No locales found in 'locale'. Try running 'make i18n'.")
		log.Fatal(err)
	}

	locales := make(map[string]string, len(localFiles))
	locales["en_US"] = "English"

	for _, f := range localFiles {
		// TODO: Ensure a language has 100% of the template file
		// Currently this is what should be on the torcheck_completed
		// branch on the translations git should be, so we don't really
		// have to check it in theory...
		code := f.Name()

		// Only accept folders which have corresponding locale
		if !f.IsDir() || webLocales[code] == (locale{}) {
			continue
		}

		// If we have a translated name for a given locale, use it
		if transName := nameTranslations[code]; transName != "" {
			locales[code] = transName
		} else {
			log.Print("No translated name for code: " + code)
			locales[code] = webLocales[code].Name
		}
	}

	return locales
}
<|endoftext|>


<commit_before>package bkdtree

import (
	"encoding/json"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"syscall"

	"github.com/pkg/errors"
)

//FileMmap mmaps the given file.
//https://medium.com/@arpith/adventures-with-mmap-463b33405223
func FileMmap(f *os.File) (data []byte, err error) {
	info, err1 := f.Stat()
	if err1 != nil {
		err = errors.Wrap(err1, "")
		return
	}
	prots := []int{syscall.PROT_WRITE | syscall.PROT_READ, syscall.PROT_READ}
	for _, prot := range prots {
		data, err = syscall.Mmap(int(f.Fd()), 0, int(info.Size()), prot, syscall.MAP_SHARED)
		if err == nil {
			break
		}
	}
	if err != nil {
		err = errors.Wrap(err, "")
		return
	}
	return
}

//FileMunmap unmaps the given file.
func FileMunmap(data []byte) (err error) {
	err = syscall.Munmap(data)
	if err != nil {
		err = errors.Wrap(err, "")
		return
	}
	return
}

//FileUnmarshal unmarshals the given file to object.
func FileUnmarshal(fp string, v interface{}) (err error) {
	var f *os.File
	var data []byte
	if f, err = os.Open(fp); err != nil {
		return
	}
	defer f.Close()
	if data, err = FileMmap(f); err != nil {
		return
	}
	defer FileMunmap(data)
	err = json.Unmarshal(data, v)
	return
}

//FileMarshal marshals the given object to file.
func FileMarshal(fp string, v interface{}) (err error) {
	var f *os.File
	var data []byte
	var count int
	if data, err = json.Marshal(v); err != nil {
		return
	}
	if f, err = os.OpenFile(fp, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600); err != nil {
		return
	}
	defer f.Close()
	if count, err = f.Write(data); err != nil {
		return
	}
	if count != len(data) {
		err = errors.Errorf("%s partial wirte %d, want %d", fp, count, len(data))
		return
	}
	return
}

//FilepathGlob is enhanced version of standard path.filepath::Glob()
func FilepathGlob(dir, patt string) (matches [][]string, err error) {
	var d *os.File
	var fns []string
	d, err = os.Open(dir)
	if os.IsNotExist(err) {
		err = nil
		return
	}
	fns, err = d.Readdirnames(0)
	if err != nil {
		err = errors.Wrap(err, "")
		return
	}
	sort.Strings(fns)
	re := regexp.MustCompile(patt)
	for _, fn := range fns {
		subs := re.FindStringSubmatch(fn)
		if subs == nil {
			continue
		}
		matches = append(matches, subs)
	}
	return
}

//FilepathGlobRm remove given files under the given directory.
func FilepathGlobRm(dir, patt string) (err error) {
	var matches [][]string
	if matches, err = FilepathGlob(dir, patt); err != nil {
		return
	}
	for _, match := range matches {
		fp := filepath.Join(dir, match[0])
		if err = os.Remove(fp); err != nil {
			err = errors.Wrap(err, "")
		}
	}
	return
}
<commit_msg>minor<commit_after>package bkdtree

import (
	"encoding/json"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"syscall"

	"github.com/pkg/errors"
)

//FileMmap mmaps the given file.
//https://medium.com/@arpith/adventures-with-mmap-463b33405223
func FileMmap(f *os.File) (data []byte, err error) {
	info, err1 := f.Stat()
	if err1 != nil {
		err = errors.Wrap(err1, "")
		return
	}
	prots := []int{syscall.PROT_WRITE | syscall.PROT_READ, syscall.PROT_READ}
	for _, prot := range prots {
		data, err = syscall.Mmap(int(f.Fd()), 0, int(info.Size()), prot, syscall.MAP_SHARED)
		if err == nil {
			break
		}
	}
	if err != nil {
		err = errors.Wrap(err, "")
		return
	}
	return
}

//FileMunmap unmaps the given file.
func FileMunmap(data []byte) (err error) {
	err = syscall.Munmap(data)
	if err != nil {
		err = errors.Wrap(err, "")
		return
	}
	return
}

//FileUnmarshal unmarshals the given file to object.
func FileUnmarshal(fp string, v interface{}) (err error) {
	var f *os.File
	var data []byte
	if f, err = os.Open(fp); err != nil {
		return
	}
	defer f.Close()
	if data, err = FileMmap(f); err != nil {
		return
	}
	defer FileMunmap(data)
	err = json.Unmarshal(data, v)
	return
}

//FileMarshal marshals the given object to file.
func FileMarshal(fp string, v interface{}) (err error) {
	var f *os.File
	var data []byte
	var count int
	if data, err = json.Marshal(v); err != nil {
		return
	}
	if f, err = os.OpenFile(fp, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600); err != nil {
		return
	}
	defer f.Close()
	if count, err = f.Write(data); err != nil {
		return
	}
	if count != len(data) {
		err = errors.Errorf("%s partial wirte %d, want %d", fp, count, len(data))
		return
	}
	return
}

//FilepathGlob is enhanced version of standard path.filepath::Glob()
func FilepathGlob(dir, patt string) (matches [][]string, err error) {
	var d *os.File
	var fns []string
	d, err = os.Open(dir)
	if os.IsNotExist(err) {
		err = nil
		return
	}
	defer d.Close()
	fns, err = d.Readdirnames(0)
	if err != nil {
		err = errors.Wrap(err, "")
		return
	}
	sort.Strings(fns)
	re := regexp.MustCompile(patt)
	for _, fn := range fns {
		subs := re.FindStringSubmatch(fn)
		if subs == nil {
			continue
		}
		matches = append(matches, subs)
	}
	return
}

//FilepathGlobRm remove given files under the given directory.
func FilepathGlobRm(dir, patt string) (err error) {
	var matches [][]string
	if matches, err = FilepathGlob(dir, patt); err != nil {
		return
	}
	for _, match := range matches {
		fp := filepath.Join(dir, match[0])
		if err = os.Remove(fp); err != nil {
			err = errors.Wrap(err, "")
		}
	}
	return
}
<|endoftext|>


<commit_before>package ics

import (
	"errors"
	"strconv"
	"time"

	"github.com/MJKWoolnough/strparse"
)

func escape(s string) []byte {
	p := make([]byte, 0, len(s))
	for i := 0; i < len(s); i++ {
		switch s[i] {
		case '\\':
			p = append(p, '\\', '\\')
		case ';':
			p = append(p, '\\', ';')
		case ',':
			p = append(p, '\\', ',')
		case '\n':
			p = append(p, '\\', 'n')
		default:
			p = append(p, s[i])
		}
	}
	return p
}

func unescape(p string) []byte {
	u := p[:0]
	for i := 0; i < len(p); i++ {
		if p[i] == '\\' && i+1 < len(p) {
			i++
			switch p[i] {
			case '\\':
				u = append(u, '\\')
			case ';':
				u = append(u, ';')
			case ',':
				u = append(u, ',')
			case 'N', 'n':
				u = append(u, '\n')
			default:
				u = append(u, p[i])
			}
		} else {
			u = append(u, p[i])
		}
	}
	return toRet
}

func escape6868(s string) []byte {
	p := make([]byte, 0, len(s))
	for i := 0; i < len(s); i++ {
		switch s[i] {
		case '\n':
			p = append(p, '^', 'n')
		case '^':
			p = append(p, '^', '^')
		case '"':
			p = append(p, '^', '\'')
		default:
			p = append(p, s[i])
		}
	}
	return p
}

func unescape6868(p []byte) []byte {
	u := p[:0]
	for i := 0; i < len(p); i++ {
		if p[i] == '^' && i+1 < len(p) {
			i++
			switch p[i] {
			case 'n':
				u = append(u, '\n') //crlf on windows?
			case '^':
				u = append(u, '^')
			case '\'':
				u = append(u, '"')
			default:
				u = append(u, '^', p[i])
			}
		} else {
			u = append(u, p[i])
		}
	}
	return u
}

func textSplit(s string, delim byte) []string {
	toRet := make([]string, 0, 1)
	lastPos := 0
	for i := 0; i < len(s); i++ {
		switch s[i] {
		case '\\':
			i++
		case delim:
			toRet = append(toRet, unescape(s[lastPos:i]))
			lastPos = i + 2
		}
	}
	if lastPos <= len(s) {
		toRet = append(toRet, unescape(s[lastPos:len(s)]))
	}
	return toRet
}

func parseDate(s string) (time.Time, error) {
	return time.Parse("20060102", s)
}

func parseDateTime(s string, l *time.Location) (time.Time, error) {
	if l == nil {
		if s[len(s)-1] == 'Z' {
			return time.Parse("20060102T150405Z", s)
		} else {
			return time.ParseInLocation("20060102T150405Z", s, time.Local)
		}
	}
	return time.ParseInLocation("20060102T150405", v, l)
}

func parseTime(s string, l *time.Location) (time.Time, error) {
	if l == nil {
		if s[len(s)-1] == 'Z' {
			return time.Parse("150405Z", s)
		} else {
			return time.ParseInLocation("150405Z", s, time.Local)
		}
	}
	return time.ParseInLocation("150405", v, l)
}

const nums = "0123456789"

func parseDuration(s string) (time.Duration, error) {
	p := strparse.Parser{Str: s}
	var (
		dur time.Duration
		neg bool
	)
	if p.Accept("-") {
		neg = true
	} else {
		p.Accept("+")
	}
	if !p.Accept("P") {
		return ErrInvalidDuration
	}
	p.Get()
	if !p.Accept("T") {
		p.AcceptRun(nums)
		num := p.Get()
		if len(num) == 0 {
			return 0, ErrInvalidDuration
		}
		n, _ := strconv.Atoi(num)
		p.Accept("DW")
		switch p.Get() {
		case "D":
			dur = time.Duration(n) * time.Hour * 24
		case "W":
			return time.Duration(n) * time.Hour * 24 * 7
		default:
			return 0, ErrInvalidDuration
		}
		p.Except("")
		switch p.Get() {
		case "":
			if neg {
				return -dur, nil
			}
			return dur, nil
		case "T":
		default:
			return 0, ErrInvalidDuration
		}
	} else {
		p.Get()
	}
	toRead := "HMS"
	for len(toRead) > 0 {
		p.AcceptRun(nums)
		num := p.Get()
		if len(num) == 0 {
			return 0, ErrInvalidDuration
		}
		n, _ := strconv.Atoi(num)
		p.Accept(toRead)
		switch p.Get() {
		case "H":
			dur += n * time.Hour
			toRead = "MS"
		case "M":
			dur += n * time.Minute
			toRead = "S"
		case "S":
			dur += n * time.Second
			toRead = ""
		default:
			return 0, ErrInvalidDuration
		}
	}
	if neg {
		return -dur, nil
	}
	return dur, nil
}

// Errors

var (
	ErrInvalidDuration = errors.New("invalid duration string")
)
<commit_msg>updated utils to match requirements of new lexer<commit_after>package ics

import (
	"errors"
	"strconv"
	"time"

	strparse "github.com/MJKWoolnough/parser"
)

func escape(s string) []byte {
	p := make([]byte, 0, len(s))
	for i := 0; i < len(s); i++ {
		switch s[i] {
		case '\\':
			p = append(p, '\\', '\\')
		case ';':
			p = append(p, '\\', ';')
		case ',':
			p = append(p, '\\', ',')
		case '\n':
			p = append(p, '\\', 'n')
		default:
			p = append(p, s[i])
		}
	}
	return p
}

func unescape(p string) []byte {
	u := make([]byte, 0, len(p))
	for i := 0; i < len(p); i++ {
		if p[i] == '\\' && i+1 < len(p) {
			i++
			switch p[i] {
			case '\\':
				u = append(u, '\\')
			case ';':
				u = append(u, ';')
			case ',':
				u = append(u, ',')
			case 'N', 'n':
				u = append(u, '\n')
			default:
				u = append(u, p[i])
			}
		} else {
			u = append(u, p[i])
		}
	}
	return u
}

func escape6868(s string) []byte {
	p := make([]byte, 0, len(s))
	for i := 0; i < len(s); i++ {
		switch s[i] {
		case '\n':
			p = append(p, '^', 'n')
		case '^':
			p = append(p, '^', '^')
		case '"':
			p = append(p, '^', '\'')
		default:
			p = append(p, s[i])
		}
	}
	return p
}

func unescape6868(p string) string {
	u := make([]byte, 0, len(p))
	for i := 0; i < len(p); i++ {
		if p[i] == '^' && i+1 < len(p) {
			i++
			switch p[i] {
			case 'n':
				u = append(u, '\n') //crlf on windows?
			case '^':
				u = append(u, '^')
			case '\'':
				u = append(u, '"')
			default:
				u = append(u, '^', p[i])
			}
		} else {
			u = append(u, p[i])
		}
	}
	return string(u)
}

func textSplit(s string, delim byte) []string {
	toRet := make([]string, 0, 1)
	lastPos := 0
	for i := 0; i < len(s); i++ {
		switch s[i] {
		case '\\':
			i++
		case delim:
			toRet = append(toRet, string(unescape(s[lastPos:i])))
			lastPos = i + 2
		}
	}
	if lastPos <= len(s) {
		toRet = append(toRet, string(unescape(s[lastPos:])))
	}
	return toRet
}

func parseDate(s string) (time.Time, error) {
	return time.Parse("20060102", s)
}

func parseDateTime(s string, l *time.Location) (time.Time, error) {
	if l == nil {
		if s[len(s)-1] == 'Z' {
			return time.Parse("20060102T150405Z", s)
		} else {
			return time.ParseInLocation("20060102T150405Z", s, time.Local)
		}
	}
	return time.ParseInLocation("20060102T150405", s, l)
}

func parseTime(s string, l *time.Location) (time.Time, error) {
	if l == nil {
		if s[len(s)-1] == 'Z' {
			return time.Parse("150405Z", s)
		} else {
			return time.ParseInLocation("150405Z", s, time.Local)
		}
	}
	return time.ParseInLocation("150405", s, l)
}

const nums = "0123456789"

func parseDuration(s string) (time.Duration, error) {
	p := strparse.NewStringParser(s)
	var (
		dur time.Duration
		neg bool
	)
	if p.Accept("-") {
		neg = true
	} else {
		p.Accept("+")
	}
	if !p.Accept("P") {
		return 0, ErrInvalidDuration
	}
	p.Get()
	if !p.Accept("T") {
		p.AcceptRun(nums)
		num := p.Get()
		if len(num) == 0 {
			return 0, ErrInvalidDuration
		}
		n, _ := strconv.Atoi(num)
		p.Accept("DW")
		switch p.Get() {
		case "D":
			dur = time.Duration(n) * time.Hour * 24
		case "W":
			return time.Duration(n) * time.Hour * 24 * 7, nil
		default:
			return 0, ErrInvalidDuration
		}
		p.Except("")
		switch p.Get() {
		case "":
			if neg {
				return -dur, nil
			}
			return dur, nil
		case "T":
		default:
			return 0, ErrInvalidDuration
		}
	} else {
		p.Get()
	}
	toRead := "HMS"
	for len(toRead) > 0 {
		p.AcceptRun(nums)
		num := p.Get()
		if len(num) == 0 {
			return 0, ErrInvalidDuration
		}
		n, _ := strconv.Atoi(num)
		p.Accept(toRead)
		switch p.Get() {
		case "H":
			dur += time.Duration(n) * time.Hour
			toRead = "MS"
		case "M":
			dur += time.Duration(n) * time.Minute
			toRead = "S"
		case "S":
			dur += time.Duration(n) * time.Second
			toRead = ""
		default:
			return 0, ErrInvalidDuration
		}
	}
	if neg {
		return -dur, nil
	}
	return dur, nil
}

// Errors

var (
	ErrInvalidDuration = errors.New("invalid duration string")
)
<|endoftext|>


<commit_before>package main

import (
	"strconv"
	"strings"
)

// convert and concatenate a []int8 array to a string
// inspired by perterGo: https://groups.google.com/forum/#!topic/golang-nuts/Jel8Bb-YwX8
func int8ArrayToString(char []int8) string {

	s := make([]string, len(char))

	for i := range s {
		s[1] = strconv.QuoteRune(rune(char[i])) // convert each int8 char to a rune
	}
	return strings.Join(s, "")
}
<commit_msg>Fix and improve the convertion to a string<commit_after>package main

import "strings"

// Convert and concatenate a []int8 slice to a string
// inspired by perterGo: https://groups.google.com/forum/#!topic/golang-nuts/Jel8Bb-YwX8
func int8SliceToString(char []int8) string {
	s := make([]string, len(char))
	for i := range s {
		s[i] = string(char[i])
	}
	return strings.Join(s, "")
}

// TODO maybe change this to convert exclusively [65]int8 array ?
<|endoftext|>


<commit_before>package opentracing

import (
	"net/http"
	"net/url"
	"strings"
)

const (
	// ContextIDHTTPHeaderPrefix precedes the opentracing-related ContextID HTTP
	// headers.
	ContextIDHTTPHeaderPrefix = "Open-Tracing-Context-Id-"

	// TagsHTTPHeaderPrefix precedes the opentracing-related trace-tags HTTP
	// headers.
	TagsHTTPHeaderPrefix = "Open-Tracing-Trace-Tags-"
)

// InjectSpanInHeader encodes Span `sp` in `h` as a series of HTTP headers.
// Values are URL-escaped.
func InjectSpanInHeader(
	sp Span,
	h http.Header,
) error {
	// First, try to inject using the GoHTTPHeader format (our preference).
	if err := sp.Tracer().Inject(sp, GoHTTPHeader, h); err == nil {
		return nil
	}

	// Else, fall back on SplitText.
	carrier := NewSplitTextCarrier()
	if err := sp.Tracer().Inject(sp, SplitText, carrier); err != nil {
		return err
	}
	for headerSuffix, val := range carrier.TracerState {
		h.Add(ContextIDHTTPHeaderPrefix+headerSuffix, url.QueryEscape(val))
	}
	for headerSuffix, val := range carrier.Baggage {
		h.Add(TagsHTTPHeaderPrefix+headerSuffix, url.QueryEscape(val))
	}
	return nil
}

// JoinFromHeader decodes a Span with operation name `operationName` from `h`,
// expecting that header values are URL-escpaed.
//
// If `operationName` is empty, the caller must later call
// `Span.SetOperationName` on the returned `Span`.
func JoinFromHeader(
	operationName string,
	h http.Header,
	tracer Tracer,
) (Span, error) {
	// First, try to Join using the GoHTTPHeader format (our preference).
	span, err := tracer.Join(operationName, GoHTTPHeader, h)
	if err == nil {
		return span, nil
	}

	// Else, fall back on SplitText.
	carrier := NewSplitTextCarrier()
	for key, val := range h {
		if strings.HasPrefix(key, ContextIDHTTPHeaderPrefix) {
			// We don't know what to do with anything beyond slice item v[0]:
			unescaped, err := url.QueryUnescape(val[0])
			if err != nil {
				return nil, err
			}
			if carrier.TracerState == nil {
				carrier.TracerState = map[string]string{}
			}
			carrier.TracerState[strings.TrimPrefix(key, ContextIDHTTPHeaderPrefix)] = unescaped
		} else if strings.HasPrefix(key, TagsHTTPHeaderPrefix) {
			// We don't know what to do with anything beyond slice item v[0]:
			unescaped, err := url.QueryUnescape(val[0])
			if err != nil {
				return nil, err
			}
			if carrier.Baggage == nil {
				carrier.Baggage = map[string]string{}
			}
			carrier.Baggage[strings.TrimPrefix(key, TagsHTTPHeaderPrefix)] = unescaped
		}
	}
	return tracer.Join(operationName, SplitText, carrier)
}
<commit_msg>keep func signatures on a single line<commit_after>package opentracing

import (
	"net/http"
	"net/url"
	"strings"
)

const (
	// ContextIDHTTPHeaderPrefix precedes the opentracing-related ContextID HTTP
	// headers.
	ContextIDHTTPHeaderPrefix = "Open-Tracing-Context-Id-"

	// TagsHTTPHeaderPrefix precedes the opentracing-related trace-tags HTTP
	// headers.
	TagsHTTPHeaderPrefix = "Open-Tracing-Trace-Tags-"
)

// InjectSpanInHeader encodes Span `sp` in `h` as a series of HTTP headers.
// Values are URL-escaped.
func InjectSpanInHeader(sp Span, h http.Header) error {
	// First, try to inject using the GoHTTPHeader format (our preference).
	if err := sp.Tracer().Inject(sp, GoHTTPHeader, h); err == nil {
		return nil
	}

	// Else, fall back on SplitText.
	carrier := NewSplitTextCarrier()
	if err := sp.Tracer().Inject(sp, SplitText, carrier); err != nil {
		return err
	}
	for headerSuffix, val := range carrier.TracerState {
		h.Add(ContextIDHTTPHeaderPrefix+headerSuffix, url.QueryEscape(val))
	}
	for headerSuffix, val := range carrier.Baggage {
		h.Add(TagsHTTPHeaderPrefix+headerSuffix, url.QueryEscape(val))
	}
	return nil
}

// JoinFromHeader decodes a Span with operation name `operationName` from `h`,
// expecting that header values are URL-escpaed.
//
// If `operationName` is empty, the caller must later call
// `Span.SetOperationName` on the returned `Span`.
func JoinFromHeader(operationName string, h http.Header, tracer Tracer) (Span, error) {
	// First, try to Join using the GoHTTPHeader format (our preference).
	span, err := tracer.Join(operationName, GoHTTPHeader, h)
	if err == nil {
		return span, nil
	}

	// Else, fall back on SplitText.
	carrier := NewSplitTextCarrier()
	for key, val := range h {
		if strings.HasPrefix(key, ContextIDHTTPHeaderPrefix) {
			// We don't know what to do with anything beyond slice item v[0]:
			unescaped, err := url.QueryUnescape(val[0])
			if err != nil {
				return nil, err
			}
			if carrier.TracerState == nil {
				carrier.TracerState = map[string]string{}
			}
			carrier.TracerState[strings.TrimPrefix(key, ContextIDHTTPHeaderPrefix)] = unescaped
		} else if strings.HasPrefix(key, TagsHTTPHeaderPrefix) {
			// We don't know what to do with anything beyond slice item v[0]:
			unescaped, err := url.QueryUnescape(val[0])
			if err != nil {
				return nil, err
			}
			if carrier.Baggage == nil {
				carrier.Baggage = map[string]string{}
			}
			carrier.Baggage[strings.TrimPrefix(key, TagsHTTPHeaderPrefix)] = unescaped
		}
	}
	return tracer.Join(operationName, SplitText, carrier)
}
<|endoftext|>


<commit_before>package gorm

import (
	"bytes"
	"database/sql"
	"fmt"
	"os"
	"reflect"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"
)

type safeMap struct {
	m map[string]string
	l *sync.RWMutex
}

func (s *safeMap) Set(key string, value string) {
	s.l.Lock()
	defer s.l.Unlock()
	s.m[key] = value
}

func (s *safeMap) Get(key string) string {
	s.l.RLock()
	defer s.l.RUnlock()
	return s.m[key]
}

func FieldByName(name string, value interface{}, withAddr ...bool) (interface{}, bool) {
	data := reflect.Indirect(reflect.ValueOf(value))
	name = SnakeToUpperCamel(name)

	if data.Kind() == reflect.Struct {
		if field := data.FieldByName(name); field.IsValid() {
			if len(withAddr) > 0 && field.CanAddr() {
				return field.Addr().Interface(), true
			} else {
				return field.Interface(), true
			}
		}
	} else if data.Kind() == reflect.Slice {
		elem := data.Type().Elem()
		if elem.Kind() == reflect.Ptr {
			return nil, reflect.New(data.Type().Elem().Elem()).Elem().FieldByName(name).IsValid()
		} else {
			return nil, reflect.New(data.Type().Elem()).Elem().FieldByName(name).IsValid()
		}
	}
	return nil, false
}

func newSafeMap() *safeMap {
	return &safeMap{l: new(sync.RWMutex), m: make(map[string]string)}
}

var smap = newSafeMap()
var umap = newSafeMap()

func ToSnake(u string) string {
	if v := smap.Get(u); v != "" {
		return v
	}

	buf := bytes.NewBufferString("")
	for i, v := range u {
		if i > 0 && v >= 'A' && v <= 'Z' {
			buf.WriteRune('_')
		}
		buf.WriteRune(v)
	}

	s := strings.ToLower(buf.String())
	go smap.Set(u, s)
	return s
}

func SnakeToUpperCamel(s string) string {
	if v := umap.Get(s); v != "" {
		return v
	}

	buf := bytes.NewBufferString("")
	for _, v := range strings.Split(s, "_") {
		if len(v) > 0 {
			buf.WriteString(strings.ToUpper(v[:1]))
			buf.WriteString(v[1:])
		}
	}

	u := buf.String()
	go umap.Set(s, u)
	return u
}

func toSearchableMap(attrs ...interface{}) (result interface{}) {
	if len(attrs) > 1 {
		if str, ok := attrs[0].(string); ok {
			result = map[string]interface{}{str: attrs[1]}
		}
	} else if len(attrs) == 1 {
		if attr, ok := attrs[0].(map[string]interface{}); ok {
			result = attr
		}

		if attr, ok := attrs[0].(interface{}); ok {
			result = attr
		}
	}
	return
}

func fileWithLineNum() string {
	for i := 1; i < 15; i++ {
		_, file, line, ok := runtime.Caller(i)
		if ok && (!regexp.MustCompile(`jinzhu/gorm/.*.go`).MatchString(file) || regexp.MustCompile(`jinzhu/gorm/.*test.go`).MatchString(file)) {
			return fmt.Sprintf("%v:%v", strings.TrimPrefix(file, os.Getenv("GOPATH")+"src/"), line)
		}
	}
	return ""
}

func setFieldValue(field reflect.Value, value interface{}) bool {
	if field.IsValid() && field.CanAddr() {
		switch field.Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			if str, ok := value.(string); ok {
				value, _ = strconv.Atoi(str)
			}
			field.SetInt(reflect.ValueOf(value).Int())
		default:
			if scanner, ok := field.Addr().Interface().(sql.Scanner); ok {
				scanner.Scan(value)
			} else {
				field.Set(reflect.ValueOf(value))
			}
		}
		return true
	}

	return false
}

func isBlank(value reflect.Value) bool {
	return reflect.DeepEqual(value.Interface(), reflect.Zero(value.Type()).Interface())
}

func convertInterfaceToMap(values interface{}) map[string]interface{} {
	attrs := map[string]interface{}{}

	switch value := values.(type) {
	case map[string]interface{}:
		for k, v := range value {
			attrs[ToSnake(k)] = v
		}
	case []interface{}:
		for _, v := range value {
			for key, value := range convertInterfaceToMap(v) {
				attrs[key] = value
			}
		}
	case interface{}:
		reflectValue := reflect.ValueOf(values)

		switch reflectValue.Kind() {
		case reflect.Map:
			for _, key := range reflectValue.MapKeys() {
				attrs[ToSnake(key.Interface().(string))] = reflectValue.MapIndex(key).Interface()
			}
		default:
			scope := Scope{Value: values}
			for _, field := range scope.Fields() {
				if !field.IsBlank {
					attrs[field.DBName] = field.Value
				}
			}
		}
	}
	return attrs
}
<commit_msg>Simplify set field value<commit_after>package gorm

import (
	"bytes"
	"database/sql"
	"fmt"
	"os"
	"reflect"
	"regexp"
	"runtime"
	"strings"
	"sync"
)

type safeMap struct {
	m map[string]string
	l *sync.RWMutex
}

func (s *safeMap) Set(key string, value string) {
	s.l.Lock()
	defer s.l.Unlock()
	s.m[key] = value
}

func (s *safeMap) Get(key string) string {
	s.l.RLock()
	defer s.l.RUnlock()
	return s.m[key]
}

func FieldByName(name string, value interface{}, withAddr ...bool) (interface{}, bool) {
	data := reflect.Indirect(reflect.ValueOf(value))
	name = SnakeToUpperCamel(name)

	if data.Kind() == reflect.Struct {
		if field := data.FieldByName(name); field.IsValid() {
			if len(withAddr) > 0 && field.CanAddr() {
				return field.Addr().Interface(), true
			} else {
				return field.Interface(), true
			}
		}
	} else if data.Kind() == reflect.Slice {
		elem := data.Type().Elem()
		if elem.Kind() == reflect.Ptr {
			return nil, reflect.New(data.Type().Elem().Elem()).Elem().FieldByName(name).IsValid()
		} else {
			return nil, reflect.New(data.Type().Elem()).Elem().FieldByName(name).IsValid()
		}
	}
	return nil, false
}

func newSafeMap() *safeMap {
	return &safeMap{l: new(sync.RWMutex), m: make(map[string]string)}
}

var smap = newSafeMap()
var umap = newSafeMap()

func ToSnake(u string) string {
	if v := smap.Get(u); v != "" {
		return v
	}

	buf := bytes.NewBufferString("")
	for i, v := range u {
		if i > 0 && v >= 'A' && v <= 'Z' {
			buf.WriteRune('_')
		}
		buf.WriteRune(v)
	}

	s := strings.ToLower(buf.String())
	go smap.Set(u, s)
	return s
}

func SnakeToUpperCamel(s string) string {
	if v := umap.Get(s); v != "" {
		return v
	}

	buf := bytes.NewBufferString("")
	for _, v := range strings.Split(s, "_") {
		if len(v) > 0 {
			buf.WriteString(strings.ToUpper(v[:1]))
			buf.WriteString(v[1:])
		}
	}

	u := buf.String()
	go umap.Set(s, u)
	return u
}

func toSearchableMap(attrs ...interface{}) (result interface{}) {
	if len(attrs) > 1 {
		if str, ok := attrs[0].(string); ok {
			result = map[string]interface{}{str: attrs[1]}
		}
	} else if len(attrs) == 1 {
		if attr, ok := attrs[0].(map[string]interface{}); ok {
			result = attr
		}

		if attr, ok := attrs[0].(interface{}); ok {
			result = attr
		}
	}
	return
}

func fileWithLineNum() string {
	for i := 1; i < 15; i++ {
		_, file, line, ok := runtime.Caller(i)
		if ok && (!regexp.MustCompile(`jinzhu/gorm/.*.go`).MatchString(file) || regexp.MustCompile(`jinzhu/gorm/.*test.go`).MatchString(file)) {
			return fmt.Sprintf("%v:%v", strings.TrimPrefix(file, os.Getenv("GOPATH")+"src/"), line)
		}
	}
	return ""
}

func setFieldValue(field reflect.Value, value interface{}) (result bool) {
	result = false
	if field.IsValid() && field.CanAddr() {
		result = true
		if scanner, ok := field.Addr().Interface().(sql.Scanner); ok {
			scanner.Scan(value)
		} else if reflect.TypeOf(value).ConvertibleTo(field.Type()) {
			field.Set(reflect.ValueOf(value).Convert(field.Type()))
		} else {
			result = false
		}
	}
	return
}

func isBlank(value reflect.Value) bool {
	return reflect.DeepEqual(value.Interface(), reflect.Zero(value.Type()).Interface())
}

func convertInterfaceToMap(values interface{}) map[string]interface{} {
	attrs := map[string]interface{}{}

	switch value := values.(type) {
	case map[string]interface{}:
		for k, v := range value {
			attrs[ToSnake(k)] = v
		}
	case []interface{}:
		for _, v := range value {
			for key, value := range convertInterfaceToMap(v) {
				attrs[key] = value
			}
		}
	case interface{}:
		reflectValue := reflect.ValueOf(values)

		switch reflectValue.Kind() {
		case reflect.Map:
			for _, key := range reflectValue.MapKeys() {
				attrs[ToSnake(key.Interface().(string))] = reflectValue.MapIndex(key).Interface()
			}
		default:
			scope := Scope{Value: values}
			for _, field := range scope.Fields() {
				if !field.IsBlank {
					attrs[field.DBName] = field.Value
				}
			}
		}
	}
	return attrs
}
<|endoftext|>


<commit_before>// +build linux

package main

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/opencontainers/runc/libcontainer"
	"github.com/opencontainers/runc/libcontainer/configs"
	"github.com/opencontainers/specs"
)

const wildcard = -1

var errEmptyID = errors.New("container id cannot be empty")

var allowedDevices = []*configs.Device{
	// allow mknod for any device
	{
		Type:        'c',
		Major:       wildcard,
		Minor:       wildcard,
		Permissions: "m",
		Allow:       true,
	},
	{
		Type:        'b',
		Major:       wildcard,
		Minor:       wildcard,
		Permissions: "m",
		Allow:       true,
	},
	{
		Type:        'c',
		Path:        "/dev/null",
		Major:       1,
		Minor:       3,
		Permissions: "rwm",
		Allow:       true,
	},
	{
		Type:        'c',
		Path:        "/dev/random",
		Major:       1,
		Minor:       8,
		Permissions: "rwm",
		Allow:       true,
	},
	{
		Type:        'c',
		Path:        "/dev/full",
		Major:       1,
		Minor:       7,
		Permissions: "rwm",
		Allow:       true,
	},
	{
		Type:        'c',
		Path:        "/dev/tty",
		Major:       5,
		Minor:       0,
		Permissions: "rwm",
		Allow:       true,
	},
	{
		Type:        'c',
		Path:        "/dev/zero",
		Major:       1,
		Minor:       5,
		Permissions: "rwm",
		Allow:       true,
	},
	{
		Type:        'c',
		Path:        "/dev/urandom",
		Major:       1,
		Minor:       9,
		Permissions: "rwm",
		Allow:       true,
	},
	{
		Path:        "/dev/console",
		Type:        'c',
		Major:       5,
		Minor:       1,
		Permissions: "rwm",
		Allow:       true,
	},
	{
		Path:        "/dev/tty0",
		Type:        'c',
		Major:       4,
		Minor:       0,
		Permissions: "rwm",
		Allow:       true,
	},
	{
		Path:        "/dev/tty1",
		Type:        'c',
		Major:       4,
		Minor:       1,
		Permissions: "rwm",
		Allow:       true,
	},
	// /dev/pts/ - pts namespaces are "coming soon"
	{
		Path:        "",
		Type:        'c',
		Major:       136,
		Minor:       wildcard,
		Permissions: "rwm",
		Allow:       true,
	},
	{
		Path:        "",
		Type:        'c',
		Major:       5,
		Minor:       2,
		Permissions: "rwm",
		Allow:       true,
	},
	// tuntap
	{
		Path:        "",
		Type:        'c',
		Major:       10,
		Minor:       200,
		Permissions: "rwm",
		Allow:       true,
	},
}

var container libcontainer.Container

func containerPreload(context *cli.Context) error {
	c, err := getContainer(context)
	if err != nil {
		return err
	}
	container = c
	return nil
}

// loadFactory returns the configured factory instance for execing containers.
func loadFactory(context *cli.Context) (libcontainer.Factory, error) {
	root := context.GlobalString("root")
	abs, err := filepath.Abs(root)
	if err != nil {
		return nil, err
	}
	return libcontainer.New(abs, libcontainer.Cgroupfs, func(l *libcontainer.LinuxFactory) error {
		l.CriuPath = context.GlobalString("criu")
		return nil
	})
}

// getContainer returns the specified container instance by loading it from state
// with the default factory.
func getContainer(context *cli.Context) (libcontainer.Container, error) {
	id := context.Args().First()
	if id == "" {
		return nil, errEmptyID
	}
	factory, err := loadFactory(context)
	if err != nil {
		return nil, err
	}
	return factory.Load(id)
}

// fatal prints the error's details if it is a libcontainer specific error type
// then exits the program with an exit status of 1.
func fatal(err error) {
	if lerr, ok := err.(libcontainer.Error); ok {
		lerr.Detail(os.Stderr)
		os.Exit(1)
	}
	fmt.Fprintln(os.Stderr, err)
	os.Exit(1)
}

func getDefaultImagePath(context *cli.Context) string {
	cwd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	return filepath.Join(cwd, "checkpoint")
}

// newProcess returns a new libcontainer Process with the arguments from the
// spec and stdio from the current process.
func newProcess(p specs.Process) *libcontainer.Process {
	return &libcontainer.Process{
		Args: p.Args,
		Env:  p.Env,
		// TODO: fix libcontainer's API to better support uid/gid in a typesafe way.
		User: fmt.Sprintf("%d:%d", p.User.UID, p.User.GID),
		Cwd:  p.Cwd,
	}
}

func dupStdio(process *libcontainer.Process, rootuid int) error {
	process.Stdin = os.Stdin
	process.Stdout = os.Stdout
	process.Stderr = os.Stderr
	for _, fd := range []uintptr{
		os.Stdin.Fd(),
		os.Stdout.Fd(),
		os.Stderr.Fd(),
	} {
		if err := syscall.Fchown(int(fd), rootuid, rootuid); err != nil {
			return err
		}
	}
	return nil
}

// If systemd is supporting sd_notify protocol, this function will add support
// for sd_notify protocol from within the container.
func setupSdNotify(spec *specs.LinuxSpec, notifySocket string) {
	spec.Mounts = append(spec.Mounts, specs.Mount{Destination: notifySocket, Type: "bind", Source: notifySocket, Options: []string{"bind"}})
	spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", notifySocket))
}

func destroy(container libcontainer.Container) {
	if err := container.Destroy(); err != nil {
		logrus.Error(err)
	}
}

func setupIO(process *libcontainer.Process, rootuid int, console string, createTTY, detach bool) (*tty, error) {
	// detach and createTty will not work unless a console path is passed
	// so error out here before changing any terminal settings
	if createTTY && detach && console == "" {
		return nil, fmt.Errorf("cannot allocate tty if runc will detach")
	}
	if createTTY {
		return createTty(process, rootuid, console)
	}
	if detach {
		if err := dupStdio(process, rootuid); err != nil {
			return nil, err
		}
		return nil, nil
	}
	return createStdioPipes(process, rootuid)
}

func createPidFile(path string, process *libcontainer.Process) error {
	pid, err := process.Pid()
	if err != nil {
		return err
	}
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = fmt.Fprintf(f, "%d", pid)
	return err
}

func createContainer(context *cli.Context, id string, spec *specs.LinuxSpec) (libcontainer.Container, error) {
	config, err := createLibcontainerConfig(id, spec)
	if err != nil {
		return nil, err
	}

	if _, err := os.Stat(config.Rootfs); err != nil {
		if os.IsNotExist(err) {
			return nil, fmt.Errorf("rootfs (%q) does not exist", config.Rootfs)
		}
		return nil, err
	}

	factory, err := loadFactory(context)
	if err != nil {
		return nil, err
	}
	return factory.Create(id, config)
}

// runProcess will create a new process in the specified container
// by executing the process specified in the 'config'.
func runProcess(container libcontainer.Container, config *specs.Process, listenFDs []*os.File, console string, pidFile string, detach bool) (int, error) {
	process := newProcess(*config)

	// Add extra file descriptors if needed
	if len(listenFDs) > 0 {
		process.Env = append(process.Env, fmt.Sprintf("LISTEN_FDS=%d", len(listenFDs)), "LISTEN_PID=1")
		process.ExtraFiles = append(process.ExtraFiles, listenFDs...)
	}

	rootuid, err := container.Config().HostUID()
	if err != nil {
		return -1, err
	}

	tty, err := setupIO(process, rootuid, console, config.Terminal, detach)
	if err != nil {
		return -1, err
	}

	handler := newSignalHandler(tty)
	defer handler.Close()

	if err := container.Start(process); err != nil {
		tty.Close()
		return -1, err
	}

	if pidFile != "" {
		if err := createPidFile(pidFile, process); err != nil {
			process.Signal(syscall.SIGKILL)
			process.Wait()
			tty.Close()
			return -1, err
		}
	}
	if detach {
		return 0, nil
	}

	return handler.forward(process)
}
<commit_msg>Prevent a panic when container fails to start<commit_after>// +build linux

package main

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/opencontainers/runc/libcontainer"
	"github.com/opencontainers/runc/libcontainer/configs"
	"github.com/opencontainers/specs"
)

const wildcard = -1

var errEmptyID = errors.New("container id cannot be empty")

var allowedDevices = []*configs.Device{
	// allow mknod for any device
	{
		Type:        'c',
		Major:       wildcard,
		Minor:       wildcard,
		Permissions: "m",
		Allow:       true,
	},
	{
		Type:        'b',
		Major:       wildcard,
		Minor:       wildcard,
		Permissions: "m",
		Allow:       true,
	},
	{
		Type:        'c',
		Path:        "/dev/null",
		Major:       1,
		Minor:       3,
		Permissions: "rwm",
		Allow:       true,
	},
	{
		Type:        'c',
		Path:        "/dev/random",
		Major:       1,
		Minor:       8,
		Permissions: "rwm",
		Allow:       true,
	},
	{
		Type:        'c',
		Path:        "/dev/full",
		Major:       1,
		Minor:       7,
		Permissions: "rwm",
		Allow:       true,
	},
	{
		Type:        'c',
		Path:        "/dev/tty",
		Major:       5,
		Minor:       0,
		Permissions: "rwm",
		Allow:       true,
	},
	{
		Type:        'c',
		Path:        "/dev/zero",
		Major:       1,
		Minor:       5,
		Permissions: "rwm",
		Allow:       true,
	},
	{
		Type:        'c',
		Path:        "/dev/urandom",
		Major:       1,
		Minor:       9,
		Permissions: "rwm",
		Allow:       true,
	},
	{
		Path:        "/dev/console",
		Type:        'c',
		Major:       5,
		Minor:       1,
		Permissions: "rwm",
		Allow:       true,
	},
	{
		Path:        "/dev/tty0",
		Type:        'c',
		Major:       4,
		Minor:       0,
		Permissions: "rwm",
		Allow:       true,
	},
	{
		Path:        "/dev/tty1",
		Type:        'c',
		Major:       4,
		Minor:       1,
		Permissions: "rwm",
		Allow:       true,
	},
	// /dev/pts/ - pts namespaces are "coming soon"
	{
		Path:        "",
		Type:        'c',
		Major:       136,
		Minor:       wildcard,
		Permissions: "rwm",
		Allow:       true,
	},
	{
		Path:        "",
		Type:        'c',
		Major:       5,
		Minor:       2,
		Permissions: "rwm",
		Allow:       true,
	},
	// tuntap
	{
		Path:        "",
		Type:        'c',
		Major:       10,
		Minor:       200,
		Permissions: "rwm",
		Allow:       true,
	},
}

var container libcontainer.Container

func containerPreload(context *cli.Context) error {
	c, err := getContainer(context)
	if err != nil {
		return err
	}
	container = c
	return nil
}

// loadFactory returns the configured factory instance for execing containers.
func loadFactory(context *cli.Context) (libcontainer.Factory, error) {
	root := context.GlobalString("root")
	abs, err := filepath.Abs(root)
	if err != nil {
		return nil, err
	}
	return libcontainer.New(abs, libcontainer.Cgroupfs, func(l *libcontainer.LinuxFactory) error {
		l.CriuPath = context.GlobalString("criu")
		return nil
	})
}

// getContainer returns the specified container instance by loading it from state
// with the default factory.
func getContainer(context *cli.Context) (libcontainer.Container, error) {
	id := context.Args().First()
	if id == "" {
		return nil, errEmptyID
	}
	factory, err := loadFactory(context)
	if err != nil {
		return nil, err
	}
	return factory.Load(id)
}

// fatal prints the error's details if it is a libcontainer specific error type
// then exits the program with an exit status of 1.
func fatal(err error) {
	if lerr, ok := err.(libcontainer.Error); ok {
		lerr.Detail(os.Stderr)
		os.Exit(1)
	}
	fmt.Fprintln(os.Stderr, err)
	os.Exit(1)
}

func getDefaultImagePath(context *cli.Context) string {
	cwd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	return filepath.Join(cwd, "checkpoint")
}

// newProcess returns a new libcontainer Process with the arguments from the
// spec and stdio from the current process.
func newProcess(p specs.Process) *libcontainer.Process {
	return &libcontainer.Process{
		Args: p.Args,
		Env:  p.Env,
		// TODO: fix libcontainer's API to better support uid/gid in a typesafe way.
		User: fmt.Sprintf("%d:%d", p.User.UID, p.User.GID),
		Cwd:  p.Cwd,
	}
}

func dupStdio(process *libcontainer.Process, rootuid int) error {
	process.Stdin = os.Stdin
	process.Stdout = os.Stdout
	process.Stderr = os.Stderr
	for _, fd := range []uintptr{
		os.Stdin.Fd(),
		os.Stdout.Fd(),
		os.Stderr.Fd(),
	} {
		if err := syscall.Fchown(int(fd), rootuid, rootuid); err != nil {
			return err
		}
	}
	return nil
}

// If systemd is supporting sd_notify protocol, this function will add support
// for sd_notify protocol from within the container.
func setupSdNotify(spec *specs.LinuxSpec, notifySocket string) {
	spec.Mounts = append(spec.Mounts, specs.Mount{Destination: notifySocket, Type: "bind", Source: notifySocket, Options: []string{"bind"}})
	spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", notifySocket))
}

func destroy(container libcontainer.Container) {
	if err := container.Destroy(); err != nil {
		logrus.Error(err)
	}
}

func setupIO(process *libcontainer.Process, rootuid int, console string, createTTY, detach bool) (*tty, error) {
	// detach and createTty will not work unless a console path is passed
	// so error out here before changing any terminal settings
	if createTTY && detach && console == "" {
		return nil, fmt.Errorf("cannot allocate tty if runc will detach")
	}
	if createTTY {
		return createTty(process, rootuid, console)
	}
	if detach {
		if err := dupStdio(process, rootuid); err != nil {
			return nil, err
		}
		return nil, nil
	}
	return createStdioPipes(process, rootuid)
}

func createPidFile(path string, process *libcontainer.Process) error {
	pid, err := process.Pid()
	if err != nil {
		return err
	}
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = fmt.Fprintf(f, "%d", pid)
	return err
}

func createContainer(context *cli.Context, id string, spec *specs.LinuxSpec) (libcontainer.Container, error) {
	config, err := createLibcontainerConfig(id, spec)
	if err != nil {
		return nil, err
	}

	if _, err := os.Stat(config.Rootfs); err != nil {
		if os.IsNotExist(err) {
			return nil, fmt.Errorf("rootfs (%q) does not exist", config.Rootfs)
		}
		return nil, err
	}

	factory, err := loadFactory(context)
	if err != nil {
		return nil, err
	}
	return factory.Create(id, config)
}

// runProcess will create a new process in the specified container
// by executing the process specified in the 'config'.
func runProcess(container libcontainer.Container, config *specs.Process, listenFDs []*os.File, console string, pidFile string, detach bool) (int, error) {
	process := newProcess(*config)

	// Add extra file descriptors if needed
	if len(listenFDs) > 0 {
		process.Env = append(process.Env, fmt.Sprintf("LISTEN_FDS=%d", len(listenFDs)), "LISTEN_PID=1")
		process.ExtraFiles = append(process.ExtraFiles, listenFDs...)
	}

	rootuid, err := container.Config().HostUID()
	if err != nil {
		return -1, err
	}

	tty, err := setupIO(process, rootuid, console, config.Terminal, detach)
	if err != nil {
		return -1, err
	}

	handler := newSignalHandler(tty)
	defer handler.Close()

	if err := container.Start(process); err != nil {
		if tty != nil {
			tty.Close()
		}
		return -1, err
	}

	if pidFile != "" {
		if err := createPidFile(pidFile, process); err != nil {
			process.Signal(syscall.SIGKILL)
			process.Wait()
			if tty != nil {
				tty.Close()
			}
			return -1, err
		}
	}
	if detach {
		return 0, nil
	}

	return handler.forward(process)
}
<|endoftext|>