00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "stdinc.h"
00028 #include "tools.h"
00029 #include "channel.h"
00030 #include "channel_mode.h"
00031 #include "client.h"
00032 #include "common.h"
00033 #include "hash.h"
00034 #include "hostmask.h"
00035 #include "irc_string.h"
00036 #include "sprintf_irc.h"
00037 #include "ircd.h"
00038 #include "list.h"
00039 #include "numeric.h"
00040 #include "s_serv.h"
00041 #include "s_user.h"
00042 #include "send.h"
00043 #include "s_conf.h"
00044 #include "event.h"
00045 #include "memory.h"
00046 #include "balloc.h"
00047
00048 struct config_channel_entry ConfigChannel;
00049 dlink_list global_channel_list = { NULL, NULL, 0 };
00050 dlink_list lazylink_channels = { NULL, NULL, 0 };
00051 BlockHeap *ban_heap;
00052
00053 static BlockHeap *topic_heap = NULL;
00054 static BlockHeap *member_heap = NULL;
00055 static BlockHeap *channel_heap = NULL;
00056
00057 static char buf[IRCD_BUFSIZE];
00058 static char modebuf[MODEBUFLEN];
00059 static char parabuf[MODEBUFLEN];
00060
00061
00062
00063
00064 void
00065 init_channels(void)
00066 {
00067 add_capability("EX", CAP_EX, 1);
00068 add_capability("IE", CAP_IE, 1);
00069 add_capability("CHW", CAP_CHW, 1);
00070
00071 channel_heap = BlockHeapCreate("channel", sizeof(struct Channel), CHANNEL_HEAP_SIZE);
00072 ban_heap = BlockHeapCreate("ban", sizeof(struct Ban), BAN_HEAP_SIZE);
00073 topic_heap = BlockHeapCreate("topic", TOPICLEN+1 + USERHOST_REPLYLEN, TOPIC_HEAP_SIZE);
00074 member_heap = BlockHeapCreate("member", sizeof(struct Membership), CHANNEL_HEAP_SIZE*2);
00075 }
00076
00077
00078
00079
00080
00081
00082
00083
00084 void
00085 add_user_to_channel(struct Channel *chptr, struct Client *who,
00086 unsigned int flags, int flood_ctrl)
00087 {
00088 struct Membership *ms = NULL;
00089
00090 if (GlobalSetOptions.joinfloodtime > 0)
00091 {
00092 if (flood_ctrl)
00093 chptr->number_joined++;
00094
00095 chptr->number_joined -= (CurrentTime - chptr->last_join_time) *
00096 (((float)GlobalSetOptions.joinfloodcount) /
00097 (float)GlobalSetOptions.joinfloodtime);
00098
00099 if (chptr->number_joined <= 0)
00100 {
00101 chptr->number_joined = 0;
00102 ClearJoinFloodNoticed(chptr);
00103 }
00104 else if (chptr->number_joined >= GlobalSetOptions.joinfloodcount)
00105 {
00106 chptr->number_joined = GlobalSetOptions.joinfloodcount;
00107
00108 if (!IsSetJoinFloodNoticed(chptr))
00109 {
00110 SetJoinFloodNoticed(chptr);
00111 sendto_realops_flags(UMODE_BOTS, L_ALL,
00112 "Possible Join Flooder %s on %s target: %s",
00113 get_client_name(who, HIDE_IP),
00114 who->servptr->name, chptr->chname);
00115 }
00116 }
00117
00118 chptr->last_join_time = CurrentTime;
00119 }
00120
00121 ms = BlockHeapAlloc(member_heap);
00122 ms->client_p = who;
00123 ms->chptr = chptr;
00124 ms->flags = flags;
00125
00126 dlinkAdd(ms, &ms->channode, &chptr->members);
00127 dlinkAdd(ms, &ms->usernode, &who->channel);
00128 }
00129
00130
00131
00132
00133
00134 void
00135 remove_user_from_channel(struct Membership *member)
00136 {
00137 struct Client *client_p = member->client_p;
00138 struct Channel *chptr = member->chptr;
00139
00140 dlinkDelete(&member->channode, &chptr->members);
00141 dlinkDelete(&member->usernode, &client_p->channel);
00142
00143 BlockHeapFree(member_heap, member);
00144
00145 if (dlink_list_length(&chptr->members) == 0)
00146 destroy_channel(chptr);
00147 }
00148
00149
00150
00151
00152
00153
00154
00155 static void
00156 send_members(struct Client *client_p, struct Channel *chptr,
00157 char *lmodebuf, char *lparabuf)
00158 {
00159 struct Membership *ms;
00160 dlink_node *ptr;
00161 int tlen;
00162 char *t, *start;
00163
00164 start = t = buf + ircsprintf(buf, ":%s SJOIN %lu %s %s %s:",
00165 ID_or_name(&me, client_p),
00166 (unsigned long)chptr->channelts,
00167 chptr->chname, lmodebuf, lparabuf);
00168
00169 DLINK_FOREACH(ptr, chptr->members.head)
00170 {
00171 ms = ptr->data;
00172
00173 tlen = strlen(IsCapable(client_p, CAP_TS6) ?
00174 ID(ms->client_p) : ms->client_p->name) + 1;
00175
00176 if (ms->flags & CHFL_CHANOP)
00177 tlen++;
00178 #ifdef HALFOPS
00179 else if (ms->flags & CHFL_HALFOP)
00180 tlen++;
00181 #endif
00182 if (ms->flags & CHFL_VOICE)
00183 tlen++;
00184
00185
00186
00187
00188 if (t + tlen - buf > sizeof(buf) - 1)
00189 {
00190 *(t - 1) = '\0';
00191 sendto_one(client_p, "%s", buf);
00192 t = start;
00193 }
00194
00195 if ((ms->flags & (CHFL_CHANOP | CHFL_HALFOP)))
00196 *t++ = (!(ms->flags & CHFL_CHANOP) && IsCapable(client_p, CAP_HOPS)) ?
00197 '%' : '@';
00198 if ((ms->flags & CHFL_VOICE))
00199 *t++ = '+';
00200
00201 if (IsCapable(client_p, CAP_TS6))
00202 strcpy(t, ID(ms->client_p));
00203 else
00204 strcpy(t, ms->client_p->name);
00205 t += strlen(t);
00206 *t++ = ' ';
00207 }
00208
00209
00210 if (chptr->members.head != NULL)
00211 t--;
00212 *t = '\0';
00213 sendto_one(client_p, "%s", buf);
00214 }
00215
00216
00217
00218
00219
00220
00221
00222 static void
00223 send_mode_list(struct Client *client_p, struct Channel *chptr,
00224 dlink_list *top, char flag)
00225 {
00226 int ts5 = !IsCapable(client_p, CAP_TS6);
00227 dlink_node *lp;
00228 struct Ban *banptr;
00229 char pbuf[IRCD_BUFSIZE];
00230 int tlen, mlen, cur_len, count = 0;
00231 char *mp = NULL, *pp = pbuf;
00232
00233 if (top == NULL || top->length == 0)
00234 return;
00235
00236 if (ts5)
00237 mlen = ircsprintf(buf, ":%s MODE %s +", me.name, chptr->chname);
00238 else
00239 mlen = ircsprintf(buf, ":%s BMASK %lu %s %c :", me.id,
00240 (unsigned long)chptr->channelts, chptr->chname, flag);
00241
00242
00243 cur_len = mlen + ts5;
00244 mp = buf + mlen;
00245
00246 DLINK_FOREACH(lp, top->head)
00247 {
00248 banptr = lp->data;
00249
00250
00251 tlen = banptr->len + 3 + ts5;
00252
00253
00254
00255
00256
00257
00258 if (cur_len + (tlen - 1) > IRCD_BUFSIZE - 2 ||
00259 (!IsCapable(client_p, CAP_TS6) &&
00260 (count >= MAXMODEPARAMS || pp - pbuf >= MODEBUFLEN)))
00261 {
00262 *(pp - 1) = '\0';
00263 sendto_one(client_p, "%s%s%s", buf, ts5 ? " " : "", pbuf);
00264
00265 cur_len = mlen + ts5;
00266 mp = buf + mlen;
00267 pp = pbuf;
00268 count = 0;
00269 }
00270
00271 count++;
00272 if (ts5)
00273 {
00274 *mp++ = flag;
00275 *mp = '\0';
00276 }
00277
00278 pp += ircsprintf(pp, "%s!%s@%s ", banptr->name, banptr->username,
00279 banptr->host);
00280 cur_len += tlen;
00281 }
00282
00283 *(pp - 1) = '\0';
00284 sendto_one(client_p, "%s%s%s", buf, ts5 ? " " : "", pbuf);
00285 }
00286
00287
00288
00289
00290
00291 void
00292 send_channel_modes(struct Client *client_p, struct Channel *chptr)
00293 {
00294 if (chptr->chname[0] != '#')
00295 return;
00296
00297 *modebuf = *parabuf = '\0';
00298 channel_modes(chptr, client_p, modebuf, parabuf);
00299 send_members(client_p, chptr, modebuf, parabuf);
00300
00301 send_mode_list(client_p, chptr, &chptr->banlist, 'b');
00302
00303 if (IsCapable(client_p, CAP_EX))
00304 send_mode_list(client_p, chptr, &chptr->exceptlist, 'e');
00305 if (IsCapable(client_p, CAP_IE))
00306 send_mode_list(client_p, chptr, &chptr->invexlist, 'I');
00307 }
00308
00309
00310
00311
00312
00313
00314 int
00315 check_channel_name(const char *name, int local)
00316 {
00317 const char *p = name;
00318 int max_length = local ? LOCAL_CHANNELLEN : CHANNELLEN;
00319 assert(name != NULL);
00320
00321 if (!IsChanPrefix(*p))
00322 return 0;
00323
00324 if (!local || !ConfigChannel.disable_fake_channels)
00325 {
00326 while (*++p)
00327 if (!IsChanChar(*p))
00328 return 0;
00329 }
00330 else
00331 {
00332 while (*++p)
00333 if (!IsVisibleChanChar(*p))
00334 return 0;
00335 }
00336
00337 return p - name <= max_length;
00338 }
00339
00340 void
00341 remove_ban(struct Ban *bptr, dlink_list *list)
00342 {
00343 dlinkDelete(&bptr->node, list);
00344
00345 MyFree(bptr->name);
00346 MyFree(bptr->username);
00347 MyFree(bptr->host);
00348 MyFree(bptr->who);
00349
00350 BlockHeapFree(ban_heap, bptr);
00351 }
00352
00353
00354
00355
00356
00357
00358
00359 void
00360 free_channel_list(dlink_list *list)
00361 {
00362 dlink_node *ptr = NULL, *next_ptr = NULL;
00363
00364 DLINK_FOREACH_SAFE(ptr, next_ptr, list->head)
00365 remove_ban(ptr->data, list);
00366
00367 assert(list->tail == NULL && list->head == NULL);
00368 }
00369
00370
00371
00372
00373
00374
00375 struct Channel *
00376 make_channel(const char *chname)
00377 {
00378 struct Channel *chptr = NULL;
00379
00380 assert(!EmptyString(chname));
00381
00382 chptr = BlockHeapAlloc(channel_heap);
00383
00384
00385 chptr->channelts = CurrentTime;
00386 chptr->last_join_time = CurrentTime;
00387
00388 strlcpy(chptr->chname, chname, sizeof(chptr->chname));
00389 dlinkAdd(chptr, &chptr->node, &global_channel_list);
00390
00391 hash_add_channel(chptr);
00392
00393 return chptr;
00394 }
00395
00396
00397
00398
00399 void
00400 destroy_channel(struct Channel *chptr)
00401 {
00402 dlink_node *ptr = NULL, *ptr_next = NULL;
00403
00404 DLINK_FOREACH_SAFE(ptr, ptr_next, chptr->invites.head)
00405 del_invite(chptr, ptr->data);
00406
00407
00408 free_channel_list(&chptr->banlist);
00409 free_channel_list(&chptr->exceptlist);
00410 free_channel_list(&chptr->invexlist);
00411
00412
00413 free_topic(chptr);
00414
00415 dlinkDelete(&chptr->node, &global_channel_list);
00416 hash_del_channel(chptr);
00417
00418 if (ServerInfo.hub)
00419 if ((ptr = dlinkFindDelete(&lazylink_channels, chptr)))
00420 free_dlink_node(ptr);
00421
00422 BlockHeapFree(channel_heap, chptr);
00423 }
00424
00425
00426
00427
00428
00429 static const char *
00430 channel_pub_or_secret(struct Channel *chptr)
00431 {
00432 if (SecretChannel(chptr))
00433 return "@";
00434 if (PrivateChannel(chptr))
00435 return "*";
00436 return "=";
00437 }
00438
00439
00440
00441
00442
00443
00444
00445 void
00446 channel_member_names(struct Client *source_p, struct Channel *chptr,
00447 int show_eon)
00448 {
00449 struct Client *target_p = NULL;
00450 struct Membership *ms = NULL;
00451 dlink_node *ptr = NULL;
00452 char lbuf[IRCD_BUFSIZE + 1];
00453 char *t = NULL, *start = NULL;
00454 int tlen = 0;
00455 int is_member = IsMember(source_p, chptr);
00456 int multi_prefix = (source_p->localClient->cap_active & CAP_MULTI_PREFIX) != 0;
00457
00458 if (PubChannel(chptr) || is_member)
00459 {
00460 t = lbuf + ircsprintf(lbuf, form_str(RPL_NAMREPLY),
00461 me.name, source_p->name,
00462 channel_pub_or_secret(chptr),
00463 chptr->chname);
00464 start = t;
00465
00466 DLINK_FOREACH(ptr, chptr->members.head)
00467 {
00468 ms = ptr->data;
00469 target_p = ms->client_p;
00470
00471 if (IsInvisible(target_p) && !is_member)
00472 continue;
00473
00474 tlen = strlen(target_p->name) + 1;
00475
00476 if (!multi_prefix)
00477 {
00478 if (ms->flags & (CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE))
00479 ++tlen;
00480 }
00481 else
00482 {
00483 if (ms->flags & CHFL_CHANOP)
00484 ++tlen;
00485 if (ms->flags & CHFL_HALFOP)
00486 ++tlen;
00487 if (ms->flags & CHFL_VOICE)
00488 ++tlen;
00489 }
00490
00491 if (t + tlen - lbuf > IRCD_BUFSIZE - 2)
00492 {
00493 *(t - 1) = '\0';
00494 sendto_one(source_p, "%s", lbuf);
00495 t = start;
00496 }
00497
00498 t += ircsprintf(t, "%s%s ", get_member_status(ms, multi_prefix),
00499 target_p->name);
00500 }
00501
00502 if (tlen != 0)
00503 {
00504 *(t - 1) = '\0';
00505 sendto_one(source_p, "%s", lbuf);
00506 }
00507 }
00508
00509 if (show_eon)
00510 sendto_one(source_p, form_str(RPL_ENDOFNAMES),
00511 me.name, source_p->name, chptr->chname);
00512 }
00513
00514
00515
00516
00517
00518 void
00519 add_invite(struct Channel *chptr, struct Client *who)
00520 {
00521 del_invite(chptr, who);
00522
00523
00524
00525
00526 if (dlink_list_length(&who->localClient->invited) >=
00527 ConfigChannel.max_chans_per_user)
00528 del_invite(who->localClient->invited.tail->data, who);
00529
00530
00531 dlinkAdd(who, make_dlink_node(), &chptr->invites);
00532
00533
00534 dlinkAdd(chptr, make_dlink_node(), &who->localClient->invited);
00535 }
00536
00537
00538
00539
00540
00541
00542 void
00543 del_invite(struct Channel *chptr, struct Client *who)
00544 {
00545 dlink_node *ptr = NULL;
00546
00547 if ((ptr = dlinkFindDelete(&who->localClient->invited, chptr)))
00548 free_dlink_node(ptr);
00549
00550 if ((ptr = dlinkFindDelete(&chptr->invites, who)))
00551 free_dlink_node(ptr);
00552 }
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565 const char *
00566 get_member_status(const struct Membership *ms, int combine)
00567 {
00568 static char buffer[4];
00569 char *p = NULL;
00570
00571 if (ms == NULL)
00572 return "";
00573 p = buffer;
00574
00575 if (ms->flags & CHFL_CHANOP)
00576 {
00577 if (!combine)
00578 return "@";
00579 *p++ = '@';
00580 }
00581
00582 #ifdef HALFOPS
00583 if (ms->flags & CHFL_HALFOP)
00584 {
00585 if (!combine)
00586 return "%";
00587 *p++ = '%';
00588 }
00589 #endif
00590
00591 if (ms->flags & CHFL_VOICE)
00592 *p++ = '+';
00593 *p = '\0';
00594
00595 return buffer;
00596 }
00597
00598
00599
00600
00601
00602
00603
00604 static int
00605 find_bmask(const struct Client *who, const dlink_list *const list)
00606 {
00607 const dlink_node *ptr = NULL;
00608
00609 DLINK_FOREACH(ptr, list->head)
00610 {
00611 struct Ban *bp = ptr->data;
00612
00613 if (match(bp->name, who->name) && match(bp->username, who->username))
00614 {
00615 switch (bp->type)
00616 {
00617 case HM_HOST:
00618 if (match(bp->host, who->host) || match(bp->host, who->sockhost))
00619 return 1;
00620 break;
00621 case HM_IPV4:
00622 if (who->localClient->aftype == AF_INET)
00623 if (match_ipv4(&who->localClient->ip, &bp->addr, bp->bits))
00624 return 1;
00625 break;
00626 #ifdef IPV6
00627 case HM_IPV6:
00628 if (who->localClient->aftype == AF_INET6)
00629 if (match_ipv6(&who->localClient->ip, &bp->addr, bp->bits))
00630 return 1;
00631 break;
00632 #endif
00633 default:
00634 assert(0);
00635 }
00636 }
00637 }
00638
00639 return 0;
00640 }
00641
00642
00643
00644
00645
00646
00647 int
00648 is_banned(struct Channel *chptr, struct Client *who)
00649 {
00650 if (find_bmask(who, &chptr->banlist))
00651 if (!ConfigChannel.use_except || !find_bmask(who, &chptr->exceptlist))
00652 return 1;
00653
00654 return 0;
00655 }
00656
00657
00658
00659
00660
00661
00662
00663
00664 int
00665 can_join(struct Client *source_p, struct Channel *chptr, const char *key)
00666 {
00667 if (is_banned(chptr, source_p))
00668 return ERR_BANNEDFROMCHAN;
00669
00670 if (chptr->mode.mode & MODE_INVITEONLY)
00671 if (!dlinkFind(&source_p->localClient->invited, chptr))
00672 if (!ConfigChannel.use_invex || !find_bmask(source_p, &chptr->invexlist))
00673 return ERR_INVITEONLYCHAN;
00674
00675 if (chptr->mode.key[0] && (!key || irccmp(chptr->mode.key, key)))
00676 return ERR_BADCHANNELKEY;
00677
00678 if (chptr->mode.limit && dlink_list_length(&chptr->members) >=
00679 chptr->mode.limit)
00680 return ERR_CHANNELISFULL;
00681
00682 return 0;
00683 }
00684
00685 int
00686 has_member_flags(struct Membership *ms, unsigned int flags)
00687 {
00688 if (ms != NULL)
00689 return ms->flags & flags;
00690 return 0;
00691 }
00692
00693 struct Membership *
00694 find_channel_link(struct Client *client_p, struct Channel *chptr)
00695 {
00696 dlink_node *ptr = NULL;
00697
00698 if (!IsClient(client_p))
00699 return NULL;
00700
00701 DLINK_FOREACH(ptr, client_p->channel.head)
00702 if (((struct Membership *)ptr->data)->chptr == chptr)
00703 return (struct Membership *)ptr->data;
00704
00705 return NULL;
00706 }
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716 int
00717 can_send(struct Channel *chptr, struct Client *source_p, struct Membership *ms)
00718 {
00719 if (IsServer(source_p))
00720 return CAN_SEND_OPV;
00721
00722 if (MyClient(source_p) && !IsExemptResv(source_p))
00723 if (!(IsOper(source_p) && ConfigFileEntry.oper_pass_resv))
00724 if (!hash_find_resv(chptr->chname) == ConfigChannel.restrict_channels)
00725 return CAN_SEND_NO;
00726
00727 if (ms != NULL || (ms = find_channel_link(source_p, chptr)))
00728 {
00729 if (ms->flags & (CHFL_CHANOP|CHFL_HALFOP|CHFL_VOICE))
00730 return CAN_SEND_OPV;
00731
00732
00733 if (ConfigChannel.quiet_on_ban && MyClient(source_p))
00734 {
00735 if (ms->flags & CHFL_BAN_SILENCED)
00736 return CAN_SEND_NO;
00737
00738 if (!(ms->flags & CHFL_BAN_CHECKED))
00739 {
00740 if (is_banned(chptr, source_p))
00741 {
00742 ms->flags |= (CHFL_BAN_CHECKED|CHFL_BAN_SILENCED);
00743 return CAN_SEND_NO;
00744 }
00745
00746 ms->flags |= CHFL_BAN_CHECKED;
00747 }
00748 }
00749 }
00750 else if (chptr->mode.mode & MODE_NOPRIVMSGS)
00751 return CAN_SEND_NO;
00752
00753 if (chptr->mode.mode & MODE_MODERATED)
00754 return CAN_SEND_NO;
00755
00756 return CAN_SEND_NONOP;
00757 }
00758
00759
00760
00761
00762
00763
00764
00765 void
00766 check_spambot_warning(struct Client *source_p, const char *name)
00767 {
00768 int t_delta = 0;
00769 int decrement_count = 0;
00770
00771 if ((GlobalSetOptions.spam_num &&
00772 (source_p->localClient->join_leave_count >=
00773 GlobalSetOptions.spam_num)))
00774 {
00775 if (source_p->localClient->oper_warn_count_down > 0)
00776 source_p->localClient->oper_warn_count_down--;
00777 else
00778 source_p->localClient->oper_warn_count_down = 0;
00779
00780 if (source_p->localClient->oper_warn_count_down == 0)
00781 {
00782
00783 if (name != NULL)
00784 sendto_realops_flags(UMODE_BOTS, L_ALL,
00785 "User %s (%s@%s) trying to join %s is a possible spambot",
00786 source_p->name, source_p->username,
00787 source_p->host, name);
00788 else
00789 sendto_realops_flags(UMODE_BOTS, L_ALL,
00790 "User %s (%s@%s) is a possible spambot",
00791 source_p->name, source_p->username,
00792 source_p->host);
00793 source_p->localClient->oper_warn_count_down = OPER_SPAM_COUNTDOWN;
00794 }
00795 }
00796 else
00797 {
00798 if ((t_delta = (CurrentTime - source_p->localClient->last_leave_time)) >
00799 JOIN_LEAVE_COUNT_EXPIRE_TIME)
00800 {
00801 decrement_count = (t_delta / JOIN_LEAVE_COUNT_EXPIRE_TIME);
00802 if (decrement_count > source_p->localClient->join_leave_count)
00803 source_p->localClient->join_leave_count = 0;
00804 else
00805 source_p->localClient->join_leave_count -= decrement_count;
00806 }
00807 else
00808 {
00809 if ((CurrentTime - (source_p->localClient->last_join_time)) <
00810 GlobalSetOptions.spam_time)
00811 {
00812
00813 source_p->localClient->join_leave_count++;
00814 }
00815 }
00816
00817 if (name != NULL)
00818 source_p->localClient->last_join_time = CurrentTime;
00819 else
00820 source_p->localClient->last_leave_time = CurrentTime;
00821 }
00822 }
00823
00824
00825
00826
00827
00828 void
00829 check_splitmode(void *unused)
00830 {
00831 if (splitchecking && (ConfigChannel.no_join_on_split ||
00832 ConfigChannel.no_create_on_split))
00833 {
00834 const unsigned int server = dlink_list_length(&global_serv_list);
00835
00836 if (!splitmode && ((server < split_servers) || (Count.total < split_users)))
00837 {
00838 splitmode = 1;
00839
00840 sendto_realops_flags(UMODE_ALL,L_ALL,
00841 "Network split, activating splitmode");
00842 eventAddIsh("check_splitmode", check_splitmode, NULL, 10);
00843 }
00844 else if (splitmode && (server > split_servers) && (Count.total > split_users))
00845 {
00846 splitmode = 0;
00847
00848 sendto_realops_flags(UMODE_ALL, L_ALL,
00849 "Network rejoined, deactivating splitmode");
00850 eventDelete(check_splitmode, NULL);
00851 }
00852 }
00853 }
00854
00855
00856
00857
00858 static void
00859 allocate_topic(struct Channel *chptr)
00860 {
00861 void *ptr = NULL;
00862
00863 if (chptr == NULL)
00864 return;
00865
00866 ptr = BlockHeapAlloc(topic_heap);
00867
00868
00869
00870
00871
00872 chptr->topic = ptr;
00873 chptr->topic_info = (char *)ptr + TOPICLEN+1;
00874 *chptr->topic = '\0';
00875 *chptr->topic_info = '\0';
00876 }
00877
00878 void
00879 free_topic(struct Channel *chptr)
00880 {
00881 void *ptr = NULL;
00882 assert(chptr);
00883 if (chptr->topic == NULL)
00884 return;
00885
00886
00887
00888
00889 ptr = chptr->topic;
00890 BlockHeapFree(topic_heap, ptr);
00891 chptr->topic = NULL;
00892 chptr->topic_info = NULL;
00893 }
00894
00895
00896
00897
00898
00899
00900
00901 void
00902 set_channel_topic(struct Channel *chptr, const char *topic,
00903 const char *topic_info, time_t topicts)
00904 {
00905 if (!EmptyString(topic))
00906 {
00907 if (chptr->topic == NULL)
00908 allocate_topic(chptr);
00909
00910 strlcpy(chptr->topic, topic, TOPICLEN+1);
00911 strlcpy(chptr->topic_info, topic_info, USERHOST_REPLYLEN);
00912 chptr->topic_time = topicts;
00913 }
00914 else
00915 {
00916
00917
00918
00919
00920 if (chptr->topic != NULL)
00921 free_topic(chptr);
00922 }
00923 }