<commit_before>// Copyright 2018 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package ui // import "miniflux.app/ui"

import (
	"net/http"

	"miniflux.app/reader/feed"
	"miniflux.app/storage"
	"miniflux.app/template"
	"miniflux.app/worker"

	"github.com/gorilla/mux"
)

// Serve declares all routes for the user interface.
func Serve(router *mux.Router, store *storage.Storage, pool *worker.Pool, feedHandler *feed.Handler) {
	middleware := newMiddleware(router, store)
	handler := &handler{router, store, template.NewEngine(router), pool, feedHandler}

	uiRouter := router.NewRoute().Subrouter()
	uiRouter.Use(middleware.handleUserSession)
	uiRouter.Use(middleware.handleAppSession)

	// Static assets.
	uiRouter.HandleFunc("/stylesheets/{name}.css", handler.showStylesheet).Name("stylesheet").Methods("GET")
	uiRouter.HandleFunc("/{name}.js", handler.showJavascript).Name("javascript").Methods("GET")
	uiRouter.HandleFunc("/favicon.ico", handler.showFavicon).Name("favicon").Methods("GET")
	uiRouter.HandleFunc("/icon/{filename}", handler.showAppIcon).Name("appIcon").Methods("GET")
	uiRouter.HandleFunc("/manifest.json", handler.showWebManifest).Name("webManifest").Methods("GET")

	// New subscription pages.
	uiRouter.HandleFunc("/subscribe", handler.showAddSubscriptionPage).Name("addSubscription").Methods("GET")
	uiRouter.HandleFunc("/subscribe", handler.submitSubscription).Name("submitSubscription").Methods("POST")
	uiRouter.HandleFunc("/subscriptions", handler.showChooseSubscriptionPage).Name("chooseSubscription").Methods("POST")
	uiRouter.HandleFunc("/bookmarklet", handler.bookmarklet).Name("bookmarklet").Methods("GET")

	// Unread page.
	uiRouter.HandleFunc("/mark-all-as-read", handler.markAllAsRead).Name("markAllAsRead").Methods("POST")
	uiRouter.HandleFunc("/unread", handler.showUnreadPage).Name("unread").Methods("GET")
	uiRouter.HandleFunc("/unread/entry/{entryID}", handler.showUnreadEntryPage).Name("unreadEntry").Methods("GET")

	// History pages.
	uiRouter.HandleFunc("/history", handler.showHistoryPage).Name("history").Methods("GET")
	uiRouter.HandleFunc("/history/entry/{entryID}", handler.showReadEntryPage).Name("readEntry").Methods("GET")
	uiRouter.HandleFunc("/history/flush", handler.flushHistory).Name("flushHistory").Methods("POST")

	// Bookmark pages.
	uiRouter.HandleFunc("/starred", handler.showStarredPage).Name("starred").Methods("GET")
	uiRouter.HandleFunc("/starred/entry/{entryID}", handler.showStarredEntryPage).Name("starredEntry").Methods("GET")

	// Search pages.
	uiRouter.HandleFunc("/search", handler.showSearchEntriesPage).Name("searchEntries").Methods("GET")
	uiRouter.HandleFunc("/search/entry/{entryID}", handler.showSearchEntryPage).Name("searchEntry").Methods("GET")

	// Feed listing pages.
	uiRouter.HandleFunc("/feeds", handler.showFeedsPage).Name("feeds").Methods("GET")
	uiRouter.HandleFunc("/feeds/refresh", handler.refreshAllFeeds).Name("refreshAllFeeds").Methods("GET")

	// Individual feed pages.
	uiRouter.HandleFunc("/feed/{feedID}/refresh", handler.refreshFeed).Name("refreshFeed").Methods("GET")
	uiRouter.HandleFunc("/feed/{feedID}/edit", handler.showEditFeedPage).Name("editFeed").Methods("GET")
	uiRouter.HandleFunc("/feed/{feedID}/remove", handler.removeFeed).Name("removeFeed").Methods("POST")
	uiRouter.HandleFunc("/feed/{feedID}/update", handler.updateFeed).Name("updateFeed").Methods("POST")
	uiRouter.HandleFunc("/feed/{feedID}/entries", handler.showFeedEntriesPage).Name("feedEntries").Methods("GET")
	uiRouter.HandleFunc("/feed/{feedID}/entries/all", handler.showFeedEntriesAllPage).Name("feedEntriesAll").Methods("GET")
	uiRouter.HandleFunc("/feed/{feedID}/entry/{entryID}", handler.showFeedEntryPage).Name("feedEntry").Methods("GET")
	uiRouter.HandleFunc("/feed/icon/{iconID}", handler.showIcon).Name("icon").Methods("GET")

	// Category pages.
	uiRouter.HandleFunc("/category/{categoryID}/entry/{entryID}", handler.showCategoryEntryPage).Name("categoryEntry").Methods("GET")
	uiRouter.HandleFunc("/categories", handler.showCategoryListPage).Name("categories").Methods("GET")
	uiRouter.HandleFunc("/category/create", handler.showCreateCategoryPage).Name("createCategory").Methods("GET")
	uiRouter.HandleFunc("/category/save", handler.saveCategory).Name("saveCategory").Methods("POST")
	uiRouter.HandleFunc("/category/{categoryID}/feeds", handler.showCategoryFeedsPage).Name("categoryFeeds").Methods("GET")
	uiRouter.HandleFunc("/category/{categoryID}/entries", handler.showCategoryEntriesPage).Name("categoryEntries").Methods("GET")
	uiRouter.HandleFunc("/category/{categoryID}/entries/all", handler.showCategoryEntriesAllPage).Name("categoryEntriesAll").Methods("GET")
	uiRouter.HandleFunc("/category/{categoryID}/edit", handler.showEditCategoryPage).Name("editCategory").Methods("GET")
	uiRouter.HandleFunc("/category/{categoryID}/update", handler.updateCategory).Name("updateCategory").Methods("POST")
	uiRouter.HandleFunc("/category/{categoryID}/remove", handler.removeCategory).Name("removeCategory").Methods("POST")

	// Entry pages.
	uiRouter.HandleFunc("/entry/status", handler.updateEntriesStatus).Name("updateEntriesStatus").Methods("POST")
	uiRouter.HandleFunc("/entry/save/{entryID}", handler.saveEntry).Name("saveEntry").Methods("POST")
	uiRouter.HandleFunc("/entry/download/{entryID}", handler.fetchContent).Name("fetchContent").Methods("POST")
	uiRouter.HandleFunc("/proxy/{encodedURL}", handler.imageProxy).Name("proxy").Methods("GET")
	uiRouter.HandleFunc("/entry/bookmark/{entryID}", handler.toggleBookmark).Name("toggleBookmark").Methods("POST")

	// Share pages.
	uiRouter.HandleFunc("/entry/share/{entryID}", handler.shareGenerate).Name("shareGenerate").Methods("GET")
	uiRouter.HandleFunc("/shared/{shareCode}", handler.sharePage).Name("share").Methods("GET")

	// User pages.
	uiRouter.HandleFunc("/users", handler.showUsersPage).Name("users").Methods("GET")
	uiRouter.HandleFunc("/user/create", handler.showCreateUserPage).Name("createUser").Methods("GET")
	uiRouter.HandleFunc("/user/save", handler.saveUser).Name("saveUser").Methods("POST")
	uiRouter.HandleFunc("/users/{userID}/edit", handler.showEditUserPage).Name("editUser").Methods("GET")
	uiRouter.HandleFunc("/users/{userID}/update", handler.updateUser).Name("updateUser").Methods("POST")
	uiRouter.HandleFunc("/users/{userID}/remove", handler.removeUser).Name("removeUser").Methods("POST")

	// Settings pages.
	uiRouter.HandleFunc("/settings", handler.showSettingsPage).Name("settings").Methods("GET")
	uiRouter.HandleFunc("/settings", handler.updateSettings).Name("updateSettings").Methods("POST")
	uiRouter.HandleFunc("/integrations", handler.showIntegrationPage).Name("integrations").Methods("GET")
	uiRouter.HandleFunc("/integration", handler.updateIntegration).Name("updateIntegration").Methods("POST")
	uiRouter.HandleFunc("/integration/pocket/authorize", handler.pocketAuthorize).Name("pocketAuthorize").Methods("GET")
	uiRouter.HandleFunc("/integration/pocket/callback", handler.pocketCallback).Name("pocketCallback").Methods("GET")
	uiRouter.HandleFunc("/about", handler.showAboutPage).Name("about").Methods("GET")

	// Session pages.
	uiRouter.HandleFunc("/sessions", handler.showSessionsPage).Name("sessions").Methods("GET")
	uiRouter.HandleFunc("/sessions/{sessionID}/remove", handler.removeSession).Name("removeSession").Methods("POST")

	// API Keys pages.
	uiRouter.HandleFunc("/keys", handler.showAPIKeysPage).Name("apiKeys").Methods("GET")
	uiRouter.HandleFunc("/keys/{keyID}/remove", handler.removeAPIKey).Name("removeAPIKey").Methods("POST")
	uiRouter.HandleFunc("/keys/create", handler.showCreateAPIKeyPage).Name("createAPIKey").Methods("GET")
	uiRouter.HandleFunc("/keys/save", handler.saveAPIKey).Name("saveAPIKey").Methods("POST")

	// OPML pages.
	uiRouter.HandleFunc("/export", handler.exportFeeds).Name("export").Methods("GET")
	uiRouter.HandleFunc("/import", handler.showImportPage).Name("import").Methods("GET")
	uiRouter.HandleFunc("/upload", handler.uploadOPML).Name("uploadOPML").Methods("POST")
	uiRouter.HandleFunc("/fetch", handler.fetchOPML).Name("fetchOPML").Methods("POST")

	// OAuth2 flow.
	uiRouter.HandleFunc("/oauth2/{provider}/unlink", handler.oauth2Unlink).Name("oauth2Unlink").Methods("GET")
	uiRouter.HandleFunc("/oauth2/{provider}/redirect", handler.oauth2Redirect).Name("oauth2Redirect").Methods("GET")
	uiRouter.HandleFunc("/oauth2/{provider}/callback", handler.oauth2Callback).Name("oauth2Callback").Methods("GET")

	// Authentication pages.
	uiRouter.HandleFunc("/login", handler.checkLogin).Name("checkLogin").Methods("POST")
	uiRouter.HandleFunc("/logout", handler.logout).Name("logout").Methods("GET")
	uiRouter.Handle("/", middleware.handleAuthProxy(http.HandlerFunc(handler.showLoginPage))).Name("login").Methods("GET")

	router.HandleFunc("/robots.txt", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/plain")
		w.Write([]byte("User-agent: *\nDisallow: /"))
	}).Name("robots")
}
<commit_msg>Change share uri<commit_after>// Copyright 2018 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package ui // import "miniflux.app/ui"

import (
	"net/http"

	"miniflux.app/reader/feed"
	"miniflux.app/storage"
	"miniflux.app/template"
	"miniflux.app/worker"

	"github.com/gorilla/mux"
)

