Defined structures for /exec and /signal response.
[src/xds/xds-server.git] / lib / xdsserver / folder-st.go
1 package xdsserver
2
3 import (
4         "fmt"
5         "os"
6         "path/filepath"
7         "strings"
8
9         "github.com/iotbzh/xds-server/lib/xsapiv1"
10         st "github.com/iotbzh/xds-server/lib/syncthing"
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         *Context
20         st                *st.SyncThing
21         fConfig           xsapiv1.FolderConfig
22         stfConfig         config.FolderConfiguration
23         eventIDs          []int
24         eventChangeCB     *FolderEventCB
25         eventChangeCBData *FolderEventCBData
26 }
27
28 // NewFolderST Create a new instance of STFolder
29 func NewFolderST(ctx *Context, sthg *st.SyncThing) *STFolder {
30         return &STFolder{
31                 Context: ctx,
32                 st:      sthg,
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 xsapiv1.FolderConfig) (*xsapiv1.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.Config.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 != xsapiv1.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 = xsapiv1.StatusErrorConfig
76                         return nil, err
77                 }
78
79                 // Register to events to update folder status
80                 for _, evName := range []string{st.EventStateChanged, st.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 = xsapiv1.StatusEnable
90         }
91
92         return &f.fConfig, nil
93 }
94
95 // GetConfig Get public part of folder config
96 func (f *STFolder) GetConfig() xsapiv1.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 // Update update some fields of a folder
147 func (f *STFolder) Update(cfg xsapiv1.FolderConfig) (*xsapiv1.FolderConfig, error) {
148         if f.fConfig.ID != cfg.ID {
149                 return nil, fmt.Errorf("Invalid id")
150         }
151         f.fConfig = cfg
152         return &f.fConfig, nil
153 }
154
155 // RegisterEventChange requests registration for folder event change
156 func (f *STFolder) RegisterEventChange(cb *FolderEventCB, data *FolderEventCBData) error {
157         f.eventChangeCB = cb
158         f.eventChangeCBData = data
159         return nil
160 }
161
162 // UnRegisterEventChange remove registered callback
163 func (f *STFolder) UnRegisterEventChange() error {
164         f.eventChangeCB = nil
165         f.eventChangeCBData = nil
166         return nil
167 }
168
169 // Sync Force folder files synchronization
170 func (f *STFolder) Sync() error {
171         return f.st.FolderScan(f.stfConfig.ID, "")
172 }
173
174 // IsInSync Check if folder files are in-sync
175 func (f *STFolder) IsInSync() (bool, error) {
176         sts, err := f.st.IsFolderInSync(f.stfConfig.ID)
177         if err != nil {
178                 return false, err
179         }
180         f.fConfig.IsInSync = sts
181         return sts, nil
182 }
183
184 // callback use to update IsInSync status
185 func (f *STFolder) cbEventState(ev st.Event, data *st.EventsCBData) {
186         prevSync := f.fConfig.IsInSync
187         prevStatus := f.fConfig.Status
188
189         switch ev.Type {
190
191         case st.EventStateChanged:
192                 to := ev.Data["to"]
193                 switch to {
194                 case "scanning", "syncing":
195                         f.fConfig.Status = xsapiv1.StatusSyncing
196                 case "idle":
197                         f.fConfig.Status = xsapiv1.StatusEnable
198                 }
199                 f.fConfig.IsInSync = (to == "idle")
200
201         case st.EventFolderPaused:
202                 if f.fConfig.Status == xsapiv1.StatusEnable {
203                         f.fConfig.Status = xsapiv1.StatusPause
204                 }
205                 f.fConfig.IsInSync = false
206         }
207
208         if f.eventChangeCB != nil &&
209                 (prevSync != f.fConfig.IsInSync || prevStatus != f.fConfig.Status) {
210                 cpConf := f.fConfig
211                 (*f.eventChangeCB)(&cpConf, f.eventChangeCBData)
212         }
213 }