gofmt peernet (#103)

This commit is contained in:
Akilan Selvacoumar
2023-02-12 09:59:31 +00:00
committed by GitHub
parent 3a1eece580
commit 9775525d35
95 changed files with 15798 additions and 15794 deletions

View File

@@ -125,27 +125,30 @@ var (
// the arithmetic needed for elliptic curve operations. // the arithmetic needed for elliptic curve operations.
// //
// The following depicts the internal representation: // The following depicts the internal representation:
// ----------------------------------------------------------------- //
// | n[9] | n[8] | ... | n[0] | // -----------------------------------------------------------------
// | 32 bits available | 32 bits available | ... | 32 bits available | // | n[9] | n[8] | ... | n[0] |
// | 22 bits for value | 26 bits for value | ... | 26 bits for value | // | 32 bits available | 32 bits available | ... | 32 bits available |
// | 10 bits overflow | 6 bits overflow | ... | 6 bits overflow | // | 22 bits for value | 26 bits for value | ... | 26 bits for value |
// | Mult: 2^(26*9) | Mult: 2^(26*8) | ... | Mult: 2^(26*0) | // | 10 bits overflow | 6 bits overflow | ... | 6 bits overflow |
// ----------------------------------------------------------------- // | Mult: 2^(26*9) | Mult: 2^(26*8) | ... | Mult: 2^(26*0) |
// -----------------------------------------------------------------
// //
// For example, consider the number 2^49 + 1. It would be represented as: // For example, consider the number 2^49 + 1. It would be represented as:
// n[0] = 1 //
// n[1] = 2^23 // n[0] = 1
// n[2..9] = 0 // n[1] = 2^23
// n[2..9] = 0
// //
// The full 256-bit value is then calculated by looping i from 9..0 and // The full 256-bit value is then calculated by looping i from 9..0 and
// doing sum(n[i] * 2^(26i)) like so: // doing sum(n[i] * 2^(26i)) like so:
// n[9] * 2^(26*9) = 0 * 2^234 = 0 //
// n[8] * 2^(26*8) = 0 * 2^208 = 0 // n[9] * 2^(26*9) = 0 * 2^234 = 0
// ... // n[8] * 2^(26*8) = 0 * 2^208 = 0
// n[1] * 2^(26*1) = 2^23 * 2^26 = 2^49 // ...
// n[0] * 2^(26*0) = 1 * 2^0 = 1 // n[1] * 2^(26*1) = 2^23 * 2^26 = 2^49
// Sum: 0 + 0 + ... + 2^49 + 1 = 2^49 + 1 // n[0] * 2^(26*0) = 1 * 2^0 = 1
// Sum: 0 + 0 + ... + 2^49 + 1 = 2^49 + 1
type fieldVal struct { type fieldVal struct {
n [10]uint32 n [10]uint32
} }

View File

@@ -4,6 +4,7 @@
// This file is ignored during the regular build due to the following build tag. // This file is ignored during the regular build due to the following build tag.
// This build tag is set during go generate. // This build tag is set during go generate.
//go:build gensecp256k1
// +build gensecp256k1 // +build gensecp256k1
package btcec package btcec

View File

@@ -306,7 +306,7 @@ func (ht *hashTable) getTotalNodesPerBucket() (total []int) {
ht.mutex.RLock() ht.mutex.RLock()
defer ht.mutex.RUnlock() defer ht.mutex.RUnlock()
for n, _ := range ht.RoutingTable { for n := range ht.RoutingTable {
total = append(total, len(ht.RoutingTable[n])) total = append(total, len(ht.RoutingTable[n]))
} }

View File

@@ -4,14 +4,14 @@
// //
// For example: // For example:
// //
// // listen on the same port. oh yeah. // // listen on the same port. oh yeah.
// l1, _ := reuse.Listen("tcp", "127.0.0.1:1234") // l1, _ := reuse.Listen("tcp", "127.0.0.1:1234")
// l2, _ := reuse.Listen("tcp", "127.0.0.1:1234") // l2, _ := reuse.Listen("tcp", "127.0.0.1:1234")
// //
// // dial from the same port. oh yeah. // // dial from the same port. oh yeah.
// l1, _ := reuse.Listen("tcp", "127.0.0.1:1234") // l1, _ := reuse.Listen("tcp", "127.0.0.1:1234")
// l2, _ := reuse.Listen("tcp", "127.0.0.1:1235") // l2, _ := reuse.Listen("tcp", "127.0.0.1:1235")
// c, _ := reuse.Dial("tcp", "127.0.0.1:1234", "127.0.0.1:1235") // c, _ := reuse.Dial("tcp", "127.0.0.1:1234", "127.0.0.1:1235")
// //
// Note: cant dial self because tcp/ip stacks use 4-tuples to identify connections, // Note: cant dial self because tcp/ip stacks use 4-tuples to identify connections,
// and doing so would clash. // and doing so would clash.

View File

@@ -7,192 +7,192 @@ Author: Peter Kleissner
package webapi package webapi
import ( import (
"crypto/tls" "crypto/tls"
"encoding/json" "encoding/json"
"errors" "errors"
"net/http" "net/http"
"sync" "sync"
"time" "time"
"github.com/IncSW/geoip2" "github.com/IncSW/geoip2"
"github.com/PeernetOfficial/core" "github.com/PeernetOfficial/core"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
) )
type WebapiInstance struct { type WebapiInstance struct {
backend *core.Backend backend *core.Backend
geoipCityReader *geoip2.CityReader geoipCityReader *geoip2.CityReader
// Router can be used to register additional API functions // Router can be used to register additional API functions
Router *mux.Router Router *mux.Router
AllowKeyInParam []string // List of paths that accept the API key as &k= parameter AllowKeyInParam []string // List of paths that accept the API key as &k= parameter
// search jobs // search jobs
allJobs map[uuid.UUID]*SearchJob allJobs map[uuid.UUID]*SearchJob
allJobsMutex sync.RWMutex allJobsMutex sync.RWMutex
// download info // download info
downloads map[uuid.UUID]*downloadInfo downloads map[uuid.UUID]*downloadInfo
downloadsMutex sync.RWMutex downloadsMutex sync.RWMutex
} }
// WSUpgrader is used for websocket functionality. It allows all requests. // WSUpgrader is used for websocket functionality. It allows all requests.
var WSUpgrader = websocket.Upgrader{ var WSUpgrader = websocket.Upgrader{
ReadBufferSize: 1024, ReadBufferSize: 1024,
WriteBufferSize: 1024, WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool { CheckOrigin: func(r *http.Request) bool {
// allow all connections by default // allow all connections by default
return true return true
}, },
} }
// Start starts the API. ListenAddresses is a list of IP:Ports. // Start starts the API. ListenAddresses is a list of IP:Ports.
// The certificate file and key are only used if SSL is enabled. The read and write timeout may be 0 for no timeout. // The certificate file and key are only used if SSL is enabled. The read and write timeout may be 0 for no timeout.
// The API key may be uuid.Nil to disable it although this is not recommended for security reasons. // The API key may be uuid.Nil to disable it although this is not recommended for security reasons.
func Start(Backend *core.Backend, ListenAddresses []string, UseSSL bool, CertificateFile, CertificateKey string, TimeoutRead, TimeoutWrite time.Duration, APIKey uuid.UUID) (api *WebapiInstance) { func Start(Backend *core.Backend, ListenAddresses []string, UseSSL bool, CertificateFile, CertificateKey string, TimeoutRead, TimeoutWrite time.Duration, APIKey uuid.UUID) (api *WebapiInstance) {
if len(ListenAddresses) == 0 { if len(ListenAddresses) == 0 {
return nil return nil
} }
api = &WebapiInstance{ api = &WebapiInstance{
backend: Backend, backend: Backend,
Router: mux.NewRouter(), Router: mux.NewRouter(),
AllowKeyInParam: []string{"/file/read", "/file/view"}, AllowKeyInParam: []string{"/file/read", "/file/view"},
allJobs: make(map[uuid.UUID]*SearchJob), allJobs: make(map[uuid.UUID]*SearchJob),
downloads: make(map[uuid.UUID]*downloadInfo), downloads: make(map[uuid.UUID]*downloadInfo),
} }
if APIKey != uuid.Nil { if APIKey != uuid.Nil {
api.Router.Use(api.authenticateMiddleware(APIKey)) api.Router.Use(api.authenticateMiddleware(APIKey))
} }
api.Router.HandleFunc("/test", apiTest).Methods("GET") api.Router.HandleFunc("/test", apiTest).Methods("GET")
api.Router.HandleFunc("/status", api.apiStatus).Methods("GET") api.Router.HandleFunc("/status", api.apiStatus).Methods("GET")
api.Router.HandleFunc("/status/peers", api.apiStatusPeers).Methods("GET") api.Router.HandleFunc("/status/peers", api.apiStatusPeers).Methods("GET")
api.Router.HandleFunc("/account/info", api.apiAccountInfo).Methods("GET") api.Router.HandleFunc("/account/info", api.apiAccountInfo).Methods("GET")
api.Router.HandleFunc("/account/delete", api.apiAccountDelete).Methods("GET") api.Router.HandleFunc("/account/delete", api.apiAccountDelete).Methods("GET")
api.Router.HandleFunc("/blockchain/header", api.apiBlockchainHeaderFunc).Methods("GET") api.Router.HandleFunc("/blockchain/header", api.apiBlockchainHeaderFunc).Methods("GET")
api.Router.HandleFunc("/blockchain/append", api.apiBlockchainAppend).Methods("POST") api.Router.HandleFunc("/blockchain/append", api.apiBlockchainAppend).Methods("POST")
api.Router.HandleFunc("/blockchain/read", api.apiBlockchainRead).Methods("GET") api.Router.HandleFunc("/blockchain/read", api.apiBlockchainRead).Methods("GET")
api.Router.HandleFunc("/blockchain/file/add", api.apiBlockchainFileAdd).Methods("POST") api.Router.HandleFunc("/blockchain/file/add", api.apiBlockchainFileAdd).Methods("POST")
api.Router.HandleFunc("/blockchain/file/list", api.apiBlockchainFileList).Methods("GET") api.Router.HandleFunc("/blockchain/file/list", api.apiBlockchainFileList).Methods("GET")
api.Router.HandleFunc("/blockchain/file/delete", api.apiBlockchainFileDelete).Methods("POST") api.Router.HandleFunc("/blockchain/file/delete", api.apiBlockchainFileDelete).Methods("POST")
api.Router.HandleFunc("/blockchain/file/update", api.apiBlockchainFileUpdate).Methods("POST") api.Router.HandleFunc("/blockchain/file/update", api.apiBlockchainFileUpdate).Methods("POST")
api.Router.HandleFunc("/profile/list", api.apiProfileList).Methods("GET") api.Router.HandleFunc("/profile/list", api.apiProfileList).Methods("GET")
api.Router.HandleFunc("/profile/read", api.apiProfileRead).Methods("GET") api.Router.HandleFunc("/profile/read", api.apiProfileRead).Methods("GET")
api.Router.HandleFunc("/profile/write", api.apiProfileWrite).Methods("POST") api.Router.HandleFunc("/profile/write", api.apiProfileWrite).Methods("POST")
api.Router.HandleFunc("/profile/delete", api.apiProfileDelete).Methods("POST") api.Router.HandleFunc("/profile/delete", api.apiProfileDelete).Methods("POST")
api.Router.HandleFunc("/search", api.apiSearch).Methods("POST") api.Router.HandleFunc("/search", api.apiSearch).Methods("POST")
api.Router.HandleFunc("/search/result", api.apiSearchResult).Methods("GET") api.Router.HandleFunc("/search/result", api.apiSearchResult).Methods("GET")
api.Router.HandleFunc("/search/result/ws", api.apiSearchResultStream).Methods("GET") api.Router.HandleFunc("/search/result/ws", api.apiSearchResultStream).Methods("GET")
api.Router.HandleFunc("/search/statistic", api.apiSearchStatistic).Methods("GET") api.Router.HandleFunc("/search/statistic", api.apiSearchStatistic).Methods("GET")
api.Router.HandleFunc("/search/terminate", api.apiSearchTerminate).Methods("GET") api.Router.HandleFunc("/search/terminate", api.apiSearchTerminate).Methods("GET")
api.Router.HandleFunc("/explore", api.apiExplore).Methods("GET") api.Router.HandleFunc("/explore", api.apiExplore).Methods("GET")
api.Router.HandleFunc("/explore/node", api.apiExploreNodeID).Methods("GET") api.Router.HandleFunc("/explore/node", api.apiExploreNodeID).Methods("GET")
api.Router.HandleFunc("/file/format", api.apiFileFormat).Methods("GET") api.Router.HandleFunc("/file/format", api.apiFileFormat).Methods("GET")
api.Router.HandleFunc("/download/start", api.apiDownloadStart).Methods("GET") api.Router.HandleFunc("/download/start", api.apiDownloadStart).Methods("GET")
api.Router.HandleFunc("/download/status", api.apiDownloadStatus).Methods("GET") api.Router.HandleFunc("/download/status", api.apiDownloadStatus).Methods("GET")
api.Router.HandleFunc("/download/action", api.apiDownloadAction).Methods("GET") api.Router.HandleFunc("/download/action", api.apiDownloadAction).Methods("GET")
api.Router.HandleFunc("/warehouse/create", api.apiWarehouseCreateFile).Methods("POST") api.Router.HandleFunc("/warehouse/create", api.apiWarehouseCreateFile).Methods("POST")
api.Router.HandleFunc("/warehouse/create/path", api.apiWarehouseCreateFilePath).Methods("GET") api.Router.HandleFunc("/warehouse/create/path", api.apiWarehouseCreateFilePath).Methods("GET")
api.Router.HandleFunc("/warehouse/read", api.apiWarehouseReadFile).Methods("GET") api.Router.HandleFunc("/warehouse/read", api.apiWarehouseReadFile).Methods("GET")
api.Router.HandleFunc("/warehouse/read/path", api.apiWarehouseReadFilePath).Methods("GET") api.Router.HandleFunc("/warehouse/read/path", api.apiWarehouseReadFilePath).Methods("GET")
api.Router.HandleFunc("/warehouse/delete", api.apiWarehouseDeleteFile).Methods("GET") api.Router.HandleFunc("/warehouse/delete", api.apiWarehouseDeleteFile).Methods("GET")
api.Router.HandleFunc("/file/read", api.apiFileRead).Methods("GET") api.Router.HandleFunc("/file/read", api.apiFileRead).Methods("GET")
api.Router.HandleFunc("/file/view", api.apiFileView).Methods("GET") api.Router.HandleFunc("/file/view", api.apiFileView).Methods("GET")
for _, listen := range ListenAddresses { for _, listen := range ListenAddresses {
go startWebAPI(Backend, listen, UseSSL, CertificateFile, CertificateKey, api.Router, "API", TimeoutRead, TimeoutWrite) go startWebAPI(Backend, listen, UseSSL, CertificateFile, CertificateKey, api.Router, "API", TimeoutRead, TimeoutWrite)
} }
return api return api
} }
// startWebAPI starts a web-server with given parameters and logs the status. If may block forever and only returns if there is an error. // startWebAPI starts a web-server with given parameters and logs the status. If may block forever and only returns if there is an error.
// The certificate file and key are only used if SSL is enabled. The read and write timeout may be 0 for no timeout. // The certificate file and key are only used if SSL is enabled. The read and write timeout may be 0 for no timeout.
func startWebAPI(Backend *core.Backend, WebListen string, UseSSL bool, CertificateFile, CertificateKey string, Handler http.Handler, Info string, ReadTimeout, WriteTimeout time.Duration) { func startWebAPI(Backend *core.Backend, WebListen string, UseSSL bool, CertificateFile, CertificateKey string, Handler http.Handler, Info string, ReadTimeout, WriteTimeout time.Duration) {
Backend.LogError("startWebAPI", "Start API at '%s'\n", WebListen) Backend.LogError("startWebAPI", "Start API at '%s'\n", WebListen)
tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12} // for security reasons disable TLS 1.0/1.1 tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12} // for security reasons disable TLS 1.0/1.1
server := &http.Server{ server := &http.Server{
Addr: WebListen, Addr: WebListen,
Handler: Handler, Handler: Handler,
ReadTimeout: ReadTimeout, // ReadTimeout is the maximum duration for reading the entire request, including the body. ReadTimeout: ReadTimeout, // ReadTimeout is the maximum duration for reading the entire request, including the body.
WriteTimeout: WriteTimeout, // WriteTimeout is the maximum duration before timing out writes of the response. This includes processing time and is therefore the max time any HTTP function may take. WriteTimeout: WriteTimeout, // WriteTimeout is the maximum duration before timing out writes of the response. This includes processing time and is therefore the max time any HTTP function may take.
//IdleTimeout: IdleTimeout, // IdleTimeout is the maximum amount of time to wait for the next request when keep-alives are enabled. //IdleTimeout: IdleTimeout, // IdleTimeout is the maximum amount of time to wait for the next request when keep-alives are enabled.
TLSConfig: tlsConfig, TLSConfig: tlsConfig,
} }
if UseSSL { if UseSSL {
// HTTPS // HTTPS
if err := server.ListenAndServeTLS(CertificateFile, CertificateKey); err != nil { if err := server.ListenAndServeTLS(CertificateFile, CertificateKey); err != nil {
Backend.LogError("startWebAPI", "Error listening on '%s': %v\n", WebListen, err) Backend.LogError("startWebAPI", "Error listening on '%s': %v\n", WebListen, err)
} }
} else { } else {
// HTTP // HTTP
if err := server.ListenAndServe(); err != nil { if err := server.ListenAndServe(); err != nil {
Backend.LogError("startWebAPI", "Error listening on '%s': %v\n", WebListen, err) Backend.LogError("startWebAPI", "Error listening on '%s': %v\n", WebListen, err)
} }
} }
} }
// EncodeJSON encodes the data as JSON // EncodeJSON encodes the data as JSON
func EncodeJSON(Backend *core.Backend, w http.ResponseWriter, r *http.Request, data interface{}) (err error) { func EncodeJSON(Backend *core.Backend, w http.ResponseWriter, r *http.Request, data interface{}) (err error) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(data) err = json.NewEncoder(w).Encode(data)
if err != nil { if err != nil {
Backend.LogError("EncodeJSON", "Error writing data for route '%s': %v\n", r.URL.Path, err) Backend.LogError("EncodeJSON", "Error writing data for route '%s': %v\n", r.URL.Path, err)
} }
return err return err
} }
// DecodeJSON decodes input JSON data server side sent either via GET or POST. It does not limit the maximum amount to read. // DecodeJSON decodes input JSON data server side sent either via GET or POST. It does not limit the maximum amount to read.
// In case of error it will automatically send an error to the client. // In case of error it will automatically send an error to the client.
func DecodeJSON(w http.ResponseWriter, r *http.Request, data interface{}) (err error) { func DecodeJSON(w http.ResponseWriter, r *http.Request, data interface{}) (err error) {
if r.Body == nil { if r.Body == nil {
http.Error(w, "", http.StatusBadRequest) http.Error(w, "", http.StatusBadRequest)
return errors.New("no data") return errors.New("no data")
} }
err = json.NewDecoder(r.Body).Decode(data) err = json.NewDecoder(r.Body).Decode(data)
if err != nil { if err != nil {
http.Error(w, "", http.StatusBadRequest) http.Error(w, "", http.StatusBadRequest)
return err return err
} }
return nil return nil
} }
// authenticateMiddleware returns a middleware function to be used with mux.Router.Use(). It handles all authentication functionality. // authenticateMiddleware returns a middleware function to be used with mux.Router.Use(). It handles all authentication functionality.
func (api *WebapiInstance) authenticateMiddleware(APIKey uuid.UUID) func(http.Handler) http.Handler { func (api *WebapiInstance) authenticateMiddleware(APIKey uuid.UUID) func(http.Handler) http.Handler {
return (func(next http.Handler) http.Handler { return (func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
keyID, err := uuid.Parse(r.Header.Get("x-api-key")) keyID, err := uuid.Parse(r.Header.Get("x-api-key"))
if err != nil { // special case for some paths if err != nil { // special case for some paths
for _, exceptPath := range api.AllowKeyInParam { for _, exceptPath := range api.AllowKeyInParam {
if exceptPath == r.URL.Path { if exceptPath == r.URL.Path {
r.ParseForm() r.ParseForm()
keyID, err = uuid.Parse(r.Form.Get("k")) keyID, err = uuid.Parse(r.Form.Get("k"))
break break
} }
} }
} }
if err != nil { // Invalid key format if err != nil { // Invalid key format
w.WriteHeader(http.StatusUnauthorized) w.WriteHeader(http.StatusUnauthorized)
return return
} }
if keyID != APIKey { if keyID != APIKey {
w.WriteHeader(http.StatusUnauthorized) w.WriteHeader(http.StatusUnauthorized)
return return
} }
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
}) })
}) })
} }

