forked from aler9/dctk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.go
152 lines (134 loc) · 3.99 KB
/
search.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package dctoolkit
import (
"fmt"
"strings"
)
// SearchType contains the search type.
type SearchType int
const (
// SearchAny searches for a file or directory by name
SearchAny SearchType = iota
// SearchDirectory searches for a directory by name
SearchDirectory
// SearchTTH searches for a file by TTH
SearchTTH
)
// SearchResult contains a single result received after a search request.
type SearchResult struct {
// whether the search result was received in passive or active mode
IsActive bool
// the peer sending the result
Peer *Peer
// path of a file or directory matching a search request
Path string
// whether the result is a directory
IsDir bool
// size (file only in NMDC, both files and directories in ADC)
Size uint64
// TTH (file only)
TTH TigerHash
// the available upload slots of the peer
SlotAvail uint
}
// SearchConf allows to configure a search request.
type SearchConf struct {
// the search type, defaults to SearchAny. See SearchType for all the available options
Type SearchType
// the minimum size of the searched file (if type is SearchAny or SearchTTH)
MinSize uint64
// the maximum size of the searched file (if type is SearchAny or SearchTTH)
MaxSize uint64
// part of a file name (if type is SearchAny), part of a directory name
// (if type is SearchAny or SearchDirectory)
Query string
// file TTH (if type is SearchTTH)
TTH TigerHash
}
type searchIncomingRequest struct {
isActive bool
stype SearchType
minSize uint64
maxSize uint64
query string // if type is SearchAny or SearchDirectory
tth TigerHash // if type is SearchTTH
}
// Search starts a file search asynchronously. See SearchConf for the available options.
func (c *Client) Search(conf SearchConf) error {
if c.protoIsAdc == true {
return c.handleAdcSearchOutgoingRequest(conf)
} else {
return c.handleNmdcSearchOutgoingRequest(conf)
}
}
func (c *Client) handleSearchIncomingRequest(req *searchIncomingRequest) ([]interface{}, error) {
var results []interface{}
var scanDir func(dname string, dir *shareDirectory, dirAddToResults bool)
// search file or directory by name
if req.stype == SearchAny || req.stype == SearchDirectory {
if len(req.query) < 3 {
return nil, fmt.Errorf("query too short: %s", req.query)
}
// normalize query
req.query = strings.ToLower(req.query)
scanDir = func(dname string, dir *shareDirectory, dirAddToResults bool) {
// always add directories
if dirAddToResults == false {
dirAddToResults = strings.Contains(strings.ToLower(dname), req.query)
}
if dirAddToResults {
results = append(results, dir)
}
if req.stype != SearchDirectory {
for fname, file := range dir.files {
fileAddToResults := dirAddToResults
if fileAddToResults == false {
fileAddToResults = strings.Contains(strings.ToLower(fname), req.query) &&
(req.minSize == 0 || file.size > req.minSize) &&
(req.maxSize == 0 || file.size < req.maxSize)
}
if fileAddToResults {
results = append(results, file)
}
}
}
for sname, sdir := range dir.dirs {
scanDir(sname, sdir, dirAddToResults)
}
}
// search file by TTH
} else {
scanDir = func(dname string, dir *shareDirectory, dirAddToResults bool) {
for _, file := range dir.files {
if file.tth == req.tth {
results = append(results, file)
}
}
for sname, sdir := range dir.dirs {
scanDir(sname, sdir, false)
}
}
}
// start searching
for alias, dir := range c.shareTree {
scanDir(alias, dir, false)
}
// Implementations should send a maximum of 5 search results to passive users
// and 10 search results to active users
if req.isActive == true {
if len(results) > 10 {
results = results[:10]
}
} else {
if len(results) > 5 {
results = results[:5]
}
}
dolog(LevelInfo, "[search] req: %+v | sent %d results", req, len(results))
return results, nil
}
func (c *Client) handleSearchResult(sr *SearchResult) {
dolog(LevelInfo, "[search] res: %+v", sr)
if c.OnSearchResult != nil {
c.OnSearchResult(sr)
}
}