7e1fe55a406bcd51eecc0f57c20d65f892d21f33
[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         return uuid.NewV1().String()[:14] + f.st.MyID[:i] + "_" + suffix
43 }
44
45 // Add a new folder
46 func (f *STFolder) Add(cfg folder.FolderConfig) (*folder.FolderConfig, error) {
47
48         // Sanity check
49         if cfg.DataCloudSync.SyncThingID == "" {
50                 return nil, fmt.Errorf("device id not set (SyncThingID field)")
51         }
52
53         // rootPath should not be empty
54         if cfg.RootPath == "" {
55                 cfg.RootPath = f.globalConfig.FileConf.ShareRootDir
56         }
57
58         f.fConfig = cfg
59
60         f.fConfig.DataCloudSync.BuilderSThgID = f.st.MyID // FIXME - should be removed after local ST config rework
61
62         // Update Syncthing folder
63         // (expect if status is ErrorConfig)
64         // TODO: add cache to avoid multiple requests on startup
65         if f.fConfig.Status != folder.StatusErrorConfig {
66                 id, err := f.st.FolderChange(f.fConfig)
67                 if err != nil {
68                         return nil, err
69                 }
70
71                 f.stfConfig, err = f.st.FolderConfigGet(id)
72                 if err != nil {
73                         f.fConfig.Status = folder.StatusErrorConfig
74                         return nil, err
75                 }
76
77                 // Register to events to update folder status
78                 for _, evName := range []string{EventStateChanged, EventFolderPaused} {
79                         evID, err := f.st.Events.Register(evName, f.cbEventState, id, nil)
80                         if err != nil {
81                                 return nil, err
82                         }
83                         f.eventIDs = append(f.eventIDs, evID)
84                 }
85
86                 f.fConfig.IsInSync = false // will be updated later by events
87                 f.fConfig.Status = folder.StatusEnable
88         }
89
90         return &f.fConfig, nil
91 }
92
93 // GetConfig Get public part of folder config
94 func (f *STFolder) GetConfig() folder.FolderConfig {
95         return f.fConfig
96 }
97
98 // GetFullPath returns the full path of a directory (from server POV)
99 func (f *STFolder) GetFullPath(dir string) string {
100         if &dir == nil {
101                 dir = ""
102         }
103         if filepath.IsAbs(dir) {
104                 return filepath.Join(f.fConfig.RootPath, dir)
105         }
106         return filepath.Join(f.fConfig.RootPath, f.fConfig.ClientPath, dir)
107 }
108
109 // ConvPathCli2Svr Convert path from Client to Server
110 func (f *STFolder) ConvPathCli2Svr(s string) string {
111         if f.fConfig.ClientPath != "" && f.fConfig.RootPath != "" {
112                 return strings.Replace(s,
113                         f.fConfig.ClientPath,
114                         f.fConfig.RootPath+"/"+f.fConfig.ClientPath,
115                         -1)
116         }
117         return s
118 }
119
120 // ConvPathSvr2Cli Convert path from Server to Client
121 func (f *STFolder) ConvPathSvr2Cli(s string) string {
122         if f.fConfig.ClientPath != "" && f.fConfig.RootPath != "" {
123                 return strings.Replace(s,
124                         f.fConfig.RootPath+"/"+f.fConfig.ClientPath,
125                         f.fConfig.ClientPath,
126                         -1)
127         }
128         return s
129 }
130
131 // Remove a folder
132 func (f *STFolder) Remove() error {
133         err := f.st.FolderDelete(f.stfConfig.ID)
134
135         // Delete folder on server side
136         err2 := os.RemoveAll(f.GetFullPath(""))
137
138         if err != nil {
139                 return err
140         }
141         return err2
142 }
143
144 // RegisterEventChange requests registration for folder event change
145 func (f *STFolder) RegisterEventChange(cb *folder.EventCB, data *folder.EventCBData) error {
146         f.eventChangeCB = cb
147         f.eventChangeCBData = data
148         return nil
149 }
150
151 // UnRegisterEventChange remove registered callback
152 func (f *STFolder) UnRegisterEventChange() error {
153         f.eventChangeCB = nil
154         f.eventChangeCBData = nil
155         return nil
156 }
157
158 // Sync Force folder files synchronization
159 func (f *STFolder) Sync() error {
160         return f.st.FolderScan(f.stfConfig.ID, "")
161 }
162
163 // IsInSync Check if folder files are in-sync
164 func (f *STFolder) IsInSync() (bool, error) {
165         sts, err := f.st.IsFolderInSync(f.stfConfig.ID)
166         if err != nil {
167                 return false, err
168         }
169         f.fConfig.IsInSync = sts
170         return sts, nil
171 }
172
173 // callback use to update IsInSync status
174 func (f *STFolder) cbEventState(ev Event, data *EventsCBData) {
175         prevSync := f.fConfig.IsInSync
176         prevStatus := f.fConfig.Status
177
178         switch ev.Type {
179
180         case EventStateChanged:
181                 to := ev.Data["to"]
182                 switch to {
183                 case "scanning", "syncing":
184                         f.fConfig.Status = folder.StatusSyncing
185                 case "idle":
186                         f.fConfig.Status = folder.StatusEnable
187                 }
188                 f.fConfig.IsInSync = (to == "idle")
189
190         case EventFolderPaused:
191                 if f.fConfig.Status == folder.StatusEnable {
192                         f.fConfig.Status = folder.StatusPause
193                 }
194                 f.fConfig.IsInSync = false
195         }
196
197         if f.eventChangeCB != nil &&
198                 (prevSync != f.fConfig.IsInSync || prevStatus != f.fConfig.Status) {
199                 cpConf := f.fConfig
200                 (*f.eventChangeCB)(&cpConf, f.eventChangeCBData)
201         }
202 }