added cmd_nicklist
[NeonServV5.git] / src / tools.c
1 /* tools.c - NeonServ v5.2
2  * Copyright (C) 2011  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 "tools.h"
18 #include "UserNode.h"
19 #include "ChanNode.h"
20 #include "lang.h"
21 #include "ClientSocket.h"
22
23 static const struct default_language_entry msgtab[] = {
24     {"TIME_MASK_2_ITEMS", "%s and %s"}, /* {ARGS: "2 days", "1 hour"} */
25     {"TIME_MASK_3_ITEMS", "%s, %s and %s"}, /* {ARGS: "2 days", "1 hour", "20 minutes"} */
26     {"TIME_YEAR", "year"},
27     {"TIME_YEARS", "years"},
28     {"TIME_MONTH", "month"},
29     {"TIME_MONTHS", "months"},
30     {"TIME_WEEK", "week"},
31     {"TIME_WEEKS", "weeks"},
32     {"TIME_DAY", "day"},
33     {"TIME_DAYS", "days"},
34     {"TIME_HOUR", "hour"},
35     {"TIME_HOURS", "hours"},
36     {"TIME_MINUTE", "minute"},
37     {"TIME_MINUTES", "minutes"},
38     {"TIME_SECOND", "second"},
39     {"TIME_SECONDS", "seconds"},
40     {NULL, NULL}
41 };
42
43 /* copied from IRCU 2.10.12 match.c */
44 /*
45  * Compare if a given string (name) matches the given
46  * mask (which can contain wild cards: '*' - match any
47  * number of chars, '?' - match any single character.
48  *
49  * return  0, if match
50  *         1, if no match
51  *
52  *  Originally by Douglas A Lewis (dalewis@acsu.buffalo.edu)
53  *  Rewritten by Timothy Vogelsang (netski), net@astrolink.org
54  */
55 int match(const char *mask, const char *name)
56 {
57   const char *m = mask, *n = name;
58   const char *m_tmp = mask, *n_tmp = name;
59   int star_p;
60
61   for (;;) switch (*m) {
62   case '\0':
63     if (!*n)
64       return 0;
65   backtrack:
66     if (m_tmp == mask)
67       return 1;
68     m = m_tmp;
69     n = ++n_tmp;
70     if (*n == '\0')
71       return 1;
72     break;
73   case '\\':
74     m++;
75     /* allow escaping to force capitalization */
76     if (*m++ != *n++)
77       goto backtrack;
78     break;
79   case '*': case '?':
80     for (star_p = 0; ; m++) {
81       if (*m == '*')
82         star_p = 1;
83       else if (*m == '?') {
84         if (!*n++)
85           goto backtrack;
86       } else break;
87     }
88     if (star_p) {
89       if (!*m)
90         return 0;
91       else if (*m == '\\') {
92         m_tmp = ++m;
93         if (!*m)
94           return 1;
95         for (n_tmp = n; *n && *n != *m; n++) ;
96       } else {
97         m_tmp = m;
98         for (n_tmp = n; *n && tolower(*n) != tolower(*m); n++) ;
99       }
100     }
101     /* and fall through */
102   default:
103     if (!*n)
104       return *m != '\0';
105     if (tolower(*m) != tolower(*n))
106       goto backtrack;
107     m++;
108     n++;
109     break;
110   }
111 }
112
113
114 //TABLES
115 struct Table *table_init(int width, int length, int flags) {
116     int row;
117     struct Table *table = malloc(sizeof(*table));
118     table->contents = malloc(length * sizeof(*table->contents));
119     for(row = 0; row < length; row++) {
120         table->contents[row] = calloc(width, sizeof(*table->contents[row]));
121     }
122     table->length = length;
123     table->width = width;
124     table->flags = flags;
125     table->col_flags = calloc(width, sizeof(int));
126     table->entrys = 0;
127     table->maxwidth = calloc(width, sizeof(int));
128     table->table_lines = NULL;
129     return table;
130 }
131
132 int table_add(struct Table *table, char **entry) {
133     int col;
134     if(table->entrys == table->length) return 0;
135     for(col = 0; col < table->width; col++) {
136         table->contents[table->entrys][col] = ((table->flags & TABLE_FLAG_USE_POINTER) || !entry[col] ? entry[col] : strdup(entry[col]));
137         if(table->contents[table->entrys][col])
138             table->col_flags[col] |= TABLE_FLAG_COL_CONTENTS;
139         if(entry[col] && strlen(entry[col]) > table->maxwidth[col])
140             table->maxwidth[col] = strlen(entry[col]);
141     }
142     table->entrys++;
143     return 1;
144 }
145
146 int table_change(struct Table *table, int row, char **entry) {
147     int col;
148     if(row >= table->length) return 0;
149     for(col = 0; col < table->width; col++) {
150         if(table->contents[row][col] && !(table->flags & TABLE_FLAG_USE_POINTER))
151             free(table->contents[row][col]);
152         table->contents[row][col] = ((table->flags & TABLE_FLAG_USE_POINTER) || !entry[col] ? entry[col] : strdup(entry[col]));
153         if(table->contents[row][col])
154             table->col_flags[col] |= TABLE_FLAG_COL_CONTENTS;
155         if(entry[col] && strlen(entry[col]) > table->maxwidth[col])
156             table->maxwidth[col] = strlen(entry[col]);
157     }
158     return 1;
159 }
160
161 int table_change_field(struct Table *table, int row, int col, char *entry) {
162     if(row >= table->length) return 0;
163     if(col >= table->width) return 0;
164     if(table->contents[row][col] && !(table->flags & TABLE_FLAG_USE_POINTER))
165         free(table->contents[row][col]);
166     table->contents[row][col] = (((table->flags & TABLE_FLAG_USE_POINTER) || !entry) ? entry : strdup(entry));
167     if(table->contents[row][col])
168         table->col_flags[col] |= TABLE_FLAG_COL_CONTENTS;
169     if(entry && strlen(entry) > table->maxwidth[col])
170         table->maxwidth[col] = strlen(entry);
171     return 1;
172 }
173
174 int table_set_bold(struct Table *table, int collum, int bold) {
175     if(bold)
176         table->col_flags[collum] |= TABLE_FLAG_COL_BOLD;
177     else
178         table->col_flags[collum] &= ~TABLE_FLAG_COL_BOLD;
179     return 1;
180 }
181
182 char **table_end(struct Table *table) {
183     int row, col, tablewidth = 0, pos,i,j,k;
184     if(!table->entrys) return NULL;
185     for(col = 0; col < table->width; col++) {
186         tablewidth += table->maxwidth[col]+1;
187         if(table->col_flags[col] & TABLE_FLAG_COL_BOLD)
188             tablewidth += 2;
189     }
190     table->table_lines = malloc(table->entrys * sizeof(table->table_lines));
191     for(row = 0; row < table->entrys; row++) {
192         table->table_lines[row] = malloc(tablewidth * sizeof(*table->table_lines[row]));
193         pos = 0;
194         for(col = 0; col < table->width; col++) {
195             if(!(table->col_flags[col] & TABLE_FLAG_COL_CONTENTS)) continue;
196             if(table->col_flags[col] & TABLE_FLAG_COL_BOLD)
197                 table->table_lines[row][pos++] = '\002';
198             i = 0;
199             j = 0;
200             if(table->contents[row][col]) {
201                 for(; i < strlen(table->contents[row][col]); i++) {
202                     table->table_lines[row][pos++] = table->contents[row][col][i];
203                     if(table->contents[row][col][i] == '\002') j++;
204                     else if(table->contents[row][col][i] == '\003') {
205                         j++;
206                         for(k = 1; k < 2; k++) {
207                             if(isdigit(table->contents[row][col][i+k]))
208                                 j++;
209                             else 
210                                 break;
211                         }
212                     }
213                 }
214             }
215             i -= j;
216             if(col < table->width-1) {
217                 for(;i < table->maxwidth[col]; i++) {
218                     table->table_lines[row][pos++] = ' ';
219                 }
220                 table->table_lines[row][pos++] = ' ';
221             } else
222                 table->table_lines[row][pos++] = '\0';
223             
224             if(table->col_flags[col] & TABLE_FLAG_COL_BOLD)
225                 table->table_lines[row][pos++] = '\002';
226         }
227     }
228     return table->table_lines;
229 }
230
231 void table_free(struct Table *table) {
232     int row, col;
233     for(row = 0; row < table->length; row++) {
234         if(!(table->flags & TABLE_FLAG_USE_POINTER) && table->entrys > row) {
235             for(col = 0; col < table->width; col++) {
236                 if(table->contents[row][col])
237                     free(table->contents[row][col]);
238             }
239         }
240         free(table->contents[row]);
241     }
242     free(table->contents);
243     free(table->col_flags);
244     free(table->maxwidth);
245     if(table->table_lines) {
246         for(row = 0; row < table->entrys; row++) {
247             free(table->table_lines[row]);
248         }
249         free(table->table_lines);
250     }
251     free(table);
252 }
253
254 char* timeToStr(struct UserNode *user, int seconds, int items, char *buf) {
255     char item[items][MAXLEN];
256     int tmp, citem = 0;
257     if(citem != items && seconds >= 31536000) { //60*60*24*365 = 31536000
258         
259         tmp = seconds / 31536000;
260         sprintf(item[citem++], "%d %s", tmp, get_language_string(user, tmp == 1 ? "TIME_YEAR" : "TIME_YEARS"));
261         seconds -= tmp * 31536000;
262     }
263     if(citem != items && seconds >= 86400) { //60*60*24 = 86400
264         tmp = seconds / 86400;
265         sprintf(item[citem++], "%d %s", tmp, get_language_string(user, tmp == 1 ? "TIME_DAY" : "TIME_DAYS"));
266         seconds -= tmp * 86400;
267     }
268     if(citem != items && seconds >= 3600) { //60*60 = 3600
269         tmp = seconds / 3600;
270         sprintf(item[citem++], "%d %s", tmp, get_language_string(user, tmp == 1 ? "TIME_HOUR" : "TIME_HOURS"));
271         seconds -= tmp * 3600;
272     }
273     if(citem != items && seconds >= 60) {
274         tmp = seconds / 60;
275         sprintf(item[citem++], "%d %s", tmp, get_language_string(user, tmp == 1 ? "TIME_MINUTE" : "TIME_MINUTES"));
276         seconds -= tmp * 60;
277     }
278     if(citem != items && seconds >= 1) {
279         sprintf(item[citem++], "%d %s", seconds, get_language_string(user, seconds == 1 ? "TIME_SECOND" : "TIME_SECONDS"));
280     }
281     if(citem == 2) {
282         build_language_string(user, buf, "TIME_MASK_2_ITEMS", item[0], item[1]);
283     } else if(citem == 3) {
284         build_language_string(user, buf, "TIME_MASK_3_ITEMS", item[0], item[1], item[2]);
285     } else {
286         int i, ii, p = 0;
287         for(i = 0; i < citem; i++) {
288             for(ii = 0; ii < strlen(item[i]); ii++) {
289                 buf[p++] = item[i][ii];
290             }
291             buf[p++] = ' ';
292         }
293         buf[(p ? p-1 : 0)] = '\0';
294     }
295     return buf;
296 }
297
298 int strToTime(struct UserNode *user, char *str) {
299     /*
300     * y = year = 365 days
301     * M = month = 30 days
302     * w = week = 7 days
303     * d = day
304     * h = hour
305     * m = minute
306     * (s) = second
307     */
308     int total_time = 0, cvalue;
309     char *p, tmpchar;
310     int unit_multiplikator;
311     while(*str) {
312         p = str;
313         while(*p && !isdigit(*p)) //skip leading chars
314             p++;
315         str = p;
316         while(*p && isdigit(*p)) //get the value
317             p++;
318         tmpchar = *p;
319         *p = '\0';
320         cvalue = isdigit(*str) ? atoi(str) : 0;
321         *p = tmpchar;
322         while(*p == ' ') //skip spaces
323             p++;
324         str = p;
325         while(*p && !isdigit(*p)) //get the unit
326             p++;
327         tmpchar = *p;
328         *p = '\0';
329         if(p - str > 1) { //unit has more than one char
330             if(!stricmp(str, "year") || !stricmp(str, "year") || !stricmp(str, get_language_string(user, "TIME_YEAR")) || !stricmp(str, get_language_string(user, "TIME_YEARS")))
331                 unit_multiplikator = 31536000; //60*60*24*365 = 31536000
332             else if(!stricmp(str, "month") || !stricmp(str, "months") || !stricmp(str, get_language_string(user, "TIME_MONTH")) || !stricmp(str, get_language_string(user, "TIME_MONTHS")))
333                 unit_multiplikator = 2592000; //60*60*24*30 = 2592000
334             else if(!stricmp(str, "week") || !stricmp(str, "weeks") || !stricmp(str, get_language_string(user, "TIME_WEEK")) || !stricmp(str, get_language_string(user, "TIME_WEEKS")))
335                 unit_multiplikator = 604800; //60*60*24*7 = 604800
336             else if(!stricmp(str, "day") || !stricmp(str, "days") || !stricmp(str, get_language_string(user, "TIME_DAY")) || !stricmp(str, get_language_string(user, "TIME_DAYS")))
337                 unit_multiplikator = 86400; //60*60*24 = 86400
338             else if(!stricmp(str, "hour") || !stricmp(str, "hours") || !stricmp(str, get_language_string(user, "TIME_HOUR")) || !stricmp(str, get_language_string(user, "TIME_HOURS")))
339                 unit_multiplikator = 3600; //60*60 = 3600
340             else if(!stricmp(str, "minute") || !stricmp(str, "minutes") || !stricmp(str, "min") || !stricmp(str, "mins") || !stricmp(str, get_language_string(user, "TIME_MINUTE")) || !stricmp(str, get_language_string(user, "TIME_MINUTES")))
341                 unit_multiplikator = 60;
342             else
343                 unit_multiplikator = 1;
344         } else {
345             switch(*str) {
346                 case 'y':
347                     unit_multiplikator = 31536000; //60*60*24*365 = 31536000
348                     break;
349                 case 'M':
350                     unit_multiplikator = 2592000; //60*60*24*30 = 2592000
351                     break;
352                 case 'w':
353                     unit_multiplikator = 604800; //60*60*24*7 = 604800
354                     break;
355                 case 'd':
356                     unit_multiplikator = 86400; //60*60*24 = 86400
357                     break;
358                 case 'h':
359                     unit_multiplikator = 3600; //60*60 = 3600
360                     break;
361                 case 'm':
362                     unit_multiplikator = 60;
363                     break;
364                 default:
365                     unit_multiplikator = 1;
366                     break;
367             }
368         }
369         total_time += (cvalue * unit_multiplikator);
370         *p = tmpchar;
371         str = p;
372     }
373     return total_time;
374 }
375
376 struct ModeBuffer* initModeBuffer(struct ClientSocket *client, struct ChanNode *chan) {
377     struct ModeBuffer *modeBuf = malloc(sizeof(*modeBuf));
378     if(!modeBuf) {
379         perror("malloc() failed");
380         return NULL;
381     }
382     modeBuf->client = client;
383     modeBuf->chan = chan;
384     modeBuf->addCount = 0;
385     modeBuf->delCount = 0;
386     return modeBuf;
387 }
388
389 void modeBufferSet(struct ModeBuffer *modeBuf, int add, char mode, char *param) {
390     if(add) {
391         modeBuf->addModes[modeBuf->addCount] = mode;
392         modeBuf->addModesParams[modeBuf->addCount] = (param ? strdup(param) : NULL);
393         modeBuf->addCount++;
394         modeBuf->addModes[modeBuf->addCount] = '\0';
395     } else {
396         modeBuf->delModes[modeBuf->delCount] = mode;
397         modeBuf->delModesParams[modeBuf->delCount] = (param ? strdup(param) : NULL);
398         modeBuf->delCount++;
399         modeBuf->delModes[modeBuf->delCount] = '\0';
400     }
401     if(modeBuf->addCount + modeBuf->delCount == MAXMODES)
402         flushModeBuffer(modeBuf);
403 }
404
405 void flushModeBuffer(struct ModeBuffer *modeBuf) {
406     char modeStr[MAXMODES+3];
407     int modePos = 0;
408     char paramStr[MAXLEN];
409     *paramStr = '\0';
410     int paramPos = 0;
411     int i;
412     if(modeBuf->addCount) {
413         modeStr[modePos++] = '+';
414         for(i = 0; i < modeBuf->addCount; i++) {
415             modeStr[modePos++] = modeBuf->addModes[i];
416             if(modeBuf->addModesParams[i]) {
417                 paramPos += sprintf(paramStr + paramPos, " %s", modeBuf->addModesParams[i]);
418             }
419         }
420         modeBuf->addCount = 0;
421     }
422     if(modeBuf->delCount) {
423         modeStr[modePos++] = '-';
424         for(i = 0; i < modeBuf->delCount; i++) {
425             modeStr[modePos++] = modeBuf->delModes[i];
426             if(modeBuf->delModesParams[i]) {
427                 paramPos += sprintf(paramStr + paramPos, " %s", modeBuf->delModesParams[i]);
428             }
429         }
430         modeBuf->delCount = 0;
431     }
432     modeStr[modePos++] = '\0';
433     putsock(modeBuf->client, "MODE %s %s%s", modeBuf->chan->name, modeStr, paramStr);
434 }
435
436 void freeModeBuffer(struct ModeBuffer *modeBuf) {
437     if(modeBuf->addCount + modeBuf->delCount)
438         flushModeBuffer(modeBuf);
439     free(modeBuf);
440 }
441
442 int is_ircmask(const char *text) {
443     while (*text && (isalnum((char)*text) || strchr("-_[]|\\`^{}?*", *text)))
444         text++;
445     if (*text++ != '!')
446         return 0;
447     while (*text && *text != '@' && !isspace((char)*text))
448         text++;
449     if (*text++ != '@')
450         return 0;
451     while (*text && !isspace((char)*text))
452         text++;
453     return !*text;
454 }
455
456 char* generate_banmask(struct UserNode *user, char *buffer) {
457     char *userhost = user->host;
458     
459     if(isFakeHost(user->host)) {
460         sprintf(buffer, "*!*@%s", userhost);
461         return buffer;
462     }
463     
464     //check if the hostname has more than 4 connections (trusted host)
465     if(countUsersWithHost(userhost) > 4) {
466         sprintf(buffer, "*!%s@%s", user->ident, userhost);
467         return buffer;
468     } else {
469         sprintf(buffer, "*!*@%s", userhost);
470         return buffer;
471     }
472 }
473
474 char* make_banmask(char *input, char* buffer) {
475     char *nick = NULL, *ident = NULL, *host = NULL;
476     char tmp[HOSTLEN];
477     char *p;
478     if((p = strstr(input, "!"))) {
479         nick = input;
480         *p = '\0';
481         ident = p+1;
482         if((p = strstr(ident, "@"))) {
483             *p = '\0';
484             host = p+1;
485         }
486     } else if((p = strstr(input, "@"))) {
487         ident = input;
488         *p = '\0';
489         host = p+1;
490     } else if((p = strstr(input, "."))) {
491         host = input;
492     } else if(*input == '*' && input[1] != '\0' && !strstr(input+1, "*")) {
493         //AUTH MASK
494         p = getAuthFakehost(input+1);
495         if(p)
496             host = p;
497         else {
498             sprintf(tmp, "%s.*", input+1);
499             host = tmp;
500         }
501     } else {
502         struct UserNode *user = searchUserByNick(input);
503         if(user)
504             return generate_banmask(user, buffer);
505         else
506             nick = input;
507     }
508     if(nick && *nick == '\0') nick = NULL;
509     if(ident && *ident == '\0') ident = NULL;
510     if(host && *host == '\0') host = NULL;
511     sprintf(buffer, "%s!%s@%s", (nick ? nick : "*"), (ident ? ident : "*"), (host ? host : "*"));
512     return buffer;
513 }
514
515 int isFakeHost(char *host) {
516     char *p1, *p2 = host;
517     
518     //find the last dot to identify if the hostmask is a fake host
519     while((p1 = strstr(p2, "."))) {
520         p2 = p1 + 1;
521     }
522     //TLD database: http://www.iana.org/domains/root/db/
523     //the longest TLD i found was 6 chars long (ignoring the stange exotic ones :D)
524     //but we even ignore '.museum' and '.travel' so we can say that the TLD of our mask needs to be less than 4 chars to be a real domain
525     return (strlen(p2+1) > 4);
526 }
527
528 static unsigned long crc_table[256];
529
530 static void crc32_init() {
531     unsigned long crc;
532     int i, j;
533     for(i = 0; i < 256; i++) {
534         crc = i;
535         for(j = 8; j > 0; j--) {
536             if(crc & 1)
537                                 crc = (crc >> 1) ^ 0xEDB88320L;
538             else
539                 crc >>= 1;
540         }
541         crc_table[i] = crc;
542     }
543 }
544
545 unsigned long crc32(const char *text) {
546     register unsigned long crc = 0xFFFFFFFF;
547     unsigned int c, i = 0;
548     while((c = (unsigned int)text[i++]) != 0)
549         crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_table[(crc^c) & 0xFF];
550     return (crc^0xFFFFFFFF);
551 }
552
553 void init_tools() {
554     register_default_language_table(msgtab);
555     crc32_init();
556 }