fixed crash caused by startup reload event
[NeonServV5.git] / src / main.c
1 /* main.c - NeonServ v5.5
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
18 #include "main.h"
19 #include "signal.h"
20 #include "ClientSocket.h"
21 #include "UserNode.h"
22 #include "ChanNode.h"
23 #include "IRCEvents.h"
24 #include "IRCParser.h"
25 #include "modcmd.h"
26 #include "WHOHandler.h"
27 #include "bots.h"
28 #include "mysqlConn.h"
29 #include "HandleInfoHandler.h"
30 #include "lang.h"
31 #include "tools.h"
32 #include "timeq.h"
33 #include "EventLogger.h"
34 #include "ModeNode.h"
35 #include "IRCQueue.h"
36 #include "DBHelper.h"
37 #include "ConfigParser.h"
38 #include "ssl.h"
39 #include "QServer.h"
40 #include "version.h"
41 #include "modules.h"
42 #include "module_commands.h"
43 #include "ModuleFunctions.h"
44
45 time_t start_time;
46 static int running, hard_restart;
47 static int statistics_requested_lusers = 0;
48 int statistics_enabled;
49 TIMEQ_CALLBACK(main_statistics);
50 TIMEQ_CALLBACK(main_checkauths);
51 static int daemonized = 0;
52 static int print_loglevel = 0;
53 static FILE *log_fptr = NULL;
54 static int process_argc;
55 static char **process_argv;
56 #ifdef HAVE_THREADS
57 int running_threads;
58 pthread_mutex_t cache_sync;
59 pthread_mutex_t whohandler_sync, whohandler_mass_sync;
60 static pthread_mutex_t log_sync;
61 #endif
62
63 static void check_firstrun();
64
65 void cleanup() {
66     stop_modules();
67     free_sockets();
68     qserver_free();
69     free_parser();
70     free_UserNode();
71     free_ChanNode();
72     free_bind();
73     free_modcmd();
74     free_whoqueue();
75     free_mysql();
76     free_handleinfohandler();
77     free_lang();
78 }
79
80 static int load_mysql_config() {
81     char *mysql_host, *mysql_user, *mysql_pass, *mysql_base;
82     int mysql_serverport;
83     
84     mysql_host = get_string_field("MySQL.host");
85     if(!mysql_host) {
86         perror("invalid neonserv.conf: missing MySQL.host");
87         return 0;
88     }
89     mysql_serverport = get_int_field("MySQL.port");
90     if(!mysql_serverport)
91         mysql_serverport = 3306;
92     mysql_user = get_string_field("MySQL.user");
93     if(!mysql_user) {
94         perror("invalid neonserv.conf: missing MySQL.user");
95         return 0;
96     }
97     mysql_pass = get_string_field("MySQL.pass");
98     if(!mysql_pass) {
99         perror("invalid neonserv.conf: missing MySQL.pass");
100         return 0;
101     }
102     mysql_base = get_string_field("MySQL.base");
103     if(!mysql_base) {
104         perror("invalid neonserv.conf: missing MySQL base");
105         return 0;
106     }
107     init_mysql(mysql_host, mysql_serverport, mysql_user, mysql_pass, mysql_base);
108     return 1;
109 }
110
111 #ifdef HAVE_THREADS
112 pthread_t *current_threads = NULL;
113
114 void * thread_main(void *arg) {
115     time_t socket_wait;
116     while(running) {
117         socket_wait = time(0) + SOCKET_SELECT_TIME;
118         do {
119             if(!socket_loop(SOCKET_SELECT_TIME)) {
120                 if(!running) break;
121                 putlog(LOGLEVEL_ERROR, "No more active Bots... shutting down.\n");
122                 cleanup();
123                 exit(0);
124             }
125         } while(time(0) < socket_wait);
126         if(!running) break;
127         clearTempUsers();
128         destroyEvents();
129         mysql_free();
130     }
131     running_threads--;
132     return NULL;
133 }
134
135 int getCurrentThreadID() {
136     if(!current_threads) return 0;
137     int i;
138     unsigned int my_tid = (unsigned int) pthread_self_tid();
139     for(i = 0; i < running_threads; i++) {
140         #ifdef WIN32
141         if((unsigned int) current_threads[i].p == my_tid)
142         #else
143         if((unsigned int) current_threads[i] == my_tid)
144         #endif
145             return i+1;
146     }
147     return 0;
148 }
149
150 #endif
151
152 void exit_daemon() {
153     running = 0;
154     if(daemonized) {
155         remove(PID_FILE);
156     }
157     if(log_fptr) {
158         fclose(log_fptr);
159         log_fptr = NULL;
160     }
161 }
162
163 int main(int argc, char *argv[]) {
164     int run_as_daemon = 1;
165     int c;
166     process_argv = argv;
167     process_argc = argc;
168     printf("NeonServ v%s\n\n", NEONSERV_VERSION);
169     struct option options[] = {
170         {"show", 1, 0, 's'},
171         {"foreground", 0, 0, 'f'},
172         {"help", 0, 0, 'h'},
173         {"version", 0, 0, 'v'},
174         {0, 0, 0, 0}
175     };
176     while ((c = getopt_long(argc, argv, "s:fvh", options, NULL)) != -1) {
177         switch (c) {
178         case 's':
179             print_loglevel = atoi(optarg);
180             break;
181         case 'f':
182             run_as_daemon = 0;
183             break;
184         case 'v':
185             printf("Version: %s.%d (%s)\n", NEONSERV_VERSION, patchlevel, (strcmp(revision, "") ? revision : "-"));
186             printf("Build: #%s %s (%s lines, " COMPILER ")\n", compilation, creation, codelines);
187             exit(0);
188             break;
189         case 'h':
190             printf("Usage: ./neonserv [-s loglevel] [-f] [-h|-v]\n");
191             printf(" -s, --show           show log lines matching loglevel in stdout.\n");
192             printf(" -f, --foreground     run NeonServ in the foreground.\n");
193             printf(" -h, --help           prints this usage message.\n");
194             printf(" -v, --version        prints this program's version.\n");
195             exit(0);
196             break;
197         }
198     }
199     #ifndef WIN32
200     if(geteuid() == 0 || getuid() == 0) {
201         fprintf(stderr, "NeonServ may not be run with super user privileges.\n");
202         exit(0);
203     }
204     #endif
205     #ifdef ENABLE_MEMORY_DEBUG
206     initMemoryDebug();
207     #endif
208     if(!loadConfig(CONF_FILE)) {
209         fprintf(stderr, "Unable to load " CONF_FILE "\n");
210         exit(0);
211     }
212     init_bind();
213     event_reload(1);
214     #if HAVE_THREADS
215     THREAD_MUTEX_INIT(log_sync);
216     #endif
217     if(!load_mysql_config()) {
218         fprintf(stderr, "Unable to connect to MySQL\n");
219         exit(0);
220     }
221     check_firstrun();
222     char **modulelist = get_all_fieldnames("modules");
223     if(!modulelist || !modulelist[0]) {
224         fprintf(stderr, "no modules loaded... Please update your configuration!\n");
225         exit(0);
226     }
227     free(modulelist);
228     if (run_as_daemon) {
229         #ifndef WIN32
230         /* Attempt to fork into the background if daemon mode is on. */
231         pid_t pid = fork();
232         if (pid < 0) {
233             fprintf(stderr, "Unable to fork: %s\n", strerror(errno));
234         } else if (pid > 0) {
235             printf("Forking into the background (pid: %d)...\n", pid);
236             putlog(LOGLEVEL_INFO, "Forking into the background (pid: %d)...\n", pid);
237             exit(0);
238         }
239         setsid();
240         daemonized = 1;
241         atexit(exit_daemon);
242         FILE *pidfile = fopen(PID_FILE, "w");
243         if (pidfile == NULL) {
244             fprintf(stderr, "Unable to create PID file: %s\n", strerror(errno));
245             putlog(LOGLEVEL_ERROR, "Unable to create PID file: %s\n", strerror(errno));
246         } else {
247             fprintf(pidfile, "%i\n", (int)getpid());
248             fclose(pidfile);
249         }
250                 FILE *retn;
251         fclose(stdin); retn = fopen("/dev/null", "r");
252         fclose(stdout); retn = fopen("/dev/null", "w");
253         fclose(stderr); retn = fopen("/dev/null", "w");
254         #endif
255     }
256     
257 main:
258     signal(SIGABRT, sighandler);
259     signal(SIGFPE, sighandler);
260     signal(SIGILL, sighandler);
261     signal(SIGINT, sighandler);
262     signal(SIGSEGV, sighandler);
263     signal(SIGTERM, sighandler);
264     
265     start_time = time(0);
266     
267     #ifdef WIN32
268     int res;
269     WSADATA wsadata;
270     // Start Windows Sockets.
271     res = WSAStartup(MAKEWORD(2, 0), &wsadata);
272     if (res)
273     {
274         perror("Unable to start Windows Sockets");
275         return 0;
276     }
277     #endif
278     
279     statistics_enabled = get_int_field("statistics.enable");
280     
281     #ifdef HAVE_THREADS
282     THREAD_MUTEX_INIT(cache_sync);
283     THREAD_MUTEX_INIT(whohandler_sync);
284     THREAD_MUTEX_INIT(whohandler_mass_sync);
285     #endif
286     
287     queue_init();
288     init_sockets();
289     init_timeq();
290     init_lang();
291     ssl_init();
292     init_parser();
293     init_UserNode();
294     init_ChanNode();
295     init_ModeNode();
296     init_bind();
297         init_modcmd();
298     register_module_commands();
299     init_handleinfohandler();
300     init_tools();
301     init_modulefunctions();
302     loadModules();
303     init_bots();
304     init_DBHelper();
305     qserver_init();
306     
307     load_languages();
308     int update_minutes = get_int_field("statistics.frequency");
309     if(!update_minutes) update_minutes = 2;
310     timeq_add(update_minutes * 60 + 10, 0, main_statistics, NULL);
311     
312     timeq_add(90, 0, main_checkauths, NULL);
313     
314     int worker_threads = get_int_field("General.worker_threads");
315     if(!worker_threads) worker_threads = 1;
316     running = 1;
317     #ifdef HAVE_THREADS
318     int tid_id = 0;
319     current_threads = calloc(worker_threads, sizeof(*current_threads));
320     for(tid_id = 0; tid_id < worker_threads; tid_id++) {
321         running_threads++;
322         pthread_create(&current_threads[tid_id], NULL, thread_main, NULL);
323     }
324     int usleep_delay = 1000000 / TICKS_PER_SECOND;
325     while(running) {
326         timeq_tick();
327         loop_modules();
328         qserver_loop();
329         queue_loop();
330         mysql_free();
331         usleep(usleep_delay);
332     }
333     for(tid_id = 0; tid_id < worker_threads; tid_id++) {
334         pthread_join(current_threads[tid_id], NULL);
335     }
336     running_threads = 0;
337     #else
338     time_t socket_wait;
339     while(running) {
340         socket_wait = time(0) + SOCKET_SELECT_TIME;
341         do {
342             if(!socket_loop(SOCKET_SELECT_TIME)) {
343                 putlog(LOGLEVEL_ERROR, "No more active Bots... shutting down.\n");
344                 cleanup();
345                 exit(0);
346             }
347         } while(time(0) < socket_wait);
348         timeq_tick();
349         loop_modules();
350         clearTempUsers();
351         destroyEvents();
352         qserver_loop();
353         queue_loop();
354         mysql_free();
355     }
356     #endif
357     cleanup();
358     if(hard_restart) {
359         restart_process();
360     }
361     goto main;
362 }
363
364 void restart_process() {
365     /* Append a NULL to the end of argv[]. */
366     char **restart_argv = (char **)alloca((process_argc + 1) * sizeof(char *));
367     memcpy(restart_argv, process_argv, process_argc * sizeof(char *));
368     restart_argv[process_argc] = NULL;
369     #ifdef WIN32
370     execv(process_argv[0], (const char * const*)restart_argv);
371     #else
372     execv(process_argv[0], restart_argv);
373     #endif
374 }
375
376 int stricmp (const char *s1, const char *s2)
377 {
378    if (s1 == NULL) return s2 == NULL ? 0 : -(*s2);
379    if (s2 == NULL) return *s1;
380    char c1, c2;
381    while ((c1 = tolower (*s1)) == (c2 = tolower (*s2)))
382    {
383      if (*s1 == '\0') break;
384      ++s1; ++s2;
385    }
386    return c1 - c2;
387 }
388
389 int stricmplen (const char *s1, const char *s2, int len)
390 {
391    if (s1 == NULL) return s2 == NULL ? 0 : -(*s2);
392    if (s2 == NULL) return *s1;
393    char c1, c2;
394    int i = 0;
395    while ((c1 = tolower (*s1)) == (c2 = tolower (*s2)))
396    {
397      i++;
398      if (*s1 == '\0') break;
399      ++s1; ++s2;
400      if(i == len) break;
401    }
402    return c1 - c2;
403 }
404
405 void restart_bot(int do_hard_restart) {
406     hard_restart = do_hard_restart;
407     running = 0;
408 }
409
410 void stop_bot() {
411     cleanup();
412     exit(0);
413 }
414
415 void reload_config() {
416     loadConfig(CONF_FILE);
417     event_reload(0);
418 }
419
420 static int getCurrentSecondsOfDay() {
421     time_t now = time(0);
422     struct tm *timeofday = localtime(&now);
423     int seconds = 0;
424     seconds += timeofday->tm_hour * 3600;
425     seconds += timeofday->tm_min * 60;
426     seconds += timeofday->tm_sec;
427     return seconds;
428 }
429
430 static AUTHLOOKUP_CALLBACK(main_checkauths_callback) {
431     //check if registered is still valid
432     MYSQL_RES *res;
433     MYSQL_ROW row;
434     printf_mysql_query("SELECT `user_id`, `user_registered` FROM `users` WHERE `user_user` = '%s'", escape_string(auth));
435     res = mysql_use();
436     if ((row = mysql_fetch_row(res)) != NULL) {
437         int diff = registered - atoi(row[1]);
438         if(diff < 0)
439             diff *= -1;
440         if(!exists || (strcmp(row[1], "0") && diff > 86400)) {
441             //User is no longer valid! Delete it...
442             deleteUser(atoi(row[0]));
443             char *alertchan = get_string_field("General.CheckAuths.alertchan");
444             if(alertchan) {
445                 char reason[MAXLEN];
446                 if(!exists) {
447                     strcpy(reason, "USER_NOT_EXISTS");
448                 } else {
449                     sprintf(reason, "USER_REGISTERED_MISSMATCH: %lu, expected %d (diff: %d)", (unsigned long) registered, atoi(row[1]), diff);
450                 }
451                 struct ChanNode *alertchan_chan = getChanByName(alertchan);
452                 struct ClientSocket *alertclient;
453                 if(alertchan_chan && (alertclient = getChannelBot(alertchan_chan, 0)) != NULL) {
454                     putsock(alertclient, "PRIVMSG %s :Deleted User %s (%s)", alertchan_chan->name, auth, reason);
455                 }
456             }
457         } else if(exists && !strcmp(row[1], "0")) {
458             printf_mysql_query("UPDATE `users` SET `user_registered` = '%lu', `user_lastcheck` = UNIX_TIMESTAMP() WHERE `user_id` = '%s'", (unsigned long) registered, row[0]);
459         } else {
460             printf_mysql_query("UPDATE `users` SET `user_lastcheck` = UNIX_TIMESTAMP() WHERE `user_id` = '%s'", row[0]);
461         }
462     }
463 }
464
465 TIMEQ_CALLBACK(main_checkauths) {
466     int next_call = 600;
467     if(get_int_field("General.CheckAuths.enabled")) {
468         int check_start_time = get_int_field("General.CheckAuths.start_time") * 3600;
469         int duration = get_int_field("General.CheckAuths.duration") * 60;
470         int now = getCurrentSecondsOfDay();
471         if(now < check_start_time && check_start_time+duration >= 86400) {
472             check_start_time -= 86400;
473         }
474         if(now >= check_start_time && now < (check_start_time + duration)) {
475             next_call = get_int_field("General.CheckAuths.interval");
476             //get the "longest-unchecked-user"
477             MYSQL_RES *res;
478             MYSQL_ROW row;
479             int lastcheck;
480             time_t unixtime = time(0);
481             int min_unckecked = get_int_field("General.CheckAuths.min_unckecked");
482             printf_mysql_query("SELECT `user_user`, `user_lastcheck` FROM `users` ORDER BY `user_lastcheck` ASC LIMIT 1");
483             res = mysql_use();
484             if ((row = mysql_fetch_row(res)) != NULL) {
485                 lastcheck = atoi(row[1]);
486                 if(!lastcheck || unixtime - lastcheck >= min_unckecked) {
487                     lookup_authname(row[0], 0, main_checkauths_callback, NULL);
488                 } else 
489                     next_call = 300;
490             }
491         } else {
492             int pending;
493             if(now > check_start_time)
494                 pending = 86400 - now + check_start_time;
495             else
496                 pending = check_start_time - now;
497             if(pending < 600)
498                 next_call = pending;
499         }
500         
501     }
502     timeq_add(next_call, 0, main_checkauths, NULL);
503 }
504
505 TIMEQ_CALLBACK(main_statistics) {
506     int update_minutes = get_int_field("statistics.frequency");
507     if(!update_minutes) update_minutes = 2;
508     timeq_add(update_minutes * 60, 0, main_statistics, NULL);
509     if(get_int_field("statistics.enable")) {
510         statistics_enabled = 1;
511         statistics_requested_lusers = 1;
512         if(get_int_field("statistics.include_lusers")) {
513             struct ClientSocket *bot, *lusersbot = NULL;
514             for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
515                 if(bot->flags & SOCKET_FLAG_PREFERRED)
516                     lusersbot = bot;
517             }
518             bot = lusersbot;
519             if(bot == NULL) bot = getBots(SOCKET_FLAG_READY, NULL);
520             putsock(bot, "LUSERS");
521         } else {
522             statistics_update();
523         }
524     } else
525         statistics_enabled = 0;
526 }
527
528 int statistics_update() {
529     if(get_int_field("statistics.enable") && statistics_requested_lusers && get_string_field("statistics.execute")) {
530         statistics_requested_lusers = 0;
531         char command[MAXLEN];
532         /* parameters:
533          - visible users
534          - visible chanusers
535          - visible channels
536          - privmsg per minute
537          - commands per minute
538          - network users
539          - network channels
540         */
541         sprintf(command, "%s %d %d %d %d %d %d %d", get_string_field("statistics.execute"), getUserCount(), getChanUserCount(), getChannelCount(), statistics_privmsg, statistics_commands, statistics_network_users, statistics_network_channels);
542         statistics_privmsg = 0;
543         statistics_commands = 0;
544         return system(command);
545     }
546         return -1;
547 }
548
549 time_t getStartTime() {
550     return start_time;
551 }
552
553 int getRunningThreads() {
554         #ifdef HAVE_THREADS
555     return running_threads;
556         #else
557         return 1;
558         #endif
559 }
560
561 void write_log(int loglevel, const char *line, int len) {
562     SYNCHRONIZE(log_sync);
563     if(!daemonized && (print_loglevel & loglevel)) {
564         printf("%s", line);
565     } else if(!daemonized && loglevel == LOGLEVEL_ERROR) {
566         fprintf(stderr, "%s", line);
567     }
568     if(get_int_field("log.loglevel") & loglevel) {
569         if(!log_fptr) {
570             log_fptr = fopen(LOG_FILE, "a");
571             if(!log_fptr) goto write_log_end;
572         }
573         time_t rawtime;
574         struct tm *timeinfo;
575         time(&rawtime);
576         timeinfo = localtime(&rawtime);
577         char timestr[20];
578         int timepos = strftime(timestr, 20, "%x %X ", timeinfo);
579         fwrite(timestr, 1, timepos, log_fptr);
580         fwrite(line, 1, len, log_fptr);
581     }
582     write_log_end:
583     DESYNCHRONIZE(log_sync);
584 }
585
586 void putlog(int loglevel, const char *text, ...) {
587     va_list arg_list;
588     char logBuf[MAXLOGLEN];
589     int pos;
590     logBuf[0] = '\0';
591     va_start(arg_list, text);
592     pos = vsnprintf(logBuf, MAXLOGLEN - 1, text, arg_list);
593     va_end(arg_list);
594     if (pos < 0 || pos > (MAXLOGLEN - 1)) pos = MAXLOGLEN - 1;
595     logBuf[pos] = '\0';
596     write_log(loglevel, logBuf, pos);
597 }
598
599 static void check_firstrun() {
600     MYSQL_RES *res;
601     MYSQL_ROW row;
602     printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_access` = 1000 LIMIT 1");
603     res = mysql_use();
604     if (mysql_fetch_row(res) == NULL) {
605         //first run!
606         printf("No superuser found...\n");
607         check_firstrun_admin:
608         printf("AuthServ account name of admin user: ");
609         char *ptr;
610         char adminuser[31];
611         ptr = fgets(adminuser, 30, stdin);
612         for(ptr = adminuser; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; }
613         if(strlen(adminuser) < 2) goto check_firstrun_admin;
614         printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(adminuser));
615         res = mysql_use();
616         if ((row = mysql_fetch_row(res)) != NULL)
617             printf_mysql_query("UPDATE `users` SET `user_access` = 1000 WHERE `user_id` = '%s'", row[0]);
618         else
619             printf_mysql_query("INSERT INTO `users` (`user_user`, `user_access`) VALUES ('%s', 1000)", escape_string(adminuser));
620     }
621     printf_mysql_query("SELECT `id` FROM `bots` WHERE `active` = 1 LIMIT 1");
622     res = mysql_use();
623     if (mysql_fetch_row(res) == NULL) {
624         //no bot active
625         printf("No active bot found...\n\n");
626         printf("ADD NEW BOT\n");
627         char *ptr;
628         char bot_nick[31];
629         check_firstrun_bot_nick:
630         printf("Nick: ");
631         ptr = fgets(bot_nick, 30, stdin);
632         for(ptr = bot_nick; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; }
633         if(strlen(bot_nick) < 2) goto check_firstrun_bot_nick;
634         char bot_ident[16];
635         check_firstrun_bot_ident:
636         printf("Ident: ");
637         ptr = fgets(bot_ident, 15, stdin);
638         for(ptr = bot_ident; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; }
639         if(strlen(bot_ident) < 2) goto check_firstrun_bot_ident;
640         char bot_realname[101];
641         check_firstrun_bot_realname:
642         printf("Realname: ");
643         ptr = fgets(bot_realname, 100, stdin);
644         for(ptr = bot_realname; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; }
645         if(strlen(bot_realname) < 2) goto check_firstrun_bot_realname;
646         char bot_server[101];
647         check_firstrun_bot_server:
648         printf("Server: [irc.onlinegamesnet.net] ");
649         ptr = fgets(bot_server, 100, stdin);
650         for(ptr = bot_server; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; }
651         if(*bot_server && strlen(bot_nick) < 5) goto check_firstrun_bot_server;
652         if(!*bot_server)
653             strcpy(bot_server, "irc.onlinegamesnet.net");
654         int bot_port;
655         char bot_port_buf[7];
656         printf("Port: [6667] ");
657         ptr = fgets(bot_port_buf, 6, stdin);
658         for(ptr = bot_port_buf; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; }
659         if(!*bot_port_buf)
660             bot_port = 6667;
661         else
662             bot_port = atoi(bot_port_buf);
663         int bot_ssl;
664         char bot_ssl_buf[5];
665         check_firstrun_bot_ssl:
666         printf("SSL: [y/N] ");
667         ptr = fgets(bot_ssl_buf, 4, stdin);
668         for(ptr = bot_ssl_buf; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; }
669         if(!*bot_ssl_buf || tolower(*bot_ssl_buf) == 'n')
670             bot_ssl = 0;
671         else if(tolower(*bot_ssl_buf) == 'y')
672             bot_ssl = 1;
673         else
674             goto check_firstrun_bot_ssl;
675         char bot_pass[101];
676         printf("Server Password: [] ");
677         ptr = fgets(bot_pass, 100, stdin);
678         for(ptr = bot_pass; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; }
679         int bot_maxchan;
680         char bot_maxchan_buf[5];
681         printf("MaxChannel: [20] ");
682         ptr = fgets(bot_maxchan_buf, 5, stdin);
683         for(ptr = bot_maxchan_buf; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; }
684         if(*bot_maxchan_buf)
685             bot_maxchan = atoi(bot_maxchan_buf);
686         else
687             bot_maxchan = 20;
688         int bot_queue;
689         char bot_queue_buf[5];
690         check_firstrun_bot_queue:
691         printf("Queue (prevents excess floods): [Y/n] ");
692         ptr = fgets(bot_queue_buf, 4, stdin);
693         for(ptr = bot_queue_buf; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; }
694         if(!*bot_queue_buf || tolower(*bot_queue_buf) == 'y')
695             bot_queue = 1;
696         else if(tolower(*bot_queue_buf) == 'n')
697             bot_queue = 0;
698         else
699             goto check_firstrun_bot_queue;
700         printf_mysql_query("INSERT INTO `bots` (`active`, `nick`, `server`, `port`, `pass`, `ssl`, `ident`, `realname`, `botclass`, `textbot`, `queue`, `defaulttrigger`, `max_channels`) VALUES ('1', '%s', '%s', '%d', '%s', '%d', '%s', '%s', '1', '1', '%d', '+', '%d')", escape_string(bot_nick), escape_string(bot_server), bot_port, escape_string(bot_pass), bot_ssl, escape_string(bot_ident), escape_string(bot_realname), bot_queue, bot_maxchan);
701     }
702 }
703