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