// Serve declares all routes for the user interface.
func Serve(router *mux.Router, store *storage.Storage, pool *worker.Pool, feedHandler *feed.Handler) {
	middleware := newMiddleware(router, store)
	handler := &handler{router, store, template.NewEngine(router), pool, feedHandler}

	uiRouter := router.NewRoute().Subrouter()
	uiRouter.Use(middleware.handleUserSession)
	uiRouter.Use(middleware.handleAppSession)

	// Static assets.
	uiRouter.HandleFunc("/stylesheets/{name}.css", handler.showStylesheet).Name("stylesheet").Methods("GET")
	uiRouter.HandleFunc("/{name}.js", handler.showJavascript).Name("javascript").Methods("GET")
	uiRouter.HandleFunc("/favicon.ico", handler.showFavicon).Name("favicon").Methods("GET")
	uiRouter.HandleFunc("/icon/{filename}", handler.showAppIcon).Name("appIcon").Methods("GET")
	uiRouter.HandleFunc("/manifest.json", handler.showWebManifest).Name("webManifest").Methods("GET")

	// New subscription pages.
	uiRouter.HandleFunc("/subscribe", handler.showAddSubscriptionPage).Name("addSubscription").Methods("GET")
	uiRouter.HandleFunc("/subscribe", handler.submitSubscription).Name("submitSubscription").Methods("POST")
	uiRouter.HandleFunc("/subscriptions", handler.showChooseSubscriptionPage).Name("chooseSubscription").Methods("POST")
	uiRouter.HandleFunc("/bookmarklet", handler.bookmarklet).Name("bookmarklet").Methods("GET")

	// Unread page.
	uiRouter.HandleFunc("/mark-all-as-read", handler.markAllAsRead).Name("markAllAsRead").Methods("POST")
	uiRouter.HandleFunc("/unread", handler.showUnreadPage).Name("unread").Methods("GET")
	uiRouter.HandleFunc("/unread/entry/{entryID}", handler.showUnreadEntryPage).Name("unreadEntry").Methods("GET")

	// History pages.
	uiRouter.HandleFunc("/history", handler.showHistoryPage).Name("history").Methods("GET")
	uiRouter.HandleFunc("/history/entry/{entryID}", handler.showReadEntryPage).Name("readEntry").Methods("GET")
	uiRouter.HandleFunc("/history/flush", handler.flushHistory).Name("flushHistory").Methods("POST")

	// Bookmark pages.
	uiRouter.HandleFunc("/starred", handler.showStarredPage).Name("starred").Methods("GET")
	uiRouter.HandleFunc("/starred/entry/{entryID}", handler.showStarredEntryPage).Name("starredEntry").Methods("GET")

	// Search pages.
	uiRouter.HandleFunc("/search", handler.showSearchEntriesPage).Name("searchEntries").Methods("GET")
	uiRouter.HandleFunc("/search/entry/{entryID}", handler.showSearchEntryPage).Name("searchEntry").Methods("GET")

	// Feed listing pages.
	uiRouter.HandleFunc("/feeds", handler.showFeedsPage).Name("feeds").Methods("GET")
	uiRouter.HandleFunc("/feeds/refresh", handler.refreshAllFeeds).Name("refreshAllFeeds").Methods("GET")

	// Individual feed pages.
	uiRouter.HandleFunc("/feed/{feedID}/refresh", handler.refreshFeed).Name("refreshFeed").Methods("GET")
	uiRouter.HandleFunc("/feed/{feedID}/edit", handler.showEditFeedPage).Name("editFeed").Methods("GET")
	uiRouter.HandleFunc("/feed/{feedID}/remove", handler.removeFeed).Name("removeFeed").Methods("POST")
	uiRouter.HandleFunc("/feed/{feedID}/update", handler.updateFeed).Name("updateFeed").Methods("POST")
	uiRouter.HandleFunc("/feed/{feedID}/entries", handler.showFeedEntriesPage).Name("feedEntries").Methods("GET")
	uiRouter.HandleFunc("/feed/{feedID}/entries/all", handler.showFeedEntriesAllPage).Name("feedEntriesAll").Methods("GET")
	uiRouter.HandleFunc("/feed/{feedID}/entry/{entryID}", handler.showFeedEntryPage).Name("feedEntry").Methods("GET")
	uiRouter.HandleFunc("/feed/icon/{iconID}", handler.showIcon).Name("icon").Methods("GET")

	// Category pages.
	uiRouter.HandleFunc("/category/{categoryID}/entry/{entryID}", handler.showCategoryEntryPage).Name("categoryEntry").Methods("GET")
	uiRouter.HandleFunc("/categories", handler.showCategoryListPage).Name("categories").Methods("GET")
	uiRouter.HandleFunc("/category/create", handler.showCreateCategoryPage).Name("createCategory").Methods("GET")
	uiRouter.HandleFunc("/category/save", handler.saveCategory).Name("saveCategory").Methods("POST")
	uiRouter.HandleFunc("/category/{categoryID}/feeds", handler.showCategoryFeedsPage).Name("categoryFeeds").Methods("GET")
	uiRouter.HandleFunc("/category/{categoryID}/entries", handler.showCategoryEntriesPage).Name("categoryEntries").Methods("GET")
	uiRouter.HandleFunc("/category/{categoryID}/entries/all", handler.showCategoryEntriesAllPage).Name("categoryEntriesAll").Methods("GET")
	uiRouter.HandleFunc("/category/{categoryID}/edit", handler.showEditCategoryPage).Name("editCategory").Methods("GET")
	uiRouter.HandleFunc("/category/{categoryID}/update", handler.updateCategory).Name("updateCategory").Methods("POST")
	uiRouter.HandleFunc("/category/{categoryID}/remove", handler.removeCategory).Name("removeCategory").Methods("POST")

	// Entry pages.
	uiRouter.HandleFunc("/entry/status", handler.updateEntriesStatus).Name("updateEntriesStatus").Methods("POST")
	uiRouter.HandleFunc("/entry/save/{entryID}", handler.saveEntry).Name("saveEntry").Methods("POST")
	uiRouter.HandleFunc("/entry/download/{entryID}", handler.fetchContent).Name("fetchContent").Methods("POST")
	uiRouter.HandleFunc("/proxy/{encodedURL}", handler.imageProxy).Name("proxy").Methods("GET")
	uiRouter.HandleFunc("/entry/bookmark/{entryID}", handler.toggleBookmark).Name("toggleBookmark").Methods("POST")

	// Share pages.
	uiRouter.HandleFunc("/entry/share/{entryID}", handler.shareGenerate).Name("shareGenerate").Methods("GET")
	uiRouter.HandleFunc("/share/{shareCode}", handler.sharePage).Name("share").Methods("GET")

	// User pages.
	uiRouter.HandleFunc("/users", handler.showUsersPage).Name("users").Methods("GET")
	uiRouter.HandleFunc("/user/create", handler.showCreateUserPage).Name("createUser").Methods("GET")
	uiRouter.HandleFunc("/user/save", handler.saveUser).Name("saveUser").Methods("POST")
	uiRouter.HandleFunc("/users/{userID}/edit", handler.showEditUserPage).Name("editUser").Methods("GET")
	uiRouter.HandleFunc("/users/{userID}/update", handler.updateUser).Name("updateUser").Methods("POST")
	uiRouter.HandleFunc("/users/{userID}/remove", handler.removeUser).Name("removeUser").Methods("POST")

	// Settings pages.
	uiRouter.HandleFunc("/settings", handler.showSettingsPage).Name("settings").Methods("GET")
	uiRouter.HandleFunc("/settings", handler.updateSettings).Name("updateSettings").Methods("POST")
	uiRouter.HandleFunc("/integrations", handler.showIntegrationPage).Name("integrations").Methods("GET")
	uiRouter.HandleFunc("/integration", handler.updateIntegration).Name("updateIntegration").Methods("POST")
	uiRouter.HandleFunc("/integration/pocket/authorize", handler.pocketAuthorize).Name("pocketAuthorize").Methods("GET")
	uiRouter.HandleFunc("/integration/pocket/callback", handler.pocketCallback).Name("pocketCallback").Methods("GET")
	uiRouter.HandleFunc("/about", handler.showAboutPage).Name("about").Methods("GET")

	// Session pages.
	uiRouter.HandleFunc("/sessions", handler.showSessionsPage).Name("sessions").Methods("GET")
	uiRouter.HandleFunc("/sessions/{sessionID}/remove", handler.removeSession).Name("removeSession").Methods("POST")

	// API Keys pages.
	uiRouter.HandleFunc("/keys", handler.showAPIKeysPage).Name("apiKeys").Methods("GET")
	uiRouter.HandleFunc("/keys/{keyID}/remove", handler.removeAPIKey).Name("removeAPIKey").Methods("POST")
	uiRouter.HandleFunc("/keys/create", handler.showCreateAPIKeyPage).Name("createAPIKey").Methods("GET")
	uiRouter.HandleFunc("/keys/save", handler.saveAPIKey).Name("saveAPIKey").Methods("POST")

	// OPML pages.
	uiRouter.HandleFunc("/export", handler.exportFeeds).Name("export").Methods("GET")
	uiRouter.HandleFunc("/import", handler.showImportPage).Name("import").Methods("GET")
	uiRouter.HandleFunc("/upload", handler.uploadOPML).Name("uploadOPML").Methods("POST")
	uiRouter.HandleFunc("/fetch", handler.fetchOPML).Name("fetchOPML").Methods("POST")

	// OAuth2 flow.
	uiRouter.HandleFunc("/oauth2/{provider}/unlink", handler.oauth2Unlink).Name("oauth2Unlink").Methods("GET")
	uiRouter.HandleFunc("/oauth2/{provider}/redirect", handler.oauth2Redirect).Name("oauth2Redirect").Methods("GET")
	uiRouter.HandleFunc("/oauth2/{provider}/callback", handler.oauth2Callback).Name("oauth2Callback").Methods("GET")

	// Authentication pages.
	uiRouter.HandleFunc("/login", handler.checkLogin).Name("checkLogin").Methods("POST")
	uiRouter.HandleFunc("/logout", handler.logout).Name("logout").Methods("GET")
	uiRouter.Handle("/", middleware.handleAuthProxy(http.HandlerFunc(handler.showLoginPage))).Name("login").Methods("GET")

	router.HandleFunc("/robots.txt", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/plain")
		w.Write([]byte("User-agent: *\nDisallow: /"))
	}).Name("robots")
}
<|endoftext|>


<commit_before>package vkapi

import (
	"encoding/json"
	"log"
	"net/url"
	"strings"
)

func (client *Client) UsersInfo(userIds []string, fieldArgs []string) (user *[]Users, err error) {
	user = new([]Users)
	args := strings.Join(fieldArgs, ",")
	ids := strings.Join(userIds, ",")

	var req Request
	req.Method = "users.get"
	req.Values = url.Values{}
	req.Values.Set("user_ids", ids)
	req.Values.Set("fields", args)
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	if res.ServerError() != nil {
		return nil, res.ServerError()
	}

	log.Println("Answer:", res.Response.String())

	if err := json.Unmarshal(res.Response.Bytes(), user); err != nil {
		return nil, err
	}

	return
}

type Users struct {
	// Full description at https://vk.com/dev/objects/user
	Id          int    `json:"id"`
	FirstName   string `json:"first_name"`
	LastName    string `json:"last_name"`
	Deactivated string `json:"deactivated"`
	Hidden      int    `json:"hidden"`

	// Optional fields
	About                  string          `json:"about"`
	Activities             string          `json:"activities"`
	Bdate                  string          `json:"bdate"`
	Blacklisted            int             `json:"blacklisted"`
	BlacklistedByMe        int             `json:"blacklisted_by_me"`
	Books                  string          `json:"books"`
	CanPost                int             `json:"can_post"`
	CanSeeAllPosts         int             `json:"can_see_all_posts"`
	CanSeeAudio            int             `json:"can_see_audio"`
	CanSendFriendRequest   int             `json:"can_send_friend_request"`
	CanWritePrivateMessage int             `json:"can_write_private_message"`
	Career                 *[]Career       `json:"career"`
	City                   *City           `json:"city"`
	CommonCount            int             `json:"common_count"`
	Skype                  string          `json:"skype"`
	Facebook               string          `json:"facebook"`
	Twitter                string          `json:"twitter"`
	Livejournal            string          `json:"livejournal"`
	Instagram              string          `json:"instagram"`
	Contacts               *Contacts       `json:"contacts"`
	Counters               *Counters       `json:"counters"`
	Country                *Country        `json:"country"`
	CropPhoto              *CropPhoto      `json:"crop_photo"`
	Domain                 string          `json:"domain"`
	University             int             `json:"university"`
	UniversityName         string          `json:"university_name"`
	Faculty                int             `json:"faculty"`
	FacultyName            string          `json:"faculty_name"`
	Graduation             int             `json:"graduation"`
	FirstNameNom           string          `json:"first_name_nom"`
	FirstNameGen           string          `json:"first_name_gen"`
	FirstNameDat           string          `json:"first_name_dat"`
	FirstNameAcc           string          `json:"first_name_acc"`
	FirstNameIns           string          `json:"first_name_ins"`
	FirstNameAbl           string          `json:"first_name_abl"`
	FollowersCount         int             `json:"followers_count"`
	FriendStatus           int             `json:"friend_status"`
	Games                  string          `json:"games"`
	HasMobile              int             `json:"has_mobile"`
	HasPhoto               int             `json:"hasPhoto"`
	HomeTown               string          `json:"home_town"`
	Interests              string          `json:"interests"`
	IsFavorite             int             `json:"is_favorite"`
	IsFriend               int             `json:"is_friend"`
	IsHiddenFromFeed       int             `json:"is_hidden_from_feed"`
	LastNameNom            string          `json:"last_name_nom"`
	LastNameGen            string          `json:"last_name_gen"`
	LastNameDat            string          `json:"last_name_dat"`
	LastNameAcc            string          `json:"last_name_acc"`
	LastNameIns            string          `json:"last_name_ins"`
	LastNameAbl            string          `json:"last_name_abl"`
	LastSeen               *LastSeen       `json:"last_seen"`
	MaidenName             string          `json:"maiden_name"`
	Military               *[]Military     `json:"military"`
	Movies                 string          `json:"movies"`
	Music                  string          `json:"music"`
	Nickname               string          `json:"nickname"`
	Occupation             *Occupation     `json:"occupation"`
	Online                 int             `json:"online"`
	Personal               *Personal       `json:"personal"`
	Photo50                string          `json:"photo_50"`
	Photo100               string          `json:"photo_100"`
	Photo200Orig           string          `json:"photo_200_orig"`
	Photo200               string          `json:"photo_200"`
	Photo400Orig           string          `json:"photo_400_orig"`
	PhotoId                string          `json:"photo_id"`
	PhotoMax               string          `json:"photo_max"`
	PhotoMaxOrig           string          `json:"photo_max_orig"`
	Quotes                 string          `json:"quotes"`
	Relatives              *[]Relatives    `json:"relatives"`
	Relation               int             `json:"relation"`
	Schools                *[]Schools      `json:"schools"`
	ScreenName             string          `json:"screen_name"`
	Sex                    int             `json:"sex"`
	Site                   string          `json:"site"`
	Status                 string          `json:"status"`
	StatusAudio            string          `json:"status_audio"`
	Timezone               int             `json:"timezone"`
	Tv                     string          `json:"tv"`
	Universities           *[]Universities `json:"universities"`
	Verified               int             `json:"verified"`
	WallComments           int             `json:"wall_comments"`
}

