unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
sjoin.c (25091B)
1 /* 2 * IRC - Internet Relay Chat, src/modules/sjoin.c 3 * (C) 2004 The UnrealIRCd Team 4 * 5 * See file AUTHORS in IRC package for additional names of 6 * the programmers. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 1, or (at your option) 11 * any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 */ 22 23 #include "unrealircd.h" 24 25 CMD_FUNC(cmd_sjoin); 26 27 #define MSG_SJOIN "SJOIN" 28 29 ModuleHeader MOD_HEADER 30 = { 31 "sjoin", 32 "5.1", 33 "command /sjoin", 34 "UnrealIRCd Team", 35 "unrealircd-6", 36 }; 37 38 char modebuf[BUFSIZE], parabuf[BUFSIZE]; 39 40 MOD_INIT() 41 { 42 CommandAdd(modinfo->handle, MSG_SJOIN, cmd_sjoin, MAXPARA, CMD_SERVER); 43 MARK_AS_OFFICIAL_MODULE(modinfo); 44 return MOD_SUCCESS; 45 } 46 47 MOD_LOAD() 48 { 49 return MOD_SUCCESS; 50 } 51 52 MOD_UNLOAD() 53 { 54 return MOD_SUCCESS; 55 } 56 57 typedef struct xParv aParv; 58 struct xParv { 59 int parc; 60 const char *parv[256]; 61 }; 62 63 aParv pparv; 64 65 aParv *mp2parv(char *xmbuf, char *parmbuf) 66 { 67 int c; 68 char *p, *s; 69 70 pparv.parv[0] = xmbuf; 71 c = 1; 72 73 for (s = strtoken(&p, parmbuf, " "); s; s = strtoken(&p, NULL, " ")) 74 { 75 pparv.parv[c] = s; 76 c++; /* in my dreams */ 77 } 78 pparv.parv[c] = NULL; 79 pparv.parc = c; 80 return (&pparv); 81 } 82 83 static void send_local_chan_mode(MessageTag *recv_mtags, Client *client, Channel *channel, char *modebuf, char *parabuf) 84 { 85 MessageTag *mtags = NULL; 86 int destroy_channel = 0; 87 88 new_message_special(client, recv_mtags, &mtags, ":%s MODE %s %s %s", client->name, channel->name, modebuf, parabuf); 89 sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags, 90 ":%s MODE %s %s %s", client->name, channel->name, modebuf, parabuf); 91 if (MyConnect(client)) 92 RunHook(HOOKTYPE_LOCAL_CHANMODE, client, channel, mtags, modebuf, parabuf, 0, -1, &destroy_channel); 93 else 94 RunHook(HOOKTYPE_REMOTE_CHANMODE, client, channel, mtags, modebuf, parabuf, 0, -1, &destroy_channel); 95 free_message_tags(mtags); 96 } 97 98 /** Call send_local_chan_mode() for multiline modes */ 99 static void send_local_chan_mode_mlm(MessageTag *recv_mtags, Client *client, Channel *channel, MultiLineMode *mlm) 100 { 101 if (mlm) 102 { 103 int i; 104 for (i = 0; i < mlm->numlines; i++) 105 send_local_chan_mode(recv_mtags, client, channel, mlm->modeline[i], mlm->paramline[i]); 106 } 107 } 108 109 /** SJOIN: Synchronize channel modes, +beI lists and users (server-to-server command) 110 * Extensive technical documentation is available at: 111 * https://www.unrealircd.org/docs/Server_protocol:SJOIN_command 112 * 113 * parv[1] = channel timestamp 114 * parv[2] = channel name 115 * 116 * if parc == 3: 117 * parv[3] = nick names + modes - all in one parameter 118 * 119 * if parc == 4: 120 * parv[3] = channel modes 121 * parv[4] = nick names + modes - all in one parameter 122 * 123 * if parc > 4: 124 * parv[3] = channel modes 125 * parv[4 to parc - 2] = mode parameters 126 * parv[parc - 1] = nick names + modes 127 */ 128 129 /* Note: with regards to message tags we use new_message_special() 130 * here extensively. This because one SJOIN command can (often) 131 * generate multiple events that are sent to clients, 132 * for example 1 SJOIN can cause multiple joins, +beI, etc. 133 * -- Syzop 134 */ 135 136 /* Some ugly macros, but useful */ 137 #define Addit(mode,param) if ((strlen(parabuf) + strlen(param) + 11 < MODEBUFLEN) && (b <= MAXMODEPARAMS)) { \ 138 if (*parabuf) \ 139 strcat(parabuf, " ");\ 140 strcat(parabuf, param);\ 141 modebuf[b++] = mode;\ 142 modebuf[b] = 0;\ 143 }\ 144 else {\ 145 send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf); \ 146 strcpy(parabuf,param);\ 147 /* modebuf[0] should stay what it was ('+' or '-') */ \ 148 modebuf[1] = mode;\ 149 modebuf[2] = '\0';\ 150 b = 2;\ 151 } 152 #define Addsingle(x) do { modebuf[b] = x; b++; modebuf[b] = '\0'; } while(0) 153 #define CheckStatus(x,y) do { if (modeflags & (y)) { Addit((x), acptr->name); } } while(0) 154 155 CMD_FUNC(cmd_sjoin) 156 { 157 unsigned short nopara; 158 unsigned short nomode; /**< An SJOIN without MODE? */ 159 unsigned short removeours; /**< Remove our modes */ 160 unsigned short removetheirs; /**< Remove their modes (or actually: do not ADD their modes, the MODE -... line will be sent later by the other side) */ 161 unsigned short merge; /**< same timestamp: merge their & our modes */ 162 char pvar[MAXMODEPARAMS][MODEBUFLEN + 3]; 163 char cbuf[1024]; 164 char scratch_buf[1024]; /**< scratch buffer */ 165 char item[1024]; /**< nick or ban/invex/exempt being processed */ 166 char item_modes[MEMBERMODESLEN]; /**< item modes, eg "b" or "vhoaq" */ 167 char prefix[16]; /**< SJOIN prefix of item for server to server traffic (eg: @) */ 168 char uid_buf[BUFSIZE]; /**< Buffer for server-to-server traffic which will be broadcasted to others (servers supporting SID/UID) */ 169 char uid_sjsby_buf[BUFSIZE]; /**< Buffer for server-to-server traffic which will be broadcasted to others (servers supporting SID/UID and SJSBY) */ 170 char sj3_parabuf[BUFSIZE]; /**< Prefix for the above SJOIN buffers (":xxx SJOIN #channel +mode :") */ 171 char *s = NULL; 172 Channel *channel; /**< Channel */ 173 aParv *ap; 174 int pcount, i; 175 Hook *h; 176 Cmode *cm; 177 time_t ts, oldts; 178 unsigned short b=0; 179 char *tp, *p, *saved = NULL; 180 181 if (!IsServer(client) || parc < 4) 182 return; 183 184 if (!IsChannelName(parv[2])) 185 return; 186 187 merge = nopara = nomode = removeours = removetheirs = 0; 188 189 if (parc < 6) 190 nopara = 1; 191 192 if (parc < 5) 193 nomode = 1; 194 195 channel = find_channel(parv[2]); 196 if (!channel) 197 { 198 channel = make_channel(parv[2]); 199 oldts = -1; 200 } else { 201 oldts = channel->creationtime; 202 } 203 204 ts = (time_t)atol(parv[1]); 205 206 if (IsInvalidChannelTS(ts)) 207 { 208 unreal_log(ULOG_WARNING, "sjoin", "SJOIN_INVALID_TIMESTAMP", client, 209 "SJOIN for channel $channel has invalid timestamp $send_timestamp (from $client)", 210 log_data_channel("channel", channel), 211 log_data_integer("send_timestamp", ts)); 212 /* Pretend they match our creation time (matches U6 behavior in m_mode.c) */ 213 ts = channel->creationtime; 214 } 215 216 if (oldts == -1) 217 { 218 /* Newly created channel (from our POV), so set the correct creationtime here */ 219 channel->creationtime = ts; 220 } else 221 if (channel->creationtime > ts) 222 { 223 removeours = 1; 224 channel->creationtime = ts; 225 } 226 else if (channel->creationtime < ts) 227 { 228 removetheirs = 1; 229 } 230 else if (channel->creationtime == ts) 231 { 232 merge = 1; 233 } 234 235 parabuf[0] = '\0'; 236 modebuf[0] = '+'; 237 modebuf[1] = '\0'; 238 239 /* Grab current modes -> modebuf & parabuf */ 240 channel_modes(client, modebuf, parabuf, sizeof(modebuf), sizeof(parabuf), channel, 1); 241 242 /* Do we need to remove all our modes, bans/exempt/inves lists and -vhoaq our users? */ 243 if (removeours) 244 { 245 Member *lp; 246 247 modebuf[0] = '-'; 248 249 /* remove our modes if any */ 250 if (!empty_mode(modebuf)) 251 { 252 MessageTag *mtags = NULL; 253 MultiLineMode *mlm; 254 ap = mp2parv(modebuf, parabuf); 255 mlm = set_mode(channel, client, ap->parc, ap->parv, &pcount, pvar); 256 send_local_chan_mode_mlm(recv_mtags, client, channel, mlm); 257 safe_free_multilinemode(mlm); 258 } 259 /* remove bans */ 260 /* reset the buffers */ 261 modebuf[0] = '-'; 262 modebuf[1] = '\0'; 263 parabuf[0] = '\0'; 264 b = 1; 265 while(channel->banlist) 266 { 267 Ban *ban = channel->banlist; 268 Addit('b', ban->banstr); 269 channel->banlist = ban->next; 270 safe_free(ban->banstr); 271 safe_free(ban->who); 272 free_ban(ban); 273 } 274 while(channel->exlist) 275 { 276 Ban *ban = channel->exlist; 277 Addit('e', ban->banstr); 278 channel->exlist = ban->next; 279 safe_free(ban->banstr); 280 safe_free(ban->who); 281 free_ban(ban); 282 } 283 while(channel->invexlist) 284 { 285 Ban *ban = channel->invexlist; 286 Addit('I', ban->banstr); 287 channel->invexlist = ban->next; 288 safe_free(ban->banstr); 289 safe_free(ban->who); 290 free_ban(ban); 291 } 292 for (lp = channel->members; lp; lp = lp->next) 293 { 294 Membership *lp2 = find_membership_link(lp->client->user->channel, channel); 295 296 /* Remove all our modes, one by one */ 297 for (p = lp->member_modes; *p; p++) 298 { 299 Addit(*p, lp->client->name); 300 } 301 /* And clear all the flags in memory */ 302 *lp->member_modes = *lp2->member_modes = '\0'; 303 } 304 if (b > 1) 305 { 306 modebuf[b] = '\0'; 307 send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf); 308 } 309 310 /* since we're dropping our modes, we want to clear the mlock as well. --nenolod */ 311 set_channel_mlock(client, channel, NULL, FALSE); 312 } 313 /* Mode setting done :), now for our beloved clients */ 314 parabuf[0] = 0; 315 modebuf[0] = '+'; 316 modebuf[1] = '\0'; 317 b = 1; 318 strlcpy(cbuf, parv[parc-1], sizeof cbuf); 319 320 sj3_parabuf[0] = '\0'; 321 for (i = 2; i <= (parc - 2); i++) 322 { 323 strlcat(sj3_parabuf, parv[i], sizeof sj3_parabuf); 324 if (((i + 1) <= (parc - 2))) 325 strlcat(sj3_parabuf, " ", sizeof sj3_parabuf); 326 } 327 328 /* Now process adding of users & adding of list modes (bans/exempt/invex) */ 329 330 snprintf(uid_buf, sizeof uid_buf, ":%s SJOIN %lld %s :", client->id, (long long)ts, sj3_parabuf); 331 snprintf(uid_sjsby_buf, sizeof uid_sjsby_buf, ":%s SJOIN %lld %s :", client->id, (long long)ts, sj3_parabuf); 332 333 for (s = strtoken(&saved, cbuf, " "); s; s = strtoken(&saved, NULL, " ")) 334 { 335 char *setby = client->name; /**< Set by (nick, nick!user@host, or server name) */ 336 time_t setat = TStime(); /**< Set at timestamp */ 337 int sjsby_info = 0; /**< Set to 1 if we receive SJSBY info to alter the above 2 vars */ 338 339 *item_modes = 0; 340 i = 0; 341 tp = s; 342 343 /* UnrealIRCd 4.2.2 and later support "SJSBY" which allows communicating 344 * setat/setby information for bans, ban exempts and invite exceptions. 345 */ 346 if (SupportSJSBY(client->direction) && (*tp == '<')) 347 { 348 /* Special prefix to communicate timestamp and setter: 349 * "<" + timestamp + "," + nick[!user@host] + ">" + normal SJOIN stuff 350 * For example: "<12345,nick>&some!nice@ban" 351 */ 352 char *end = strchr(tp, '>'), *p; 353 if (!end) 354 { 355 /* this obviously should never happen */ 356 unreal_log(ULOG_WARNING, "sjoin", "SJOIN_INVALID_SJSBY", client, 357 "SJOIN for channel $channel has invalid SJSBY in item '$item' (from $client)", 358 log_data_channel("channel", channel), 359 log_data_string("item", s)); 360 continue; 361 } 362 *end++ = '\0'; 363 364 p = strchr(tp, ','); 365 if (!p) 366 { 367 /* missing setby parameter */ 368 unreal_log(ULOG_WARNING, "sjoin", "SJOIN_INVALID_SJSBY", client, 369 "SJOIN for channel $channel has invalid SJSBY in item '$item' (from $client)", 370 log_data_channel("channel", channel), 371 log_data_string("item", s)); 372 continue; 373 } 374 *p++ = '\0'; 375 376 setat = atol(tp+1); 377 setby = p; 378 sjsby_info = 1; 379 380 tp = end; /* the remainder is used for the actual ban/exempt/invex */ 381 } 382 383 /* Process the SJOIN prefixes... */ 384 for (p = tp; *p; p++) 385 { 386 char m = sjoin_prefix_to_mode(*p); 387 if (!m) 388 break; /* end of prefix stuff, or so we hope anyway :D */ 389 // TODO: do we want safety here for if one side has prefixmodes loaded 390 // and the other does not? and if so, in what way do we want this? 391 392 strlcat_letter(item_modes, m, sizeof(item_modes)); 393 394 /* For list modes (+beI) stop processing immediately, 395 * so we don't accidentally eat additional prefix chars. 396 */ 397 if (strchr("beI", m)) 398 { 399 p++; 400 break; 401 } 402 } 403 404 /* Now set 'prefix' to the prefixes we encountered. 405 * This is basically the range tp..p 406 */ 407 strlncpy(prefix, tp, sizeof(prefix), p - tp); 408 409 /* Now copy the "nick" (which can actually be a ban/invex/exempt) */ 410 strlcpy(item, p, sizeof(item)); 411 if (*item == '\0') 412 continue; 413 414 /* If not a list mode... then we deal with users... */ 415 if (!strchr(item_modes, 'b') && !strchr(item_modes, 'e') && !strchr(item_modes, 'I')) 416 { 417 Client *acptr; 418 419 /* The user may no longer exist. This can happen in case of a 420 * SVSKILL traveling in the other direction. Nothing to worry about. 421 */ 422 if (!(acptr = find_user(item, NULL))) 423 continue; 424 425 if (acptr->direction != client->direction) 426 { 427 if (IsMember(acptr, channel)) 428 { 429 /* Nick collision, don't kick or it desyncs -Griever*/ 430 continue; 431 } 432 433 sendto_one(client, NULL, 434 ":%s KICK %s %s :Fake direction", 435 me.id, channel->name, acptr->name); 436 unreal_log(ULOG_WARNING, "sjoin", "SJOIN_FAKE_DIRECTION", client, 437 "Fake direction from server $client in SJOIN " 438 "for user $existing_client on $existing_client.user.servername " 439 "(item: $buf)", 440 log_data_client("existing_client", acptr), 441 log_data_string("buf", item)); 442 continue; 443 } 444 445 if (removetheirs) 446 *item_modes = '\0'; 447 448 if (!IsMember(acptr, channel)) 449 { 450 /* User joining the channel, send JOIN to local users. 451 */ 452 MessageTag *mtags = NULL; 453 454 add_user_to_channel(channel, acptr, item_modes); 455 unreal_log(ULOG_INFO, "join", "REMOTE_CLIENT_JOIN", acptr, 456 "User $client joined $channel", 457 log_data_channel("channel", channel), 458 log_data_string("modes", item_modes)); 459 RunHook(HOOKTYPE_REMOTE_JOIN, acptr, channel, recv_mtags); 460 new_message_special(acptr, recv_mtags, &mtags, ":%s JOIN %s", acptr->name, channel->name); 461 send_join_to_local_users(acptr, channel, mtags); 462 free_message_tags(mtags); 463 } 464 465 /* Set the +vhoaq */ 466 for (p = item_modes; *p; p++) 467 Addit(*p, acptr->name); 468 469 if (strlen(uid_buf) + strlen(prefix) + IDLEN > BUFSIZE - 10) 470 { 471 /* Send what we have and start a new buffer */ 472 sendto_server(client, 0, PROTO_SJSBY, recv_mtags, "%s", uid_buf); 473 snprintf(uid_buf, sizeof(uid_buf), ":%s SJOIN %lld %s :", client->id, (long long)ts, channel->name); 474 /* Double-check the new buffer is sufficient to concat the data */ 475 if (strlen(uid_buf) + strlen(prefix) + strlen(acptr->id) > BUFSIZE - 5) 476 { 477 unreal_log(ULOG_ERROR, "sjoin", "BUG_OVERSIZED_SJOIN", client, 478 "Oversized SJOIN [$sjoin_place] in channel $channel when adding '$str$str2' to '$buf'", 479 log_data_channel("channel", channel), 480 log_data_string("sjoin_place", "UID-MEMBER"), 481 log_data_string("str", prefix), 482 log_data_string("str2", acptr->id), 483 log_data_string("buf", uid_buf)); 484 continue; 485 } 486 } 487 sprintf(uid_buf+strlen(uid_buf), "%s%s ", prefix, acptr->id); 488 489 if (strlen(uid_sjsby_buf) + strlen(prefix) + IDLEN > BUFSIZE - 10) 490 { 491 /* Send what we have and start a new buffer */ 492 sendto_server(client, PROTO_SJSBY, 0, recv_mtags, "%s", uid_sjsby_buf); 493 snprintf(uid_sjsby_buf, sizeof(uid_sjsby_buf), ":%s SJOIN %lld %s :", client->id, (long long)ts, channel->name); 494 /* Double-check the new buffer is sufficient to concat the data */ 495 if (strlen(uid_sjsby_buf) + strlen(prefix) + strlen(acptr->id) > BUFSIZE - 5) 496 { 497 unreal_log(ULOG_ERROR, "sjoin", "BUG_OVERSIZED_SJOIN", client, 498 "Oversized SJOIN [$sjoin_place] in channel $channel when adding '$str$str2' to '$buf'", 499 log_data_channel("channel", channel), 500 log_data_string("sjoin_place", "SJS-MEMBER"), 501 log_data_string("str", prefix), 502 log_data_string("str2", acptr->id), 503 log_data_string("buf", uid_sjsby_buf)); 504 continue; 505 } 506 } 507 sprintf(uid_sjsby_buf+strlen(uid_sjsby_buf), "%s%s ", prefix, acptr->id); 508 } 509 else 510 { 511 /* It's a list mode................ */ 512 const char *str; 513 514 if (removetheirs) 515 continue; 516 517 /* Validate syntax */ 518 519 /* non-extbans: prevent bans without ! or @. a good case of "should never happen". */ 520 if ((item[0] != '~') && (!strchr(item, '!') || !strchr(item, '@') || (item[0] == '!'))) 521 continue; 522 523 str = clean_ban_mask(item, MODE_ADD, client, 0); 524 if (!str) 525 continue; /* invalid ban syntax */ 526 strlcpy(item, str, sizeof(item)); 527 528 /* Adding of list modes */ 529 if (*item_modes == 'b') 530 { 531 if (add_listmode_ex(&channel->banlist, client, channel, item, setby, setat) != -1) 532 { 533 Addit('b', item); 534 } 535 } 536 if (*item_modes == 'e') 537 { 538 if (add_listmode_ex(&channel->exlist, client, channel, item, setby, setat) != -1) 539 { 540 Addit('e', item); 541 } 542 } 543 if (*item_modes == 'I') 544 { 545 if (add_listmode_ex(&channel->invexlist, client, channel, item, setby, setat) != -1) 546 { 547 Addit('I', item); 548 } 549 } 550 551 if (strlen(uid_buf) + strlen(prefix) + strlen(item) > BUFSIZE - 10) 552 { 553 /* Send what we have and start a new buffer */ 554 sendto_server(client, 0, PROTO_SJSBY, recv_mtags, "%s", uid_buf); 555 snprintf(uid_buf, sizeof(uid_buf), ":%s SJOIN %lld %s :", client->id, (long long)ts, channel->name); 556 /* Double-check the new buffer is sufficient to concat the data */ 557 if (strlen(uid_buf) + strlen(prefix) + strlen(item) > BUFSIZE - 5) 558 { 559 unreal_log(ULOG_ERROR, "sjoin", "BUG_OVERSIZED_SJOIN", client, 560 "Oversized SJOIN [$sjoin_place] in channel $channel when adding '$str$str2' to '$buf'", 561 log_data_channel("channel", channel), 562 log_data_string("sjoin_place", "UID-LMODE"), 563 log_data_string("str", prefix), 564 log_data_string("str2", item), 565 log_data_string("buf", uid_buf)); 566 continue; 567 } 568 } 569 sprintf(uid_buf+strlen(uid_buf), "%s%s ", prefix, item); 570 571 *scratch_buf = '\0'; 572 if (sjsby_info) 573 add_sjsby(scratch_buf, setby, setat); 574 strcat(scratch_buf, prefix); 575 strcat(scratch_buf, item); 576 strcat(scratch_buf, " "); 577 if (strlen(uid_sjsby_buf) + strlen(scratch_buf) > BUFSIZE - 10) 578 { 579 /* Send what we have and start a new buffer */ 580 sendto_server(client, PROTO_SJSBY, 0, recv_mtags, "%s", uid_sjsby_buf); 581 snprintf(uid_sjsby_buf, sizeof(uid_sjsby_buf), ":%s SJOIN %lld %s :", client->id, (long long)ts, channel->name); 582 /* Double-check the new buffer is sufficient to concat the data */ 583 if (strlen(uid_sjsby_buf) + strlen(scratch_buf) > BUFSIZE - 5) 584 { 585 unreal_log(ULOG_ERROR, "sjoin", "BUG_OVERSIZED_SJOIN", client, 586 "Oversized SJOIN [$sjoin_place] in channel $channel when adding '$str' to '$buf'", 587 log_data_channel("channel", channel), 588 log_data_string("sjoin_place", "SJS-LMODE"), 589 log_data_string("str", scratch_buf), 590 log_data_string("buf", uid_sjsby_buf)); 591 continue; 592 } 593 } 594 strcpy(uid_sjsby_buf+strlen(uid_sjsby_buf), scratch_buf); /* size already checked above */ 595 } 596 continue; 597 } 598 599 /* Send out any possible remainder.. */ 600 sendto_server(client, 0, PROTO_SJSBY, recv_mtags, "%s", uid_buf); 601 sendto_server(client, PROTO_SJSBY, 0, recv_mtags, "%s", uid_sjsby_buf); 602 603 if (!empty_mode(modebuf)) 604 { 605 modebuf[b] = '\0'; 606 send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf); 607 } 608 609 if (!merge && !removetheirs && !nomode) 610 { 611 MessageTag *mtags = NULL; 612 MultiLineMode *mlm; 613 614 strlcpy(modebuf, parv[3], sizeof modebuf); 615 parabuf[0] = '\0'; 616 if (!nopara) 617 { 618 for (b = 4; b <= (parc - 2); b++) 619 { 620 strlcat(parabuf, parv[b], sizeof parabuf); 621 strlcat(parabuf, " ", sizeof parabuf); 622 } 623 } 624 ap = mp2parv(modebuf, parabuf); 625 mlm = set_mode(channel, client, ap->parc, ap->parv, &pcount, pvar); 626 send_local_chan_mode_mlm(recv_mtags, client, channel, mlm); 627 safe_free_multilinemode(mlm); 628 } 629 630 if (merge && !nomode) 631 { 632 CoreChannelModeTable *acp; 633 MultiLineMode *mlm; 634 Mode oldmode; /**< The old mode (OUR mode) */ 635 636 /* Copy current mode to oldmode (need to duplicate all extended mode params too..) */ 637 memcpy(&oldmode, &channel->mode, sizeof(oldmode)); 638 memset(&oldmode.mode_params, 0, sizeof(oldmode.mode_params)); 639 extcmode_duplicate_paramlist(channel->mode.mode_params, oldmode.mode_params); 640 641 /* Now merge the modes */ 642 strlcpy(modebuf, parv[3], sizeof modebuf); 643 parabuf[0] = '\0'; 644 if (!nopara) 645 { 646 for (b = 4; b <= (parc - 2); b++) 647 { 648 strlcat(parabuf, parv[b], sizeof parabuf); 649 strlcat(parabuf, " ", sizeof parabuf); 650 } 651 } 652 653 /* First we set the mode (in memory) BUT we don't send the 654 * mode change out to anyone, hence the immediate freeing 655 * of 'mlm'. We do the actual rebuilding of the string and 656 * sending it out a few lines further down. 657 */ 658 ap = mp2parv(modebuf, parabuf); 659 mlm = set_mode(channel, client, ap->parc, ap->parv, &pcount, pvar); 660 safe_free_multilinemode(mlm); 661 662 /* Good, now we got modes, now for the differencing and outputting of modes 663 * We first see if any para modes are set. 664 */ 665 strlcpy(modebuf, "-", sizeof modebuf); 666 parabuf[0] = '\0'; 667 b = 1; 668 669 /* Check if we had +s and it became +p, then revert it silently (as it is no-change) */ 670 if (has_channel_mode_raw(oldmode.mode, 's') && has_channel_mode(channel, 'p')) 671 { 672 /* stay +s ! */ 673 long mode_p = get_extmode_bitbychar('p'); 674 long mode_s = get_extmode_bitbychar('s'); 675 channel->mode.mode &= ~mode_p; 676 channel->mode.mode |= mode_s; 677 /* TODO: all the code of above would ideally be in a module */ 678 } 679 /* (And the other condition, +p to +s, is already handled below by the generic code) */ 680 681 /* First, check if we had something that is now gone 682 * note that: oldmode.* = us, channel->mode.* = merged. 683 */ 684 for (cm=channelmodes; cm; cm = cm->next) 685 { 686 if (cm->letter && 687 !cm->local && 688 (oldmode.mode & cm->mode) && 689 !(channel->mode.mode & cm->mode)) 690 { 691 if (cm->paracount) 692 { 693 const char *parax = cm_getparameter_ex(oldmode.mode_params, cm->letter); 694 //char *parax = cm->get_param(extcmode_get_struct(oldmode.modeparam, cm->letter)); 695 Addit(cm->letter, parax); 696 } else { 697 Addsingle(cm->letter); 698 } 699 } 700 } 701 702 if (b > 1) 703 { 704 Addsingle('+'); 705 } 706 else 707 { 708 strlcpy(modebuf, "+", sizeof modebuf); 709 b = 1; 710 } 711 712 /* Now, check if merged modes contain something we didn't have before. 713 * note that: oldmode.* = us before, channel->mode.* = merged. 714 * 715 * First the simple single letter modes... 716 */ 717 for (cm=channelmodes; cm; cm = cm->next) 718 { 719 if ((cm->letter) && 720 !(oldmode.mode & cm->mode) && 721 (channel->mode.mode & cm->mode)) 722 { 723 if (cm->paracount) 724 { 725 const char *parax = cm_getparameter(channel, cm->letter); 726 if (parax) 727 { 728 Addit(cm->letter, parax); 729 } 730 } else { 731 Addsingle(cm->letter); 732 } 733 } 734 } 735 736 /* now, if we had diffent para modes - this loop really could be done better, but */ 737 738 /* Now, check for any param differences in extended channel modes.. 739 * note that: oldmode.* = us before, channel->mode.* = merged. 740 * if we win: copy oldmode to channel mode, if they win: send the mode 741 */ 742 for (cm=channelmodes; cm; cm = cm->next) 743 { 744 if (cm->letter && cm->paracount && 745 (oldmode.mode & cm->mode) && 746 (channel->mode.mode & cm->mode)) 747 { 748 int r; 749 const char *parax; 750 char flag = cm->letter; 751 void *ourm = GETPARASTRUCTEX(oldmode.mode_params, flag); 752 void *theirm = GETPARASTRUCT(channel, flag); 753 754 r = cm->sjoin_check(channel, ourm, theirm); 755 switch (r) 756 { 757 case EXSJ_WEWON: 758 parax = cm_getparameter_ex(oldmode.mode_params, flag); /* grab from old */ 759 cm_putparameter(channel, flag, parax); /* put in new (won) */ 760 break; 761 762 case EXSJ_THEYWON: 763 parax = cm_getparameter(channel, flag); 764 Addit(cm->letter, parax); 765 break; 766 767 case EXSJ_SAME: 768 break; 769 770 case EXSJ_MERGE: 771 parax = cm_getparameter_ex(oldmode.mode_params, flag); /* grab from old */ 772 cm_putparameter(channel, flag, parax); /* put in new (won) */ 773 Addit(flag, parax); 774 break; 775 776 default: 777 unreal_log(ULOG_ERROR, "sjoin", "BUG_SJOIN_CHECK", client, 778 "[BUG] channel.c:m_sjoin:param diff checker: unknown return value $return_value", 779 log_data_integer("return_value", r)); 780 break; 781 } 782 } 783 } 784 785 Addsingle('\0'); 786 787 if (!empty_mode(modebuf)) 788 send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf); 789 790 /* free the oldmode.* crap :( */ 791 extcmode_free_paramlist(oldmode.mode_params); 792 } 793 794 for (h = Hooks[HOOKTYPE_CHANNEL_SYNCED]; h; h = h->next) 795 { 796 int i = (*(h->func.intfunc))(channel,merge,removetheirs,nomode); 797 if (i == 1) 798 return; /* channel no longer exists */ 799 } 800 801 /* we should be synced by now, */ 802 if ((oldts != -1) && (oldts != channel->creationtime)) 803 { 804 unreal_log(ULOG_INFO, "channel", "CHANNEL_SYNC_TS_CHANGE", client, 805 "Channel $channel: timestamp changed from $old_ts -> $new_ts " 806 "after syncing with server $client.", 807 log_data_channel("channel", channel), 808 log_data_integer("old_ts", oldts), 809 log_data_integer("new_ts", channel->creationtime)); 810 } 811 812 /* If something went wrong with processing of the SJOIN above and 813 * the channel actually has no users in it at this point, 814 * then destroy the channel. 815 */ 816 if (!channel->users) 817 { 818 sub1_from_channel(channel); 819 return; 820 } 821 }