View File

@@ -1,13 +1,13 @@
package webapi package webapi
import ( import (
"fmt" "fmt"
"testing" "testing"
) )
// Test function // Test function
func TestDecodeBlake3Hash(t *testing.T) { func TestDecodeBlake3Hash(t *testing.T) {
hash, bool := DecodeBlake3Hash("") hash, bool := DecodeBlake3Hash("")
fmt.Println(hash) fmt.Println(hash)
fmt.Println(bool) fmt.Println(bool)
} }

View File

@@ -16,61 +16,61 @@ Author: Peter Kleissner
package webapi package webapi
import ( import (
"net/http" "net/http"
"strconv" "strconv"
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
) )
// SearchRequest is the information from the end-user for the search. Filters and sort order may be applied when starting the search, or at runtime when getting the results. // SearchRequest is the information from the end-user for the search. Filters and sort order may be applied when starting the search, or at runtime when getting the results.
type SearchRequest struct { type SearchRequest struct {
Term string `json:"term"` // Search term. Term string `json:"term"` // Search term.
Timeout int `json:"timeout"` // Timeout in seconds. 0 means default. This is the entire time the search may take. Found results are still available after this timeout. Timeout int `json:"timeout"` // Timeout in seconds. 0 means default. This is the entire time the search may take. Found results are still available after this timeout.
MaxResults int `json:"maxresults"` // Total number of max results. 0 means default. MaxResults int `json:"maxresults"` // Total number of max results. 0 means default.
DateFrom string `json:"datefrom"` // Date from, both from/to are required if set. Format "2006-01-02 15:04:05". DateFrom string `json:"datefrom"` // Date from, both from/to are required if set. Format "2006-01-02 15:04:05".
DateTo string `json:"dateto"` // Date to, both from/to are required if set. Format "2006-01-02 15:04:05". DateTo string `json:"dateto"` // Date to, both from/to are required if set. Format "2006-01-02 15:04:05".
Sort int `json:"sort"` // See SortX. Sort int `json:"sort"` // See SortX.
TerminateID []uuid.UUID `json:"terminate"` // Optional: Previous search IDs to terminate. This is if the user makes a new search from the same tab. Same as first calling /search/terminate. TerminateID []uuid.UUID `json:"terminate"` // Optional: Previous search IDs to terminate. This is if the user makes a new search from the same tab. Same as first calling /search/terminate.
FileType int `json:"filetype"` // File type such as binary, text document etc. See core.TypeX. -1 = not used. FileType int `json:"filetype"` // File type such as binary, text document etc. See core.TypeX. -1 = not used.
FileFormat int `json:"fileformat"` // File format such as PDF, Word, Ebook, etc. See core.FormatX. -1 = not used. FileFormat int `json:"fileformat"` // File format such as PDF, Word, Ebook, etc. See core.FormatX. -1 = not used.
SizeMin int `json:"sizemin"` // Min file size in bytes. -1 = not used. SizeMin int `json:"sizemin"` // Min file size in bytes. -1 = not used.
SizeMax int `json:"sizemax"` // Max file size in bytes. -1 = not used. SizeMax int `json:"sizemax"` // Max file size in bytes. -1 = not used.
} }
// Sort orders // Sort orders
const ( const (
SortNone = 0 // No sorting. Results are returned as they come in. SortNone = 0 // No sorting. Results are returned as they come in.
SortRelevanceAsc = 1 // Least relevant results first. SortRelevanceAsc = 1 // Least relevant results first.
SortRelevanceDec = 2 // Most relevant results first. SortRelevanceDec = 2 // Most relevant results first.
SortDateAsc = 3 // Oldest first. SortDateAsc = 3 // Oldest first.
SortDateDesc = 4 // Newest first. SortDateDesc = 4 // Newest first.
SortNameAsc = 5 // File name ascending. The folder name is not used for sorting. SortNameAsc = 5 // File name ascending. The folder name is not used for sorting.
SortNameDesc = 6 // File name descending. The folder name is not used for sorting. SortNameDesc = 6 // File name descending. The folder name is not used for sorting.
SortSizeAsc = 7 // File size ascending. Smallest files first. SortSizeAsc = 7 // File size ascending. Smallest files first.
SortSizeDesc = 8 // File size descending. Largest files first. SortSizeDesc = 8 // File size descending. Largest files first.
SortSharedByCountAsc = 9 // Shared by count ascending. Files that are shared by the least count of peers first. SortSharedByCountAsc = 9 // Shared by count ascending. Files that are shared by the least count of peers first.
SortSharedByCountDesc = 10 // Shared by count descending. Files that are shared by the most count of peers first. SortSharedByCountDesc = 10 // Shared by count descending. Files that are shared by the most count of peers first.
) )
// SearchRequestResponse is the result to the initial search request // SearchRequestResponse is the result to the initial search request
type SearchRequestResponse struct { type SearchRequestResponse struct {
ID uuid.UUID `json:"id"` // ID of the search job. This is used to get the results. ID uuid.UUID `json:"id"` // ID of the search job. This is used to get the results.
Status int `json:"status"` // Status of the search: 0 = Success (ID valid), 1 = Invalid Term, 2 = Error Max Concurrent Searches Status int `json:"status"` // Status of the search: 0 = Success (ID valid), 1 = Invalid Term, 2 = Error Max Concurrent Searches
} }
// SearchResult contains the search results. // SearchResult contains the search results.
type SearchResult struct { type SearchResult struct {
Status int `json:"status"` // Status: 0 = Success with results, 1 = No more results available, 2 = Search ID not found, 3 = No results yet available keep trying Status int `json:"status"` // Status: 0 = Success with results, 1 = No more results available, 2 = Search ID not found, 3 = No results yet available keep trying
Files []apiFile `json:"files"` // List of files found Files []apiFile `json:"files"` // List of files found
Statistic interface{} `json:"statistic"` // Statistics of all results (independent from applied filters), if requested. Only set if files are returned (= if statistics changed). See SearchStatisticData. Statistic interface{} `json:"statistic"` // Statistics of all results (independent from applied filters), if requested. Only set if files are returned (= if statistics changed). See SearchStatisticData.
} }
// SearchStatistic contains statistics on search results. Statistics are always calculated over all results, regardless of any applied runtime filters. // SearchStatistic contains statistics on search results. Statistics are always calculated over all results, regardless of any applied runtime filters.
type SearchStatistic struct { type SearchStatistic struct {
SearchStatisticData SearchStatisticData
Status int `json:"status"` // Status: 0 = Success Status int `json:"status"` // Status: 0 = Success
IsTerminated bool `json:"terminated"` // Whether the search is terminated, meaning that statistics won't change IsTerminated bool `json:"terminated"` // Whether the search is terminated, meaning that statistics won't change
} }
const apiDateFormat = "2006-01-02 15:04:05" const apiDateFormat = "2006-01-02 15:04:05"
@@ -84,29 +84,29 @@ Result: 200 on success with JSON SearchRequestResponse
400 on invalid JSON 400 on invalid JSON
*/ */
func (api *WebapiInstance) apiSearch(w http.ResponseWriter, r *http.Request) { func (api *WebapiInstance) apiSearch(w http.ResponseWriter, r *http.Request) {
var input SearchRequest var input SearchRequest
if err := DecodeJSON(w, r, &input); err != nil { if err := DecodeJSON(w, r, &input); err != nil {
return return
} }
if input.Timeout <= 0 { if input.Timeout <= 0 {
input.Timeout = 20 input.Timeout = 20
} }
if input.MaxResults <= 0 { if input.MaxResults <= 0 {
input.MaxResults = 200 input.MaxResults = 200
} }
// Terminate previous searches, if their IDs were supplied. This allows terminating the old search immediately without making a separate /search/terminate request. // Terminate previous searches, if their IDs were supplied. This allows terminating the old search immediately without making a separate /search/terminate request.
for _, terminate := range input.TerminateID { for _, terminate := range input.TerminateID {
if job := api.JobLookup(terminate); job != nil { if job := api.JobLookup(terminate); job != nil {
job.Terminate() job.Terminate()
api.RemoveJob(job) api.RemoveJob(job)
} }
} }
job := api.dispatchSearch(input) job := api.dispatchSearch(input)
EncodeJSON(api.backend, w, r, SearchRequestResponse{Status: 0, ID: job.id}) EncodeJSON(api.backend, w, r, SearchRequestResponse{Status: 0, ID: job.id})
} }
/* /*
@@ -128,82 +128,82 @@ Optional parameters:
Result: 200 with JSON structure SearchResult. Check the field status. Result: 200 with JSON structure SearchResult. Check the field status.
*/ */
func (api *WebapiInstance) apiSearchResult(w http.ResponseWriter, r *http.Request) { func (api *WebapiInstance) apiSearchResult(w http.ResponseWriter, r *http.Request) {
r.ParseForm() r.ParseForm()
jobID, err := uuid.Parse(r.Form.Get("id")) jobID, err := uuid.Parse(r.Form.Get("id"))
if err != nil { if err != nil {
http.Error(w, "", http.StatusBadRequest) http.Error(w, "", http.StatusBadRequest)
return return
} }
limit, err := strconv.Atoi(r.Form.Get("limit")) limit, err := strconv.Atoi(r.Form.Get("limit"))
if err != nil { if err != nil {
limit = 100 limit = 100
} }
offset, errOffset := strconv.Atoi(r.Form.Get("offset")) offset, errOffset := strconv.Atoi(r.Form.Get("offset"))
// find the job ID // find the job ID
job := api.JobLookup(jobID) job := api.JobLookup(jobID)
if job == nil { if job == nil {
EncodeJSON(api.backend, w, r, SearchResult{Status: 2}) EncodeJSON(api.backend, w, r, SearchResult{Status: 2})
return return
} }
// filters and sort parameter // filters and sort parameter
if filterReset, _ := strconv.ParseBool(r.Form.Get("reset")); filterReset { if filterReset, _ := strconv.ParseBool(r.Form.Get("reset")); filterReset {
fileType, _ := strconv.Atoi(r.Form.Get("filetype")) fileType, _ := strconv.Atoi(r.Form.Get("filetype"))
fileFormat, _ := strconv.Atoi(r.Form.Get("fileformat")) fileFormat, _ := strconv.Atoi(r.Form.Get("fileformat"))
dateFrom := r.Form.Get("from") dateFrom := r.Form.Get("from")
dateTo := r.Form.Get("to") dateTo := r.Form.Get("to")
sort, _ := strconv.Atoi(r.Form.Get("sort")) sort, _ := strconv.Atoi(r.Form.Get("sort"))
sizeMin, _ := strconv.Atoi(r.Form.Get("sizemin")) sizeMin, _ := strconv.Atoi(r.Form.Get("sizemin"))
sizeMax, _ := strconv.Atoi(r.Form.Get("sizemax")) sizeMax, _ := strconv.Atoi(r.Form.Get("sizemax"))
filter := inputToSearchFilter(sort, fileType, fileFormat, dateFrom, dateTo, sizeMin, sizeMax) filter := inputToSearchFilter(sort, fileType, fileFormat, dateFrom, dateTo, sizeMin, sizeMax)
job.RuntimeFilter(filter) job.RuntimeFilter(filter)
} }
// query all results // query all results
var resultFiles []*apiFile var resultFiles []*apiFile
if errOffset == nil { if errOffset == nil {
resultFiles = job.ReturnResult(offset, limit) resultFiles = job.ReturnResult(offset, limit)
} else { } else {
resultFiles = job.ReturnNext(limit) resultFiles = job.ReturnNext(limit)
} }
var result SearchResult var result SearchResult
result.Files = []apiFile{} result.Files = []apiFile{}
// loop over results // loop over results
for n := range resultFiles { for n := range resultFiles {
result.Files = append(result.Files, *resultFiles[n]) result.Files = append(result.Files, *resultFiles[n])
} }
// set the status // set the status
if len(result.Files) > 0 { if len(result.Files) > 0 {
if job.IsSearchResults() { if job.IsSearchResults() {
result.Status = 0 // 0 = Success with results result.Status = 0 // 0 = Success with results
} else { } else {
result.Status = 1 // No more results to expect result.Status = 1 // No more results to expect
} }
} else { } else {
switch job.Status { switch job.Status {
case SearchStatusLive: case SearchStatusLive:
result.Status = 3 // No results yet available keep trying result.Status = 3 // No results yet available keep trying
case SearchStatusTerminated: case SearchStatusTerminated:
result.Status = 1 // No more results to expect result.Status = 1 // No more results to expect
default: // SearchStatusNoIndex, SearchStatusNotStarted default: // SearchStatusNoIndex, SearchStatusNotStarted
result.Status = 1 // No more results to expect result.Status = 1 // No more results to expect
} }
} }
// embedded statistics? // embedded statistics?
if returnStats, _ := strconv.ParseBool(r.Form.Get("stats")); returnStats { if returnStats, _ := strconv.ParseBool(r.Form.Get("stats")); returnStats {
result.Statistic = job.Statistics() result.Statistic = job.Statistics()
} }
EncodeJSON(api.backend, w, r, result) EncodeJSON(api.backend, w, r, result)
} }
/* /*
@@ -215,80 +215,80 @@ Result: If successful, upgrades to a websocket and sends JSON structure Sear
Limit is optional. Not used if ommitted or 0. Limit is optional. Not used if ommitted or 0.
*/ */
func (api *WebapiInstance) apiSearchResultStream(w http.ResponseWriter, r *http.Request) { func (api *WebapiInstance) apiSearchResultStream(w http.ResponseWriter, r *http.Request) {
r.ParseForm() r.ParseForm()
jobID, err := uuid.Parse(r.Form.Get("id")) jobID, err := uuid.Parse(r.Form.Get("id"))
if err != nil { if err != nil {
http.Error(w, "", http.StatusBadRequest) http.Error(w, "", http.StatusBadRequest)
return return
} }
limit, err := strconv.Atoi(r.Form.Get("limit")) limit, err := strconv.Atoi(r.Form.Get("limit"))
useLimit := err == nil useLimit := err == nil
// look up the job // look up the job
job := api.JobLookup(jobID) job := api.JobLookup(jobID)
if job == nil { if job == nil {
EncodeJSON(api.backend, w, r, SearchResult{Status: 2}) EncodeJSON(api.backend, w, r, SearchResult{Status: 2})
return return
} }
// upgrade to websocket // upgrade to websocket
conn, err := WSUpgrader.Upgrade(w, r, nil) conn, err := WSUpgrader.Upgrade(w, r, nil)
if err != nil { if err != nil {
// gorilla will automatically respond with "400 Bad Request", no other response is therefore necessary // gorilla will automatically respond with "400 Bad Request", no other response is therefore necessary
return return
} }
defer conn.Close() defer conn.Close()
// loop to get new results and send out via the web socket. // loop to get new results and send out via the web socket.
// Only exit if limit is reached if used, otherwise only if there are no result or the connection breaks. // Only exit if limit is reached if used, otherwise only if there are no result or the connection breaks.
for { for {
// query all results // query all results
var resultFiles []*apiFile var resultFiles []*apiFile
queryCount := 1 queryCount := 1
if useLimit { if useLimit {
queryCount = limit queryCount = limit
} }
resultFiles = job.ReturnNext(queryCount) resultFiles = job.ReturnNext(queryCount)
if useLimit { if useLimit {
limit -= len(resultFiles) limit -= len(resultFiles)
} }
// loop over results // loop over results
var result SearchResult var result SearchResult
result.Files = []apiFile{} result.Files = []apiFile{}
for n := range resultFiles { for n := range resultFiles {
result.Files = append(result.Files, *resultFiles[n]) result.Files = append(result.Files, *resultFiles[n])
} }
if !job.IsSearchResults() { if !job.IsSearchResults() {
result.Status = 1 // No more results to expect result.Status = 1 // No more results to expect
if len(result.Files) == 0 { if len(result.Files) == 0 {
conn.WriteJSON(result) // final message conn.WriteJSON(result) // final message
return return
} }
} }
// if no results, stall // if no results, stall
if len(result.Files) == 0 { if len(result.Files) == 0 {
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
continue continue
} }
// send out the results via the websocket // send out the results via the websocket
if err := conn.WriteJSON(result); err != nil { if err := conn.WriteJSON(result); err != nil {
return return
} }
// Check whether to continue. If the limit is used break once all done. // Check whether to continue. If the limit is used break once all done.
if (useLimit && limit <= 0) || result.Status == 1 { if (useLimit && limit <= 0) || result.Status == 1 {
break break
} }
} }
} }
/* /*
@@ -302,25 +302,25 @@ Response: 204 Empty
*/ */
func (api *WebapiInstance) apiSearchTerminate(w http.ResponseWriter, r *http.Request) { func (api *WebapiInstance) apiSearchTerminate(w http.ResponseWriter, r *http.Request) {
r.ParseForm() r.ParseForm()
jobID, err := uuid.Parse(r.Form.Get("id")) jobID, err := uuid.Parse(r.Form.Get("id"))
if err != nil { if err != nil {
http.Error(w, "", http.StatusBadRequest) http.Error(w, "", http.StatusBadRequest)
return return
} }
// look up the job // look up the job
job := api.JobLookup(jobID) job := api.JobLookup(jobID)
if job == nil { if job == nil {
http.Error(w, "", http.StatusNotFound) http.Error(w, "", http.StatusNotFound)
return return
} }
// terminate and remove it from the list // terminate and remove it from the list
job.Terminate() job.Terminate()
api.RemoveJob(job) api.RemoveJob(job)
w.WriteHeader(http.StatusNoContent) w.WriteHeader(http.StatusNoContent)
} }
/* /*
@@ -330,23 +330,23 @@ Request: GET /search/result?id=[UUID]
Result: 200 with JSON structure SearchStatistic. Check the field status (0 = Success, 2 = ID not found). Result: 200 with JSON structure SearchStatistic. Check the field status (0 = Success, 2 = ID not found).
*/ */
func (api *WebapiInstance) apiSearchStatistic(w http.ResponseWriter, r *http.Request) { func (api *WebapiInstance) apiSearchStatistic(w http.ResponseWriter, r *http.Request) {
r.ParseForm() r.ParseForm()
jobID, err := uuid.Parse(r.Form.Get("id")) jobID, err := uuid.Parse(r.Form.Get("id"))
if err != nil { if err != nil {
http.Error(w, "", http.StatusBadRequest) http.Error(w, "", http.StatusBadRequest)
return return
} }
// find the job ID // find the job ID
job := api.JobLookup(jobID) job := api.JobLookup(jobID)
if job == nil { if job == nil {
EncodeJSON(api.backend, w, r, SearchStatistic{Status: 2}) EncodeJSON(api.backend, w, r, SearchStatistic{Status: 2})
return return
} }
stats := job.Statistics() stats := job.Statistics()
EncodeJSON(api.backend, w, r, SearchStatistic{SearchStatisticData: stats, Status: 0, IsTerminated: job.IsTerminated()}) EncodeJSON(api.backend, w, r, SearchStatistic{SearchStatisticData: stats, Status: 0, IsTerminated: job.IsTerminated()})
} }
/* /*
@@ -357,21 +357,21 @@ Request: GET /explore?limit=[max records]&type=[file type]&offset=[offset]
Result: 200 with JSON structure SearchResult. Check the field status. Result: 200 with JSON structure SearchResult. Check the field status.
*/ */
func (api *WebapiInstance) apiExplore(w http.ResponseWriter, r *http.Request) { func (api *WebapiInstance) apiExplore(w http.ResponseWriter, r *http.Request) {
r.ParseForm() r.ParseForm()
offset, _ := strconv.Atoi(r.Form.Get("offset")) offset, _ := strconv.Atoi(r.Form.Get("offset"))
limit, err := strconv.Atoi(r.Form.Get("limit")) limit, err := strconv.Atoi(r.Form.Get("limit"))
if err != nil { if err != nil {
limit = 100 limit = 100
} }
fileType, err := strconv.Atoi(r.Form.Get("type")) fileType, err := strconv.Atoi(r.Form.Get("type"))
if err != nil { if err != nil {
fileType = -1 fileType = -1
} }
result := api.ExploreHelper(fileType,limit,offset,[]byte{},false) result := api.ExploreHelper(fileType, limit, offset, []byte{}, false)
EncodeJSON(api.backend, w, r, result) EncodeJSON(api.backend, w, r, result)
} }
/* /*
@@ -382,75 +382,75 @@ Request: GET /explore/node?limit=[max records]&type=[file type]&offset=[offse
Result: 200 with JSON structure SearchResult. Check the field status. Result: 200 with JSON structure SearchResult. Check the field status.
*/ */
func (api *WebapiInstance) apiExploreNodeID(w http.ResponseWriter, r *http.Request) { func (api *WebapiInstance) apiExploreNodeID(w http.ResponseWriter, r *http.Request) {
r.ParseForm() r.ParseForm()
offset, _ := strconv.Atoi(r.Form.Get("offset")) offset, _ := strconv.Atoi(r.Form.Get("offset"))
limit, err := strconv.Atoi(r.Form.Get("limit")) limit, err := strconv.Atoi(r.Form.Get("limit"))
if err != nil { if err != nil {
limit = 100 limit = 100
} }
// ID fields for results for a specific node ID. // ID fields for results for a specific node ID.
NodeId, _ := DecodeBlake3Hash(r.Form.Get("NodeID")) NodeId, _ := DecodeBlake3Hash(r.Form.Get("NodeID"))
fileType, err := strconv.Atoi(r.Form.Get("type")) fileType, err := strconv.Atoi(r.Form.Get("type"))
if err != nil { if err != nil {
fileType = -1 fileType = -1
} }
result := api.ExploreHelper(fileType,limit,offset,NodeId,true) result := api.ExploreHelper(fileType, limit, offset, NodeId, true)
EncodeJSON(api.backend, w, r, result) EncodeJSON(api.backend, w, r, result)
} }
// ExploreHelper Helper function for the explore route with the possibility search based on a node ID // ExploreHelper Helper function for the explore route with the possibility search based on a node ID
func (api *WebapiInstance) ExploreHelper(fileType int, limit, offset int, nodeID []byte, nodeIDState bool) *SearchResult{ func (api *WebapiInstance) ExploreHelper(fileType int, limit, offset int, nodeID []byte, nodeIDState bool) *SearchResult {
resultFiles := api.queryRecentShared(api.backend, fileType, uint64(limit*20/100), uint64(offset), uint64(limit), nodeID, nodeIDState) resultFiles := api.queryRecentShared(api.backend, fileType, uint64(limit*20/100), uint64(offset), uint64(limit), nodeID, nodeIDState)
var result SearchResult var result SearchResult
result.Files = []apiFile{} result.Files = []apiFile{}
// loop over results // loop over results
for n := range resultFiles { for n := range resultFiles {
result.Files = append(result.Files, blockRecordFileToAPI(resultFiles[n])) result.Files = append(result.Files, blockRecordFileToAPI(resultFiles[n]))
} }
if len(result.Files) == 0 { if len(result.Files) == 0 {
result.Status = 3 // No results yet available keep trying result.Status = 3 // No results yet available keep trying
} }
result.Status = 1 // No more results to expect result.Status = 1 // No more results to expect
return &result return &result
} }
func (input *SearchRequest) Parse() (Timeout time.Duration) { func (input *SearchRequest) Parse() (Timeout time.Duration) {
if input.Timeout == 0 { if input.Timeout == 0 {
Timeout = time.Second * 20 // default timeout: 20 seconds Timeout = time.Second * 20 // default timeout: 20 seconds
} else { } else {
Timeout = time.Duration(input.Timeout) * time.Second Timeout = time.Duration(input.Timeout) * time.Second
} }
return return
} }
// ToSearchFilter converts the user input to a valid search filter // ToSearchFilter converts the user input to a valid search filter
func (input *SearchRequest) ToSearchFilter() (output SearchFilter) { func (input *SearchRequest) ToSearchFilter() (output SearchFilter) {
return inputToSearchFilter(input.Sort, input.FileType, input.FileFormat, input.DateFrom, input.DateTo, input.SizeMin, input.SizeMax) return inputToSearchFilter(input.Sort, input.FileType, input.FileFormat, input.DateFrom, input.DateTo, input.SizeMin, input.SizeMax)
} }
func inputToSearchFilter(Sort, FileType, FileFormat int, DateFrom, DateTo string, SizeMin, SizeMax int) (output SearchFilter) { func inputToSearchFilter(Sort, FileType, FileFormat int, DateFrom, DateTo string, SizeMin, SizeMax int) (output SearchFilter) {
output.Sort = Sort output.Sort = Sort
output.FileType = FileType output.FileType = FileType
output.FileFormat = FileFormat output.FileFormat = FileFormat
output.SizeMin = SizeMin output.SizeMin = SizeMin
output.SizeMax = SizeMax output.SizeMax = SizeMax
dateFrom, errFrom := time.Parse(apiDateFormat, DateFrom) dateFrom, errFrom := time.Parse(apiDateFormat, DateFrom)
dateTo, errTo := time.Parse(apiDateFormat, DateTo) dateTo, errTo := time.Parse(apiDateFormat, DateTo)
if errFrom == nil && errTo == nil && dateFrom.Before(dateTo) { if errFrom == nil && errTo == nil && dateFrom.Before(dateTo) {
output.DateFrom = dateFrom output.DateFrom = dateFrom
output.DateTo = dateTo output.DateTo = dateTo
output.IsDates = true output.IsDates = true
} }
return return
} }