type Career struct {
	GroupId   int    `json:"group_id"`
	Company   string `json:"company"`
	CountryId int    `json:"country_id"`
	CityId    int    `json:"city_id"`
	CityName  string `json:"city_name"`
	From      int    `json:"from"`
	Until     int    `json:"until"`
	Position  string `json:"position"`
}

type City struct {
	Id    int    `json:"id"`
	Title string `json:"title"`
}

type Contacts struct {
	MobilePhone string `json:"mobile_phone"`
	HomePhone   string `json:"home_phone"`
}

type Counters struct {
	Albums        int `json:"albums"`
	Videos        int `json:"videos"`
	Audios        int `json:"audios"`
	Photos        int `json:"photos"`
	Notes         int `json:"notes"`
	Friends       int `json:"friends"`
	Groups        int `json:"groups"`
	OnlineFriends int `json:"online_friends"`
	MutualFriends int `json:"mutual_friends"`
	UserVideos    int `json:"user_videos"`
	Followers     int `json:"followers"`
	Pages         int `json:"pages"`
}

type Country struct {
	Id    int    `json:"id"`
	Title string `json:"title"`
}

type CropPhoto struct {
	/*Photo struct{} `json:"photo"`*/
	Crop *Rect `json:"crop"`
	Rect *Rect `json:"rect"`
}

type Rect struct {
	x  float64
	x2 float64
	y  float64
	y2 float64
}

type LastSeen struct {
	Time     int64 `json:"time"`
	Platform int   `json:"platform"`
}

type Military struct {
	Unit      string `json:"unit"`
	UnitId    int    `json:"unit_id"`
	CountryId int    `json:"country_id"`
	From      int    `json:"from"`
	Until     int    `json:"until"`
}

type Occupation struct {
	Type string `json:"type"`
	Id   int    `json:"id"`
	Name string `json:"name"`
}

type Personal struct {
	Political  int      `json:"political"`
	Langs      []string `json:"langs"`
	Religion   string   `json:"religion"`
	InspiredBy string   `json:"inspired_by"`
	PeopleMain int      `json:"people_main"`
	LifeMain   int      `json:"life_main"`
	Smoking    int      `json:"smoking"`
	Alcohol    int      `json:"alcohol"`
}

type Relatives struct {
	Id   int    `json:"id"`
	Name string `json:"name"`
	Type string `json:"type"`
}

type Schools struct {
	Id            string `json:"id"`
	Country       int    `json:"country"`
	City          int    `json:"city"`
	Name          string `json:"name"`
	YearFrom      int    `json:"year_from"`
	YearTo        int    `json:"year_to"`
	YearGraduated int    `json:"year_graduated"`
	Class         string `json:"class"`
	Speciality    string `json:"speciality"`
	Type          int    `json:"type"`
	TypeStr       string `json:"type_str"`
}

type Universities struct {
	Id              int    `json:"id"`
	Country         int    `json:"country"`
	City            int    `json:"city"`
	Name            string `json:"name"`
	Faculty         int    `json:"faculty"`
	FacultyName     string `json:"faculty_name"`
	Chair           int    `json:"chair"`
	ChairName       string `json:"chair_name"`
	Graduation      int    `json:"graduation"`
	EducationForm   string `json:"education_form"`
	EducationStatus string `json:"education_status"`
}

var AllFields = []string{"about",
	"activities",
	"bdate",
	"blacklisted",
	"blacklisted_by_me",
	"books",
	"can_post",
	"can_see_all_posts",
	"can_see_audio",
	"can_send_friend_request",
	"can_write_private_message",
	"career",
	"city",
	"common_count",
	"connections",
	"contacts",
	"counters",
	"country",
	"crop_photo",
	"domain",
	"education",
	"first_name_nom",
	"first_name_gen",
	"first_name_dat",
	"first_name_acc",
	"first_name_ins",
	"first_name_abl",
	"followers_count",
	"friend_status",
	"games",
	"has_mobile",
	"hasPhoto",
	"home_town",
	"interests",
	"is_favorite",
	"is_friend",
	"is_hidden_from_feed",
	"last_name_nom",
	"last_name_gen",
	"last_name_dat",
	"last_name_acc",
	"last_name_ins",
	"last_name_abl",
	"last_seen",
	"maiden_name",
	"military",
	"movies",
	"music",
	"nickname",
	"occupation",
	"online",
	"personal",
	"photo_50",
	"photo_100",
	"photo_200_orig",
	"photo_200",
	"photo_400_orig",
	"photo_id",
	"photo_max",
	"photo_max_orig",
	"quotes",
	"relatives",
	"relation",
	"schools",
	"screen_name",
	"sex",
	"site",
	"status",
	"status_audio",
	"timezone",
	"tv",
	"universities",
	"verified",
	"wall_comments"}
<commit_msg>Added InitMyProfile and fixed UserInfo<commit_after>package vkapi

import (
	"encoding/json"
	"errors"
	"log"
	"net/url"
	"strings"
)

func (client *Client) UsersInfo(userIds []string, fieldArgs []string) (users []Users, err *Error) {
	var req Request
	req.Method = "users.get"
	req.Values = url.Values{}

	if len(userIds) > 0 {
		ids := strings.Join(userIds, ",")
		req.Values.Set("user_ids", ids)
	}

	if len(userIds) > 0 {
		args := strings.Join(fieldArgs, ",")
		req.Values.Set("fields", args)
	}

	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	log.Println("Answer:", res.Response.String())

	if err := json.Unmarshal(res.Response.Bytes(), &users); err != nil {
		return nil, NewError(ErrBadCode, err.Error())
	}

	return
}

func (client *Client) InitMyProfile(fieldArgs []string) error {
	users, err := client.UsersInfo([]string{}, fieldArgs)
	if err != nil {
		return err
	}

	if len(users) == 0 {
		return errors.New("An unexpected error occurred.")
	}

	client.User = users[0]
	return nil
}

type Users struct {
	// Full description at https://vk.com/dev/objects/user
	Id          int64  `json:"id"`
	FirstName   string `json:"first_name"`
	LastName    string `json:"last_name"`
	Deactivated string `json:"deactivated"`
	Hidden      int    `json:"hidden"`

	// Optional fields
	About                  string          `json:"about"`
	Activities             string          `json:"activities"`
	Bdate                  string          `json:"bdate"`
	Blacklisted            int             `json:"blacklisted"`
	BlacklistedByMe        int             `json:"blacklisted_by_me"`
	Books                  string          `json:"books"`
	CanPost                int             `json:"can_post"`
	CanSeeAllPosts         int             `json:"can_see_all_posts"`
	CanSeeAudio            int             `json:"can_see_audio"`
	CanSendFriendRequest   int             `json:"can_send_friend_request"`
	CanWritePrivateMessage int             `json:"can_write_private_message"`
	Career                 *[]Career       `json:"career"`
	City                   *City           `json:"city"`
	CommonCount            int64           `json:"common_count"`
	Skype                  string          `json:"skype"`
	Facebook               string          `json:"facebook"`
	Twitter                string          `json:"twitter"`
	Livejournal            string          `json:"livejournal"`
	Instagram              string          `json:"instagram"`
	Contacts               *Contacts       `json:"contacts"`
	Counters               *Counters       `json:"counters"`
	Country                *Country        `json:"country"`
	CropPhoto              *CropPhoto      `json:"crop_photo"`
	Domain                 string          `json:"domain"`
	University             int64           `json:"university"`
	UniversityName         string          `json:"university_name"`
	Faculty                int64           `json:"faculty"`
	FacultyName            string          `json:"faculty_name"`
	Graduation             int64           `json:"graduation"`
	FirstNameNom           string          `json:"first_name_nom"`
	FirstNameGen           string          `json:"first_name_gen"`
	FirstNameDat           string          `json:"first_name_dat"`
	FirstNameAcc           string          `json:"first_name_acc"`
	FirstNameIns           string          `json:"first_name_ins"`
	FirstNameAbl           string          `json:"first_name_abl"`
	FollowersCount         int64           `json:"followers_count"`
	FriendStatus           int             `json:"friend_status"`
	Games                  string          `json:"games"`
	HasMobile              int             `json:"has_mobile"`
	HasPhoto               int             `json:"hasPhoto"`
	HomeTown               string          `json:"home_town"`
	Interests              string          `json:"interests"`
	IsFavorite             int             `json:"is_favorite"`
	IsFriend               int             `json:"is_friend"`
	IsHiddenFromFeed       int             `json:"is_hidden_from_feed"`
	LastNameNom            string          `json:"last_name_nom"`
	LastNameGen            string          `json:"last_name_gen"`
	LastNameDat            string          `json:"last_name_dat"`
	LastNameAcc            string          `json:"last_name_acc"`
	LastNameIns            string          `json:"last_name_ins"`
	LastNameAbl            string          `json:"last_name_abl"`
	LastSeen               *LastSeen       `json:"last_seen"`
	MaidenName             string          `json:"maiden_name"`
	Military               *[]Military     `json:"military"`
	Movies                 string          `json:"movies"`
	Music                  string          `json:"music"`
	Nickname               string          `json:"nickname"`
	Occupation             *Occupation     `json:"occupation"`
	Online                 int             `json:"online"`
	Personal               *Personal       `json:"personal"`
	Photo50                string          `json:"photo_50"`
	Photo100               string          `json:"photo_100"`
	Photo200Orig           string          `json:"photo_200_orig"`
	Photo200               string          `json:"photo_200"`
	Photo400Orig           string          `json:"photo_400_orig"`
	PhotoId                string          `json:"photo_id"`
	PhotoMax               string          `json:"photo_max"`
	PhotoMaxOrig           string          `json:"photo_max_orig"`
	Quotes                 string          `json:"quotes"`
	Relatives              *[]Relatives    `json:"relatives"`
	Relation               int             `json:"relation"`
	Schools                *[]Schools      `json:"schools"`
	ScreenName             string          `json:"screen_name"`
	Sex                    int             `json:"sex"`
	Site                   string          `json:"site"`
	Status                 string          `json:"status"`
	StatusAudio            string          `json:"status_audio"`
	Timezone               int             `json:"timezone"`
	Tv                     string          `json:"tv"`
	Universities           *[]Universities `json:"universities"`
	Verified               int             `json:"verified"`
	WallComments           int             `json:"wall_comments"`
}

type Career struct {
	GroupId   int64  `json:"group_id"`
	Company   string `json:"company"`
	CountryId int64  `json:"country_id"`
	CityId    int64  `json:"city_id"`
	CityName  string `json:"city_name"`
	From      int64  `json:"from"`
	Until     int64  `json:"until"`
	Position  string `json:"position"`
}

type City struct {
	Id    int    `json:"id"`
	Title string `json:"title"`
}

type Contacts struct {
	MobilePhone string `json:"mobile_phone"`
	HomePhone   string `json:"home_phone"`
}

type Counters struct {
	Albums        int `json:"albums"`
	Videos        int `json:"videos"`
	Audios        int `json:"audios"`
	Photos        int `json:"photos"`
	Notes         int `json:"notes"`
	Friends       int `json:"friends"`
	Groups        int `json:"groups"`
	OnlineFriends int `json:"online_friends"`
	MutualFriends int `json:"mutual_friends"`
	UserVideos    int `json:"user_videos"`
	Followers     int `json:"followers"`
	Pages         int `json:"pages"`
}

type Country struct {
	Id    int    `json:"id"`
	Title string `json:"title"`
}

type CropPhoto struct {
	/*Photo struct{} `json:"photo"`*/
	Crop *Rect `json:"crop"`
	Rect *Rect `json:"rect"`
}

type Rect struct {
	x  float64
	x2 float64
	y  float64
	y2 float64
}

type LastSeen struct {
	Time     int64 `json:"time"`
	Platform int   `json:"platform"`
}

type Military struct {
	Unit      string `json:"unit"`
	UnitId    int    `json:"unit_id"`
	CountryId int    `json:"country_id"`
	From      int    `json:"from"`
	Until     int    `json:"until"`
}

type Occupation struct {
	Type string `json:"type"`
	Id   int    `json:"id"`
	Name string `json:"name"`
}

type Personal struct {
	Political  int      `json:"political"`
	Langs      []string `json:"langs"`
	Religion   string   `json:"religion"`
	InspiredBy string   `json:"inspired_by"`
	PeopleMain int      `json:"people_main"`
	LifeMain   int      `json:"life_main"`
	Smoking    int      `json:"smoking"`
	Alcohol    int      `json:"alcohol"`
}

type Relatives struct {
	Id   int    `json:"id"`
	Name string `json:"name"`
	Type string `json:"type"`
}

type Schools struct {
	Id            string `json:"id"`
	Country       int    `json:"country"`
	City          int    `json:"city"`
	Name          string `json:"name"`
	YearFrom      int    `json:"year_from"`
	YearTo        int    `json:"year_to"`
	YearGraduated int    `json:"year_graduated"`
	Class         string `json:"class"`
	Speciality    string `json:"speciality"`
	Type          int    `json:"type"`
	TypeStr       string `json:"type_str"`
}

type Universities struct {
	Id              int    `json:"id"`
	Country         int    `json:"country"`
	City            int    `json:"city"`
	Name            string `json:"name"`
	Faculty         int    `json:"faculty"`
	FacultyName     string `json:"faculty_name"`
	Chair           int    `json:"chair"`
	ChairName       string `json:"chair_name"`
	Graduation      int    `json:"graduation"`
	EducationForm   string `json:"education_form"`
	EducationStatus string `json:"education_status"`
}

