ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/src/client.c
Revision: 747
Committed: Mon Jul 24 22:14:17 2006 UTC (17 years, 8 months ago) by adx
Content type: text/x-csrc
File size: 43929 byte(s)
Log Message:
+ fixed everything except m_gline and m_spoof.
+ still to do: ban storage, limits, iphash, parser, conf_connect_allowed

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
3 * client.c: Controls clients.
4 *
5 * Copyright (C) 2002 by the past and present ircd coders, and others.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
21 *
22 * $Id$
23 */
24
25 #include "stdinc.h"
26 #include "conf/conf.h"
27 #include "client.h"
28 #include "channel.h"
29 #include "channel_mode.h"
30 #include "common.h"
31 #include "hash.h"
32 #include "ircd.h"
33 #include "numeric.h"
34 #include "packet.h"
35 #include "s_auth.h"
36 #include "parse_aline.h"
37 #include "s_serv.h"
38 #include "send.h"
39 #include "whowas.h"
40 #include "s_user.h"
41 #include "listener.h"
42 #include "userhost.h"
43 #include "watch.h"
44
45 dlink_list listing_client_list = { NULL, NULL, 0 };
46 /* Pointer to beginning of Client list */
47 dlink_list global_client_list = {NULL, NULL, 0};
48 /* unknown/client pointer lists */
49 dlink_list unknown_list = {NULL, NULL, 0};
50 dlink_list local_client_list = {NULL, NULL, 0};
51 dlink_list serv_list = {NULL, NULL, 0};
52 dlink_list global_serv_list = {NULL, NULL, 0};
53 dlink_list oper_list = {NULL, NULL, 0};
54
55 static EVH check_pings;
56
57 static BlockHeap *client_heap = NULL;
58 static BlockHeap *lclient_heap = NULL;
59
60 static dlink_list dead_list = { NULL, NULL, 0};
61 static dlink_list abort_list = { NULL, NULL, 0};
62
63 static dlink_node *eac_next; /* next aborted client to exit */
64
65 static void check_pings_list(dlink_list *);
66 static void check_unknowns_list(void);
67 static void close_connection(struct Client *);
68 static void ban_them(struct Client *client_p, const char *, const char *);
69 static void del_all_accepts(struct Client *);
70
71 /* init_client()
72 *
73 * inputs - NONE
74 * output - NONE
75 * side effects - initialize client free memory
76 */
77 void
78 init_client(void)
79 {
80 /* start off the check ping event .. -- adrian
81 * Every 30 seconds is plenty -- db
82 */
83 client_heap = BlockHeapCreate("client", sizeof(struct Client), CLIENT_HEAP_SIZE);
84 lclient_heap = BlockHeapCreate("local client", sizeof(struct LocalUser), LCLIENT_HEAP_SIZE);
85 eventAdd("check_pings", check_pings, NULL, 5);
86 }
87
88 /*
89 * make_client - create a new Client struct and set it to initial state.
90 *
91 * from == NULL, create local client (a client connected
92 * to a socket).
93 * WARNING: This leaves the client in a dangerous
94 * state where fd == -1, dead flag is not set and
95 * the client is on the unknown_list; therefore,
96 * the first thing to do after calling make_client(NULL)
97 * is setting fd to something reasonable. -adx
98 *
99 * from, create remote client (behind a socket
100 * associated with the client defined by
101 * 'from'). ('from' is a local client!!).
102 */
103 struct Client *
104 make_client(struct Client *from)
105 {
106 struct Client *client_p = BlockHeapAlloc(client_heap);
107
108 if (from == NULL)
109 {
110 client_p->from = client_p; /* 'from' of local client is self! */
111 client_p->since = client_p->lasttime = client_p->firsttime = CurrentTime;
112
113 client_p->localClient = BlockHeapAlloc(lclient_heap);
114 client_p->localClient->class = default_class;
115 client_p->localClient->registration = REG_INIT;
116 /* as good a place as any... */
117 dlinkAdd(client_p, &client_p->localClient->lclient_node, &unknown_list);
118 }
119 else
120 client_p->from = from;
121
122 client_p->hnext = client_p;
123 client_p->status = STAT_UNKNOWN;
124 strcpy(client_p->username, "unknown");
125
126 return client_p;
127 }
128
129 /*
130 * free_client
131 *
132 * inputs - pointer to client
133 * output - NONE
134 * side effects - client pointed to has its memory freed
135 */
136 static void
137 free_client(struct Client *client_p)
138 {
139 assert(client_p != NULL);
140 assert(client_p != &me);
141 assert(client_p->hnext == client_p);
142 assert(client_p->channel.head == NULL);
143 assert(dlink_list_length(&client_p->channel) == 0);
144
145 MyFree(client_p->away);
146 MyFree(client_p->serv);
147
148 if (MyConnect(client_p))
149 {
150 assert(dlink_list_length(&client_p->localClient->invited) == 0);
151 assert(client_p->localClient->invited.head == NULL);
152 assert(IsClosing(client_p) && IsDead(client_p));
153
154 MyFree(client_p->localClient->response);
155 MyFree(client_p->localClient->auth_oper);
156
157 /*
158 * clean up extra sockets from P-lines which have been discarded.
159 */
160 if (client_p->localClient->listener)
161 {
162 assert(0 < client_p->localClient->listener->ref_count);
163 if (0 == --client_p->localClient->listener->ref_count &&
164 !client_p->localClient->listener->active)
165 free_listener(client_p->localClient->listener);
166 }
167
168 dbuf_clear(&client_p->localClient->buf_recvq);
169 dbuf_clear(&client_p->localClient->buf_sendq);
170
171 BlockHeapFree(lclient_heap, client_p->localClient);
172 }
173
174 BlockHeapFree(client_heap, client_p);
175 }
176
177 /*
178 * check_pings - go through the local client list and check activity
179 * kill off stuff that should die
180 *
181 * inputs - NOT USED (from event)
182 * output - next time_t when check_pings() should be called again
183 * side effects -
184 *
185 *
186 * A PING can be sent to clients as necessary.
187 *
188 * Client/Server ping outs are handled.
189 */
190
191 /*
192 * Addon from adrian. We used to call this after nextping seconds,
193 * however I've changed it to run once a second. This is only for
194 * PING timeouts, not K/etc-line checks (thanks dianora!). Having it
195 * run once a second makes life a lot easier - when a new client connects
196 * and they need a ping in 4 seconds, if nextping was set to 20 seconds
197 * we end up waiting 20 seconds. This is stupid. :-)
198 * I will optimise (hah!) check_pings() once I've finished working on
199 * tidying up other network IO evilnesses.
200 * -- adrian
201 */
202
203 static void
204 check_pings(void *notused)
205 {
206 check_pings_list(&local_client_list);
207 check_pings_list(&serv_list);
208 check_unknowns_list();
209 }
210
211 /* check_pings_list()
212 *
213 * inputs - pointer to list to check
214 * output - NONE
215 * side effects -
216 */
217 static void
218 check_pings_list(dlink_list *list)
219 {
220 char scratch[32]; // way too generous but...
221 struct Client *client_p; // current local client_p being examined
222 int ping, pingwarn; // ping time value from client
223 dlink_node *ptr, *next_ptr;
224
225 DLINK_FOREACH_SAFE(ptr, next_ptr, list->head)
226 {
227 client_p = ptr->data;
228
229 /*
230 * Note: No need to notify opers here. It's
231 * already done when "FLAGS_DEADSOCKET" is set.
232 */
233 if (IsDead(client_p))
234 {
235 // Ignore it, it's been exited already
236 continue;
237 }
238
239 if (client_p->localClient->reject_delay > 0)
240 {
241 if (client_p->localClient->reject_delay <= CurrentTime)
242 exit_client(client_p, &me, "Rejected");
243 continue;
244 }
245
246 if (GlobalSetOptions.idletime && IsClient(client_p))
247 {
248 if (!IsExemptKline(client_p) && !IsIdlelined(client_p) &&
249 !IsOper(client_p) && ((CurrentTime - client_p->localClient->last) >
250 GlobalSetOptions.idletime))
251 {
252 struct KillConf *conf = MyMalloc(sizeof(struct KillConf));
253
254 conf->access.type = acb_type_kline;
255 DupString(conf->access.user, client_p->username);
256 DupString(conf->access.host, client_p->host);
257 conf->access.expires = CurrentTime + 60;
258 DupString(conf->reason, "idle exceeder");
259 add_access_conf(&conf->access);
260
261 sendto_realops_flags(UMODE_ALL, L_ALL,
262 "Idle time limit exceeded for %s - temp k-lining",
263 get_client_name(client_p, HIDE_IP));
264 exit_client(client_p, &me, conf->reason);
265 continue;
266 }
267 }
268
269 ping = client_p->localClient->class->ping_time;
270 pingwarn = client_p->localClient->class->ping_warning;
271
272 if (ping < CurrentTime - client_p->lasttime)
273 {
274 if (!IsPingSent(client_p))
275 {
276 /*
277 * if we havent PINGed the connection and we havent
278 * heard from it in a while, PING it to make sure
279 * it is still alive.
280 */
281 SetPingSent(client_p);
282 ClearPingWarning(client_p);
283 client_p->lasttime = CurrentTime - ping;
284 sendto_one(client_p, "PING :%s", ID_or_name(&me, client_p));
285 }
286 else
287 {
288 if (CurrentTime - client_p->lasttime >= 2 * ping)
289 {
290 /*
291 * If the client/server hasn't talked to us in 2*ping seconds
292 * and it has a ping time, then close its connection.
293 */
294 if (IsServer(client_p) || IsHandshake(client_p))
295 {
296 sendto_realops_flags(UMODE_ALL, L_ADMIN,
297 "No response from %s, closing link",
298 get_client_name(client_p, HIDE_IP));
299 sendto_realops_flags(UMODE_ALL, L_OPER,
300 "No response from %s, closing link",
301 get_client_name(client_p, MASK_IP));
302 ilog(L_NOTICE, "No response from %s, closing link",
303 get_client_name(client_p, HIDE_IP));
304 }
305 ircsprintf(scratch, "Ping timeout: %d seconds",
306 (int)(CurrentTime - client_p->lasttime));
307
308 exit_client(client_p, &me, scratch);
309 }
310 else if (!IsPingWarning(client_p) && pingwarn > 0 &&
311 (IsServer(client_p) || IsHandshake(client_p)) &&
312 CurrentTime - client_p->lasttime >= ping + pingwarn)
313 {
314 /*
315 * If the server hasn't replied in pingwarn seconds after sending
316 * the PING, notify the opers so that they are aware of the problem.
317 */
318 SetPingWarning(client_p);
319 sendto_realops_flags(UMODE_ALL, L_ADMIN,
320 "Warning, no response from %s in %d seconds",
321 get_client_name(client_p, HIDE_IP), pingwarn);
322 sendto_realops_flags(UMODE_ALL, L_OPER,
323 "Warning, no response from %s in %d seconds",
324 get_client_name(client_p, MASK_IP), pingwarn);
325 ilog(L_NOTICE, "No response from %s in %d seconds",
326 get_client_name(client_p, HIDE_IP), pingwarn);
327 }
328 }
329 }
330 }
331 }
332
333 /* check_unknowns_list()
334 *
335 * inputs - pointer to list of unknown clients
336 * output - NONE
337 * side effects - unknown clients get marked for termination after n seconds
338 */
339 static void
340 check_unknowns_list(void)
341 {
342 dlink_node *ptr = NULL, *next_ptr = NULL;
343
344 DLINK_FOREACH_SAFE(ptr, next_ptr, unknown_list.head)
345 {
346 struct Client *client_p = ptr->data;
347
348 if (client_p->localClient->reject_delay > 0)
349 {
350 if (client_p->localClient->reject_delay <= CurrentTime)
351 exit_client(client_p, &me, "Rejected");
352 continue;
353 }
354
355 /*
356 * Check UNKNOWN connections - if they have been in this state
357 * for > 30s, close them.
358 */
359 if (IsAuthFinished(client_p) && (CurrentTime - client_p->firsttime) > 30)
360 exit_client(client_p, &me, "Registration timed out");
361 }
362 }
363
364 /* check_conf_klines()
365 *
366 * inputs - NONE
367 * output - NONE
368 * side effects - Check all connections for a pending kline against the
369 * client, exit the client if a kline matches.
370 */
371 void
372 check_conf_klines(void)
373 {
374 struct Client *client_p = NULL; // current local client_p being examined
375 dlink_node *ptr, *next_ptr;
376 struct DenyConf *conf;
377 char *type, *reason;
378
379 DLINK_FOREACH_SAFE(ptr, next_ptr, local_client_list.head)
380 {
381 client_p = ptr->data;
382
383 // If a client is already being exited
384 if (IsDead(client_p) || !IsClient(client_p))
385 continue;
386
387 // if there is a returned type then kill it
388 if (execute_callback(is_client_banned, client_p, &type, &reason))
389 {
390 ban_them(client_p, type, reason);
391 continue; // and go examine next fd/client_p
392 }
393 }
394
395 // also check the unknowns list for new dlines
396 DLINK_FOREACH_SAFE(ptr, next_ptr, unknown_list.head)
397 {
398 client_p = ptr->data;
399
400 if ((conf = find_dline(&client_p->localClient->ip)))
401 exit_client(client_p, &me, "D-lined");
402 }
403 }
404
405 /*
406 * ban_them
407 *
408 * inputs - pointer to client to ban
409 * - type of ban (e.g. K-line, X-line)
410 * - ban reason
411 * output - NONE
412 * side effects - given client_p is banned
413 */
414 static void
415 ban_them(struct Client *client_p, const char *type, const char *reason)
416 {
417 const char *user_reason; // What is sent to user
418 const char *channel_reason; // What is sent to channel
419
420 user_reason = (EmptyString(reason) || !General.kline_with_reason) ?
421 type : reason;
422 channel_reason = General.kline_reason ? General.kline_reason : user_reason;
423
424 sendto_realops_flags(UMODE_ALL, L_ALL, "%s active for %s",
425 type, get_client_name(client_p, HIDE_IP));
426
427 if (IsClient(client_p))
428 sendto_one(client_p, form_str(ERR_YOUREBANNEDCREEP),
429 me.name, client_p->name, user_reason);
430
431 exit_client(client_p, &me, channel_reason);
432 }
433
434 /* find_person()
435 *
436 * inputs - pointer to name
437 * output - return client pointer
438 * side effects - find person by (nick)name
439 */
440 struct Client *
441 find_person(const struct Client *source_p, const char *name)
442 {
443 struct Client *target_p = NULL;
444
445 if (IsDigit(*name) && IsServer(source_p->from))
446 target_p = hash_find_id(name);
447 else
448 target_p = find_client(name);
449
450 return (target_p && IsClient(target_p)) ? target_p : NULL;
451 }
452
453 /*
454 * find_chasing - find the client structure for a nick name (user)
455 * using history mechanism if necessary. If the client is not found,
456 * an error message (NO SUCH NICK) is generated. If the client was found
457 * through the history, chasing will be 1 and otherwise 0.
458 */
459 struct Client *
460 find_chasing(struct Client *source_p, const char *user, int *chasing)
461 {
462 struct Client *who = find_person(source_p, user);
463
464 if (chasing)
465 *chasing = 0;
466
467 if (who)
468 return who;
469
470 if (IsDigit(*user))
471 return NULL;
472
473 who = whowas_get_history(user, (time_t) General.kill_chase_time_limit);
474 if (!who)
475 {
476 sendto_one(source_p, form_str(ERR_NOSUCHNICK),
477 me.name, source_p->name, user);
478 return NULL;
479 }
480
481 if (chasing)
482 *chasing = 1;
483
484 return who;
485 }
486
487 /*
488 * get_client_name - Return the name of the client
489 * for various tracking and
490 * admin purposes. The main purpose of this function is to
491 * return the "socket host" name of the client, if that
492 * differs from the advertised name (other than case).
493 * But, this can be used to any client structure.
494 *
495 * NOTE 1:
496 * Watch out the allocation of "nbuf", if either source_p->name
497 * or source_p->sockhost gets changed into pointers instead of
498 * directly allocated within the structure...
499 *
500 * NOTE 2:
501 * Function return either a pointer to the structure (source_p) or
502 * to internal buffer (nbuf). *NEVER* use the returned pointer
503 * to modify what it points!!!
504 */
505 const char *
506 get_client_name(const struct Client *client, int showip)
507 {
508 static char nbuf[HOSTLEN * 2 + USERLEN + 5];
509
510 assert(client != NULL);
511
512 if (!irccmp(client->name, client->host))
513 return client->name;
514
515 if (ServerHide.hide_server_ips)
516 if (IsServer(client) || IsConnecting(client) || IsHandshake(client))
517 showip = MASK_IP;
518
519 if (General.hide_spoof_ips)
520 if (showip == SHOW_IP && IsIPSpoof(client))
521 showip = MASK_IP;
522
523 /* And finally, let's get the host information, ip or name */
524 switch (showip)
525 {
526 case SHOW_IP:
527 if (MyConnect(client))
528 {
529 ircsprintf(nbuf, "%s[%s@%s]", client->name, client->username,
530 client->sockhost);
531 break;
532 }
533 case MASK_IP:
534 ircsprintf(nbuf, "%s[%s@255.255.255.255]", client->name,
535 client->username);
536 break;
537 default:
538 ircsprintf(nbuf, "%s[%s@%s]", client->name, client->username,
539 client->host);
540 }
541
542 return nbuf;
543 }
544
545 void
546 free_exited_clients(void)
547 {
548 dlink_node *ptr, *next;
549
550 DLINK_FOREACH_SAFE(ptr, next, dead_list.head)
551 {
552 free_client(ptr->data);
553 dlinkDelete(ptr, &dead_list);
554 free_dlink_node(ptr);
555 }
556 }
557
558 /*
559 * Exit one client, local or remote. Assuming all dependents have
560 * been already removed, and socket closed for local client.
561 *
562 * The only messages generated are QUITs on channels.
563 */
564 static void
565 exit_one_client(struct Client *source_p, const char *quitmsg)
566 {
567 dlink_node *lp = NULL, *next_lp = NULL;
568
569 assert(!IsMe(source_p) && (source_p != &me));
570
571 if (IsClient(source_p))
572 {
573 assert(source_p->servptr);
574 assert(source_p->servptr->serv);
575
576 --Count.total;
577 if (IsOper(source_p))
578 --Count.oper;
579 if (IsInvisible(source_p))
580 --Count.invisi;
581
582 dlinkDelete(&source_p->lnode, &source_p->servptr->serv->client_list);
583
584 /*
585 * If a person is on a channel, send a QUIT notice to every
586 * client (person) on the same channel (so that the client
587 * can show the "**signoff" message). (Note: The notice is
588 * to the local clients *only*)
589 */
590 sendto_common_channels_local(source_p, 0, ":%s!%s@%s QUIT :%s",
591 source_p->name, source_p->username,
592 source_p->host, quitmsg);
593 DLINK_FOREACH_SAFE(lp, next_lp, source_p->channel.head)
594 remove_user_from_channel(lp->data);
595
596 whowas_add_history(source_p, 0);
597 whowas_off_history(source_p);
598
599 assert(source_p->whowas.head == NULL);
600 assert(source_p->whowas.tail == NULL);
601
602 watch_check_hash(source_p, RPL_LOGOFF);
603
604 /* Do local vs. remote processing here */
605 if (MyConnect(source_p))
606 {
607 /* Clean up invitefield */
608 DLINK_FOREACH_SAFE(lp, next_lp, source_p->localClient->invited.head)
609 del_invite(lp->data, source_p);
610
611 /* Clean up allow lists */
612 del_all_accepts(source_p);
613 }
614 }
615 else if (IsServer(source_p))
616 {
617 sendto_realops_flags(UMODE_EXTERNAL, L_ALL,
618 "Server %s split from %s",
619 source_p->name, source_p->servptr->name);
620
621 dlinkDelete(&source_p->lnode, &source_p->servptr->serv->server_list);
622
623 if ((lp = dlinkFindDelete(&global_serv_list, source_p)) != NULL)
624 free_dlink_node(lp);
625 }
626
627 if (splitchecking && !splitmode)
628 check_splitmode(NULL);
629
630 /* Remove source_p from the client lists */
631 if (HasID(source_p))
632 hash_del_id(source_p);
633 if (source_p->name[0])
634 hash_del_client(source_p);
635
636 if (IsUserHostIp(source_p))
637 delete_user_host(source_p->username, source_p->host, !MyConnect(source_p));
638
639 /* remove from global client list
640 * NOTE: source_p->node.next cannot be NULL if the client is added
641 * to global_client_list (there is always &me at its end)
642 */
643 if (source_p != NULL && source_p->node.next != NULL)
644 dlinkDelete(&source_p->node, &global_client_list);
645
646 /* Check to see if the client isn't already on the dead list */
647 assert(dlinkFind(&dead_list, source_p) == NULL);
648
649 /* add to dead client dlist */
650 SetDead(source_p);
651 dlinkAdd(source_p, make_dlink_node(), &dead_list);
652 }
653
654 /* Recursively send QUITs and SQUITs for source_p and all its dependent clients
655 * and servers to those servers that need them. A server needs the client
656 * QUITs if it can't figure them out from the SQUIT (ie pre-TS4) or if it
657 * isn't getting the SQUIT because of @#(*&@)# hostmasking. With TS4, once
658 * a link gets a SQUIT, it doesn't need any QUIT/SQUITs for clients depending
659 * on that one -orabidoo
660 *
661 * This is now called on each local server -adx
662 */
663 static void
664 recurse_send_quits(struct Client *original_source_p, struct Client *source_p,
665 struct Client *from, struct Client *to, const char *comment,
666 const char *splitstr, const char *myname)
667 {
668 dlink_node *ptr, *next;
669 struct Client *target_p;
670 int hidden = match(myname, source_p->name);
671
672 assert(to != source_p); /* should be already removed from serv_list */
673
674 /* If this server can handle quit storm (QS) removal
675 * of dependents, just send the SQUIT
676 *
677 * Always check *all* dependent servers if some of them are
678 * hidden behind fakename. If so, send out the QUITs -adx
679 */
680 if (hidden || !IsCapable(to, CAP_QS))
681 {
682 DLINK_FOREACH_SAFE(ptr, next, source_p->serv->client_list.head)
683 {
684 target_p = ptr->data;
685 sendto_one(to, ":%s QUIT :%s", target_p->name, splitstr);
686 }
687 }
688
689 DLINK_FOREACH_SAFE(ptr, next, source_p->serv->server_list.head)
690 recurse_send_quits(original_source_p, ptr->data, from, to,
691 comment, splitstr, myname);
692
693 if (!hidden && ((source_p == original_source_p && to != from) ||
694 !IsCapable(to, CAP_QS)))
695 {
696 /* don't use a prefix here - we have to be 100% sure the message
697 * will be accepted without Unknown prefix etc.. */
698 sendto_one(to, "SQUIT %s :%s", ID_or_name(source_p, to), comment);
699 }
700 }
701
702 /*
703 * Remove all clients that depend on source_p; assumes all (S)QUITs have
704 * already been sent. we make sure to exit a server's dependent clients
705 * and servers before the server itself; exit_one_client takes care of
706 * actually removing things off llists. tweaked from +CSr31 -orabidoo
707 */
708 static void
709 recurse_remove_clients(struct Client *source_p, const char *quitmsg)
710 {
711 dlink_node *ptr = NULL, *next = NULL;
712
713 DLINK_FOREACH_SAFE(ptr, next, source_p->serv->client_list.head)
714 exit_one_client(ptr->data, quitmsg);
715
716 DLINK_FOREACH_SAFE(ptr, next, source_p->serv->server_list.head)
717 {
718 recurse_remove_clients(ptr->data, quitmsg);
719 exit_one_client(ptr->data, quitmsg);
720 }
721 }
722
723 /*
724 ** Remove *everything* that depends on source_p, from all lists, and sending
725 ** all necessary QUITs and SQUITs. source_p itself is still on the lists,
726 ** and its SQUITs have been sent except for the upstream one -orabidoo
727 */
728 static void
729 remove_dependents(struct Client *source_p, struct Client *from,
730 const char *comment, const char *splitstr)
731 {
732 struct Client *to;
733 struct ConnectConf *conf;
734 static char myname[HOSTLEN+1];
735 dlink_node *ptr;
736
737 DLINK_FOREACH(ptr, serv_list.head)
738 {
739 to = ptr->data;
740
741 if ((conf = to->serv->sconf) != NULL)
742 strlcpy(myname, my_name_for_link(conf), sizeof(myname));
743 else
744 strlcpy(myname, me.name, sizeof(myname));
745 recurse_send_quits(source_p, source_p, from, to,
746 comment, splitstr, myname);
747 }
748
749 recurse_remove_clients(source_p, splitstr);
750 }
751
752 /*
753 * exit_client - exit a client of any type. Generally, you can use
754 * this on any struct Client, regardless of its state.
755 *
756 * Note, you shouldn't exit remote _users_ without first doing
757 * SetKilled and propagating a kill or similar message. However,
758 * it is perfectly correct to call exit_client to force a _server_
759 * quit (either local or remote one).
760 *
761 * inputs: - a client pointer that is going to be exited
762 * - for servers, the second argument is a pointer to who
763 * is firing the server. This side won't get any generated
764 * messages. NEVER NULL!
765 * output: none
766 * side effects: the client is delinked from all lists, disconnected,
767 * and the rest of IRC network is notified of the exit.
768 * Client memory is scheduled to be freed
769 */
770 void
771 exit_client(struct Client *source_p, struct Client *from, const char *comment)
772 {
773 dlink_node *m = NULL;
774
775 if (MyConnect(source_p))
776 {
777 /*
778 * DO NOT REMOVE. exit_client can be called twice after a failed
779 * read/write.
780 */
781 if (IsClosing(source_p))
782 return;
783
784 SetClosing(source_p);
785
786 if (IsIpHash(source_p))
787 remove_one_ip(&source_p->localClient->ip);
788
789 delete_auth(source_p);
790
791 if (IsClient(source_p))
792 {
793 --Count.local;
794
795 if (IsOper(source_p))
796 if ((m = dlinkFindDelete(&oper_list, source_p)) != NULL)
797 free_dlink_node(m);
798
799 dlinkDelete(&source_p->localClient->lclient_node, &local_client_list);
800
801 if (source_p->localClient->list_task != NULL)
802 free_list_task(source_p->localClient->list_task, source_p);
803
804 watch_del_watch_list(source_p);
805 sendto_realops_flags(UMODE_CCONN, L_ALL,
806 "Client exiting: %s (%s@%s) [%s] [%s]",
807 source_p->name, source_p->username, source_p->host, comment,
808 General.hide_spoof_ips && IsIPSpoof(source_p) ?
809 "255.255.255.255" : source_p->sockhost);
810 }
811 else if (IsServer(source_p))
812 {
813 if ((m = dlinkFindDelete(&serv_list, source_p)) != NULL)
814 unset_chcap_usage_counts(source_p);
815
816 --Count.myserver;
817 }
818 else
819 {
820 /*
821 * This source_p could have status of one of STAT_UNKNOWN,
822 * STAT_CONNECTING or STAT_HANDSHAKE
823 * all of which are lumped together into unknown_list
824 *
825 * In all above cases IsRegistered() will not be true.
826 */
827 assert(!IsRegistered(source_p));
828 dlinkDelete(&source_p->localClient->lclient_node, &unknown_list);
829 }
830
831 log_user_exit(source_p);
832
833 if (!IsDead(source_p))
834 {
835 if (IsServer(source_p))
836 {
837 /* for them, we are exiting the network */
838 sendto_one(source_p, ":%s SQUIT %s :%s",
839 ID_or_name(from, source_p), me.name, comment);
840 }
841
842 sendto_one(source_p, "ERROR :Closing Link: %s (%s)",
843 source_p->host, comment);
844 }
845
846 /*
847 * Close the Client connection first and mark it so that no
848 * messages are attempted to send to it. Remember it makes
849 * source_p->from == NULL.
850 */
851 close_connection(source_p);
852 }
853
854 if (IsClient(source_p) && !IsKilled(source_p))
855 {
856 sendto_server(from->from, source_p, NULL, CAP_TS6, NOCAPS,
857 ":%s QUIT :%s", ID(source_p), comment);
858 sendto_server(from->from, source_p, NULL, NOCAPS, CAP_TS6,
859 ":%s QUIT :%s", source_p->name, comment);
860 }
861 else if (IsServer(source_p))
862 {
863 char splitstr[HOSTLEN + HOSTLEN + 2];
864
865 /* This shouldn't ever happen */
866 assert(source_p->serv != NULL && source_p->servptr != NULL);
867
868 if (ServerHide.hide_servers)
869 /*
870 * set netsplit message to "*.net *.split" to still show
871 * that it's a split, but hide the servers splitting
872 */
873 strcpy(splitstr, "*.net *.split");
874 else
875 snprintf(splitstr, sizeof(splitstr), "%s %s",
876 source_p->servptr->name, source_p->name);
877
878 remove_dependents(source_p, from->from, comment, splitstr);
879
880 if (MyConnect(source_p))
881 {
882 sendto_realops_flags(UMODE_ALL, L_ALL,
883 "%s was connected for %d seconds. %llu/%llu sendK/recvK.",
884 source_p->name, (int)(CurrentTime - source_p->firsttime),
885 source_p->localClient->send.bytes >> 10,
886 source_p->localClient->recv.bytes >> 10);
887 ilog(L_NOTICE, "%s was connected for %d seconds. %llu/%llu sendK/recvK.",
888 source_p->name, (int)(CurrentTime - source_p->firsttime),
889 source_p->localClient->send.bytes >> 10,
890 source_p->localClient->recv.bytes >> 10);
891 }
892 }
893
894 /* The client *better* be off all of the lists */
895 assert(dlinkFind(&unknown_list, source_p) == NULL);
896 assert(dlinkFind(&local_client_list, source_p) == NULL);
897 assert(dlinkFind(&serv_list, source_p) == NULL);
898 assert(dlinkFind(&oper_list, source_p) == NULL);
899
900 exit_one_client(source_p, comment);
901 }
902
903 /*
904 * close_connection
905 * Close the physical connection. This function sets client_p->from == NULL.
906 */
907 static void
908 close_connection(struct Client *client_p)
909 {
910 assert(NULL != client_p);
911
912 if (!IsDead(client_p))
913 {
914 /* attempt to flush any pending dbufs. Evil, but .. -- adrian */
915 /* there is still a chance that we might send data to this socket
916 * even if it is marked as blocked (COMM_SELECT_READ handler is called
917 * before COMM_SELECT_WRITE). Let's try, nothing to lose.. -adx
918 */
919 ClearSendqBlocked(client_p);
920 send_queued_write(client_p);
921 }
922
923 if (IsClient(client_p))
924 {
925 ++ServerStats.is_cl;
926 ServerStats.is_cbs += client_p->localClient->send.bytes;
927 ServerStats.is_cbr += client_p->localClient->recv.bytes;
928 ServerStats.is_cti += CurrentTime - client_p->firsttime;
929 }
930 else if (IsServer(client_p))
931 {
932 ++ServerStats.is_sv;
933 ServerStats.is_sbs += client_p->localClient->send.bytes;
934 ServerStats.is_sbr += client_p->localClient->recv.bytes;
935 ServerStats.is_sti += CurrentTime - client_p->firsttime;
936 }
937 else
938 ++ServerStats.is_ni;
939
940 #ifdef HAVE_LIBCRYPTO
941 if (client_p->localClient->fd.ssl)
942 {
943 SSL_set_shutdown(client_p->localClient->fd.ssl, SSL_RECEIVED_SHUTDOWN);
944
945 if (!SSL_shutdown(client_p->localClient->fd.ssl))
946 SSL_shutdown(client_p->localClient->fd.ssl);
947 }
948 #endif
949 if (client_p->localClient->fd.flags.open)
950 fd_close(&client_p->localClient->fd);
951
952 if (HasServlink(client_p))
953 if (client_p->localClient->ctrlfd.flags.open)
954 fd_close(&client_p->localClient->ctrlfd);
955
956 dbuf_clear(&client_p->localClient->buf_sendq);
957 dbuf_clear(&client_p->localClient->buf_recvq);
958
959 MyFree(client_p->localClient->passwd);
960
961 unref_class(client_p->localClient->class);
962 client_p->localClient->class = NULL;
963 if (client_p->serv && client_p->serv->sconf)
964 {
965 unref_link(client_p->serv->sconf);
966 client_p->serv->sconf = NULL;
967 }
968
969 client_p->from = NULL; /* ...this should catch them! >:) --msa */
970 }
971
972 /*
973 * report_error - report an error from an errno.
974 * Record error to log and also send a copy to all *LOCAL* opers online.
975 *
976 * text is a *format* string for outputing error. It must
977 * contain only two '%s', the first will be replaced
978 * by the sockhost from the client_p, and the latter will
979 * be taken from sys_errlist[errno].
980 *
981 * client_p if not NULL, is the *LOCAL* client associated with
982 * the error.
983 *
984 * Cannot use perror() within daemon. stderr is closed in
985 * ircd and cannot be used. And, worse yet, it might have
986 * been reassigned to a normal connection...
987 *
988 * Actually stderr is still there IFF ircd was run with -s --Rodder
989 */
990 void
991 report_error(int level, const char *text, const char *who, int error)
992 {
993 who = (who != NULL) ? who : "";
994
995 sendto_realops_flags(UMODE_DEBUG, level, text, who, strerror(error));
996 log_oper_action(LOG_IOERR_TYPE, NULL, "%s %s %s\n", who, text, strerror(error));
997 ilog(L_ERROR, text, who, strerror(error));
998 }
999
1000 /*
1001 * dead_link_on_write - report a write error if not already dead,
1002 * mark it as dead then exit it
1003 */
1004 void
1005 dead_link_on_write(struct Client *client_p, int ierrno)
1006 {
1007 dlink_node *ptr;
1008
1009 if (IsDefunct(client_p))
1010 return;
1011
1012 dbuf_clear(&client_p->localClient->buf_recvq);
1013 dbuf_clear(&client_p->localClient->buf_sendq);
1014
1015 assert(dlinkFind(&abort_list, client_p) == NULL);
1016 ptr = make_dlink_node();
1017 /* don't let exit_aborted_clients() finish yet */
1018 dlinkAddTail(client_p, ptr, &abort_list);
1019
1020 if (eac_next == NULL)
1021 eac_next = ptr;
1022
1023 SetDead(client_p); /* You are dead my friend */
1024 }
1025
1026 /*
1027 * dead_link_on_read - report a read error if not already dead,
1028 * mark it as dead then exit it
1029 */
1030 void
1031 dead_link_on_read(struct Client *client_p, int error)
1032 {
1033 char errmsg[IRCD_BUFSIZE];
1034 int current_error;
1035
1036 if (IsDefunct(client_p))
1037 return;
1038
1039 dbuf_clear(&client_p->localClient->buf_recvq);
1040 dbuf_clear(&client_p->localClient->buf_sendq);
1041
1042 current_error = get_sockerr(client_p->localClient->fd.fd);
1043
1044 if (IsServer(client_p) || IsHandshake(client_p))
1045 {
1046 int connected = CurrentTime - client_p->firsttime;
1047
1048 if (error == 0)
1049 {
1050 /* Admins get the real IP */
1051 sendto_realops_flags(UMODE_ALL, L_ADMIN,
1052 "Server %s closed the connection",
1053 get_client_name(client_p, SHOW_IP));
1054
1055 /* Opers get a masked IP */
1056 sendto_realops_flags(UMODE_ALL, L_OPER,
1057 "Server %s closed the connection",
1058 get_client_name(client_p, MASK_IP));
1059
1060 ilog(L_NOTICE, "Server %s closed the connection",
1061 get_client_name(client_p, SHOW_IP));
1062 }
1063 else
1064 {
1065 report_error(L_ADMIN, "Lost connection to %s: %d",
1066 get_client_name(client_p, SHOW_IP), current_error);
1067 report_error(L_OPER, "Lost connection to %s: %d",
1068 get_client_name(client_p, MASK_IP), current_error);
1069 }
1070
1071 sendto_realops_flags(UMODE_ALL, L_ALL,
1072 "%s had been connected for %d day%s, %2d:%02d:%02d",
1073 client_p->name, connected/86400,
1074 (connected/86400 == 1) ? "" : "s",
1075 (connected % 86400) / 3600, (connected % 3600) / 60,
1076 connected % 60);
1077 }
1078
1079 if (error == 0)
1080 strlcpy(errmsg, "Remote host closed the connection",
1081 sizeof(errmsg));
1082 else
1083 ircsprintf(errmsg, "Read error: %s",
1084 strerror(current_error));
1085
1086 exit_client(client_p, &me, errmsg);
1087 }
1088
1089 void
1090 exit_aborted_clients(void)
1091 {
1092 dlink_node *ptr;
1093 struct Client *target_p;
1094 const char *notice;
1095
1096 DLINK_FOREACH_SAFE(ptr, eac_next, abort_list.head)
1097 {
1098 target_p = ptr->data;
1099 eac_next = ptr->next;
1100
1101 if (target_p == NULL)
1102 {
1103 sendto_realops_flags(UMODE_ALL, L_ALL,
1104 "Warning: null client on abort_list!");
1105 dlinkDelete(ptr, &abort_list);
1106 free_dlink_node(ptr);
1107 continue;
1108 }
1109
1110 dlinkDelete(ptr, &abort_list);
1111
1112 if (IsSendQExceeded(target_p))
1113 notice = "Max SendQ exceeded";
1114 else
1115 notice = "Write error: connection closed";
1116
1117 exit_client(target_p, &me, notice);
1118 free_dlink_node(ptr);
1119 }
1120 }
1121
1122 /*
1123 * accept processing, this adds a form of "caller ID" to ircd
1124 *
1125 * If a client puts themselves into "caller ID only" mode,
1126 * only clients that match a client pointer they have put on
1127 * the accept list will be allowed to message them.
1128 *
1129 * Diane Bruce, "Dianora" db@db.net
1130 */
1131
1132 void
1133 del_accept(struct Accept *acceptvar, struct Client *client_p)
1134 {
1135 dlinkDelete(&acceptvar->node, &client_p->localClient->acceptlist);
1136
1137 MyFree(acceptvar->nick);
1138 MyFree(acceptvar->user);
1139 MyFree(acceptvar->host);
1140 MyFree(acceptvar);
1141 }
1142
1143 struct Accept *
1144 find_accept(const char *nick, const char *user,
1145 const char *host, struct Client *client_p, int do_match)
1146 {
1147 dlink_node *ptr = NULL;
1148 /* XXX We wouldn't need that if match() would return 0 on match */
1149 int (*cmpfunc)(const char *, const char *) = do_match ? match : irccmp;
1150
1151 DLINK_FOREACH(ptr, client_p->localClient->acceptlist.head)
1152 {
1153 struct Accept *acceptvar = ptr->data;
1154
1155 if (cmpfunc(acceptvar->nick, nick) == do_match &&
1156 cmpfunc(acceptvar->user, user) == do_match &&
1157 cmpfunc(acceptvar->host, host) == do_match)
1158 return acceptvar;
1159 }
1160
1161 return NULL;
1162 }
1163
1164 /* accept_message()
1165 *
1166 * inputs - pointer to source client
1167 * - pointer to target client
1168 * output - 1 if accept this message 0 if not
1169 * side effects - See if source is on target's allow list
1170 */
1171 int
1172 accept_message(struct Client *source,
1173 struct Client *target)
1174 {
1175 dlink_node *ptr = NULL;
1176
1177 if (source == target || find_accept(source->name, source->username,
1178 source->host, target, 1))
1179 return 1;
1180
1181 if (IsSoftCallerId(target))
1182 DLINK_FOREACH(ptr, target->channel.head)
1183 if (IsMember(source, ((struct Membership *)ptr->data)->chptr))
1184 return 1;
1185
1186 return 0;
1187 }
1188
1189 /* del_all_accepts()
1190 *
1191 * inputs - pointer to exiting client
1192 * output - NONE
1193 * side effects - Walk through given clients acceptlist and remove all entries
1194 */
1195 void
1196 del_all_accepts(struct Client *client_p)
1197 {
1198 dlink_node *ptr = NULL, *next_ptr = NULL;
1199
1200 DLINK_FOREACH_SAFE(ptr, next_ptr, client_p->localClient->acceptlist.head)
1201 del_accept(ptr->data, client_p);
1202 }
1203
1204 /* set_initial_nick()
1205 *
1206 * inputs
1207 * output
1208 * side effects -
1209 *
1210 * This function is only called to set up an initially registering
1211 * client.
1212 */
1213 void
1214 set_initial_nick(struct Client *source_p, const char *nick)
1215 {
1216 char buf[USERLEN + 1];
1217
1218 /* This had to be copied here to avoid problems.. */
1219 source_p->tsinfo = CurrentTime;
1220 source_p->localClient->registration &= ~REG_NEED_NICK;
1221
1222 if (source_p->name[0])
1223 hash_del_client(source_p);
1224
1225 strlcpy(source_p->name, nick, sizeof(source_p->name));
1226 hash_add_client(source_p);
1227
1228 /* fd_desc is long enough */
1229 fd_note(&source_p->localClient->fd, "Nick: %s", nick);
1230
1231 if (!source_p->localClient->registration)
1232 {
1233 strlcpy(buf, source_p->username, sizeof(buf));
1234
1235 /*
1236 * USER already received, now we have NICK.
1237 * *NOTE* For servers "NICK" *must* precede the
1238 * user message (giving USER before NICK is possible
1239 * only for local client connection!). register_user
1240 * may reject the client and call exit_client for it
1241 * --must test this and exit m_nick too!!!
1242 */
1243 register_local_user(source_p, buf);
1244 }
1245 }
1246
1247 /* change_local_nick()
1248 *
1249 * inputs - pointer to server
1250 * - pointer to client
1251 * - nick
1252 * output -
1253 * side effects - changes nick of a LOCAL user
1254 */
1255 void
1256 change_local_nick(struct Client *client_p, struct Client *source_p, const char *nick)
1257 {
1258 int samenick = 0;
1259
1260 /*
1261 * Client just changing his/her nick. If he/she is
1262 * on a channel, send note of change to all clients
1263 * on that channel. Propagate notice to other servers.
1264 */
1265 if ((source_p->localClient->last_nick_change +
1266 General.max_nick_time) < CurrentTime)
1267 source_p->localClient->number_of_nick_changes = 0;
1268 source_p->localClient->last_nick_change = CurrentTime;
1269 source_p->localClient->number_of_nick_changes++;
1270
1271 if ((General.anti_nick_flood &&
1272 (source_p->localClient->number_of_nick_changes
1273 <= General.max_nick_changes)) ||
1274 !General.anti_nick_flood ||
1275 (IsOper(source_p) && General.no_oper_flood))
1276 {
1277 samenick = !irccmp(source_p->name, nick);
1278 if (!samenick)
1279 source_p->tsinfo = CurrentTime;
1280
1281 /* XXX - the format of this notice should eventually be changed
1282 * to either %s[%s@%s], or even better would be get_client_name() -bill
1283 */
1284 sendto_realops_flags(UMODE_NCHANGE, L_ALL, "Nick change: From %s to %s [%s@%s]",
1285 source_p->name, nick, source_p->username, source_p->host);
1286 sendto_common_channels_local(source_p, 1, ":%s!%s@%s NICK :%s",
1287 source_p->name, source_p->username,
1288 source_p->host, nick);
1289
1290 whowas_add_history(source_p, 1);
1291
1292 sendto_server(client_p, source_p, NULL, CAP_TS6, NOCAPS,
1293 ":%s NICK %s :%lu",
1294 ID(source_p), nick, (unsigned long)source_p->tsinfo);
1295 sendto_server(client_p, source_p, NULL, NOCAPS, CAP_TS6,
1296 ":%s NICK %s :%lu",
1297 source_p->name, nick, (unsigned long)source_p->tsinfo);
1298 }
1299 else
1300 {
1301 sendto_one(source_p, form_str(ERR_NICKTOOFAST),
1302 me.name, source_p->name, source_p->name,
1303 nick, General.max_nick_time);
1304 return;
1305 }
1306
1307 /* Finally, add to hash */
1308 assert(source_p->name[0]);
1309
1310 hash_del_client(source_p);
1311
1312 if (!samenick)
1313 watch_check_hash(source_p, RPL_LOGOFF);
1314 strcpy(source_p->name, nick);
1315 hash_add_client(source_p);
1316
1317 if (!samenick)
1318 watch_check_hash(source_p, RPL_LOGON);
1319
1320 /* fd_desc is long enough */
1321 fd_note(&client_p->localClient->fd, "Nick: %s", nick);
1322 }
1323
1324 /* log_user_exit()
1325 *
1326 * inputs - pointer to connecting client
1327 * output - NONE
1328 * side effects - Current exiting client is logged to
1329 * either SYSLOG or to file.
1330 */
1331 void
1332 log_user_exit(struct Client *source_p)
1333 {
1334 time_t on_for = CurrentTime - source_p->firsttime;
1335 #ifdef SYSLOG_USERS
1336 if (IsClient(source_p))
1337 {
1338 ilog(L_INFO, "%s (%3ld:%02ld:%02ld): %s!%s@%s %llu/%llu\n",
1339 myctime(source_p->firsttime),
1340 (signed long) on_for / 3600,
1341 (signed long) (on_for % 3600)/60,
1342 (signed long) on_for % 60,
1343 source_p->name, source_p->username, source_p->host,
1344 source_p->localClient->send.bytes>>10,
1345 source_p->localClient->recv.bytes>>10);
1346 }
1347 #else
1348 {
1349 char linebuf[BUFSIZ];
1350
1351 /*
1352 * This conditional makes the logfile active only after
1353 * it's been created - thus logging can be turned off by
1354 * removing the file.
1355 * -Taner
1356 */
1357 if (IsClient(source_p))
1358 {
1359 if (user_log_fb == NULL)
1360 if ((Logging.userlog[0] != '\0') &&
1361 (user_log_fb = fbopen(Logging.userlog, "r")) != NULL)
1362 {
1363 fbclose(user_log_fb);
1364 user_log_fb = fbopen(Logging.userlog, "a");
1365 }
1366
1367 if (user_log_fb != NULL)
1368 {
1369 size_t nbytes = ircsprintf(linebuf,
1370 "%s (%3ld:%02ld:%02ld): %s!%s@%s %llu/%llu\n",
1371 myctime(source_p->firsttime),
1372 (signed long) on_for / 3600,
1373 (signed long) (on_for % 3600)/60,
1374 (signed long) on_for % 60,
1375 source_p->name, source_p->username, source_p->host,
1376 source_p->localClient->send.bytes>>10,
1377 source_p->localClient->recv.bytes>>10);
1378 fbputs(linebuf, user_log_fb, nbytes);
1379 }
1380 }
1381 }
1382 #endif
1383 }
1384
1385
1386 /* log_oper_action()
1387 *
1388 * inputs - type of oper log entry
1389 * - pointer to oper
1390 * - const char *pattern == format string
1391 * - var args for format string
1392 * output - none
1393 * side effects - corresponding log is written to, if its present.
1394 *
1395 * rewritten sept 5 2005 - Dianora
1396 */
1397 void
1398 log_oper_action(int log_type, const struct Client *source_p,
1399 const char *pattern, ...)
1400 {
1401 va_list args;
1402 char linebuf[IRCD_BUFSIZE];
1403 FBFILE *log_fb;
1404 char *logfile;
1405 const char *log_message;
1406 size_t nbytes;
1407 size_t n_preamble;
1408 char *p;
1409
1410 switch(log_type)
1411 {
1412 case LOG_OPER_TYPE:
1413 logfile = Logging.operlog;
1414 log_message = "OPER";
1415 break;
1416 case LOG_FAILED_OPER_TYPE:
1417 logfile = Logging.failed_operlog;
1418 log_message = "FAILED OPER";
1419 break;
1420 case LOG_KLINE_TYPE:
1421 logfile = Logging.klinelog;
1422 log_message = "KLINE";
1423 break;
1424 case LOG_RKLINE_TYPE:
1425 logfile = Logging.klinelog;
1426 log_message = "RKLINE";
1427 break;
1428 case LOG_DLINE_TYPE:
1429 logfile = Logging.klinelog;
1430 log_message = "DLINE";
1431 break;
1432 case LOG_TEMP_DLINE_TYPE:
1433 logfile = Logging.klinelog;
1434 log_message = "TEMP DLINE";
1435 break;
1436 case LOG_TEMP_KLINE_TYPE:
1437 logfile = Logging.klinelog;
1438 log_message = "TEMP KLINE";
1439 break;
1440 case LOG_GLINE_TYPE:
1441 logfile = Logging.glinelog;
1442 log_message = "GLINE";
1443 break;
1444 case LOG_KILL_TYPE:
1445 logfile = Logging.killlog;
1446 log_message = "KILL";
1447 break;
1448 case LOG_IOERR_TYPE:
1449 logfile = Logging.ioerrlog;
1450 log_message = "IO ERR";
1451 break;
1452 default:
1453 return;
1454 }
1455
1456 if (*logfile == '\0')
1457 return;
1458
1459 p = linebuf;
1460 if (source_p != NULL)
1461 {
1462 n_preamble = ircsprintf(linebuf, "%s %s by (%s!%s@%s) :",
1463 myctime(CurrentTime), log_message,
1464 source_p->name, source_p->username, source_p->host);
1465
1466 }
1467 else
1468 {
1469 n_preamble = ircsprintf(linebuf, "%s %s :",
1470 myctime(CurrentTime), log_message);
1471 }
1472
1473 p += n_preamble;
1474
1475 if ((log_fb = fbopen(logfile, "r")) != NULL)
1476 {
1477 fbclose(log_fb);
1478 log_fb = fbopen(logfile, "a");
1479 if (log_fb == NULL)
1480 return;
1481 va_start(args, pattern);
1482 /* XXX add check for IRCD_BUFSIZE-(n_preamble+1) < 0 ? -db */
1483 nbytes = vsnprintf(p, IRCD_BUFSIZE-(n_preamble+1), pattern, args);
1484 nbytes += n_preamble;
1485 va_end(args);
1486 fbputs(linebuf, log_fb, nbytes);
1487 fbclose(log_fb);
1488 }
1489 }

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision