/
sum-backend-userlist-file.js
303 lines (251 loc) · 9.88 KB
/
sum-backend-userlist-file.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/**
* handels userlist file update by using a file in shared file system
*
* @copyright Copyright (c) Tobias Zeising (http://www.aditu.de)
* @license GPLv3 (http://www.gnu.org/licenses/gpl-3.0.html)
*/
define('sum-backend-userlist-file', Class.extend({
/**
* backends
*/
backend: injected('sum-backend'),
/**
* backends helpers
*/
backendHelpers: injected('sum-backend-helpers'),
/**
* timestamp userfile was written
*/
userfileTimestamp: false,
/**
* cache for all additional userinfos
*/
userinfos: {},
/**
* count userfile update error
*/
userfileError: 0,
/**
* write extended userfile with avatar, ip, chatport and other less frequently updated information
* @param ip (string) current ip address
* @param port (int) current port
* @param key (string) current key
* @param avatar (string) current avatar
* @param success (callback) will be executed after successfully writing file
*/
userlistUpdateUsersOwnFile: function(ip, port, key, avatar, version, success) {
var file = config.user_file_extended.replace(/\?/, CryptoJS.MD5(this.backendHelpers.getUsername()));
var that = this;
this.backendHelpers.writeJsonFile(
file,
{
ip: ip,
port: port,
key: key.getPublicPEM(),
avatar: avatar,
version: version
},
function() {
that.userfileTimestamp = new Date().getTime();
if (typeof success != 'undefined')
success();
},
that.backend.error
);
},
/**
* timer: regular userfile and userlist update for current user
*/
userlistUpdateTimer: function() {
var that = this;
this.backendHelpers.lock(function(err) {
// can't get lock for exclusive userfile access? retry in random timeout
if (typeof err != 'undefined') {
console.info(new Date() + " Lockfile nicht bekommen");
console.info(err);
var randomTimeout = Math.floor(Math.random() * config.lock_retry_maximum) + config.lock_retry_minimum;
window.setTimeout(function() {
that.userlistUpdateTimer();
}, randomTimeout);
console.info(new Date() + " random timeout " + randomTimeout);
return;
}
// have lock? Update userlist
that.userlistUpdater();
});
},
/**
* all users are registered in a single json file. This method updates or adds
* an entry of the current user.
*/
userlistUpdater: function() {
var that = this;
that.backendHelpers.readJsonFile(
config.user_file,
function(users) {
that.userlistUpdate(users);
},
function(err) {
// userfile does not exist or wrong json parse error? create new one
if (typeof err != 'undefined' && (typeof err.code != 'undefined' || err === 'json parse error')) {
that.userlistUpdate([]);
return;
}
// more than 5 retries failed
if (that.userfileError > 5)
that.backend.error('Zugriff auf die Userliste nicht möglich');
else
that.userfileError++;
// start next run
window.setTimeout(function() {
that.userlistUpdateTimer();
}, config.user_list_update_intervall);
}
);
},
/**
* updates userlist
* @param users list of users
*/
userlistUpdate: function(users) {
// reset error counter
this.userfileError = 0;
var currentuser = this.backendHelpers.getUsername();
var now = new Date().getTime();
// remove orphaned user entries
var userlist = [];
for(var i=0; i<users.length; i++) {
// ignore entries without username and timestamp
if (typeof users[i].username == 'undefined' || typeof users[i].timestamp == 'undefined')
continue;
// current user will be added later
if (users[i].username == currentuser)
continue;
// only save active users
if (users[i].timestamp + config.user_remove > now) {
// if user has a timeout, set status to offline
if (users[i].status == 'online' && users[i].timestamp + config.user_timeout < now) {
users[i].status = 'offline';
}
userlist[userlist.length] = users[i];
}
}
// add current user
userlist[userlist.length] = {
timestamp: now,
status: 'online',
userfileTimestamp: this.userfileTimestamp,
rooms: this.backend.roomlist,
username: currentuser
};
// write back updated userfile
var that = this;
this.backendHelpers.writeJsonFile(
config.user_file,
userlist,
function() {
// release lock
that.backendHelpers.unlock();
},
this.backend.error
);
// load additional userinfos and update local userlist
this.userlistLoadAdditionalUserinfos(userlist);
},
/**
* loads all additional userinfos from users single files
* @param users (array) fetched users
*/
userlistLoadAdditionalUserinfos: function(users) {
var that = this;
// next step will be executed if all additonal informations of all users was loaded (toMerge == 0)
var toMerge = users.length;
// checks whether all userinfos was fetched
var checkAllHandledThenExecuteRefreshUserlist = function() {
toMerge--;
if(toMerge===0)
that.userlistRefreshFrontend(users);
};
// loads current userinfos for a single user
var loadUserinfos = function(currentIndex) {
// load from users file if not in cache or newer one available
if (typeof that.userinfos[users[currentIndex].username] == 'undefined' ||
users[currentIndex].userfileTimestamp != that.userinfos[users[currentIndex].username].timestamp) {
// read userinfos from file
var file = config.user_file_extended.replace(/\?/, CryptoJS.MD5(users[currentIndex].username));
that.backendHelpers.readJsonFile(
file,
function(userinfos) {
// merge user and userinfos
if (typeof userinfos.ip != 'undefined' && typeof userinfos.port != 'undefined' && typeof userinfos.key != 'undefined') {
users[currentIndex] = that.backendHelpers.mergeUserAndUserinfos(users[currentIndex], userinfos);
// save userinfos in cache
userinfos.timestamp = users[currentIndex].userfileTimestamp;
that.userinfos[users[currentIndex].username] = userinfos;
}
checkAllHandledThenExecuteRefreshUserlist();
},
function() {
checkAllHandledThenExecuteRefreshUserlist();
}
);
// load from cache
} else {
users[currentIndex] = that.backendHelpers.mergeUserAndUserinfos(users[currentIndex], that.userinfos[users[currentIndex].username]);
checkAllHandledThenExecuteRefreshUserlist();
}
};
// load additional infos per user from users own files
for (var i=0; i<users.length; i++)
loadUserinfos(i);
},
/**
* refresh current userlist after reading userfile
* @param users (array) fetched users
*/
userlistRefreshFrontend: function(users) {
// fix corrupt userlist
users = this.compensateCorruptUserlist(users);
// sort userlist by username
users = this.backendHelpers.sortUserlistByUsername(users);
// show notification for users which are now online/offline/removed
this.backend.showOnlineOfflineNotifications(users);
// save userlist
this.backend.userlist = users;
// inform frontend that new userlist is available
if(typeof this.backend.hasUserlistUpdate != "undefined")
this.backend.hasUserlistUpdate();
// initialize next update
var that = this;
window.setTimeout(function() {
that.userlistUpdateTimer();
}, config.user_list_update_intervall);
},
/**
* Compensates corrupt or wrong userfile. If a user is in local userlist and not in userlist.json
* and user is not timedout for removing from list, the user will be restored in the userlist.
* @param users userlist
* @returns (array) userlist with restored users
*/
compensateCorruptUserlist: function(users) {
// search in old userlist
$.each(this.backend.userlist, function(index, oldUser) {
var found = false;
// search oldUser in new userlist
$.each(users, function(index, user) {
if(oldUser.username == user.username) {
found = true;
return false;
}
return true;
});
// if user is not in new userlist and not timedout: restore it
var now = new Date().getTime();
if (found === false && oldUser.timestamp + config.user_remove > now) {
users[users.length] = oldUser;
}
return true;
});
return users;
}
}));