var AllFields = []string{"about",
	"activities",
	"bdate",
	"blacklisted",
	"blacklisted_by_me",
	"books",
	"can_post",
	"can_see_all_posts",
	"can_see_audio",
	"can_send_friend_request",
	"can_write_private_message",
	"career",
	"city",
	"common_count",
	"connections",
	"contacts",
	"counters",
	"country",
	"crop_photo",
	"domain",
	"education",
	"first_name_nom",
	"first_name_gen",
	"first_name_dat",
	"first_name_acc",
	"first_name_ins",
	"first_name_abl",
	"followers_count",
	"friend_status",
	"games",
	"has_mobile",
	"hasPhoto",
	"home_town",
	"interests",
	"is_favorite",
	"is_friend",
	"is_hidden_from_feed",
	"last_name_nom",
	"last_name_gen",
	"last_name_dat",
	"last_name_acc",
	"last_name_ins",
	"last_name_abl",
	"last_seen",
	"maiden_name",
	"military",
	"movies",
	"music",
	"nickname",
	"occupation",
	"online",
	"personal",
	"photo_50",
	"photo_100",
	"photo_200_orig",
	"photo_200",
	"photo_400_orig",
	"photo_id",
	"photo_max",
	"photo_max_orig",
	"quotes",
	"relatives",
	"relation",
	"schools",
	"screen_name",
	"sex",
	"site",
	"status",
	"status_audio",
	"timezone",
	"tv",
	"universities",
	"verified",
	"wall_comments"}
<|endoftext|>


<commit_before>package obj

import (
	"context"
	"io"
	"sync"
	"time"

	"github.com/hashicorp/golang-lru/simplelru"
	log "github.com/sirupsen/logrus"
)

var _ Client = &cacheClient{}

type cacheClient struct {
	slow, fast Client

	mu           sync.Mutex
	cache        *simplelru.LRU
	populateOnce sync.Once
}

// NewCacheClient returns slow wrapped in an LRU write-through cache using fast for storing
// cached data.
func NewCacheClient(slow, fast Client, size int) Client {
	if size == 0 {
		return slow
	}
	client := &cacheClient{
		slow: slow,
		fast: fast,
	}
	cache, err := simplelru.NewLRU(size, client.onEvicted)
	if err != nil {
		// lru.NewWithEvict only errors for size < 1
		panic(err)
	}
	client.cache = cache
	return client
}

func (c *cacheClient) Reader(ctx context.Context, p string, offset, size uint64) (io.ReadCloser, error) {
	c.doPopulateOnce(ctx)
	c.mu.Lock()
	defer c.mu.Unlock()
	if _, exists := c.cache.Get(p); exists {
		return c.fast.Reader(ctx, p, offset, size)
	}
	if err := Copy(ctx, c.slow, c.fast, p, p); err != nil {
		return nil, err
	}
	c.cache.Add(p, struct{}{})
	return c.fast.Reader(ctx, p, offset, size)
}

func (c *cacheClient) Writer(ctx context.Context, p string) (io.WriteCloser, error) {
	wc, err := c.slow.Writer(ctx, p)
	if err != nil {
		return nil, err
	}
	return &cacheWriter{WriteCloser: wc, path: p, client: c}, nil
}

func (c *cacheClient) Delete(ctx context.Context, p string) error {
	if err := c.slow.Delete(ctx, p); err != nil {
		return err
	}
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.deleteFromCache(ctx, p)
}

func (c *cacheClient) Exists(ctx context.Context, p string) bool {
	c.doPopulateOnce(ctx)
	return c.fast.Exists(ctx, p) || c.slow.Exists(ctx, p)
}

func (c *cacheClient) Walk(ctx context.Context, p string, cb func(p string) error) error {
	return c.slow.Walk(ctx, p, cb)
}

func (c *cacheClient) IsIgnorable(err error) bool {
	return c.fast.IsIgnorable(err) || c.slow.IsIgnorable(err)
}

func (c *cacheClient) IsNotExist(err error) bool {
	return c.fast.IsNotExist(err) || c.slow.IsNotExist(err)
}

func (c *cacheClient) IsRetryable(err error) bool {
	return c.fast.IsRetryable(err) || c.slow.IsRetryable(err)
}

// deleteFromCache should only be called with c.mu
func (c *cacheClient) deleteFromCache(ctx context.Context, p string) error {
	if !c.cache.Contains(p) {
		return nil
	}
	if err := c.fast.Delete(ctx, p); err != nil && !c.fast.IsNotExist(err) {
		return err
	}
	c.cache.Remove(p)
	return nil
}

// onEvicted is called by the cache implementation.
// the gorountine executing onEvicted will have c.mu
func (c *cacheClient) onEvicted(key, value interface{}) {
	p := key.(string)
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	if err := c.fast.Delete(ctx, p); err != nil && !c.fast.IsNotExist(err) {
		log.Error("could not delete from cache's fast store: %v", err)
	}
}

func (c *cacheClient) populate(ctx context.Context) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.fast.Walk(ctx, "", func(p string) error {
		c.cache.Add(p, struct{}{})
		return nil
	})
}

func (c *cacheClient) doPopulateOnce(ctx context.Context) {
	c.populateOnce.Do(func() {
		if err := c.populate(ctx); err != nil {
			log.Warnf("could not populate cache: %v", err)
		}
	})
}

type cacheWriter struct {
	io.WriteCloser
	path   string
	client *cacheClient
}

func (cw *cacheWriter) Close() error {
	cw.client.mu.Lock()
	defer cw.client.mu.Unlock()
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	return cw.client.deleteFromCache(ctx, cw.path)
}
<commit_msg>fix writecloser<commit_after>package obj

import (
	"context"
	"io"
	"sync"
	"time"

	"github.com/hashicorp/golang-lru/simplelru"
	log "github.com/sirupsen/logrus"
)

var _ Client = &cacheClient{}

type cacheClient struct {
	slow, fast Client

	mu           sync.Mutex
	cache        *simplelru.LRU
	populateOnce sync.Once
}

// NewCacheClient returns slow wrapped in an LRU write-through cache using fast for storing
// cached data.
func NewCacheClient(slow, fast Client, size int) Client {
	if size == 0 {
		return slow
	}
	client := &cacheClient{
		slow: slow,
		fast: fast,
	}
	cache, err := simplelru.NewLRU(size, client.onEvicted)
	if err != nil {
		// lru.NewWithEvict only errors for size < 1
		panic(err)
	}
	client.cache = cache
	return client
}

func (c *cacheClient) Reader(ctx context.Context, p string, offset, size uint64) (io.ReadCloser, error) {
	c.doPopulateOnce(ctx)
	c.mu.Lock()
	defer c.mu.Unlock()
	if _, exists := c.cache.Get(p); exists {
		return c.fast.Reader(ctx, p, offset, size)
	}
	if err := Copy(ctx, c.slow, c.fast, p, p); err != nil {
		return nil, err
	}
	c.cache.Add(p, struct{}{})
	return c.fast.Reader(ctx, p, offset, size)
}

func (c *cacheClient) Writer(ctx context.Context, p string) (io.WriteCloser, error) {
	wc, err := c.slow.Writer(ctx, p)
	if err != nil {
		return nil, err
	}
	return &cacheWriter{WriteCloser: wc, path: p, client: c}, nil
}

func (c *cacheClient) Delete(ctx context.Context, p string) error {
	if err := c.slow.Delete(ctx, p); err != nil {
		return err
	}
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.deleteFromCache(ctx, p)
}

func (c *cacheClient) Exists(ctx context.Context, p string) bool {
	c.doPopulateOnce(ctx)
	return c.fast.Exists(ctx, p) || c.slow.Exists(ctx, p)
}

func (c *cacheClient) Walk(ctx context.Context, p string, cb func(p string) error) error {
	return c.slow.Walk(ctx, p, cb)
}

func (c *cacheClient) IsIgnorable(err error) bool {
	return c.fast.IsIgnorable(err) || c.slow.IsIgnorable(err)
}

func (c *cacheClient) IsNotExist(err error) bool {
	return c.fast.IsNotExist(err) || c.slow.IsNotExist(err)
}

func (c *cacheClient) IsRetryable(err error) bool {
	return c.fast.IsRetryable(err) || c.slow.IsRetryable(err)
}

// deleteFromCache should only be called with c.mu
func (c *cacheClient) deleteFromCache(ctx context.Context, p string) error {
	if !c.cache.Contains(p) {
		return nil
	}
	if err := c.fast.Delete(ctx, p); err != nil && !c.fast.IsNotExist(err) {
		return err
	}
	c.cache.Remove(p)
	return nil
}

// onEvicted is called by the cache implementation.
// the gorountine executing onEvicted will have c.mu
func (c *cacheClient) onEvicted(key, value interface{}) {
	p := key.(string)
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	if err := c.fast.Delete(ctx, p); err != nil && !c.fast.IsNotExist(err) {
		log.Error("could not delete from cache's fast store: %v", err)
	}
}

func (c *cacheClient) populate(ctx context.Context) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.fast.Walk(ctx, "", func(p string) error {
		c.cache.Add(p, struct{}{})
		return nil
	})
}

func (c *cacheClient) doPopulateOnce(ctx context.Context) {
	c.populateOnce.Do(func() {
		if err := c.populate(ctx); err != nil {
			log.Warnf("could not populate cache: %v", err)
		}
	})
}

type cacheWriter struct {
	io.WriteCloser
	path   string
	client *cacheClient
}

func (cw *cacheWriter) Close() error {
	if err := cw.WriteCloser.Close(); err != nil {
		return err
	}
	cw.client.mu.Lock()
	defer cw.client.mu.Unlock()
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	return cw.client.deleteFromCache(ctx, cw.path)
}
<|endoftext|>


<commit_before>// Copyright 2012 Oliver Eilhard. All rights reserved.
// Use of this source code is governed by a MIT-license.
// See http://olivere.mit-license.org/license.txt for details.

package elastic

import (
	"strings"
)

// Removes all characters unless they're
// between greater or equal to 0x2d (-) or
// less than or equal to (z) or a tilde (~).
func cleanPathString(s string) string {
	return strings.Map(func(r rune) rune {
		if r >= 0x2d && r <= 0x7a || r == '~' {
			return r
		}
		return -1
	}, s)
}
<commit_msg>Included *,+,- in Index name<commit_after>// Copyright 2012 Oliver Eilhard. All rights reserved.
// Use of this source code is governed by a MIT-license.
// See http://olivere.mit-license.org/license.txt for details.

package elastic

import (
	"strings"
)

// Removes all characters unless they're
// between greater or equal to 0x2d (-) or
// less than or equal to (z) or a tilde (~).
func cleanPathString(s string) string {
	return strings.Map(func(r rune) rune {
		if r >= 0x2a && r <= 0x7a && r != 0x2c || r == '~'{
			return r
		}
		return -1
	}, s)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/gorilla/securecookie"
	"io/ioutil"
	"log"
	"net/http"
	"runtime"
	"strings"
)

// LogError calls log.Printf on error, and adds location in source code
func LogError(err error) {
	_, file, line, ok := runtime.Caller(1)
	if ok {
		log.Printf("%s:%d : %s", file, line, err)
	} else {
		log.Println(err)
	}
}

// LogHttp log error and sends it to browser
func LogHttp(w http.ResponseWriter, err error) {
	_, file, line, ok := runtime.Caller(1)
	if ok {
		log.Printf("%s:%d : %s", file, line, err)
	} else {
		log.Println(err)
	}
	http.Error(w, err.Error(), http.StatusInternalServerError)
}

// LogFatal calls log.Fatalf on error, and adds location in source code
func LogFatal(err error) {
	_, file, line, ok := runtime.Caller(1)
	if ok {
		log.Fatalf("%s:%d : %s", file, line, err)
	} else {
		log.Fatal(err)
	}

}

// WriteFiles write the files it's given as argument to w
func writeFiles(w http.ResponseWriter, files ...string) error {
	for _, file := range files {
		b, err := ioutil.ReadFile(file)
		if err != nil {
			return err
		}
		w.Write(b)
	}
	return nil
}

func ok(w http.ResponseWriter) {
	w.WriteHeader(http.StatusNotImplemented)
	w.Write([]byte("ok"))
}
func ko(w http.ResponseWriter) {
	w.Write([]byte("ko"))
}

var hashKey = []byte(securecookie.GenerateRandomKey(32))
var blockKey = []byte(securecookie.GenerateRandomKey(32))
var s = securecookie.New(hashKey, blockKey)

func SetToken(w http.ResponseWriter, token string) error {
	encoded, err := s.Encode("auth-token", token)
	if err != nil { return MkIErr(err) }

	cookie := &http.Cookie {
		Name	:	"auth-token",
		Value	:	encoded,
		Path	:	"/",
	}
	http.SetCookie(w, cookie)

	return nil
}

func UnsetToken(w http.ResponseWriter) {
	cookie := &http.Cookie {
		Name	:	"auth-token",
		Value	:	"",
		Path	:	"/",
		MaxAge	:	-1,
	}
	http.SetCookie(w, cookie)
}

func VerifyToken(r *http.Request) (token string, err error) {
	cookie, err := r.Cookie("auth-token")
	if err == nil {
		err = s.Decode("auth-token", cookie.Value, &token)
	}

	if err != nil || !CheckToken(token) {
		return "", MouldyCookie
	}

	return token, nil
}