View File

@@ -6,98 +6,98 @@ Author: Peter Kleissner
package webapi package webapi
import ( import (
"fmt" "fmt"
"time" "time"
"github.com/PeernetOfficial/core" "github.com/PeernetOfficial/core"
"github.com/PeernetOfficial/core/blockchain" "github.com/PeernetOfficial/core/blockchain"
) )
// queryRecentShared returns recently shared files on the network from random peers until the limit is reached. // queryRecentShared returns recently shared files on the network from random peers until the limit is reached.
func (api *WebapiInstance) queryRecentShared(backend *core.Backend, fileType int, limitPeer, offsetTotal, limitTotal uint64, nodeID []byte, nodeIDState bool) (files []blockchain.BlockRecordFile) { func (api *WebapiInstance) queryRecentShared(backend *core.Backend, fileType int, limitPeer, offsetTotal, limitTotal uint64, nodeID []byte, nodeIDState bool) (files []blockchain.BlockRecordFile) {
if limitPeer == 0 { if limitPeer == 0 {
limitPeer = 1 limitPeer = 1
} }
// Assign peer list as an empty array // Assign peer list as an empty array
var peerList []*core.PeerInfo var peerList []*core.PeerInfo
// check if the NodeID is provided or not // check if the NodeID is provided or not
if len(nodeID) == 0 && !nodeIDState { if len(nodeID) == 0 && !nodeIDState {
// Use the peer list to know about active peers. Random order! // Use the peer list to know about active peers. Random order!
peerList = api.backend.PeerlistGet() peerList = api.backend.PeerlistGet()
} else { } else {
// Get peer information based on the NodeID provided // Get peer information based on the NodeID provided
_, peer, err := api.backend.FindNode(nodeID, time.Second*5) _, peer, err := api.backend.FindNode(nodeID, time.Second*5)
if err != nil { if err != nil {
return return
} }
peerList = append(peerList, peer) peerList = append(peerList, peer)
} }
// Files from peers exceeding the limit. It is used if from all peers the total limit is not reached. // Files from peers exceeding the limit. It is used if from all peers the total limit is not reached.
var filesSeconday []blockchain.BlockRecordFile var filesSeconday []blockchain.BlockRecordFile
for _, peer := range peerList { for _, peer := range peerList {
if peer.BlockchainHeight == 0 { if peer.BlockchainHeight == 0 {
continue continue
} }
var filesFromPeer uint64 var filesFromPeer uint64
// decode blocks from top down // decode blocks from top down
blockLoop: blockLoop:
for blockN := peer.BlockchainHeight - 1; blockN > 0; blockN-- { for blockN := peer.BlockchainHeight - 1; blockN > 0; blockN-- {
blockDecoded, _, found, _ := backend.ReadBlock(peer.PublicKey, peer.BlockchainVersion, blockN) blockDecoded, _, found, _ := backend.ReadBlock(peer.PublicKey, peer.BlockchainVersion, blockN)
if !found { if !found {
continue continue
} }
for _, record := range blockDecoded.RecordsDecoded { for _, record := range blockDecoded.RecordsDecoded {
if file, ok := record.(blockchain.BlockRecordFile); ok && isFileTypeMatchBlock(&file, fileType) { if file, ok := record.(blockchain.BlockRecordFile); ok && isFileTypeMatchBlock(&file, fileType) {
// add the tags 'Shared By Count' and 'Shared By GeoIP' // add the tags 'Shared By Count' and 'Shared By GeoIP'
file.Tags = append(file.Tags, blockchain.TagFromNumber(blockchain.TagSharedByCount, 1)) file.Tags = append(file.Tags, blockchain.TagFromNumber(blockchain.TagSharedByCount, 1))
if latitude, longitude, valid := api.Peer2GeoIP(peer); valid { if latitude, longitude, valid := api.Peer2GeoIP(peer); valid {
sharedByGeoIP := fmt.Sprintf("%.4f", latitude) + "," + fmt.Sprintf("%.4f", longitude) sharedByGeoIP := fmt.Sprintf("%.4f", latitude) + "," + fmt.Sprintf("%.4f", longitude)
file.Tags = append(file.Tags, blockchain.TagFromText(blockchain.TagSharedByGeoIP, sharedByGeoIP)) file.Tags = append(file.Tags, blockchain.TagFromText(blockchain.TagSharedByGeoIP, sharedByGeoIP))
} }
// found a new file! append. // found a new file! append.
if filesFromPeer < limitPeer { if filesFromPeer < limitPeer {
filesFromPeer++ filesFromPeer++
if offsetTotal > 0 { if offsetTotal > 0 {
offsetTotal-- offsetTotal--
continue continue
} }
files = append(files, file) files = append(files, file)
if uint64(len(files)) >= limitTotal { if uint64(len(files)) >= limitTotal {
return return
} }
} else if uint64(len(filesSeconday)) < limitTotal-uint64(len(files)) { } else if uint64(len(filesSeconday)) < limitTotal-uint64(len(files)) {
filesSeconday = append(filesSeconday, file) filesSeconday = append(filesSeconday, file)
} else { } else {
break blockLoop break blockLoop
} }
} }
} }
} }
} }
files = append(files, filesSeconday...) files = append(files, filesSeconday...)
return return
} }
// isFileTypeMatchBlock checks if the file type matches. -1 = accept any. -2 = core.TypeBinary, core.TypeCompressed, core.TypeContainer, core.TypeExecutable. // isFileTypeMatchBlock checks if the file type matches. -1 = accept any. -2 = core.TypeBinary, core.TypeCompressed, core.TypeContainer, core.TypeExecutable.
func isFileTypeMatchBlock(file *blockchain.BlockRecordFile, fileType int) bool { func isFileTypeMatchBlock(file *blockchain.BlockRecordFile, fileType int) bool {
if fileType == -1 { if fileType == -1 {
return true return true
} else if fileType == -2 { } else if fileType == -2 {
return file.Type == core.TypeBinary || file.Type == core.TypeCompressed || file.Type == core.TypeContainer || file.Type == core.TypeExecutable return file.Type == core.TypeBinary || file.Type == core.TypeCompressed || file.Type == core.TypeContainer || file.Type == core.TypeExecutable
} }
return file.Type == uint8(fileType) return file.Type == uint8(fileType)
} }