added experimental multi thread support
[NeonServV5.git] / src / UserNode.c
1 /* UserNode.c - NeonServ v5.3
2  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17 #include "UserNode.h"
18 #include "ChanNode.h"
19 #include "ChanUser.h"
20 #include "tools.h"
21 #include "IRCEvents.h"
22 #include "IPNode.h"
23
24 static struct UserNode **userList;
25
26 void init_UserNode() {
27     userList = calloc(VALID_NICK_CHARS_FIRST_LEN+1, sizeof(*userList));
28 }
29
30 void free_UserNode() {
31     //kamikaze free all users
32     //chanusers will be destroyed in free_ChanNode()
33     int i;
34     struct UserNode *user, *next;
35     for(i = 0; i < VALID_NICK_CHARS_FIRST_LEN+1; i++) {
36         for(user = userList[i]; user; user = next) {
37             next = user->next;
38             free(user);
39         }
40     }
41     free(userList);
42 }
43
44 int is_valid_nick(const char *nick) {
45     unsigned int i;
46     //first char must be one of: a-zA-Z{|}~[\]^_`
47     if (!strchr(VALID_NICK_CHARS_FIRST, *nick))
48         return 0;
49     //all other chars must be one of: a-zA-Z0-9{|}~[\]^_`
50     for (i = 0; nick[i]; ++i)
51         if (!strchr(VALID_NICK_CHARS, nick[i]))
52             return 0;
53     if (strlen(nick) > NICKLEN)
54         return 0;
55     return 1;
56 }
57
58 static int get_nicklist_entry(int nick) {
59     int i;
60     char *valid_chars = VALID_NICK_CHARS_FIRST;
61     for(i = 0; i < VALID_NICK_CHARS_FIRST_LEN; i++) {
62         if(valid_chars[i] == nick)
63             return i;
64     }
65     return -1; //ERROR!
66 }
67
68 struct UserNode* getUserByNick(const char *nick) { //case sensitive
69     int userListIndex = get_nicklist_entry(*nick);
70     if(userListIndex == -1 || userList[userListIndex] == NULL)
71         return NULL;
72     struct UserNode *user;
73     for(user = userList[userListIndex]; user; user = user->next) {
74         if(!stricmp(nick, user->nick))
75             return user;
76     }
77     return NULL;
78 }
79
80 struct UserNode* getUserByMask(const char *mask) { //case sensitive
81     char cmask[strlen(mask)+1];
82     strcpy(cmask, mask);
83     int i;
84     struct UserNode *user = NULL;
85     for(i = 0; i < strlen(mask); i++) {
86         if(cmask[i] == '!') {
87             cmask[i] = 0;
88             user = getUserByNick(&cmask[0]);
89             return user;
90         } else if(cmask[i] == '.') {
91             //it's a server
92             return NULL;
93         }
94     }
95     return NULL;
96 }
97
98 struct UserNode* searchUserByNick(const char *nick) { //case insensitive
99     if(!isalpha(*nick)) 
100         return getUserByNick(nick);
101
102     int userListIndex;
103     struct UserNode *user;
104
105     //search in the lower case "section"
106     userListIndex = get_nicklist_entry(tolower(*nick));
107     if(userListIndex != -1 && userList[userListIndex] != NULL) {
108         for(user = userList[userListIndex]; user; user = user->next) {
109             if(!stricmp(nick, user->nick))
110                 return user;
111         }
112     }
113     //search in the upper case "section"
114     userListIndex = get_nicklist_entry(toupper(*nick));
115     if(userListIndex != -1 && userList[userListIndex] != NULL) {
116         for(user = userList[userListIndex]; user; user = user->next) {
117             if(!stricmp(nick, user->nick))
118                 return user;
119         }
120     }
121     return NULL;
122 }
123
124 int countUsersWithHost(char *host) {
125     int i, count = 0;
126     struct UserNode *user;
127     for(i = 0; i < VALID_NICK_CHARS_FIRST_LEN; i++) {
128         for(user = userList[i]; user; user = user->next) {
129             if(!strcmp(user->host, host)) {
130                 count++;
131             }
132         }
133     }
134     return count;
135 }
136
137 char *getAuthFakehost(char *auth) {
138     int i;
139     struct UserNode *user;
140     for(i = 0; i < VALID_NICK_CHARS_FIRST_LEN; i++) {
141         for(user = userList[i]; user; user = user->next) {
142             if((user->flags & USERFLAG_ISAUTHED) && !strcmp(user->auth, auth) && isFakeHost(user->host)) {
143                 return user->host;
144             }
145         }
146     }
147     return NULL;
148 }
149
150 struct UserNode* getAllUsers(struct UserNode *last) {
151     if(last == NULL || last->next == NULL) {
152         int cindex;
153         if(last == NULL)
154             cindex = 0;
155         else
156             cindex = get_nicklist_entry(last->nick[0]) + 1;
157         while(userList[cindex] == NULL && cindex < VALID_NICK_CHARS_FIRST_LEN)
158             cindex++;
159         if(cindex > VALID_NICK_CHARS_FIRST_LEN) return NULL;
160         return userList[cindex];
161     } else
162         return last->next;
163 }
164
165 struct UserNode* getUsersWithAuth(const char *auth, struct UserNode *last) {
166     int cindex = (last ? get_nicklist_entry(last->nick[0]) : 0);
167     struct UserNode *cuser = last;
168     while(cindex <= VALID_NICK_CHARS_FIRST_LEN) {
169         for(cuser = (cuser ? cuser->next : userList[cindex]); cuser; cuser = cuser->next) {
170             if((cuser->flags & USERFLAG_ISAUTHED) && !strcmp(cuser->auth, auth))
171                 return cuser;
172         }
173         cindex++;
174         cuser = NULL;
175     }
176     return NULL;
177 }
178
179 int getUserCount() {
180     int i, count = 0;
181     struct UserNode *user;
182     for(i = 0; i < VALID_NICK_CHARS_FIRST_LEN; i++) {
183         for(user = userList[i]; user; user = user->next) {
184             count++;
185         }
186     }
187     return count;
188 }
189
190 struct UserNode* addUser(const char *nick) {
191     int userListIndex = get_nicklist_entry(*nick);
192     if(userListIndex == -1 || !is_valid_nick(nick))
193         return NULL;
194     struct UserNode *user = malloc(sizeof(*user));
195     if (!user)
196     {
197         perror("malloc() failed");
198         return NULL;
199     }
200     strcpy(user->nick, nick);
201     user->created = time(0);
202     user->ident[0] = 0;
203     user->host[0] = 0;
204     user->ip = NULL;
205     user->realname[0] = 0;
206     user->flags = 0;
207     user->channel = NULL;
208     user->last_who = 0;
209     SYNCHRONIZE(cache_sync);
210     user->next = userList[userListIndex];
211     userList[userListIndex] = user;
212     DESYNCHRONIZE(cache_sync);
213     return user;
214 }
215
216 struct UserNode* addUserMask(const char *mask) {
217     char cmask[strlen(mask)+1];
218     strcpy(cmask, mask);
219     int i, ii = 0;
220     struct UserNode *user = NULL;
221     for(i = 0; i < strlen(mask)+1; i++) {
222         if(cmask[i] == '!') {
223             cmask[i] = 0;
224             user = addUser(cmask);
225             if(user == NULL) return NULL;
226             ii = i+1;
227         } else if(cmask[i] == '.' && !user) {
228             //it's a server
229             return NULL;
230         } else if(cmask[i] == '@') {
231             if(user == NULL) return NULL;
232             cmask[i] = 0;
233             strcpy(user->ident, &cmask[ii]);
234             ii = i+1;
235         } else if(cmask[i] == '\0') {
236             if(user == NULL) return NULL;
237             strcpy(user->host, &cmask[ii]);
238         }
239     }
240     return user;
241 }
242
243 struct UserNode* createTempUser(const char *nick) {
244     int already_on_list = 0;
245     struct UserNode *user = NULL;
246     if(!is_valid_nick(nick)) {
247         return NULL;
248     }
249     for(user = userList[TEMPUSER_LIST_INDEX]; user; user = user->next) {
250         if(!stricmp(user->nick, nick)) {
251             already_on_list = 1;
252             break;
253         }
254     }
255     if(!user) {
256         user = malloc(sizeof(*user));
257         if (!user) {
258             perror("malloc() failed");
259             return NULL;
260         }
261         user->ident[0] = 0;
262         user->host[0] = 0;
263         user->ip = NULL;
264         user->realname[0] = 0;
265         user->flags = 0;
266         user->channel = NULL;
267         user->last_who = 0;
268     } else
269         user->flags &= ~USERFLAG_FREETMPUSER;
270     user->created = time(0);
271     if(user->created - user->last_who > REWHO_TIMEOUT)
272         user->flags &= ~USERFLAG_ISAUTHED; //remove authed flag (security reasons)
273     strcpy(user->nick, nick);
274     if(!already_on_list) {
275         SYNCHRONIZE(cache_sync);
276         user->next = userList[TEMPUSER_LIST_INDEX];
277         userList[TEMPUSER_LIST_INDEX] = user;
278         DESYNCHRONIZE(cache_sync);
279     }
280     return user;
281 }
282
283 struct UserNode* createTempUserMask(const char *mask) {
284     //note: it could also be a server we have to create a temponary user for...
285     char cmask[strlen(mask)+1];
286     strcpy(cmask, mask);
287     int i, ii = 0;
288     int already_on_list = 0;
289     struct UserNode *user = NULL;
290     for(i = 0; i < strlen(mask)+1; i++) {
291         if(cmask[i] == '!') {
292             cmask[i] = 0;
293             if(!is_valid_nick(cmask)) {
294                 return NULL;
295             }
296             for(user = userList[TEMPUSER_LIST_INDEX]; user; user = user->next) {
297                 if(!stricmp(user->nick, cmask)) {
298                     already_on_list = 1;
299                     break;
300                 }
301             }
302             if(!user) {
303                 user = malloc(sizeof(*user));
304                 if (!user) {
305                     perror("malloc() failed");
306                     return NULL;
307                 }
308                 user->ident[0] = 0;
309                 user->host[0] = 0;
310                 user->ip = NULL;
311                 user->realname[0] = 0;
312                 user->flags = 0;
313                 user->channel = NULL;
314                 user->last_who = 0;
315             } else
316                 user->flags &= ~USERFLAG_FREETMPUSER;
317             user->created = time(0);
318             if(user->created - user->last_who > REWHO_TIMEOUT)
319                 user->flags &= ~USERFLAG_ISAUTHED; //remove authed flag (security reasons)
320             strcpy(user->nick, cmask);
321             ii = i+1;
322         } else if(cmask[i] == '.' && !user) {
323             //it's a server
324             user = malloc(sizeof(*user));
325             if (!user)
326             {
327                 perror("malloc() failed");
328                 return NULL;
329             }
330             strcpy(user->host, cmask);
331             user->created = time(0);
332             user->ident[0] = 0;
333             user->host[0] = 0;
334             user->ip = NULL;
335             user->realname[0] = 0;
336             user->flags = USERFLAG_ISSERVER;
337             user->channel = NULL;
338             user->last_who = 0;
339             return user;
340         } else if(cmask[i] == '@') {
341             if(user == NULL) return NULL;
342             cmask[i] = 0;
343             strcpy(user->ident, &cmask[ii]);
344             ii = i+1;
345         } else if(cmask[i] == '\0') {
346             if(user == NULL) {
347                 //nick only
348                 user = malloc(sizeof(*user));
349                 if (!user)
350                 {
351                     perror("malloc() failed");
352                     return NULL;
353                 }
354                 strcpy(user->nick, cmask);
355                 user->created = time(0);
356                 user->ident[0] = 0;
357                 user->host[0] = 0;
358                 user->ip = NULL;
359                 user->realname[0] = 0;
360                 user->flags = 0;
361                 user->channel = NULL;
362                 user->last_who = 0;
363                 break;
364             }
365             strcpy(user->host, &cmask[ii]);
366         }
367     }
368     if(!already_on_list) {
369         SYNCHRONIZE(cache_sync);
370         user->next = userList[TEMPUSER_LIST_INDEX];
371         userList[TEMPUSER_LIST_INDEX] = user;
372         DESYNCHRONIZE(cache_sync);
373     }
374     return user;
375 }
376
377 int renameUser(struct UserNode* user, const char *new_nick) {
378     if(!is_valid_nick(new_nick))
379         return 0;
380     if(user->nick[0] == *new_nick) {
381         strcpy(user->nick, new_nick);
382         return 1;
383     }
384     //delUser(user, 0); //EPIC FAIL! This deletes the user from the channel Userlist -.-
385     //manually remove the user from the old userList
386     SYNCHRONIZE(cache_sync);
387     int userListIndex = get_nicklist_entry(user->nick[0]);
388     if(userListIndex != -1) {
389         struct UserNode *cuser, *last_user = NULL;
390         for(cuser = userList[userListIndex]; cuser; cuser = cuser->next) {
391             if(cuser == user) {
392                 if(last_user)
393                     last_user->next = user->next;
394                 else
395                     userList[userListIndex] = user->next;
396                 break;
397             } else
398                 last_user = cuser;
399         }
400     }
401     userListIndex = get_nicklist_entry(*new_nick);
402     strcpy(user->nick, new_nick);
403     user->next = userList[userListIndex];
404     userList[userListIndex] = user;
405     DESYNCHRONIZE(cache_sync);
406     return 1;
407 }
408
409 void delUser(struct UserNode* user, int freeUser) {
410     int userListIndex = ((user->flags & USERFLAG_ISTMPUSER) ? TEMPUSER_LIST_INDEX : get_nicklist_entry(user->nick[0]));
411     if(userListIndex == -1) return;
412     SYNCHRONIZE(cache_sync);
413     event_freeuser(user);
414     struct UserNode *cuser, *last_user = NULL;
415     for(cuser = userList[userListIndex]; cuser; cuser = cuser->next) {
416         if(cuser == user) {
417             if(last_user)
418                 last_user->next = user->next;
419             else
420                 userList[userListIndex] = user->next;
421             break;
422         } else
423             last_user = cuser;
424     }
425     if(freeUser && (user->flags & USERFLAG_IS_ON_WHO_QUEUE)) {
426         user->flags |= USERFLAG_FREE_AFTER_WHO;
427         freeUser = 0;
428     }
429     if(user->channel) {
430         struct ChanUser *chanUser, *next;
431         for(chanUser = user->channel; chanUser; chanUser = next) {
432             next = chanUser->next_chan;
433             removeChanUserFromLists(chanUser, 1, 0, freeUser);
434         }
435     }
436     if(freeUser) {
437         if(user->ip)
438             freeIPNode(user->ip);
439         free(user);
440     } else
441         user->next = NULL;
442     DESYNCHRONIZE(cache_sync);
443 }
444
445 void clearTempUsers() {
446     SYNCHRONIZE(cache_sync);
447     int userListIndex = TEMPUSER_LIST_INDEX;
448     struct UserNode *cuser, *next;
449     time_t now = time(0);
450     for(cuser = userList[userListIndex]; cuser; cuser = next) {
451         next = cuser->next;
452         if(cuser->flags & USERFLAG_FREETMPUSER || now - cuser->created >= 300) {
453             delUser(cuser, 1);
454         }
455     }
456     DESYNCHRONIZE(cache_sync);
457 }