f25a505f7b860d5949eca0aa8ae6866e40b64d0b
[src/xds/xds-server.git] / lib / syncthing / folder-st.go
1 package st
2
3 import (
4         "fmt"
5         "os"
6         "path/filepath"
7         "strings"
8
9         "github.com/iotbzh/xds-server/lib/folder"
10         "github.com/iotbzh/xds-server/lib/xdsconfig"
11         uuid "github.com/satori/go.uuid"
12         "github.com/syncthing/syncthing/lib/config"
13 )
14
15 // IFOLDER interface implementation for syncthing
16
17 // STFolder .
18 type STFolder struct {
19         globalConfig      *xdsconfig.Config
20         st                *SyncThing
21         fConfig           folder.FolderConfig
22         stfConfig         config.FolderConfiguration
23         eventIDs          []int
24         eventChangeCB     *folder.EventCB
25         eventChangeCBData *folder.EventCBData
26 }
27
28 // NewFolderST Create a new instance of STFolder
29 func (s *SyncThing) NewFolderST(gc *xdsconfig.Config) *STFolder {
30         return &STFolder{
31                 globalConfig: gc,
32                 st:           s,
33         }
34 }
35
36 // NewUID Get a UUID
37 func (f *STFolder) NewUID(suffix string) string {
38         i := len(f.st.MyID)
39         if i > 15 {
40                 i = 15
41         }
42         uuid := uuid.NewV1().String()[:14] + f.st.MyID[:i]
43         if len(suffix) > 0 {
44                 uuid += "_" + suffix
45         }
46         return uuid
47 }
48
49 // Add a new folder
50 func (f *STFolder) Add(cfg folder.FolderConfig) (*folder.FolderConfig, error) {
51
52         // Sanity check
53         if cfg.DataCloudSync.SyncThingID == "" {
54                 return nil, fmt.Errorf("device id not set (SyncThingID field)")
55         }
56
57         // rootPath should not be empty
58         if cfg.RootPath == "" {
59                 cfg.RootPath = f.globalConfig.FileConf.ShareRootDir
60         }
61
62         f.fConfig = cfg
63
64         // Update Syncthing folder
65         // (except if status is ErrorConfig)
66         // TODO: add cache to avoid multiple requests on startup
67         if f.fConfig.Status != folder.StatusErrorConfig {
68                 id, err := f.st.FolderChange(f.fConfig)
69                 if err != nil {
70                         return nil, err
71                 }
72
73                 f.stfConfig, err = f.st.FolderConfigGet(id)
74                 if err != nil {
75                         f.fConfig.Status = folder.StatusErrorConfig
76                         return nil, err
77                 }
78
79                 // Register to events to update folder status
80                 for _, evName := range []string{EventStateChanged, EventFolderPaused} {
81                         evID, err := f.st.Events.Register(evName, f.cbEventState, id, nil)
82                         if err != nil {
83                                 return nil, err
84                         }
85                         f.eventIDs = append(f.eventIDs, evID)
86                 }
87
88                 f.fConfig.IsInSync = false // will be updated later by events
89                 f.fConfig.Status = folder.StatusEnable
90         }
91
92         return &f.fConfig, nil
93 }
94
95 // GetConfig Get public part of folder config
96 func (f *STFolder) GetConfig() folder.FolderConfig {
97         return f.fConfig
98 }
99
100 // GetFullPath returns the full path of a directory (from server POV)
101 func (f *STFolder) GetFullPath(dir string) string {
102         if &dir == nil {
103                 dir = ""
104         }
105         if filepath.IsAbs(dir) {
106                 return filepath.Join(f.fConfig.RootPath, dir)
107         }
108         return filepath.Join(f.fConfig.RootPath, f.fConfig.ClientPath, dir)
109 }
110
111 // ConvPathCli2Svr Convert path from Client to Server
112 func (f *STFolder) ConvPathCli2Svr(s string) string {
113         if f.fConfig.ClientPath != "" && f.fConfig.RootPath != "" {
114                 return strings.Replace(s,
115                         f.fConfig.ClientPath,
116                         f.fConfig.RootPath+"/"+f.fConfig.ClientPath,
117                         -1)
118         }
119         return s
120 }
121
122 // ConvPathSvr2Cli Convert path from Server to Client
123 func (f *STFolder) ConvPathSvr2Cli(s string) string {
124         if f.fConfig.ClientPath != "" && f.fConfig.RootPath != "" {
125                 return strings.Replace(s,
126                         f.fConfig.RootPath+"/"+f.fConfig.ClientPath,
127                         f.fConfig.ClientPath,
128                         -1)
129         }
130         return s
131 }
132
133 // Remove a folder
134 func (f *STFolder) Remove() error {
135         err := f.st.FolderDelete(f.stfConfig.ID)
136
137         // Delete folder on server side
138         err2 := os.RemoveAll(f.GetFullPath(""))
139
140         if err != nil {
141                 return err
142         }
143         return err2
144 }
145
146 // RegisterEventChange requests registration for folder event change
147 func (f *STFolder) RegisterEventChange(cb *folder.EventCB, data *folder.EventCBData) error {
148         f.eventChangeCB = cb
149         f.eventChangeCBData = data
150         return nil
151 }
152
153 // UnRegisterEventChange remove registered callback
154 func (f *STFolder) UnRegisterEventChange() error {
155         f.eventChangeCB = nil
156         f.eventChangeCBData = nil
157         return nil
158 }
159
160 // Sync Force folder files synchronization
161 func (f *STFolder) Sync() error {
162         return f.st.FolderScan(f.stfConfig.ID, "")
163 }
164
165 // IsInSync Check if folder files are in-sync
166 func (f *STFolder) IsInSync() (bool, error) {
167         sts, err := f.st.IsFolderInSync(f.stfConfig.ID)
168         if err != nil {
169                 return false, err
170         }
171         f.fConfig.IsInSync = sts
172         return sts, nil
173 }
174
175 // callback use to update IsInSync status
176 func (f *STFolder) cbEventState(ev Event, data *EventsCBData) {
177         prevSync := f.fConfig.IsInSync
178         prevStatus := f.fConfig.Status
179
180         switch ev.Type {
181
182         case EventStateChanged:
183                 to := ev.Data["to"]
184                 switch to {
185                 case "scanning", "syncing":
186                         f.fConfig.Status = folder.StatusSyncing
187                 case "idle":
188                         f.fConfig.Status = folder.StatusEnable
189                 }
190                 f.fConfig.IsInSync = (to == "idle")
191
192         case EventFolderPaused:
193                 if f.fConfig.Status == folder.StatusEnable {
194                         f.fConfig.Status = folder.StatusPause
195                 }
196                 f.fConfig.IsInSync = false
197         }
198
199         if f.eventChangeCB != nil &&
200                 (prevSync != f.fConfig.IsInSync || prevStatus != f.fConfig.Status) {
201                 cpConf := f.fConfig
202                 (*f.eventChangeCB)(&cpConf, f.eventChangeCBData)
203         }
204 }