func SetInfo(w http.ResponseWriter, msg string) {
	cookie := &http.Cookie {
		Name	:	"auth-info",
		Value	:	strings.Replace(msg, " ", "_", -1),
		Path	:	"/",
	}
	http.SetCookie(w, cookie)
}

func SetError(w http.ResponseWriter, err error) {
	SetInfo(w, "Error: "+err.Error())
}
<commit_msg>remove dead code<commit_after>package main

import (
	"github.com/gorilla/securecookie"
	"io/ioutil"
	"net/http"
	"strings"
)

// WriteFiles write the files it's given as argument to w
func writeFiles(w http.ResponseWriter, files ...string) error {
	for _, file := range files {
		b, err := ioutil.ReadFile(file)
		if err != nil {
			return err
		}
		w.Write(b)
	}
	return nil
}

func ok(w http.ResponseWriter) {
	w.WriteHeader(http.StatusNotImplemented)
	w.Write([]byte("ok"))
}
func ko(w http.ResponseWriter) {
	w.Write([]byte("ko"))
}

var hashKey = []byte(securecookie.GenerateRandomKey(32))
var blockKey = []byte(securecookie.GenerateRandomKey(32))
var s = securecookie.New(hashKey, blockKey)

func SetToken(w http.ResponseWriter, token string) error {
	encoded, err := s.Encode("auth-token", token)
	if err != nil { return MkIErr(err) }

	cookie := &http.Cookie {
		Name	:	"auth-token",
		Value	:	encoded,
		Path	:	"/",
	}
	http.SetCookie(w, cookie)

	return nil
}

func UnsetToken(w http.ResponseWriter) {
	cookie := &http.Cookie {
		Name	:	"auth-token",
		Value	:	"",
		Path	:	"/",
		MaxAge	:	-1,
	}
	http.SetCookie(w, cookie)
}

func VerifyToken(r *http.Request) (token string, err error) {
	cookie, err := r.Cookie("auth-token")
	if err == nil {
		err = s.Decode("auth-token", cookie.Value, &token)
	}

	if err != nil || !CheckToken(token) {
		return "", MouldyCookie
	}

	return token, nil
}

func SetInfo(w http.ResponseWriter, msg string) {
	cookie := &http.Cookie {
		Name	:	"auth-info",
		Value	:	strings.Replace(msg, " ", "_", -1),
		Path	:	"/",
	}
	http.SetCookie(w, cookie)
}

func SetError(w http.ResponseWriter, err error) {
	SetInfo(w, "Error: "+err.Error())
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"github.com/gosimple/slug"
	"github.com/russross/blackfriday"
	"os"
)

// EncodeURL takes a string and encodes it.
func EncodeURL(s string) string {
	return slug.MakeLang(s, "en")
}

// URLize turns a title into a filename dot html.
func URLize(t string) string {
	return StringConcat(slug.MakeLang(t, "en"), ".html")
}

// Pageify gives a name "page/pagen.html", where n is the index of the topic.
func Pageify(index int) string {
	return fmt.Sprintf("page/page%d.html", index)
}

// Categorify gives a name "category/title.html", where title is the URL of the category.
func Categorify(title string) string {
	return fmt.Sprintf("category/%s.html", EncodeURL(title))
}

var (
	// BaseURL is the base URL.
	BaseURL = ""
	// Author is the author.
	Author = ""
	// Description is the description.
	Description = ""
	// Lang is the language.
	Lang = ""
	// Footer is a footer.
	Footer = ""
)

// GetURL gets the URL based on the BaseURL path.
func GetURL(rel string) string {
	return StringConcat(StringConcat(BaseURL, "/"), rel)
}

// FprintHeader prints the standard HTML5 header.
func FprintHeader(file *os.File, title string) {
	fmt.Fprintf(file,
		"<!doctype html>\n"+
			"\n"+
			"\n"+
			"  <meta charset=\"utf-8\">\n"+
			"  <title>%s</title>\n"+
			"  <meta name=\"description\" content=\"%s\">\n"+
			"  <meta name=\"author\" content=\"%s\">\n"+
			"  <link href=\"https://fonts.googleapis.com/css?family=Roboto\" rel=\"stylesheet\">\n"+
			"  \n"+
			"\n"+
			"\n\n",
		Lang, title, Description, Author)
}

// FprintFooter prints the footer.
func FprintFooter(file *os.File) {
	fmt.Fprintf(file,
		"%s\n\n"+
			"\n"+
			"\n",
		Footer)
}

// Markdown uses BlackFriday's markdown renderer to convert markdown to html.
func Markdown(mark string) string {
	return string(blackfriday.MarkdownCommon([]byte(mark)))
}

// StringConcat concatenates two strings.
func StringConcat(s1, s2 string) string {
	var buffer bytes.Buffer
	buffer.WriteString(s1)
	buffer.WriteString(s2)
	return buffer.String()
}
<commit_msg>Adding MathJax for math expressions.<commit_after>package main

import (
	"bytes"
	"fmt"
	"github.com/gosimple/slug"
	"github.com/russross/blackfriday"
	"os"
)

// EncodeURL takes a string and encodes it.
func EncodeURL(s string) string {
	return slug.MakeLang(s, "en")
}

// URLize turns a title into a filename dot html.
func URLize(t string) string {
	return StringConcat(slug.MakeLang(t, "en"), ".html")
}

// Pageify gives a name "page/pagen.html", where n is the index of the topic.
func Pageify(index int) string {
	return fmt.Sprintf("page/page%d.html", index)
}

// Categorify gives a name "category/title.html", where title is the URL of the category.
func Categorify(title string) string {
	return fmt.Sprintf("category/%s.html", EncodeURL(title))
}

var (
	// BaseURL is the base URL.
	BaseURL = ""
	// Author is the author.
	Author = ""
	// Description is the description.
	Description = ""
	// Lang is the language.
	Lang = ""
	// Footer is a footer.
	Footer = ""
)

// GetURL gets the URL based on the BaseURL path.
func GetURL(rel string) string {
	return StringConcat(StringConcat(BaseURL, "/"), rel)
}

// FprintHeader prints the standard HTML5 header.
func FprintHeader(file *os.File, title string) {
	fmt.Fprintf(file,
		"<!doctype html>\n"+
			"\n"+
			"\n"+
			"  <meta charset=\"utf-8\">\n"+
			"  <title>%s</title>\n"+
			"  <meta name=\"description\" content=\"%s\">\n"+
			"  <meta name=\"author\" content=\"%s\">\n"+
			"  <link href=\"https://fonts.googleapis.com/css?family=Roboto\" rel=\"stylesheet\">\n"+
			"  \n"+
			"  \n"+
			"\n"+
			"\n\n",
		Lang, title, Description, Author)
}

// FprintFooter prints the footer.
func FprintFooter(file *os.File) {
	fmt.Fprintf(file,
		"%s\n\n"+
			"\n"+
			"\n",
		Footer)
}

// Markdown uses BlackFriday's markdown renderer to convert markdown to html.
func Markdown(mark string) string {
	return string(blackfriday.MarkdownCommon([]byte(mark)))
}

// StringConcat concatenates two strings.
func StringConcat(s1, s2 string) string {
	var buffer bytes.Buffer
	buffer.WriteString(s1)
	buffer.WriteString(s2)
	return buffer.String()
}
<|endoftext|>


<commit_before>// Copyright 2014, The Serviced Authors. All rights reserved.
// Use of this source code is governed by a
// license that can be found in the LICENSE file.

// Package agent implements a service that runs on a serviced node. It is
// responsible for ensuring that a particular node is running the correct services
// and reporting the state and health of those services back to the master
// serviced.

package serviced

