1 /*
2 * chanprog.c -- handles:
3 * rmspace()
4 * maintaining the server list
5 * revenge punishment
6 * timers, utimers
7 * telling the current programmed settings
8 * initializing a lot of stuff and loading the tcl scripts
9 */
10 /*
11 * Copyright (C) 1997 Robey Pointer
12 * Copyright (C) 1999 - 2021 Eggheads Development Team
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29 #include "main.h"
30
31 #ifdef HAVE_GETRUSAGE
32 # include <sys/resource.h>
33 # ifdef HAVE_SYS_RUSAGE_H
34 # include <sys/rusage.h>
35 # endif
36 #endif
37
38 #include <sys/utsname.h>
39
40 #include "modules.h"
41
42 extern struct dcc_t *dcc;
43 extern struct userrec *userlist;
44 extern log_t *logs;
45 extern Tcl_Interp *interp;
46 extern char ver[], botnetnick[], firewall[], motdfile[], userfile[], helpdir[],
47 moddir[], notify_new[], configfile[];
48 extern time_t now, online_since;
49 extern int backgrd, term_z, con_chan, cache_hit, cache_miss, firewallport,
50 default_flags, max_logs, conmask, protect_readonly, make_userfile,
51 noshare, ignore_time, max_socks;
52 #ifdef TLS
53 extern SSL_CTX *ssl_ctx;
54 #endif
55
56 tcl_timer_t *timer = NULL; /* Minutely timer */
57 tcl_timer_t *utimer = NULL; /* Secondly timer */
58 unsigned long timer_id = 1; /* Next timer of any sort will
59 * have this number */
60 struct chanset_t *chanset = NULL; /* Channel list */
61 char admin[121] = ""; /* Admin info */
62 char origbotname[NICKLEN];
63 char botname[NICKLEN]; /* Primary botname */
64 char owner[121] = ""; /* Permanent botowner(s) */
65
66
67 /* Remove leading and trailing whitespaces.
68 */
rmspace(char * s)69 void rmspace(char *s)
70 {
71 char *p = NULL, *q = NULL;
72
73 if (!s || !*s)
74 return;
75
76 /* Remove trailing whitespaces. */
77 for (q = s + strlen(s) - 1; q >= s && egg_isspace(*q); q--);
78 *(q + 1) = 0;
79
80 /* Remove leading whitespaces. */
81 for (p = s; egg_isspace(*p); p++);
82
83 if (p != s)
84 memmove(s, p, q - p + 2);
85 }
86
87
88 /* Returns memberfields if the nick is in the member list.
89 */
ismember(struct chanset_t * chan,char * nick)90 memberlist *ismember(struct chanset_t *chan, char *nick)
91 {
92 memberlist *x;
93
94 for (x = chan->channel.member; x && x->nick[0]; x = x->next)
95 if (!rfc_casecmp(x->nick, nick))
96 return x;
97 return NULL;
98 }
99
100 /* Find a chanset by channel name as the server knows it (ie !ABCDEchannel)
101 */
findchan(const char * name)102 struct chanset_t *findchan(const char *name)
103 {
104 struct chanset_t *chan;
105
106 for (chan = chanset; chan; chan = chan->next)
107 if (!rfc_casecmp(chan->name, name))
108 return chan;
109 return NULL;
110 }
111
112 /* Find a chanset by display name (ie !channel)
113 */
findchan_by_dname(const char * name)114 struct chanset_t *findchan_by_dname(const char *name)
115 {
116 struct chanset_t *chan;
117
118 for (chan = chanset; chan; chan = chan->next)
119 if (!rfc_casecmp(chan->dname, name))
120 return chan;
121 return NULL;
122 }
123
124
125 /*
126 * "caching" functions
127 */
128
129 /* Shortcut for get_user_by_host -- might have user record in one
130 * of the channel caches.
131 */
check_chanlist(const char * host)132 struct userrec *check_chanlist(const char *host)
133 {
134 char *nick, *uhost, buf[UHOSTLEN];
135 memberlist *m;
136 struct chanset_t *chan;
137
138 strlcpy(buf, host, sizeof buf);
139 uhost = buf;
140 nick = splitnick(&uhost);
141 for (chan = chanset; chan; chan = chan->next)
142 for (m = chan->channel.member; m && m->nick[0]; m = m->next)
143 if (!rfc_casecmp(nick, m->nick) && !strcasecmp(uhost, m->userhost))
144 return m->user;
145 return NULL;
146 }
147
148 /* Shortcut for get_user_by_handle -- might have user record in channels
149 */
check_chanlist_hand(const char * hand)150 struct userrec *check_chanlist_hand(const char *hand)
151 {
152 struct chanset_t *chan;
153 memberlist *m;
154
155 for (chan = chanset; chan; chan = chan->next)
156 for (m = chan->channel.member; m && m->nick[0]; m = m->next)
157 if (m->user && !strcasecmp(m->user->handle, hand))
158 return m->user;
159 return NULL;
160 }
161
162 /* Clear the user pointers in the chanlists.
163 *
164 * Necessary when a hostmask is added/removed, a user is added or a new
165 * userfile is loaded.
166 */
clear_chanlist(void)167 void clear_chanlist(void)
168 {
169 memberlist *m;
170 struct chanset_t *chan;
171
172 for (chan = chanset; chan; chan = chan->next)
173 for (m = chan->channel.member; m && m->nick[0]; m = m->next) {
174 m->user = NULL;
175 m->tried_getuser = 0;
176 }
177 }
178
179 /* Clear the user pointer of a specific nick in the chanlists.
180 *
181 * Necessary when a hostmask is added/removed, a nick changes, etc.
182 * Does not completely invalidate the channel cache like clear_chanlist().
183 */
clear_chanlist_member(const char * nick)184 void clear_chanlist_member(const char *nick)
185 {
186 memberlist *m;
187 struct chanset_t *chan;
188
189 for (chan = chanset; chan; chan = chan->next)
190 for (m = chan->channel.member; m && m->nick[0]; m = m->next)
191 if (!rfc_casecmp(m->nick, nick)) {
192 m->user = NULL;
193 m->tried_getuser = 0;
194 break;
195 }
196 }
197
198 /* If this user@host is in a channel, set it (it was null)
199 */
set_chanlist(const char * host,struct userrec * rec)200 void set_chanlist(const char *host, struct userrec *rec)
201 {
202 char *nick, *uhost, buf[UHOSTLEN];
203 memberlist *m;
204 struct chanset_t *chan;
205
206 strlcpy(buf, host, sizeof buf);
207 uhost = buf;
208 nick = splitnick(&uhost);
209 for (chan = chanset; chan; chan = chan->next)
210 for (m = chan->channel.member; m && m->nick[0]; m = m->next)
211 if (!rfc_casecmp(nick, m->nick) && !strcasecmp(uhost, m->userhost))
212 m->user = rec;
213 }
214
215 /* Calculate the memory we should be using
216 */
expmem_chanprog()217 int expmem_chanprog()
218 {
219 int tot = 0;
220 tcl_timer_t *t;
221
222 for (t = timer; t; t = t->next)
223 tot += sizeof(tcl_timer_t) + strlen(t->cmd) + 1;
224 for (t = utimer; t; t = t->next)
225 tot += sizeof(tcl_timer_t) + strlen(t->cmd) + 1;
226 return tot;
227 }
228
getcputime()229 float getcputime()
230 {
231 #ifdef HAVE_GETRUSAGE
232 float stime, utime;
233 struct rusage ru;
234
235 getrusage(RUSAGE_SELF, &ru);
236 utime = ru.ru_utime.tv_sec + (ru.ru_utime.tv_usec / 1000000.00);
237 stime = ru.ru_stime.tv_sec + (ru.ru_stime.tv_usec / 1000000.00);
238 return (utime + stime);
239 #else
240 return (clock() / (CLOCKS_PER_SEC * 1.00));
241 #endif
242 }
243
244 /* Dump uptime info out to dcc (guppy 9Jan99)
245 */
tell_verbose_uptime(int idx)246 void tell_verbose_uptime(int idx)
247 {
248 char s[256], s1[121];
249 time_t now2, hr, min;
250
251 now2 = now - online_since;
252 s[0] = 0;
253 if (now2 > 86400) {
254 /* days */
255 sprintf(s, "%d day", (int) (now2 / 86400));
256 if ((int) (now2 / 86400) >= 2)
257 strcat(s, "s");
258 strcat(s, ", ");
259 now2 -= (((int) (now2 / 86400)) * 86400);
260 }
261 hr = (time_t) ((int) now2 / 3600);
262 now2 -= (hr * 3600);
263 min = (time_t) ((int) now2 / 60);
264 sprintf(&s[strlen(s)], "%02d:%02d", (int) hr, (int) min);
265 s1[0] = 0;
266 if (backgrd)
267 strcpy(s1, MISC_BACKGROUND);
268 else {
269 if (term_z >= 0)
270 strcpy(s1, MISC_TERMMODE);
271 else if (con_chan)
272 strcpy(s1, MISC_STATMODE);
273 else
274 strcpy(s1, MISC_LOGMODE);
275 }
276 dprintf(idx, "%s %s (%s)\n", MISC_ONLINEFOR, s, s1);
277 }
278
279 /* Dump status info out to dcc
280 */
tell_verbose_status(int idx)281 void tell_verbose_status(int idx)
282 {
283 char s[256], s1[121], s2[81], *sysrel;
284 int i;
285 time_t now2 = now - online_since, hr, min;
286 double cputime, cache_total;
287
288 i = count_users(userlist);
289 dprintf(idx, "I am %s, running %s: %d user%s (mem: %uk).\n",
290 botnetnick, ver, i, i == 1 ? "" : "s",
291 (int) (expected_memory() / 1024));
292
293 s[0] = 0;
294 if (now2 > 86400) {
295 /* days */
296 sprintf(s, "%d day", (int) (now2 / 86400));
297 if ((int) (now2 / 86400) >= 2)
298 strcat(s, "s");
299 strcat(s, ", ");
300 now2 -= (((int) (now2 / 86400)) * 86400);
301 }
302 hr = (time_t) ((int) now2 / 3600);
303 now2 -= (hr * 3600);
304 min = (time_t) ((int) now2 / 60);
305 sprintf(&s[strlen(s)], "%02d:%02d", (int) hr, (int) min);
306 s1[0] = 0;
307 if (backgrd)
308 strlcpy(s1, MISC_BACKGROUND, sizeof s1);
309 else {
310 if (term_z >= 0)
311 strlcpy(s1, MISC_TERMMODE, sizeof s1);
312 else if (con_chan)
313 strlcpy(s1, MISC_STATMODE, sizeof s1);
314 else
315 strlcpy(s1, MISC_LOGMODE, sizeof s1);
316 }
317 cputime = getcputime();
318 if (cputime < 0)
319 sprintf(s2, "CPU: unknown");
320 else {
321 hr = cputime / 60;
322 cputime -= hr * 60;
323 sprintf(s2, "CPU: %02d:%05.2f", (int) hr, cputime); /* Actually min/sec */
324 }
325 if (cache_hit + cache_miss) { /* 2019, still can't divide by zero */
326 cache_total = 100.0 * (cache_hit) / (cache_hit + cache_miss);
327 } else cache_total = 0;
328 dprintf(idx, "%s %s (%s) - %s - %s: %4.1f%%\n", MISC_ONLINEFOR,
329 s, s1, s2, MISC_CACHEHIT, cache_total);
330
331 dprintf(idx, "Configured with: " EGG_AC_ARGS "\n");
332 if (admin[0])
333 dprintf(idx, "Admin: %s\n", admin);
334
335 dprintf(idx, "Config file: %s\n", configfile);
336 sysrel = egg_uname();
337 if (*sysrel)
338 dprintf(idx, "OS: %s\n", sysrel);
339 dprintf(idx, "Process ID: %d (parent %d)\n", getpid(), getppid());
340
341 /* info library */
342 dprintf(idx, "%s %s\n", MISC_TCLLIBRARY,
343 ((interp) && (Tcl_Eval(interp, "info library") == TCL_OK)) ?
344 tcl_resultstring() : "*unknown*");
345
346 /* info tclversion/patchlevel */
347 dprintf(idx, "%s %s (%s %s)\n", MISC_TCLVERSION,
348 ((interp) && (Tcl_Eval(interp, "info patchlevel") == TCL_OK)) ?
349 tcl_resultstring() : (Tcl_Eval(interp, "info tclversion") == TCL_OK) ?
350 tcl_resultstring() : "*unknown*", MISC_TCLHVERSION, TCL_PATCH_LEVEL);
351
352 if (tcl_threaded())
353 dprintf(idx, "Tcl is threaded.\n");
354 #ifdef TLS
355 dprintf(idx, "TLS support is enabled.\n"
356 #if defined HAVE_EVP_PKEY_GET1_EC_KEY && defined HAVE_OPENSSL_MD5
357 "TLS library: %s\n",
358 #elif !defined HAVE_EVP_PKEY_GET1_EC_KEY && defined HAVE_OPENSSL_MD5
359 "TLS library: %s\n (no elliptic curve support)\n",
360 #elif defined HAVE_EVP_PKEY_GET1_EC_KEY && !defined HAVE_OPENSSL_MD5
361 "TLS library: %s\n (no MD5 support)\n",
362 #elif !defined HAVE_EVP_PKEY_GET1_EC_KEY && !defined HAVE_OPENSSL_MD5
363 "TLS library: %s\n (no elliptic curve or MD5 support)\n",
364 #endif
365 SSLeay_version(SSLEAY_VERSION));
366 #else
367 dprintf(idx, "TLS support is not available.\n");
368 #endif
369 #ifdef IPV6
370 dprintf(idx, "IPv6 support is enabled.\n"
371 #else
372 dprintf(idx, "IPv6 support is not available.\n"
373 #endif
374 #ifdef EGG_TDNS
375 "Threaded DNS core (beta) is enabled.\n"
376 #else
377 "Threaded DNS core (beta) is disabled.\n"
378 #endif
379 "Socket table: %d/%d\n", threaddata()->MAXSOCKS, max_socks);
380 }
381
382 /* Show all internal state variables
383 */
tell_settings(int idx)384 void tell_settings(int idx)
385 {
386 char s[1024];
387 int i;
388 struct flag_record fr = { FR_GLOBAL, 0, 0, 0, 0, 0 };
389
390 dprintf(idx, "Botnet nickname: %s\n", botnetnick);
391 if (firewall[0])
392 dprintf(idx, "Firewall: %s:%d\n", firewall, firewallport);
393 dprintf(idx, "Userfile: %s\n", userfile);
394 dprintf(idx, "Motd: %s\n", motdfile);
395 dprintf(idx, "Directories:\n");
396 #ifndef STATIC
397 dprintf(idx, " Help : %s\n", helpdir);
398 dprintf(idx, " Modules: %s\n", moddir);
399 #else
400 dprintf(idx, " Help: %s\n", helpdir);
401 #endif
402 fr.global = default_flags;
403
404 build_flags(s, &fr, NULL);
405 dprintf(idx, "%s [%s], %s: %s\n", MISC_NEWUSERFLAGS, s,
406 MISC_NOTIFY, notify_new);
407 if (owner[0])
408 dprintf(idx, "%s: %s\n", MISC_PERMOWNER, owner);
409 for (i = 0; i < max_logs; i++)
410 if (logs[i].filename != NULL) {
411 dprintf(idx, "Logfile #%d: %s on %s (%s: %s)\n", i + 1,
412 logs[i].filename, logs[i].chname,
413 masktype(logs[i].mask), maskname(logs[i].mask));
414 }
415 dprintf(idx, "Ignores last %d minute%s.\n", ignore_time,
416 (ignore_time != 1) ? "s" : "");
417 }
418
reaffirm_owners()419 void reaffirm_owners()
420 {
421 char *p, *q, s[121];
422 struct userrec *u;
423
424 /* Please stop breaking this function. */
425 if (owner[0]) {
426 q = owner;
427 p = strchr(q, ',');
428 while (p) {
429 strlcpy(s, q, (p - q) + 1);
430 rmspace(s);
431 u = get_user_by_handle(userlist, s);
432 if (u)
433 u->flags = sanity_check(u->flags | USER_OWNER);
434 q = p + 1;
435 p = strchr(q, ',');
436 }
437 strcpy(s, q);
438 rmspace(s);
439 u = get_user_by_handle(userlist, s);
440 if (u)
441 u->flags = sanity_check(u->flags | USER_OWNER);
442 }
443 }
444
chanprog()445 void chanprog()
446 {
447 int i;
448
449 admin[0] = 0;
450 helpdir[0] = 0;
451 /* default mkcoblxs */
452 conmask = LOG_MSGS|LOG_MODES|LOG_CMDS|LOG_MISC|LOG_BOTS|LOG_BOTMSG|LOG_FILES|LOG_SERV;
453
454 for (i = 0; i < max_logs; i++)
455 logs[i].flags |= LF_EXPIRING;
456
457 /* Turn off read-only variables (make them write-able) for rehash */
458 protect_readonly = 0;
459
460 /* Now read it */
461 if (!readtclprog(configfile))
462 fatal(MISC_NOCONFIGFILE, 0);
463
464 for (i = 0; i < max_logs; i++) {
465 if (logs[i].flags & LF_EXPIRING) {
466 if (logs[i].filename != NULL) {
467 nfree(logs[i].filename);
468 logs[i].filename = NULL;
469 }
470 if (logs[i].chname != NULL) {
471 nfree(logs[i].chname);
472 logs[i].chname = NULL;
473 }
474 if (logs[i].f != NULL) {
475 fclose(logs[i].f);
476 logs[i].f = NULL;
477 }
478 logs[i].mask = 0;
479 logs[i].flags = 0;
480 }
481 }
482
483 /* We should be safe now */
484 call_hook(HOOK_REHASH);
485 protect_readonly = 1;
486
487 if (!botnetnick[0])
488 set_botnetnick(origbotname);
489
490 if (!botnetnick[0])
491 fatal("I don't have a botnet nick!!\n", 0);
492
493 if (!userfile[0])
494 fatal(MISC_NOUSERFILE2, 0);
495
496 if (!readuserfile(userfile, &userlist)) {
497 if (!make_userfile) {
498 char tmp[178];
499
500 egg_snprintf(tmp, sizeof tmp, MISC_NOUSERFILE, configfile);
501 fatal(tmp, 0);
502 }
503 printf("\n\n%s\n", MISC_NOUSERFILE2);
504 if (module_find("server", 0, 0))
505 printf(MISC_USERFCREATE1, origbotname);
506 printf("%s\n\n", MISC_USERFCREATE2);
507 } else if (make_userfile) {
508 make_userfile = 0;
509 printf("%s\n", MISC_USERFEXISTS);
510 }
511
512 if (helpdir[0])
513 if (helpdir[strlen(helpdir) - 1] != '/')
514 strcat(helpdir, "/");
515
516 reaffirm_owners();
517 check_tcl_event("userfile-loaded");
518 }
519
520 /* Reload the user file from disk
521 */
reload()522 void reload()
523 {
524 if (!file_readable(userfile)) {
525 putlog(LOG_MISC, "*", MISC_CANTRELOADUSER);
526 return;
527 }
528
529 noshare = 1;
530 clear_userlist(userlist);
531 noshare = 0;
532 userlist = NULL;
533 if (!readuserfile(userfile, &userlist))
534 fatal(MISC_MISSINGUSERF, 0);
535 reaffirm_owners();
536 add_hq_user();
537 check_tcl_event("userfile-loaded");
538 call_hook(HOOK_READ_USERFILE);
539 }
540
rehash()541 void rehash()
542 {
543 call_hook(HOOK_PRE_REHASH);
544 noshare = 1;
545 clear_userlist(userlist);
546 noshare = 0;
547 userlist = NULL;
548 chanprog();
549 add_hq_user();
550 }
551
552 /*
553 * Brief venture into timers
554 */
555
556 /* Add a timer
557 */
add_timer(tcl_timer_t ** stack,int elapse,int count,char * cmd,unsigned long prev_id)558 unsigned long add_timer(tcl_timer_t ** stack, int elapse, int count,
559 char *cmd, unsigned long prev_id)
560 {
561 tcl_timer_t *old = (*stack);
562
563 *stack = nmalloc(sizeof **stack);
564 (*stack)->next = old;
565 (*stack)->mins = (*stack)->interval = elapse;
566 (*stack)->count = count;
567 (*stack)->cmd = nmalloc(strlen(cmd) + 1);
568 strcpy((*stack)->cmd, cmd);
569 /* If it's just being added back and already had an id,
570 * don't create a new one.
571 */
572 if (prev_id > 0)
573 (*stack)->id = prev_id;
574 else
575 (*stack)->id = timer_id++;
576 return (*stack)->id;
577 }
578
579 /* Remove a timer, by id
580 */
remove_timer(tcl_timer_t ** stack,unsigned long id)581 int remove_timer(tcl_timer_t ** stack, unsigned long id)
582 {
583 tcl_timer_t *old;
584 int ok = 0;
585
586 while (*stack) {
587 if ((*stack)->id == id) {
588 ok++;
589 old = *stack;
590 *stack = ((*stack)->next);
591 nfree(old->cmd);
592 nfree(old);
593 } else
594 stack = &((*stack)->next);
595 }
596 return ok;
597 }
598
599 /* Check timers, execute the ones that have expired.
600 */
do_check_timers(tcl_timer_t ** stack)601 void do_check_timers(tcl_timer_t ** stack)
602 {
603 tcl_timer_t *mark = *stack, *old = NULL;
604 char x[26];
605
606 /* New timers could be added by a Tcl script inside a current timer
607 * so i'll just clear out the timer list completely, and add any
608 * unexpired timers back on.
609 */
610 *stack = NULL;
611 while (mark) {
612 if (mark->mins > 0)
613 mark->mins--;
614 old = mark;
615 mark = mark->next;
616 if (!old->mins) {
617 snprintf(x, sizeof x, "timer%lu", old->id);
618 do_tcl(x, old->cmd);
619 if (old->count == 1) {
620 nfree(old->cmd);
621 nfree(old);
622 continue;
623 } else {
624 old->mins = old->interval;
625 if (old->count > 1)
626 old->count--;
627 }
628 }
629 old->next = *stack;
630 *stack = old;
631 }
632 }
633
634 /* Wipe all timers.
635 */
wipe_timers(Tcl_Interp * irp,tcl_timer_t ** stack)636 void wipe_timers(Tcl_Interp *irp, tcl_timer_t **stack)
637 {
638 tcl_timer_t *mark = *stack, *old;
639
640 while (mark) {
641 old = mark;
642 mark = mark->next;
643 nfree(old->cmd);
644 nfree(old);
645 }
646 *stack = NULL;
647 }
648
649 /* Return list of timers
650 */
list_timers(Tcl_Interp * irp,tcl_timer_t * stack)651 void list_timers(Tcl_Interp *irp, tcl_timer_t *stack)
652 {
653 char mins[11], count[11], id[26], *x;
654 EGG_CONST char *argv[4];
655 tcl_timer_t *mark;
656
657 for (mark = stack; mark; mark = mark->next) {
658 snprintf(mins, sizeof mins, "%u", mark->mins);
659 snprintf(id, sizeof id, "timer%lu", mark->id);
660 snprintf(count, sizeof count, "%u", mark->count);
661 argv[0] = mins;
662 argv[1] = mark->cmd;
663 argv[2] = id;
664 argv[3] = count;
665 x = Tcl_Merge(sizeof(argv)/sizeof(*argv), argv);
666 Tcl_AppendElement(irp, x);
667 Tcl_Free((char *) x);
668 }
669 }
670
isowner(char * name)671 int isowner(char *name) {
672 char s[sizeof owner];
673 char *sep = ", \t\n\v\f\r";
674 char *word;
675
676 strcpy(s, owner);
677 for (word = strtok(s, sep); word; word = strtok(NULL, sep)) {
678 if (!strcasecmp(name, word)) {
679 return 1;
680 }
681 }
682 return 0;
683 }
684
685 /*
686 * Adds the -HQ user to the userlist and takes care of needed permissions
687 */
add_hq_user()688 void add_hq_user()
689 {
690 if (!backgrd && term_z >= 0) {
691 /* HACK: Workaround using dcc[].nick not to pass literal "-HQ" as a non-const arg */
692 dcc[term_z].user = get_user_by_handle(userlist, dcc[term_z].nick);
693 /* Make sure there's an innocuous -HQ user if needed */
694 if (!dcc[term_z].user) {
695 userlist = adduser(userlist, dcc[term_z].nick, "none", "-", USER_PARTY);
696 dcc[term_z].user = get_user_by_handle(userlist, dcc[term_z].nick);
697 }
698 /* Give all useful flags: efjlmnoptuvx */
699 dcc[term_z].user->flags = USER_EXEMPT | USER_FRIEND | USER_JANITOR |
700 USER_HALFOP | USER_MASTER | USER_OWNER | USER_OP |
701 USER_PARTY | USER_BOTMAST | USER_UNSHARED |
702 USER_VOICE | USER_XFER;
703 /* Add to permowner list if there's place */
704 if (strlen(owner) + sizeof EGG_BG_HANDLE < sizeof owner)
705 strcat(owner, " " EGG_BG_HANDLE);
706
707 /* Update laston info, gets cleared at rehash/reload */
708 touch_laston(dcc[term_z].user, "partyline", now);
709 }
710 }
711