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