import (
	"github.com/zenoss/glog"
	"github.com/zenoss/serviced/dao"

	"bufio"
	"fmt"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

var hostIDCmdString = "/usr/bin/hostid"

// HostID retreives the system's unique id, on linux this maps
// to /usr/bin/hostid.
func HostID() (hostid string, err error) {
	cmd := exec.Command(hostIDCmdString)
	stdout, err := cmd.Output()
	if err != nil {
		return hostid, err
	}
	return strings.TrimSpace(string(stdout)), err
}

// Path to meminfo file. Placed here so getMemorySize() is testable.
var meminfoFile = "/proc/meminfo"

// validOwnerSpec returns true if the owner is specified in owner:group format and the
// identifiers are valid POSIX.1-2008 username and group strings, respectively.
func validOwnerSpec(owner string) bool {
	var pattern = regexp.MustCompile(`^[a-zA-Z]+[a-zA-Z0-9.-]*:[a-zA-Z]+[a-zA-Z0-9.-]*$`)
	return pattern.MatchString(owner)
}

// getMemorySize attempts to get the size of the installed RAM.
func getMemorySize() (size uint64, err error) {
	file, err := os.Open(meminfoFile)
	if err != nil {
		return 0, err
	}
	defer file.Close()
	reader := bufio.NewReader(file)
	line, err := reader.ReadString('\n')
	for err == nil {
		if strings.Contains(line, "MemTotal:") {
			parts := strings.Fields(line)
			if len(parts) < 3 {
				return 0, err
			}
			size, err := strconv.Atoi(parts[1])
			if err != nil {
				return 0, err
			}
			return uint64(size) * 1024, nil
		}
		line, err = reader.ReadString('\n')
	}
	return 0, err
}

// RouteEntry represents a entry from the route command
type RouteEntry struct {
	Destination string
	Gateway     string
	Genmask     string
	Flags       string
	Metric      int
	Ref         int
	Use         int
	Iface       string
}

// wrapper around the route command
func routeCmd() (routes []RouteEntry, err error) {
	output, err := exec.Command("/sbin/route", "-A", "inet").Output()
	if err != nil {
		return routes, err
	}

	columnMap := make(map[string]int)
	lines := strings.Split(strings.TrimSpace(string(output)), "\n")
	if len(lines) < 2 {
		return routes, fmt.Errorf("no routes found")
	}
	routes = make([]RouteEntry, len(lines)-2)
	for lineNum, line := range lines {
		line = strings.TrimSpace(line)
		switch {
		// skip first line
		case lineNum == 0:
			continue
		case lineNum == 1:
			for number, name := range strings.Fields(line) {
				columnMap[name] = number
			}
			continue
		default:
			fields := strings.Fields(line)
			metric, err := strconv.Atoi(fields[columnMap["Metric"]])
			if err != nil {
				return routes, err
			}
			ref, err := strconv.Atoi(fields[columnMap["Ref"]])
			if err != nil {
				return routes, err
			}
			use, err := strconv.Atoi(fields[columnMap["Use"]])
			if err != nil {
				return routes, err
			}
			routes[lineNum-2] = RouteEntry{
				Destination: fields[columnMap["Destination"]],
				Gateway:     fields[columnMap["Gateway"]],
				Genmask:     fields[columnMap["Genmask"]],
				Flags:       fields[columnMap["Flags"]],
				Metric:      metric,
				Ref:         ref,
				Use:         use,
				Iface:       fields[columnMap["Iface"]],
			}
		}
	}
	return routes, err
}

// GetIPAddress attempts to find the IP address to the default outbout interface.
func GetIPAddress() (ip string, err error) {
	ip, err = getIPAddrFromHostname()
	if err != nil || strings.HasPrefix(ip, "127") {
		ip, err = getIPAddrFromOutGoingConnection()
		if err == nil && strings.HasPrefix(ip, "127") {
			return "", fmt.Errorf("unable to identify local ip address")
		}
	}

	return ip, err
}

// getIPAddrFromHostname returns the ip address associated with hostname -i.
func getIPAddrFromHostname() (ip string, err error) {
	output, err := exec.Command("hostname", "-i").Output()
	if err != nil {
		return ip, err
	}
	return strings.TrimSpace(string(output)), err
}

// getIPAddrFromOutGoingConnection get the IP bound to the interface which
// handles the default route traffic.
func getIPAddrFromOutGoingConnection() (ip string, err error) {
	addr, err := net.ResolveUDPAddr("udp4", "8.8.8.8:53")
	if err != nil {
		return "", err
	}

	conn, err := net.DialUDP("udp4", nil, addr)
	if err != nil {
		return "", err
	}

	localAddr := conn.LocalAddr()
	parts := strings.Split(localAddr.String(), ":")
	return parts[0], nil
}

// CurrentContextAsHost creates a dao.Host object of the host running
// this method. The passed in poolID is used as the resource pool in the result.
func CurrentContextAsHost(poolID string) (host *dao.Host, err error) {
	cpus := runtime.NumCPU()
	memory, err := getMemorySize()
	if err != nil {
		return nil, err
	}
	host = dao.NewHost()
	hostname, err := os.Hostname()
	if err != nil {
		return nil, err
	}
	host.Name = hostname
	hostidStr, err := HostID()
	if err != nil {
		return nil, err
	}

	host.IpAddr, err = GetIPAddress()
	if err != nil {
		return host, err
	}

	host.Id = hostidStr
	host.Cores = cpus
	host.Memory = memory

	routes, err := routeCmd()
	if err != nil {
		return nil, err
	}
	for _, route := range routes {
		if route.Iface == "docker0" {
			host.PrivateNetwork = route.Destination + "/" + route.Genmask
			break
		}
	}
	host.PoolId = poolID
	return host, err
}

// ExecPath returns the path to the currently running executable.
func ExecPath() (string, string, error) {
	path, err := os.Readlink("/proc/self/exe")
	if err != nil {
		return "", "", err
	}
	return filepath.Dir(path), filepath.Base(path), nil
}

// DockerVersion contains the tuples that describe the version of docker.
type DockerVersion struct {
	Client []int
	Server []int
}

// equals compares two DockerVersion structs and returns true if they are equal.
func (a *DockerVersion) equals(b *DockerVersion) bool {
	if len(a.Client) != len(b.Client) {
		return false
	}
	for i, aI := range a.Client {
		if aI != b.Client[i] {
			return false
		}
	}
	if len(a.Server) != len(b.Server) {
		return false
	}
	for i, aI := range a.Server {
		if aI != b.Server[i] {
			return false
		}
	}
	return true
}

// GetDockerVersion returns docker version number.
func GetDockerVersion() (DockerVersion, error) {
	cmd := exec.Command("docker", "version")
	output, err := cmd.Output()
	if err != nil {
		return DockerVersion{}, err
	}
	return parseDockerVersion(string(output))
}

// parseDockerVersion parses the output of the 'docker version' commmand and
// returns a DockerVersion object.
func parseDockerVersion(output string) (version DockerVersion, err error) {

	for _, line := range strings.Split(output, "\n") {
		parts := strings.SplitN(line, ":", 2)
		if len(parts) < 2 {
			continue
		}
		if strings.HasPrefix(parts[0], "Client version") {
			a := strings.SplitN(strings.TrimSpace(parts[1]), "-", 2)
			b := strings.Split(a[0], ".")
			version.Client = make([]int, len(b))
			for i, v := range b {
				x, err := strconv.Atoi(v)
				if err != nil {
					return version, err
				}
				version.Client[i] = x
			}
		}
		if strings.HasPrefix(parts[0], "Server version") {
			a := strings.SplitN(strings.TrimSpace(parts[1]), "-", 2)
			b := strings.Split(a[0], ".")
			version.Server = make([]int, len(b))
			for i, v := range b {
				x, err := strconv.Atoi(v)
				if err != nil {
					return version, err
				}
				version.Server[i] = x
			}
		}
	}
	if len(version.Client) == 0 {
		return version, fmt.Errorf("no client version found")
	}
	if len(version.Server) == 0 {
		return version, fmt.Errorf("no server version found")
	}
	return version, nil
}

// CreateDirectory creates a directory using the given username as the owner and the
// given perm as the directory permission.
func CreateDirectory(path, username string, perm os.FileMode) error {
	user, err := user.Lookup(username)
	if err == nil {
		err = os.MkdirAll(path, perm)
		if err == nil || err == os.ErrExist {
			uid, _ := strconv.Atoi(user.Uid)
			gid, _ := strconv.Atoi(user.Gid)
			err = os.Chown(path, uid, gid)
		}
	}
	return err
}

// singleJoiningSlash joins a and b ensuring there is only a single /
// character between them.
func singleJoiningSlash(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")
	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}

// NewReverseProxy differs from httputil.NewSingleHostReverseProxy in that it rewrites
// the path so that it does /not/ include the incoming path. e.g. request for
// "/mysvc/thing" when proxy is served from "/mysvc" means target is
// targeturl.Path + "/thing"; vs. httputil.NewSingleHostReverseProxy, in which
// it would be targeturl.Path + "/mysvc/thing".
func NewReverseProxy(path string, targeturl *url.URL) *httputil.ReverseProxy {
	targetQuery := targeturl.RawQuery
	director := func(r *http.Request) {
		r.URL.Scheme = targeturl.Scheme
		r.URL.Host = targeturl.Host
		newpath := strings.TrimPrefix(r.URL.Path, path)
		r.URL.Path = singleJoiningSlash(targeturl.Path, newpath)
		if targetQuery == "" || r.URL.RawQuery == "" {
			r.URL.RawQuery = targetQuery + r.URL.RawQuery
		} else {
			r.URL.RawQuery = targetQuery + "&" + r.URL.RawQuery
		}
	}
	return &httputil.ReverseProxy{Director: director}
}

var dockerRun = func(imageSpec string, args ...string) (output string, err error) {
	targs := []string{"run", "run", "-rm", imageSpec}
	for _, s := range args {
		targs = append(targs, s)
	}
	docker := exec.Command("docker", targs...)
	var outputBytes []byte
	outputBytes, err = docker.CombinedOutput()
	if err != nil {
		return
	}
	output = string(outputBytes)
	return
}

func getInternalImageIds(userSpec, imageSpec string) (uid, gid int, err error) {
	var output string
	output, err = dockerRun(imageSpec, "/bin/sh", "-c",
		fmt.Sprintf(`touch test.txt && chown %s test.txt && ls -ln | awk '{ print $3 " " $4 }'`,
			userSpec))
	if err != nil {
		return
	}
	s := strings.TrimSpace(string(output))
	pattern := regexp.MustCompile(`^\d+ \d+$`)

	if !pattern.MatchString(s) {
		err = fmt.Errorf("unexpected output from getInternalImageIds: %s", s)
		return
	}
	fields := strings.Fields(s)
	if len(fields) != 2 {
		err = fmt.Errorf("unexpected number of fields from container spec: %s", fields)
		return
	}
	uid, err = strconv.Atoi(fields[0])
	if err != nil {
		return
	}
	gid, err = strconv.Atoi(fields[1])
	if err != nil {
		return
	}
	return
}

var createVolumeDirMutex sync.Mutex

// createVolumeDir() creates a directory on the running host using the user ids
// found within the specified image. For example, it can create a directory owned
// by the mysql user (as seen by the container) despite there being no mysql user
// on the host system
func createVolumeDir(hostPath, containerSpec, imageSpec, userSpec, permissionSpec string) error {

	createVolumeDirMutex.Lock()
        defer createVolumeDirMutex.Unlock()
	// FIXME: this relies on the underlying container to have /bin/sh that supports
	// some advanced shell options. This should be rewriten so that serviced injects itself in the
	// container and performs the operations using only go!

	var err error
	var output []byte
	for i:= 0; i < 1; i++ {
    	docker := exec.Command("docker", "run", "-rm",
		"-v", hostPath+":/tmp",
		imageSpec,
		"/bin/sh", "-c",
		fmt.Sprintf(`
chown %s /tmp && \
chmod %s /tmp && \
shopt -s nullglob && \
shopt -s dotglob && \
files=(/tmp/*) && \
if [ ${#files[@]} -eq 0 ]; then
	cp -rp %s/* /tmp/
fi
`, userSpec, permissionSpec, containerSpec))
	output, err = docker.CombinedOutput()
	if err == nil {
		return nil
	}
	time.Sleep(time.Second)
	}

		glog.Errorf("could not create host volume: %s", string(output))
	return err
}
<commit_msg>add todo<commit_after>// Copyright 2014, The Serviced Authors. All rights reserved.
// Use of this source code is governed by a
// license that can be found in the LICENSE file.

// Package agent implements a service that runs on a serviced node. It is
// responsible for ensuring that a particular node is running the correct services
// and reporting the state and health of those services back to the master
// serviced.

package serviced

import (
	"github.com/zenoss/glog"
	"github.com/zenoss/serviced/dao"

	"bufio"
	"fmt"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

var hostIDCmdString = "/usr/bin/hostid"

// HostID retreives the system's unique id, on linux this maps
// to /usr/bin/hostid.
func HostID() (hostid string, err error) {
	cmd := exec.Command(hostIDCmdString)
	stdout, err := cmd.Output()
	if err != nil {
		return hostid, err
	}
	return strings.TrimSpace(string(stdout)), err
}

// Path to meminfo file. Placed here so getMemorySize() is testable.
var meminfoFile = "/proc/meminfo"

// validOwnerSpec returns true if the owner is specified in owner:group format and the
// identifiers are valid POSIX.1-2008 username and group strings, respectively.
func validOwnerSpec(owner string) bool {
	var pattern = regexp.MustCompile(`^[a-zA-Z]+[a-zA-Z0-9.-]*:[a-zA-Z]+[a-zA-Z0-9.-]*$`)
	return pattern.MatchString(owner)
}

// getMemorySize attempts to get the size of the installed RAM.
func getMemorySize() (size uint64, err error) {
	file, err := os.Open(meminfoFile)
	if err != nil {
		return 0, err
	}
	defer file.Close()
	reader := bufio.NewReader(file)
	line, err := reader.ReadString('\n')
	for err == nil {
		if strings.Contains(line, "MemTotal:") {
			parts := strings.Fields(line)
			if len(parts) < 3 {
				return 0, err
			}
			size, err := strconv.Atoi(parts[1])
			if err != nil {
				return 0, err
			}
			return uint64(size) * 1024, nil
		}
		line, err = reader.ReadString('\n')
	}
	return 0, err
}

// RouteEntry represents a entry from the route command
type RouteEntry struct {
	Destination string
	Gateway     string
	Genmask     string
	Flags       string
	Metric      int
	Ref         int
	Use         int
	Iface       string
}

// wrapper around the route command
func routeCmd() (routes []RouteEntry, err error) {
	output, err := exec.Command("/sbin/route", "-A", "inet").Output()
	if err != nil {
		return routes, err
	}

	columnMap := make(map[string]int)
	lines := strings.Split(strings.TrimSpace(string(output)), "\n")
	if len(lines) < 2 {
		return routes, fmt.Errorf("no routes found")
	}
	routes = make([]RouteEntry, len(lines)-2)
	for lineNum, line := range lines {
		line = strings.TrimSpace(line)
		switch {
		// skip first line
		case lineNum == 0:
			continue
		case lineNum == 1:
			for number, name := range strings.Fields(line) {
				columnMap[name] = number
			}
			continue
		default:
			fields := strings.Fields(line)
			metric, err := strconv.Atoi(fields[columnMap["Metric"]])
			if err != nil {
				return routes, err
			}
			ref, err := strconv.Atoi(fields[columnMap["Ref"]])
			if err != nil {
				return routes, err
			}
			use, err := strconv.Atoi(fields[columnMap["Use"]])
			if err != nil {
				return routes, err
			}
			routes[lineNum-2] = RouteEntry{
				Destination: fields[columnMap["Destination"]],
				Gateway:     fields[columnMap["Gateway"]],
				Genmask:     fields[columnMap["Genmask"]],
				Flags:       fields[columnMap["Flags"]],
				Metric:      metric,
				Ref:         ref,
				Use:         use,
				Iface:       fields[columnMap["Iface"]],
			}
		}
	}
	return routes, err
}

// GetIPAddress attempts to find the IP address to the default outbout interface.
func GetIPAddress() (ip string, err error) {
	ip, err = getIPAddrFromHostname()
	if err != nil || strings.HasPrefix(ip, "127") {
		ip, err = getIPAddrFromOutGoingConnection()
		if err == nil && strings.HasPrefix(ip, "127") {
			return "", fmt.Errorf("unable to identify local ip address")
		}
	}

	return ip, err
}

// getIPAddrFromHostname returns the ip address associated with hostname -i.
func getIPAddrFromHostname() (ip string, err error) {
	output, err := exec.Command("hostname", "-i").Output()
	if err != nil {
		return ip, err
	}
	return strings.TrimSpace(string(output)), err
}

// getIPAddrFromOutGoingConnection get the IP bound to the interface which
// handles the default route traffic.
func getIPAddrFromOutGoingConnection() (ip string, err error) {
	addr, err := net.ResolveUDPAddr("udp4", "8.8.8.8:53")
	if err != nil {
		return "", err
	}

	conn, err := net.DialUDP("udp4", nil, addr)
	if err != nil {
		return "", err
	}

	localAddr := conn.LocalAddr()
	parts := strings.Split(localAddr.String(), ":")
	return parts[0], nil
}

// CurrentContextAsHost creates a dao.Host object of the host running
// this method. The passed in poolID is used as the resource pool in the result.
func CurrentContextAsHost(poolID string) (host *dao.Host, err error) {
	cpus := runtime.NumCPU()
	memory, err := getMemorySize()
	if err != nil {
		return nil, err
	}
	host = dao.NewHost()
	hostname, err := os.Hostname()
	if err != nil {
		return nil, err
	}
	host.Name = hostname
	hostidStr, err := HostID()
	if err != nil {
		return nil, err
	}

	host.IpAddr, err = GetIPAddress()
	if err != nil {
		return host, err
	}

	host.Id = hostidStr
	host.Cores = cpus
	host.Memory = memory

	routes, err := routeCmd()
	if err != nil {
		return nil, err
	}
	for _, route := range routes {
		if route.Iface == "docker0" {
			host.PrivateNetwork = route.Destination + "/" + route.Genmask
			break
		}
	}
	host.PoolId = poolID
	return host, err
}

// ExecPath returns the path to the currently running executable.
func ExecPath() (string, string, error) {
	path, err := os.Readlink("/proc/self/exe")
	if err != nil {
		return "", "", err
	}
	return filepath.Dir(path), filepath.Base(path), nil
}

// DockerVersion contains the tuples that describe the version of docker.
type DockerVersion struct {
	Client []int
	Server []int
}

// equals compares two DockerVersion structs and returns true if they are equal.
func (a *DockerVersion) equals(b *DockerVersion) bool {
	if len(a.Client) != len(b.Client) {
		return false
	}
	for i, aI := range a.Client {
		if aI != b.Client[i] {
			return false
		}
	}
	if len(a.Server) != len(b.Server) {
		return false
	}
	for i, aI := range a.Server {
		if aI != b.Server[i] {
			return false
		}
	}
	return true
}

// GetDockerVersion returns docker version number.
func GetDockerVersion() (DockerVersion, error) {
	cmd := exec.Command("docker", "version")
	output, err := cmd.Output()
	if err != nil {
		return DockerVersion{}, err
	}
	return parseDockerVersion(string(output))
}

// parseDockerVersion parses the output of the 'docker version' commmand and
// returns a DockerVersion object.
func parseDockerVersion(output string) (version DockerVersion, err error) {

	for _, line := range strings.Split(output, "\n") {
		parts := strings.SplitN(line, ":", 2)
		if len(parts) < 2 {
			continue
		}
		if strings.HasPrefix(parts[0], "Client version") {
			a := strings.SplitN(strings.TrimSpace(parts[1]), "-", 2)
			b := strings.Split(a[0], ".")
			version.Client = make([]int, len(b))
			for i, v := range b {
				x, err := strconv.Atoi(v)
				if err != nil {
					return version, err
				}
				version.Client[i] = x
			}
		}
		if strings.HasPrefix(parts[0], "Server version") {
			a := strings.SplitN(strings.TrimSpace(parts[1]), "-", 2)
			b := strings.Split(a[0], ".")
			version.Server = make([]int, len(b))
			for i, v := range b {
				x, err := strconv.Atoi(v)
				if err != nil {
					return version, err
				}
				version.Server[i] = x
			}
		}
	}
	if len(version.Client) == 0 {
		return version, fmt.Errorf("no client version found")
	}
	if len(version.Server) == 0 {
		return version, fmt.Errorf("no server version found")
	}
	return version, nil
}

// CreateDirectory creates a directory using the given username as the owner and the
// given perm as the directory permission.
func CreateDirectory(path, username string, perm os.FileMode) error {
	user, err := user.Lookup(username)
	if err == nil {
		err = os.MkdirAll(path, perm)
		if err == nil || err == os.ErrExist {
			uid, _ := strconv.Atoi(user.Uid)
			gid, _ := strconv.Atoi(user.Gid)
			err = os.Chown(path, uid, gid)
		}
	}
	return err
}

// singleJoiningSlash joins a and b ensuring there is only a single /
// character between them.
func singleJoiningSlash(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")
	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}

// NewReverseProxy differs from httputil.NewSingleHostReverseProxy in that it rewrites
// the path so that it does /not/ include the incoming path. e.g. request for
// "/mysvc/thing" when proxy is served from "/mysvc" means target is
// targeturl.Path + "/thing"; vs. httputil.NewSingleHostReverseProxy, in which
// it would be targeturl.Path + "/mysvc/thing".
func NewReverseProxy(path string, targeturl *url.URL) *httputil.ReverseProxy {
	targetQuery := targeturl.RawQuery
	director := func(r *http.Request) {
		r.URL.Scheme = targeturl.Scheme
		r.URL.Host = targeturl.Host
		newpath := strings.TrimPrefix(r.URL.Path, path)
		r.URL.Path = singleJoiningSlash(targeturl.Path, newpath)
		if targetQuery == "" || r.URL.RawQuery == "" {
			r.URL.RawQuery = targetQuery + r.URL.RawQuery
		} else {
			r.URL.RawQuery = targetQuery + "&" + r.URL.RawQuery
		}
	}
	return &httputil.ReverseProxy{Director: director}
}

var dockerRun = func(imageSpec string, args ...string) (output string, err error) {
	targs := []string{"run", "run", "-rm", imageSpec}
	for _, s := range args {
		targs = append(targs, s)
	}
	docker := exec.Command("docker", targs...)
	var outputBytes []byte
	outputBytes, err = docker.CombinedOutput()
	if err != nil {
		return
	}
	output = string(outputBytes)
	return
}

// TODO: refactor createVolumeDir to use this function
func getInternalImageIds(userSpec, imageSpec string) (uid, gid int, err error) {
	var output string
	output, err = dockerRun(imageSpec, "/bin/sh", "-c",
		fmt.Sprintf(`touch test.txt && chown %s test.txt && ls -ln | awk '{ print $3 " " $4 }'`,
			userSpec))
	if err != nil {
		return
	}
	s := strings.TrimSpace(string(output))
	pattern := regexp.MustCompile(`^\d+ \d+$`)

	if !pattern.MatchString(s) {
		err = fmt.Errorf("unexpected output from getInternalImageIds: %s", s)
		return
	}
	fields := strings.Fields(s)
	if len(fields) != 2 {
		err = fmt.Errorf("unexpected number of fields from container spec: %s", fields)
		return
	}
	uid, err = strconv.Atoi(fields[0])
	if err != nil {
		return
	}
	gid, err = strconv.Atoi(fields[1])
	if err != nil {
		return
	}
	return
}

var createVolumeDirMutex sync.Mutex

// createVolumeDir() creates a directory on the running host using the user ids
// found within the specified image. For example, it can create a directory owned
// by the mysql user (as seen by the container) despite there being no mysql user
// on the host system
func createVolumeDir(hostPath, containerSpec, imageSpec, userSpec, permissionSpec string) error {

	createVolumeDirMutex.Lock()
        defer createVolumeDirMutex.Unlock()
	// FIXME: this relies on the underlying container to have /bin/sh that supports
	// some advanced shell options. This should be rewriten so that serviced injects itself in the
	// container and performs the operations using only go!

	var err error
	var output []byte
	for i:= 0; i < 1; i++ {
    	docker := exec.Command("docker", "run", "-rm",
		"-v", hostPath+":/tmp",
		imageSpec,
		"/bin/sh", "-c",
		fmt.Sprintf(`
chown %s /tmp && \
chmod %s /tmp && \
shopt -s nullglob && \
shopt -s dotglob && \
files=(/tmp/*) && \
if [ ${#files[@]} -eq 0 ]; then
	cp -rp %s/* /tmp/
fi
`, userSpec, permissionSpec, containerSpec))
	output, err = docker.CombinedOutput()
	if err == nil {
		return nil
	}
	time.Sleep(time.Second)
	}

		glog.Errorf("could not create host volume: %s", string(output))
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"image/jpeg"
	"io/ioutil"
	"net/http"
	"os"
	"strings"

	"github.com/jinzhu/gorm"
	"github.com/nfnt/resize"
	"github.com/serbe/ncp"
)

var (
	urls = []string{
		"http://nnm-club.me/forum/viewforum.php?f=218",
		"http://nnm-club.me/forum/viewforum.php?f=270",
	}
	commands = []string{
		"get",
		"update",
		"name",
		"rating",
		"poster",
	}
)

// App struct variables
type App struct {
	db  gorm.DB
	net *ncp.NCp
	hd  string
}

type config struct {
	Nnm struct {
		Login    string `json:"login"`
		Password string `json:"password"`
	} `json:"nnmclub"`
	Pq struct {
		User     string `json:"user"`
		Password string `json:"password"`
		Dbname   string `json:"dbname"`
		Sslmode  string `json:"sslmode"`
	} `json:"postgresql"`
	Hd string `json:"httpdir"`
}

func getConfig() (config, error) {
	c := config{}
	file, err := ioutil.ReadFile("./config.json")
	if err != nil {
		return c, err
	}
	err = json.Unmarshal(file, &c)
	return c, err
}

func contain(args []string, str string) bool {
	result := false
	for _, item := range args {
		if item == str {
			result = true
			return result
		}
	}
	return result
}

func containCommand(args []string) bool {
	result := false
	for _, item := range commands {
		if contain(args, item) {
			result = true
			return result
		}
	}
	return result
}

func exit(err error) {
	if err == nil {
		os.Exit(0)
	} else {
		os.Exit(1)
	}
}

func (a *App) checkName(ncf ncp.Film) ncp.Film {
	if ncf.Name != strings.ToUpper(ncf.Name) {
		return ncf
	}
	name, err := a.getMovieName(ncf)
	if err == nil {
		ncf.Name = name
		return ncf
	}
	return ncf
}

func (a *App) getPoster(url string) (string, error) {
	var poster string
	resp, err := http.Get(url)
	if err != nil {
		return poster, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return poster, err
	}
	img, err := jpeg.Decode(bytes.NewReader(body))
	if err != nil {
		return poster, err
	}
	m := resize.Resize(15, 0, img, resize.Lanczos3)
	outName := strings.Replace(url, "/", "", -1)
	outName = strings.Replace(url, ":", "", -1)
	if len(outName) < 20 {
		outName = outName[:len(outName)-4]
	} else {
		outName = outName[len(outName)-20 : len(outName)-4]
	}
    poster = outName + ".jpg"
	out, err := os.Create(a.hd + poster)
	if err != nil {
		return poster, err
	}
	defer out.Close()
	jpeg.Encode(out, m, nil)
	return poster, nil
}
<commit_msg>fix get poster<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"image/jpeg"
	"io/ioutil"
	"net/http"
	"os"
	"strings"

	"github.com/jinzhu/gorm"
	"github.com/nfnt/resize"
	"github.com/serbe/ncp"
)

var (
	urls = []string{
		"http://nnm-club.me/forum/viewforum.php?f=218",
		"http://nnm-club.me/forum/viewforum.php?f=270",
	}
	commands = []string{
		"get",
		"update",
		"name",
		"rating",
		"poster",
	}
)

// App struct variables
type App struct {
	db  gorm.DB
	net *ncp.NCp
	hd  string
}

type config struct {
	Nnm struct {
		Login    string `json:"login"`
		Password string `json:"password"`
	} `json:"nnmclub"`
	Pq struct {
		User     string `json:"user"`
		Password string `json:"password"`
		Dbname   string `json:"dbname"`
		Sslmode  string `json:"sslmode"`
	} `json:"postgresql"`
	Hd string `json:"httpdir"`
}

func getConfig() (config, error) {
	c := config{}
	file, err := ioutil.ReadFile("./config.json")
	if err != nil {
		return c, err
	}
	err = json.Unmarshal(file, &c)
	return c, err
}

func contain(args []string, str string) bool {
	result := false
	for _, item := range args {
		if item == str {
			result = true
			return result
		}
	}
	return result
}

func containCommand(args []string) bool {
	result := false
	for _, item := range commands {
		if contain(args, item) {
			result = true
			return result
		}
	}
	return result
}

func exit(err error) {
	if err == nil {
		os.Exit(0)
	} else {
		os.Exit(1)
	}
}

func (a *App) checkName(ncf ncp.Film) ncp.Film {
	if ncf.Name != strings.ToUpper(ncf.Name) {
		return ncf
	}
	name, err := a.getMovieName(ncf)
	if err == nil {
		ncf.Name = name
		return ncf
	}
	return ncf
}

func (a *App) getPoster(url string) (string, error) {
	var poster string
	resp, err := http.Get(url)
	if err != nil {
		return poster, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return poster, err
	}
	img, err := jpeg.Decode(bytes.NewReader(body))
	if err != nil {
		return poster, err
	}
	m := resize.Resize(150, 0, img, resize.Lanczos3)
	outName := strings.Replace(url, "/", "", -1)
	outName = strings.Replace(outName, ":", "", -1)
	if len(outName) < 20 {
		outName = outName[:len(outName)-4]
	} else {
		outName = outName[len(outName)-20 : len(outName)-4]
	}
    poster = outName + ".jpg"
	out, err := os.Create(a.hd + poster)
	if err != nil {
		return poster, err
	}
	defer out.Close()
	jpeg.Encode(out, m, nil)
	return poster, nil
}
<|endoftext|>


<commit_before>package fs

import (
	"errors"
	"fmt"
	"io"
)

const copyBufferSize = 1024 * 1024 * 4

// CopyFile copies a single file between different file systems.
// If dest has a path that does not exist, then the directories
// up to that path will be created.
// If dest is an existing directory, then a file with the base name
// of src will be created there.
func CopyFile(src, dest File, perm ...Permissions) error {
	var buf []byte
	return CopyFileBuf(src, dest, &buf, perm...)
}

// CopyFileBuf copies a single file between different file systems.
// If dest has a path that does not exist, then the directories
// up to that path will be created.
// If dest is an existing directory, then a file with the base name
// of src will be created there.
// buf must point to a []byte variable.
// If that variable is initialized with a byte slice, then this slice will be used as buffer,
// else a byte slice will be allocated for the variable.
// Use this function to re-use buffers between CopyFileBuf calls.
func CopyFileBuf(src, dest File, buf *[]byte, perm ...Permissions) error {
	if buf == nil {
		panic("CopyFileBuf: buf is nil")
	}

	// Handle directories
	if dest.IsDir() {
		dest = dest.Join(src.Name())
	} else {
		err := dest.Dir().MakeDir()
		if err != nil {
			return err
		}
	}

	// Use same file system copy if possible
	fs := src.FileSystem()
	if fs == dest.FileSystem() {
		return fs.CopyFile(src.Path(), dest.Path(), buf)
	}

	r, err := src.OpenReader()
	if err != nil {
		return err
	}
	defer r.Close()

	if len(perm) == 0 {
		perm = []Permissions{src.Permissions()}
	}
	w, err := dest.OpenWriter(perm...)
	if err != nil {
		return err
	}
	defer w.Close()

	if *buf == nil {
		*buf = make([]byte, copyBufferSize)
	}
	_, err = io.CopyBuffer(w, r, *buf)
	return err
}

// CopyRecursive can copy between files of different file systems.
// The filter patterns are applied on filename level, not the whole path.
func CopyRecursive(src, dest File, patterns ...string) error {
	var buf []byte
	return copyRecursive(src, dest, patterns, &buf)
}

func copyRecursive(src, dest File, patterns []string, buf *[]byte) error {
	if !src.IsDir() {
		// Just copy one file
		return CopyFileBuf(src, dest, buf)
	}

	if dest.Exists() && !dest.IsDir() {
		return fmt.Errorf("Can't copy a directory (%s) over a file (%s)", src.URL(), dest.URL())
	}

	// TODO better check
	if !dest.Exists() {
		err := dest.MakeDir()
		if err != nil {
			return err
		}
	}

	// Copy directories recursive
	return src.ListDir(func(file File) error {
		return copyRecursive(file, dest.Join(file.Name()), patterns, buf)
	}, patterns...)
}

// FilesToURLs returns the URLs of a slice of Files.
func FilesToURLs(files []File) (fileURLs []string) {
	fileURLs = make([]string, len(files))
	for i, file := range files {
		fileURLs[i] = file.URL()
	}
	return fileURLs
}

// FilesToPaths returns the FileSystem specific paths of a slice of Files.
func FilesToPaths(files []File) (paths []string) {
	paths = make([]string, len(files))
	for i, file := range files {
		paths[i] = file.Path()
	}
	return paths
}

// FilesToNames returns a string slice with the names pars from the files
func FilesToNames(files []File) (names []string) {
	names = make([]string, len(files))
	for i, file := range files {
		names[i] = file.Name()
	}
	return names
}

// URIsToFiles returns Files for the given fileURIs.
func URIsToFiles(fileURIs []string) (files []File) {
	files = make([]File, len(fileURIs))
	for i := range fileURIs {
		files[i] = File(fileURIs[i])
	}
	return files
}

type FileCallback func(File) error

func (f FileCallback) FileInfoCallback(file File, info FileInfo) error {
	return f(file)
}

// SameFile returns if a and b describe the same file or directory
func SameFile(a, b File) bool {
	aFS, aPath := a.ParseRawURI()
	bFS, bPath := b.ParseRawURI()
	return aFS == bFS && aPath == bPath
}

// IdenticalDirContents returns true if the files in dirA and dirB are identical in size and content.
// If recursive is true, then directories will be considered too.
func IdenticalDirContents(dirA, dirB File, recursive bool) (identical bool, err error) {
	if SameFile(dirA, dirB) {
		return true, nil
	}

	fileInfosA := make(map[string]FileInfo)
	err = dirA.ListDirInfo(func(file File, info FileInfo) error {
		if !info.IsDir || recursive {
			fileInfosA[info.Name] = info
		}
		return nil
	})
	if err != nil {
		return false, err
	}

	fileInfosB := make(map[string]FileInfo, len(fileInfosA))
	hasDiff := errors.New("hasDiff")
	err = dirB.ListDirInfo(func(file File, info FileInfo) error {
		if !info.IsDir || recursive {
			infoA, found := fileInfosA[info.Name]
			if !found || info.Size != infoA.Size || info.IsDir != infoA.IsDir {
				return hasDiff
			}
			fileInfosB[info.Name] = info
		}
		return nil
	})
	if err == hasDiff || len(fileInfosB) != len(fileInfosA) {
		return false, nil
	}
	if err != nil {
		return false, err
	}

	for filename, infoA := range fileInfosA {
		infoB := fileInfosB[filename]

		if recursive && infoA.IsDir {
			identical, err = IdenticalDirContents(dirA.Join(filename), dirB.Join(filename), true)
			if !identical {
				return false, err
			}
		} else {
			hashA := infoA.ContentHash
			if hashA == "" {
				hashA, err = dirA.Join(filename).ContentHash()
				if err != nil {
					return false, err
				}
			}
			hashB := infoB.ContentHash
			if hashB == "" {
				hashB, err = dirB.Join(filename).ContentHash()
				if err != nil {
					return false, err
				}
			}

			if hashA != hashB {
				return false, nil
			}
		}
	}

	return true, nil
}
<commit_msg>CopyFile with FileReader instead of File as src argument<commit_after>package fs

import (
	"errors"
	"fmt"
	"io"
)

const copyBufferSize = 1024 * 1024 * 4

// CopyFile copies a single file between different file systems.
// If dest has a path that does not exist, then the directories
// up to that path will be created.
// If dest is an existing directory, then a file with the base name
// of src will be created there.
func CopyFile(src FileReader, dest File, perm ...Permissions) error {
	var buf []byte
	return CopyFileBuf(src, dest, &buf, perm...)
}

// CopyFileBuf copies a single file between different file systems.
// If dest has a path that does not exist, then the directories
// up to that path will be created.
// If dest is an existing directory, then a file with the base name
// of src will be created there.
// buf must point to a []byte variable.
// If that variable is initialized with a byte slice, then this slice will be used as buffer,
// else a byte slice will be allocated for the variable.
// Use this function to re-use buffers between CopyFileBuf calls.
func CopyFileBuf(src FileReader, dest File, buf *[]byte, perm ...Permissions) error {
	if buf == nil {
		panic("CopyFileBuf: buf is nil")
	}

	// Handle directories
	if dest.IsDir() {
		dest = dest.Join(src.Name())
	} else {
		err := dest.Dir().MakeDir()
		if err != nil {
			return err
		}
	}

	// Use same file system copy if possible
	srcFile, srcIsFile := src.(File)
	if srcIsFile {
		fs := srcFile.FileSystem()
		if fs == dest.FileSystem() {
			return fs.CopyFile(srcFile.Path(), dest.Path(), buf)
		}
	}

	r, err := src.OpenReader()
	if err != nil {
		return err
	}
	defer r.Close()

	if len(perm) == 0 && srcIsFile {
		perm = []Permissions{srcFile.Permissions()}
	}
	w, err := dest.OpenWriter(perm...)
	if err != nil {
		return err
	}
	defer w.Close()

	if *buf == nil {
		*buf = make([]byte, copyBufferSize)
	}
	_, err = io.CopyBuffer(w, r, *buf)
	return err
}

// CopyRecursive can copy between files of different file systems.
// The filter patterns are applied on filename level, not the whole path.
func CopyRecursive(src, dest File, patterns ...string) error {
	var buf []byte
	return copyRecursive(src, dest, patterns, &buf)
}

func copyRecursive(src, dest File, patterns []string, buf *[]byte) error {
	if !src.IsDir() {
		// Just copy one file
		return CopyFileBuf(src, dest, buf)
	}

	if dest.Exists() && !dest.IsDir() {
		return fmt.Errorf("Can't copy a directory (%s) over a file (%s)", src.URL(), dest.URL())
	}

	// TODO better check
	if !dest.Exists() {
		err := dest.MakeDir()
		if err != nil {
			return err
		}
	}

	// Copy directories recursive
	return src.ListDir(func(file File) error {
		return copyRecursive(file, dest.Join(file.Name()), patterns, buf)
	}, patterns...)
}

// FilesToURLs returns the URLs of a slice of Files.
func FilesToURLs(files []File) (fileURLs []string) {
	fileURLs = make([]string, len(files))
	for i, file := range files {
		fileURLs[i] = file.URL()
	}
	return fileURLs
}

// FilesToPaths returns the FileSystem specific paths of a slice of Files.
func FilesToPaths(files []File) (paths []string) {
	paths = make([]string, len(files))
	for i, file := range files {
		paths[i] = file.Path()
	}
	return paths
}

// FilesToNames returns a string slice with the names pars from the files
func FilesToNames(files []File) (names []string) {
	names = make([]string, len(files))
	for i, file := range files {
		names[i] = file.Name()
	}
	return names
}

// FilesToFileReaders converts a slice of File to a slice of FileReader
func FilesToFileReaders(files []File) (fileReaders []FileReader) {
	fileReaders = make([]FileReader, len(files))
	for i, file := range files {
		fileReaders[i] = file
	}
	return fileReaders
}

// URIsToFiles returns Files for the given fileURIs.
func URIsToFiles(fileURIs []string) (files []File) {
	files = make([]File, len(fileURIs))
	for i := range fileURIs {
		files[i] = File(fileURIs[i])
	}
	return files
}

type FileCallback func(File) error

func (f FileCallback) FileInfoCallback(file File, info FileInfo) error {
	return f(file)
}

// SameFile returns if a and b describe the same file or directory
func SameFile(a, b File) bool {
	aFS, aPath := a.ParseRawURI()
	bFS, bPath := b.ParseRawURI()
	return aFS == bFS && aPath == bPath
}

// IdenticalDirContents returns true if the files in dirA and dirB are identical in size and content.
// If recursive is true, then directories will be considered too.
func IdenticalDirContents(dirA, dirB File, recursive bool) (identical bool, err error) {
	if SameFile(dirA, dirB) {
		return true, nil
	}

	fileInfosA := make(map[string]FileInfo)
	err = dirA.ListDirInfo(func(file File, info FileInfo) error {
		if !info.IsDir || recursive {
			fileInfosA[info.Name] = info
		}
		return nil
	})
	if err != nil {
		return false, err
	}

	fileInfosB := make(map[string]FileInfo, len(fileInfosA))
	hasDiff := errors.New("hasDiff")
	err = dirB.ListDirInfo(func(file File, info FileInfo) error {
		if !info.IsDir || recursive {
			infoA, found := fileInfosA[info.Name]
			if !found || info.Size != infoA.Size || info.IsDir != infoA.IsDir {
				return hasDiff
			}
			fileInfosB[info.Name] = info
		}
		return nil
	})
	if err == hasDiff || len(fileInfosB) != len(fileInfosA) {
		return false, nil
	}
	if err != nil {
		return false, err
	}

	for filename, infoA := range fileInfosA {
		infoB := fileInfosB[filename]

		if recursive && infoA.IsDir {
			identical, err = IdenticalDirContents(dirA.Join(filename), dirB.Join(filename), true)
			if !identical {
				return false, err
			}
		} else {
			hashA := infoA.ContentHash
			if hashA == "" {
				hashA, err = dirA.Join(filename).ContentHash()
				if err != nil {
					return false, err
				}
			}
			hashB := infoB.ContentHash
			if hashB == "" {
				hashB, err = dirB.Join(filename).ContentHash()
				if err != nil {
					return false, err
				}
			}

			if hashA != hashB {
				return false, nil
			}
		}
	}

	return true, nil
}
<|endoftext|>


<commit_before>package go_utils

// Iif_string is and immediate if helper that takes a boolean expression
// and returns a string, true_val if the expression is true else false_val
func Iif_string(expr bool, true_val string, false_val string) string {
	return map[bool]string{true: true_val, false: false_val}[expr]
}
<commit_msg>add Join, Substr, padRight, padLeft<commit_after>package go_utils

import (
	"fmt"
	"strings"
	"reflect"q
)

// Iif_string is and immediate if helper that takes a boolean expression
// and returns a string, true_val if the expression is true else false_val
func Iif_string(expr bool, true_val string, false_val string) string {
	return map[bool]string{true: true_val, false: false_val}[expr]
}

// Concat joins the strings in a slice, delimiting them with a comma, but it
// allows you to pass the delimiter string to create a single string
// Ex:  data: {[A B C]}; Join(data) ==> "A,B,C" ; Join(data, "|") ==> "A|B|C"
func Join(slice []string, args ...interface{}) string {
	delimiter := ","
	for _, arg := range args {
		switch t := arg.(type) {
		case string:
			delimiter = t
		default:
			panic(fmt.Sprintf("ERROR - Invalid argument (%v).  Must be a string.", arg))
		}
	}
	ret := ""
	for i, s := range slice {
		// append delimiter except at the very end
		ret += s + Iif_string((i < len(slice) - 1), delimiter, "")
	}
	return ret
}

// Substr returns a portion (length characters) of string (s), beginning at a specified position (pos)
func Substr(s string, pos, length int) string{
	runes:=[]rune(s)
	l := pos+length
	if l > len(runes) {
		l = len(runes)
	}
	return string(runes[pos:l])
}

// padRight pads a string (s) with with a specified string (optional parameter) for padLen characters
// If no string argument is passed, then s will be padded, to the right, with a single space character
func padRight(s string, padLen int, args ...interface{}) string{
	padStr := " "
	for _, arg := range args {
		switch t := arg.(type) {
		case string:
			padStr = t
		default:
			panic("Unknown argument")
		}
	}
	return s + strings.Repeat(padStr, padLen);
}

// padLeft pads a string (s) with with a specified string (optional parameter) for padLen characters
// If no string argument is passed, then s will be padded, to the left, with a single space character
func padLeft(s string, padLen int, args ...interface{}) string{
	padStr := " "
	for _, arg := range args {
		switch t := arg.(type) {
		case string:
			padStr = t
		default:
			panic("Unknown argument")
		}
	}
	return strings.Repeat(padStr, padLen) + s;
}

// TypeOf returns the reflection Type of the value in the interface{}.
// TypeOf(nil) returns nil.
func TypeOf(i interface{}) string {
	return fmt.Sprintf("%v", reflect.TypeOf(i))
}
<|endoftext|>