Merge remote-tracking branch 'origin/wip'
[src/xds/xds-server.git] / lib / syncthing / folder-st.go
1 package st
2
3 import (
4         "fmt"
5         "path/filepath"
6
7         "github.com/iotbzh/xds-server/lib/folder"
8         "github.com/iotbzh/xds-server/lib/xdsconfig"
9         uuid "github.com/satori/go.uuid"
10         "github.com/syncthing/syncthing/lib/config"
11 )
12
13 // IFOLDER interface implementation for syncthing
14
15 // STFolder .
16 type STFolder struct {
17         globalConfig      *xdsconfig.Config
18         st                *SyncThing
19         fConfig           folder.FolderConfig
20         stfConfig         config.FolderConfiguration
21         eventIDs          []int
22         eventChangeCB     *folder.EventCB
23         eventChangeCBData *folder.EventCBData
24 }
25
26 // NewFolderST Create a new instance of STFolder
27 func (s *SyncThing) NewFolderST(gc *xdsconfig.Config) *STFolder {
28         return &STFolder{
29                 globalConfig: gc,
30                 st:           s,
31         }
32 }
33
34 // NewUID Get a UUID
35 func (f *STFolder) NewUID(suffix string) string {
36         i := len(f.st.MyID)
37         if i > 15 {
38                 i = 15
39         }
40         return uuid.NewV1().String()[:14] + f.st.MyID[:i] + "_" + suffix
41 }
42
43 // Add a new folder
44 func (f *STFolder) Add(cfg folder.FolderConfig) (*folder.FolderConfig, error) {
45
46         // Sanity check
47         if cfg.DataCloudSync.SyncThingID == "" {
48                 return nil, fmt.Errorf("device id not set (SyncThingID field)")
49         }
50
51         // rootPath should not be empty
52         if cfg.RootPath == "" {
53                 cfg.RootPath = f.globalConfig.FileConf.ShareRootDir
54         }
55
56         f.fConfig = cfg
57
58         f.fConfig.DataCloudSync.BuilderSThgID = f.st.MyID // FIXME - should be removed after local ST config rework
59
60         // Update Syncthing folder
61         // (expect 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
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 // Remove a folder
108 func (f *STFolder) Remove() error {
109         return f.st.FolderDelete(f.stfConfig.ID)
110 }
111
112 // RegisterEventChange requests registration for folder event change
113 func (f *STFolder) RegisterEventChange(cb *folder.EventCB, data *folder.EventCBData) error {
114         f.eventChangeCB = cb
115         f.eventChangeCBData = data
116         return nil
117 }
118
119 // UnRegisterEventChange remove registered callback
120 func (f *STFolder) UnRegisterEventChange() error {
121         f.eventChangeCB = nil
122         f.eventChangeCBData = nil
123         return nil
124 }
125
126 // Sync Force folder files synchronization
127 func (f *STFolder) Sync() error {
128         return f.st.FolderScan(f.stfConfig.ID, "")
129 }
130
131 // IsInSync Check if folder files are in-sync
132 func (f *STFolder) IsInSync() (bool, error) {
133         sts, err := f.st.IsFolderInSync(f.stfConfig.ID)
134         if err != nil {
135                 return false, err
136         }
137         f.fConfig.IsInSync = sts
138         return sts, nil
139 }
140
141 // callback use to update IsInSync status
142 func (f *STFolder) cbEventState(ev Event, data *EventsCBData) {
143         prevSync := f.fConfig.IsInSync
144         prevStatus := f.fConfig.Status
145
146         switch ev.Type {
147
148         case EventStateChanged:
149                 to := ev.Data["to"]
150                 switch to {
151                 case "scanning", "syncing":
152                         f.fConfig.Status = folder.StatusSyncing
153                 case "idle":
154                         f.fConfig.Status = folder.StatusEnable
155                 }
156                 f.fConfig.IsInSync = (to == "idle")
157
158         case EventFolderPaused:
159                 if f.fConfig.Status == folder.StatusEnable {
160                         f.fConfig.Status = folder.StatusPause
161                 }
162                 f.fConfig.IsInSync = false
163         }
164
165         if f.eventChangeCB != nil &&
166                 (prevSync != f.fConfig.IsInSync || prevStatus != f.fConfig.Status) {
167                 cpConf := f.fConfig
168                 (*f.eventChangeCB)(&cpConf, f.eventChangeCBData)
169         }
170 }