+/*
+ * set_hostmask() - derived from hide_hostmask()
+ *
+ */
+int set_hostmask(struct Client *cptr, char *hostmask, char *password)
+{
+ int restore = 0;
+ int freeform = 0;
+ char *host, *new_vhost, *vhost_pass;
+ char hiddenhost[USERLEN + HOSTLEN + 2];
+ struct Membership *chan;
+
+ Debug((DEBUG_INFO, "set_hostmask() %C, %s, %s", cptr, hostmask, password));
+
+ /* sethost enabled? */
+ if (MyConnect(cptr) && !feature_bool(FEAT_SETHOST)) {
+ send_reply(cptr, ERR_DISABLED, "SETHOST");
+ return 0;
+ }
+
+ /* sethost enabled for users? */
+ if (MyConnect(cptr) && !IsAnOper(cptr) && !feature_bool(FEAT_SETHOST_USER)) {
+ send_reply(cptr, ERR_NOPRIVILEGES);
+ return 0;
+ }
+
+ /* MODE_DEL: restore original hostmask */
+ if (EmptyString(hostmask)) {
+ /* is already sethost'ed? */
+ if (IsSetHost(cptr)) {
+ restore = 1;
+ sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Host change");
+ /* If they are +rx, we need to return to their +x host, not their "real" host */
+ if (HasHiddenHost(cptr))
+ ircd_snprintf(0, cli_user(cptr)->host, HOSTLEN, "%s.%s",
+ cli_user(cptr)->account, feature_str(FEAT_HIDDEN_HOST));
+ else
+ strncpy(cli_user(cptr)->host, cli_user(cptr)->realhost, HOSTLEN);
+ strncpy(cli_user(cptr)->username, cli_user(cptr)->realusername, USERLEN);
+ /* log it */
+ if (MyConnect(cptr))
+ log_write(LS_SETHOST, L_INFO, LOG_NOSNOTICE,
+ "SETHOST (%s@%s) by (%#R): restoring real hostmask",
+ cli_user(cptr)->username, cli_user(cptr)->host, cptr);
+ } else
+ return 0;
+ /* MODE_ADD: set a new hostmask */
+ } else {
+ /* chop up ident and host.cc */
+ if ((host = strrchr(hostmask, '@'))) /* oper can specifiy ident@host.cc */
+ *host++ = '\0';
+ else /* user can only specifiy host.cc [password] */
+ host = hostmask;
+ /*
+ * Oper sethost
+ */
+ if (MyConnect(cptr)) {
+ if (IsAnOper(cptr)) {
+ if ((new_vhost = IsVhost(host, 1)) == NULL) {
+ if (!feature_bool(FEAT_SETHOST_FREEFORM)) {
+ send_reply(cptr, ERR_HOSTUNAVAIL, hostmask);
+ log_write(LS_SETHOST, L_INFO, LOG_NOSNOTICE,
+ "SETHOST (%s@%s) by (%#R): no such s-line",
+ (host != hostmask) ? hostmask : cli_user(cptr)->username, host, cptr);
+ return 0;
+ } else /* freeform active, log and go */
+ freeform = 1;
+ }
+ sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Host change");
+ /* set the new ident and host */
+ if (host != hostmask) /* oper only specified host.cc */
+ strncpy(cli_user(cptr)->username, hostmask, USERLEN);
+ strncpy(cli_user(cptr)->host, host, HOSTLEN);
+ /* log it */
+ log_write(LS_SETHOST, (freeform) ? L_NOTICE : L_INFO,
+ (freeform) ? 0 : LOG_NOSNOTICE, "SETHOST (%s@%s) by (%#R)%s",
+ cli_user(cptr)->username, cli_user(cptr)->host, cptr,
+ (freeform) ? ": using freeform" : "");
+ /*
+ * plain user sethost, handled here
+ */
+ } else {
+ /* empty password? */
+ if (EmptyString(password)) {
+ send_reply(cptr, ERR_NEEDMOREPARAMS, "MODE");
+ return 0;
+ }
+ /* no such s-line */
+ if ((new_vhost = IsVhost(host, 0)) == NULL) {
+ send_reply(cptr, ERR_HOSTUNAVAIL, hostmask);
+ log_write(LS_SETHOST, L_INFO, LOG_NOSNOTICE, "SETHOST (%s@%s %s) by (%#R): no such s-line",
+ cli_user(cptr)->username, host, password, cptr);
+ return 0;
+ }
+ /* no password */
+ if ((vhost_pass = IsVhostPass(new_vhost)) == NULL) {
+ send_reply(cptr, ERR_PASSWDMISMATCH);
+ log_write(LS_SETHOST, L_INFO, 0, "SETHOST (%s@%s %s) by (%#R): trying to use an oper s-line",
+ cli_user(cptr)->username, host, password, cptr);
+ return 0;
+ }
+ /* incorrect password */
+ if (strCasediff(vhost_pass, password)) {
+ send_reply(cptr, ERR_PASSWDMISMATCH);
+ log_write(LS_SETHOST, L_NOTICE, 0, "SETHOST (%s@%s %s) by (%#R): incorrect password",
+ cli_user(cptr)->username, host, password, cptr);
+ return 0;
+ }
+ sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Host change");
+ /* set the new host */
+ strncpy(cli_user(cptr)->host, new_vhost, HOSTLEN);
+ /* log it */
+ log_write(LS_SETHOST, L_INFO, LOG_NOSNOTICE, "SETHOST (%s@%s) by (%#R)",
+ cli_user(cptr)->username, cli_user(cptr)->host, cptr);
+ }
+ } else { /* remote user */
+ sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Host change");
+ if (host != hostmask) /* oper only specified host.cc */
+ strncpy(cli_user(cptr)->username, hostmask, USERLEN);
+ strncpy(cli_user(cptr)->host, host, HOSTLEN);
+ }
+ }
+
+ if (restore)
+ ClearSetHost(cptr);
+ else
+ SetSetHost(cptr);
+
+ if (MyConnect(cptr)) {
+ ircd_snprintf(0, hiddenhost, HOSTLEN + USERLEN + 2, "%s@%s",
+ cli_user(cptr)->username, cli_user(cptr)->host);
+ send_reply(cptr, RPL_HOSTHIDDEN, hiddenhost);
+ }
+
+#if 0
+ /* Code copied from hide_hostmask(). This is the old (pre-delayedjoin)
+ * version. Switch this in if you're not using the delayed join patch. */
+ /*
+ * Go through all channels the client was on, rejoin him
+ * and set the modes, if any
+ */
+ for (chan = cli_user(cptr)->channel; chan; chan = chan->next_channel) {
+ if (IsZombie(chan))
+ continue;
+ sendcmdto_channel_butserv_butone(cptr, CMD_JOIN, chan->channel, cptr,
+ "%H", chan->channel);
+ if (IsChanOp(chan) && HasVoice(chan)) {
+ sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan->channel, cptr,
+ "%H +ov %C %C", chan->channel, cptr, cptr);
+ } else if (IsChanOp(chan) || HasVoice(chan)) {
+ sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan->channel, cptr,
+ "%H +%c %C", chan->channel, IsChanOp(chan) ? 'o' : 'v', cptr);
+ }
+ }
+#endif
+
+ /*
+ * Go through all channels the client was on, rejoin him
+ * and set the modes, if any
+ */
+ for (chan = cli_user(cptr)->channel; chan; chan = chan->next_channel) {
+ if (IsZombie(chan))
+ continue;
+ /* If this channel has delayed joins and the user has no modes, just set
+ * the delayed join flag rather than showing the join, even if the user
+ * was visible before */
+ if (!IsChanOp(chan) && !HasVoice(chan)
+ && (chan->channel->mode.mode & MODE_DELJOINS)) {
+ SetDelayedJoin(chan);
+ } else {
+ sendcmdto_channel_butserv_butone(cptr, CMD_JOIN, chan->channel, cptr, 0,
+ "%H", chan->channel);
+ }
+ if (IsChanOp(chan) && HasVoice(chan)) {
+ sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan->channel, cptr, 0,
+ "%H +ov %C %C", chan->channel, cptr, cptr);
+ } else if (IsChanOp(chan) || HasVoice(chan)) {
+ sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan->channel, cptr, 0,
+ "%H +%c %C", chan->channel, IsChanOp(chan) ? 'o' : 'v', cptr);
+ }
+ }
+ return 1;
+}
+