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