ircd/s_user.c
author Chris Porter <chris@warp13.co.uk>
Mon Mar 24 01:33:32 2008 +0000 (4 years ago)
changeset 147 2da2b881d9f5
parent 14489defd4c9708
child 1507afd40e93981
permissions -rw-r--r--
Apply remote crash exploit fix to public repo.
slug@0
     1
/*
slug@0
     2
 * IRC - Internet Relay Chat, ircd/s_user.c (formerly ircd/s_msg.c)
slug@0
     3
 * Copyright (C) 1990 Jarkko Oikarinen and
slug@0
     4
 *                    University of Oulu, Computing Center
slug@0
     5
 *
slug@0
     6
 * See file AUTHORS in IRC package for additional names of
slug@0
     7
 * the programmers.
slug@0
     8
 *
slug@0
     9
 * This program is free software; you can redistribute it and/or modify
slug@0
    10
 * it under the terms of the GNU General Public License as published by
slug@0
    11
 * the Free Software Foundation; either version 1, or (at your option)
slug@0
    12
 * any later version.
slug@0
    13
 *
slug@0
    14
 * This program is distributed in the hope that it will be useful,
slug@0
    15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
slug@0
    16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
slug@0
    17
 * GNU General Public License for more details.
slug@0
    18
 *
slug@0
    19
 * You should have received a copy of the GNU General Public License
slug@0
    20
 * along with this program; if not, write to the Free Software
slug@0
    21
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
slug@0
    22
 */
slug@0
    23
/** @file
slug@0
    24
 * @brief Miscellaneous user-related helper functions.
paul@126
    25
 * @version $Id: s_user.c,v 1.99.2.11 2007/08/21 01:13:15 entrope Exp $
slug@0
    26
 */
slug@0
    27
#include "config.h"
slug@0
    28
slug@0
    29
#include "s_user.h"
slug@0
    30
#include "IPcheck.h"
slug@0
    31
#include "channel.h"
slug@0
    32
#include "class.h"
slug@0
    33
#include "client.h"
slug@0
    34
#include "hash.h"
slug@0
    35
#include "ircd.h"
slug@0
    36
#include "ircd_alloc.h"
slug@0
    37
#include "ircd_chattr.h"
slug@0
    38
#include "ircd_features.h"
slug@0
    39
#include "ircd_log.h"
slug@0
    40
#include "ircd_reply.h"
slug@0
    41
#include "ircd_snprintf.h"
slug@0
    42
#include "ircd_string.h"
slug@0
    43
#include "list.h"
slug@0
    44
#include "match.h"
slug@0
    45
#include "motd.h"
slug@0
    46
#include "msg.h"
slug@0
    47
#include "msgq.h"
slug@0
    48
#include "numeric.h"
slug@0
    49
#include "numnicks.h"
slug@0
    50
#include "parse.h"
slug@0
    51
#include "querycmds.h"
slug@0
    52
#include "random.h"
paul@33
    53
#include "s_auth.h"
slug@0
    54
#include "s_bsd.h"
slug@0
    55
#include "s_conf.h"
slug@0
    56
#include "s_debug.h"
slug@0
    57
#include "s_misc.h"
slug@0
    58
#include "s_serv.h" /* max_client_count */
slug@0
    59
#include "send.h"
slug@0
    60
#include "struct.h"
slug@0
    61
#include "supported.h"
slug@0
    62
#include "sys.h"
slug@0
    63
#include "userload.h"
slug@0
    64
#include "version.h"
slug@0
    65
#include "whowas.h"
slug@0
    66
slug@0
    67
#include "handlers.h" /* m_motd and m_lusers */
slug@0
    68
slug@0
    69
/* #include <assert.h> -- Now using assert in ircd_log.h */
slug@0
    70
#include <fcntl.h>
slug@0
    71
#include <stdio.h>
slug@0
    72
#include <stdlib.h>
slug@0
    73
#include <string.h>
slug@0
    74
#include <sys/stat.h>
slug@0
    75
paul@30
    76
static char *IsVhost(char *hostmask, int oper);
paul@30
    77
static char *IsVhostPass(char *hostmask);
paul@30
    78
slug@0
    79
/** Count of allocated User structures. */
slug@0
    80
static int userCount = 0;
slug@0
    81
slug@0
    82
/** Makes sure that \a cptr has a User information block.
slug@0
    83
 * If cli_user(cptr) != NULL, does nothing.
slug@0
    84
 * @param[in] cptr Client to attach User struct to.
slug@0
    85
 * @return User struct associated with \a cptr.
slug@0
    86
 */
slug@0
    87
struct User *make_user(struct Client *cptr)
slug@0
    88
{
slug@0
    89
  assert(0 != cptr);
slug@0
    90
slug@0
    91
  if (!cli_user(cptr)) {
slug@0
    92
    cli_user(cptr) = (struct User*) MyMalloc(sizeof(struct User));
slug@0
    93
    assert(0 != cli_user(cptr));
slug@0
    94
slug@0
    95
    /* All variables are 0 by default */
slug@0
    96
    memset(cli_user(cptr), 0, sizeof(struct User));
slug@0
    97
    ++userCount;
slug@0
    98
    cli_user(cptr)->refcnt = 1;
slug@0
    99
  }
slug@0
   100
  return cli_user(cptr);
slug@0
   101
}
slug@0
   102
slug@0
   103
/** Dereference \a user.
slug@0
   104
 * User structures are reference-counted; if the refcount of \a user
slug@0
   105
 * becomes zero, free it.
slug@0
   106
 * @param[in] user User to dereference.
slug@0
   107
 */
slug@0
   108
void free_user(struct User* user)
slug@0
   109
{
slug@0
   110
  assert(0 != user);
slug@0
   111
  assert(0 < user->refcnt);
slug@0
   112
slug@0
   113
  if (--user->refcnt == 0) {
slug@0
   114
    if (user->away)
slug@0
   115
      MyFree(user->away);
paul@80
   116
    if (user->opername)
paul@80
   117
      MyFree(user->opername);
slug@0
   118
    /*
slug@0
   119
     * sanity check
slug@0
   120
     */
slug@0
   121
    assert(0 == user->joined);
slug@0
   122
    assert(0 == user->invited);
slug@0
   123
    assert(0 == user->channel);
slug@0
   124
slug@0
   125
    MyFree(user);
paul@115
   126
    assert(userCount>0);
slug@0
   127
    --userCount;
slug@0
   128
  }
slug@0
   129
}
slug@0
   130
slug@0
   131
/** Find number of User structs allocated and memory used by them.
slug@0
   132
 * @param[out] count_out Receives number of User structs allocated.
slug@0
   133
 * @param[out] bytes_out Receives number of bytes used by User structs.
slug@0
   134
 */
slug@0
   135
void user_count_memory(size_t* count_out, size_t* bytes_out)
slug@0
   136
{
slug@0
   137
  assert(0 != count_out);
slug@0
   138
  assert(0 != bytes_out);
slug@0
   139
  *count_out = userCount;
slug@0
   140
  *bytes_out = userCount * sizeof(struct User);
slug@0
   141
}
slug@0
   142
slug@0
   143
slug@0
   144
/** Find the next client (starting at \a next) with a name that matches \a ch.
slug@0
   145
 * Normal usage loop is:
slug@0
   146
 * for (x = client; x = next_client(x,mask); x = x->next)
slug@0
   147
 *     HandleMatchingClient;
slug@0
   148
 *
slug@0
   149
 * @param[in] next First client to check.
slug@0
   150
 * @param[in] ch Name mask to check against.
slug@0
   151
 * @return Next matching client found, or NULL if none.
slug@0
   152
 */
slug@0
   153
struct Client *next_client(struct Client *next, const char* ch)
slug@0
   154
{
slug@0
   155
  struct Client *tmp = next;
slug@0
   156
slug@0
   157
  if (!tmp)
slug@0
   158
    return NULL;
slug@0
   159
slug@0
   160
  next = FindClient(ch);
slug@0
   161
  next = next ? next : tmp;
slug@0
   162
  if (cli_prev(tmp) == next)
slug@0
   163
    return NULL;
slug@0
   164
  if (next != tmp)
slug@0
   165
    return next;
slug@0
   166
  for (; next; next = cli_next(next))
slug@0
   167
    if (!match(ch, cli_name(next)))
slug@0
   168
      break;
slug@0
   169
  return next;
slug@0
   170
}
slug@0
   171
slug@0
   172
/** Find the destination server for a command, and forward it if that is not us.
slug@0
   173
 *
slug@0
   174
 * \a server may be a nickname, server name, server mask (if \a from
slug@0
   175
 * is a local user) or server numnick (if \a is a server or remote
slug@0
   176
 * user).
slug@0
   177
 *
slug@0
   178
 * @param[in] from Client that sent the command to us.
slug@0
   179
 * @param[in] cmd Long-form command text.
slug@0
   180
 * @param[in] tok Token-form command text.
slug@0
   181
 * @param[in] one Client that originated the command (ignored).
slug@0
   182
 * @param[in] MustBeOper If non-zero and \a from is not an operator, return HUNTED_NOSUCH.
slug@0
   183
 * @param[in] pattern Format string of arguments to command.
slug@0
   184
 * @param[in] server Index of target name or mask in \a parv.
slug@0
   185
 * @param[in] parc Number of valid elements in \a parv (must be less than 9).
slug@0
   186
 * @param[in] parv Array of arguments to command.
slug@0
   187
 * @return One of HUNTED_ISME, HUNTED_NOSUCH or HUNTED_PASS.
slug@0
   188
 */
slug@0
   189
int hunt_server_cmd(struct Client *from, const char *cmd, const char *tok,
slug@0
   190
                    struct Client *one, int MustBeOper, const char *pattern,
slug@0
   191
                    int server, int parc, char *parv[])
slug@0
   192
{
slug@0
   193
  struct Client *acptr;
slug@0
   194
  char *to;
slug@0
   195
slug@0
   196
  /* Assume it's me, if no server or an unregistered client */
slug@0
   197
  if (parc <= server || EmptyString((to = parv[server])) || IsUnknown(from))
slug@0
   198
    return (HUNTED_ISME);
slug@0
   199
paul@114
   200
  if (MustBeOper && (!IsPrivileged(from) || (IsAnOper(from) && !HasPriv(from, PRIV_SERVERINFO))))
slug@0
   201
  {
slug@0
   202
    send_reply(from, ERR_NOPRIVILEGES);
slug@0
   203
    return HUNTED_NOSUCH;
slug@0
   204
  }
slug@0
   205
slug@0
   206
  /* Make sure it's a server */
slug@0
   207
  if (MyUser(from)) {
slug@0
   208
    /* Make sure it's a server */
slug@0
   209
    if (!strchr(to, '*')) {
slug@0
   210
      if (0 == (acptr = FindClient(to))) {
slug@0
   211
        send_reply(from, ERR_NOSUCHSERVER, to);
slug@0
   212
        return HUNTED_NOSUCH;
slug@0
   213
      }
slug@0
   214
slug@0
   215
      if (cli_user(acptr))
slug@0
   216
        acptr = cli_user(acptr)->server;
slug@0
   217
    } else if (!(acptr = find_match_server(to))) {
slug@0
   218
      send_reply(from, ERR_NOSUCHSERVER, to);
slug@0
   219
      return (HUNTED_NOSUCH);
slug@0
   220
    }
slug@0
   221
  } else if (!(acptr = FindNServer(to))) {
slug@0
   222
    send_reply(from, SND_EXPLICIT | ERR_NOSUCHSERVER, "* :Server has disconnected");
slug@0
   223
    return (HUNTED_NOSUCH);        /* Server broke off in the meantime */
slug@0
   224
  }
slug@0
   225
slug@0
   226
  if (IsMe(acptr))
slug@0
   227
    return (HUNTED_ISME);
slug@0
   228
slug@0
   229
  if (MustBeOper && !IsPrivileged(from)) {
slug@0
   230
    send_reply(from, ERR_NOPRIVILEGES);
slug@0
   231
    return HUNTED_NOSUCH;
slug@0
   232
  }
slug@0
   233
slug@0
   234
  /* assert(!IsServer(from)); */
slug@0
   235
slug@0
   236
  parv[server] = (char *) acptr; /* HACK! HACK! HACK! ARGH! */
slug@0
   237
slug@0
   238
  sendcmdto_one(from, cmd, tok, acptr, pattern, parv[1], parv[2], parv[3],
slug@0
   239
                parv[4], parv[5], parv[6], parv[7], parv[8]);
slug@0
   240
slug@0
   241
  return (HUNTED_PASS);
slug@0
   242
}
slug@0
   243
slug@0
   244
/** Find the destination server for a command, and forward it (as a
slug@0
   245
 * high-priority command) if that is not us.
slug@0
   246
 *
slug@0
   247
 * \a server may be a nickname, server name, server mask (if \a from
slug@0
   248
 * is a local user) or server numnick (if \a is a server or remote
slug@0
   249
 * user).
slug@0
   250
 * Unlike hunt_server_cmd(), this appends the message to the
slug@0
   251
 * high-priority message queue for the destination server.
slug@0
   252
 *
slug@0
   253
 * @param[in] from Client that sent the command to us.
slug@0
   254
 * @param[in] cmd Long-form command text.
slug@0
   255
 * @param[in] tok Token-form command text.
slug@0
   256
 * @param[in] one Client that originated the command (ignored).
slug@0
   257
 * @param[in] MustBeOper If non-zero and \a from is not an operator, return HUNTED_NOSUCH.
slug@0
   258
 * @param[in] pattern Format string of arguments to command.
slug@0
   259
 * @param[in] server Index of target name or mask in \a parv.
slug@0
   260
 * @param[in] parc Number of valid elements in \a parv (must be less than 9).
slug@0
   261
 * @param[in] parv Array of arguments to command.
slug@0
   262
 * @return One of HUNTED_ISME, HUNTED_NOSUCH or HUNTED_PASS.
slug@0
   263
 */
slug@0
   264
int hunt_server_prio_cmd(struct Client *from, const char *cmd, const char *tok,
slug@0
   265
			 struct Client *one, int MustBeOper,
slug@0
   266
			 const char *pattern, int server, int parc,
slug@0
   267
			 char *parv[])
slug@0
   268
{
slug@0
   269
  struct Client *acptr;
slug@0
   270
  char *to;
slug@0
   271
slug@0
   272
  /* Assume it's me, if no server or an unregistered client */
slug@0
   273
  if (parc <= server || EmptyString((to = parv[server])) || IsUnknown(from))
slug@0
   274
    return (HUNTED_ISME);
slug@0
   275
slug@0
   276
  /* Make sure it's a server */
slug@0
   277
  if (MyUser(from)) {
slug@0
   278
    /* Make sure it's a server */
slug@0
   279
    if (!strchr(to, '*')) {
slug@0
   280
      if (0 == (acptr = FindClient(to))) {
slug@0
   281
        send_reply(from, ERR_NOSUCHSERVER, to);
slug@0
   282
        return HUNTED_NOSUCH;
slug@0
   283
      }
slug@0
   284
slug@0
   285
      if (cli_user(acptr))
slug@0
   286
        acptr = cli_user(acptr)->server;
slug@0
   287
    } else if (!(acptr = find_match_server(to))) {
slug@0
   288
      send_reply(from, ERR_NOSUCHSERVER, to);
slug@0
   289
      return (HUNTED_NOSUCH);
slug@0
   290
    }
slug@0
   291
  } else if (!(acptr = FindNServer(to)))
slug@0
   292
    return (HUNTED_NOSUCH);        /* Server broke off in the meantime */
slug@0
   293
slug@0
   294
  if (IsMe(acptr))
slug@0
   295
    return (HUNTED_ISME);
slug@0
   296
slug@0
   297
  if (MustBeOper && !IsPrivileged(from)) {
slug@0
   298
    send_reply(from, ERR_NOPRIVILEGES);
slug@0
   299
    return HUNTED_NOSUCH;
slug@0
   300
  }
slug@0
   301
slug@0
   302
  /* assert(!IsServer(from)); SETTIME to particular destinations permitted */
slug@0
   303
slug@0
   304
  parv[server] = (char *) acptr; /* HACK! HACK! HACK! ARGH! */
slug@0
   305
slug@0
   306
  sendcmdto_prio_one(from, cmd, tok, acptr, pattern, parv[1], parv[2], parv[3],
slug@0
   307
		     parv[4], parv[5], parv[6], parv[7], parv[8]);
slug@0
   308
slug@0
   309
  return (HUNTED_PASS);
slug@0
   310
}
slug@0
   311
slug@0
   312
slug@0
   313
/*
slug@0
   314
 * register_user
slug@0
   315
 *
slug@0
   316
 * This function is called when both NICK and USER messages
slug@0
   317
 * have been accepted for the client, in whatever order. Only
slug@0
   318
 * after this the USER message is propagated.
slug@0
   319
 *
slug@0
   320
 * NICK's must be propagated at once when received, although
slug@0
   321
 * it would be better to delay them too until full info is
slug@0
   322
 * available. Doing it is not so simple though, would have
slug@0
   323
 * to implement the following:
slug@0
   324
 *
slug@0
   325
 * 1) user telnets in and gives only "NICK foobar" and waits
slug@0
   326
 * 2) another user far away logs in normally with the nick
slug@0
   327
 *    "foobar" (quite legal, as this server didn't propagate it).
slug@0
   328
 * 3) now this server gets nick "foobar" from outside, but
slug@0
   329
 *    has already the same defined locally. Current server
slug@0
   330
 *    would just issue "KILL foobar" to clean out dups. But,
slug@0
   331
 *    this is not fair. It should actually request another
slug@0
   332
 *    nick from local user or kill him/her...
slug@0
   333
 */
slug@0
   334
/** Finish registering a user who has sent both NICK and USER.
slug@0
   335
 * For local connections, possibly check IAuth; make sure there is a
slug@0
   336
 * matching Client config block; clean the username field; check
slug@0
   337
 * K/k-lines; check for "hacked" looking usernames; assign a numnick;
slug@0
   338
 * and send greeting (WELCOME, ISUPPORT, MOTD, etc).
slug@0
   339
 * For all connections, update the invisible user and operator counts;
slug@0
   340
 * run IPcheck against their address; and forward the NICK.
slug@0
   341
 *
slug@0
   342
 * @param[in] cptr Client who introduced the user.
slug@0
   343
 * @param[in,out] sptr Client who has been fully introduced.
slug@0
   344
 * @return Zero or CPTR_KILLED.
slug@0
   345
 */
paul@33
   346
int register_user(struct Client *cptr, struct Client *sptr)
slug@0
   347
{
slug@0
   348
  char*            parv[4];
slug@0
   349
  char*            tmpstr;
slug@0
   350
  struct User*     user = cli_user(sptr);
slug@0
   351
  char             ip_base64[25];
chris@144
   352
  int ipv6andopername[] = {FLAG_IPV6,FLAG_OPERNAME};
slug@0
   353
slug@0
   354
  user->last = CurrentTime;
slug@0
   355
  parv[0] = cli_name(sptr);
slug@0
   356
  parv[1] = parv[2] = NULL;
slug@0
   357
slug@0
   358
  if (MyConnect(sptr))
slug@0
   359
  {
paul@33
   360
    assert(cptr == sptr);
slug@0
   361
slug@0
   362
    Count_unknownbecomesclient(sptr, UserStats);
slug@0
   363
paul@30
   364
    if (MyConnect(sptr) && feature_bool(FEAT_AUTOINVISIBLE))
paul@30
   365
      SetInvisible(sptr);
paul@30
   366
    
paul@30
   367
    if(MyConnect(sptr) && feature_bool(FEAT_SETHOST_AUTO)) {
paul@30
   368
      if (conf_check_slines(sptr)) {
paul@30
   369
        send_reply(sptr, RPL_USINGSLINE);
paul@30
   370
        SetSetHost(sptr);
paul@30
   371
      }
paul@30
   372
    }
paul@30
   373
slug@0
   374
    SetUser(sptr);
slug@0
   375
    cli_handler(sptr) = CLIENT_HANDLER;
slug@0
   376
    SetLocalNumNick(sptr);
slug@0
   377
    send_reply(sptr,
slug@0
   378
               RPL_WELCOME,
slug@0
   379
               feature_str(FEAT_NETWORK),
slug@0
   380
               feature_str(FEAT_PROVIDER) ? " via " : "",
slug@0
   381
               feature_str(FEAT_PROVIDER) ? feature_str(FEAT_PROVIDER) : "",
paul@33
   382
               cli_name(sptr));
slug@0
   383
    /*
slug@0
   384
     * This is a duplicate of the NOTICE but see below...
slug@0
   385
     */
slug@0
   386
    send_reply(sptr, RPL_YOURHOST, cli_name(&me), version);
slug@0
   387
    send_reply(sptr, RPL_CREATED, creation);
slug@0
   388
    send_reply(sptr, RPL_MYINFO, cli_name(&me), version, infousermodes,
slug@0
   389
               infochanmodes, infochanmodeswithparams);
slug@0
   390
    send_supported(sptr);
slug@0
   391
    m_lusers(sptr, sptr, 1, parv);
slug@0
   392
    update_load();
slug@0
   393
    motd_signon(sptr);
slug@0
   394
    if (cli_snomask(sptr) & SNO_NOISY)
slug@0
   395
      set_snomask(sptr, cli_snomask(sptr) & SNO_NOISY, SNO_ADD);
slug@0
   396
    if (feature_bool(FEAT_CONNEXIT_NOTICES))
slug@0
   397
      sendto_opmask_butone(0, SNO_CONNEXIT,
slug@0
   398
                           "Client connecting: %s (%s@%s) [%s] {%s} [%s] <%s%s>",
slug@0
   399
                           cli_name(sptr), user->username, user->host,
slug@0
   400
                           cli_sock_ip(sptr), get_client_class(sptr),
slug@0
   401
                           cli_info(sptr), NumNick(cptr) /* two %s's */);
slug@0
   402
slug@0
   403
    IPcheck_connect_succeeded(sptr);
slug@0
   404
    /*
slug@0
   405
     * Set user's initial modes
slug@0
   406
     */
slug@0
   407
    tmpstr = (char*)client_get_default_umode(sptr);
paul@115
   408
    if (tmpstr) {
paul@121
   409
      char *umodev[] = { NULL, NULL, NULL, NULL };
paul@121
   410
      umodev[2] = tmpstr;
paul@115
   411
      set_user_mode(cptr, sptr, 1, umodev, ALLOWMODES_ANY);
slug@0
   412
    }
paul@115
   413
slug@0
   414
  }
slug@0
   415
  else {
paul@33
   416
    struct Client *acptr = user->server;
slug@0
   417
slug@0
   418
    if (cli_from(acptr) != cli_from(sptr))
slug@0
   419
    {
slug@0
   420
      sendcmdto_one(&me, CMD_KILL, cptr, "%C :%s (%s != %s[%s])",
slug@0
   421
                    sptr, cli_name(&me), cli_name(user->server), cli_name(cli_from(acptr)),
slug@0
   422
                    cli_sockhost(cli_from(acptr)));
slug@0
   423
      SetFlag(sptr, FLAG_KILLED);
slug@0
   424
      return exit_client(cptr, sptr, &me, "NICK server wrong direction");
slug@0
   425
    }
slug@0
   426
    else if (HasFlag(acptr, FLAG_TS8))
slug@0
   427
      SetFlag(sptr, FLAG_TS8);
slug@0
   428
slug@0
   429
    /*
slug@0
   430
     * Check to see if this user is being propagated
slug@0
   431
     * as part of a net.burst, or is using protocol 9.
slug@0
   432
     * FIXME: This can be sped up - its stupid to check it for
slug@0
   433
     * every NICK message in a burst again  --Run.
slug@0
   434
     */
slug@0
   435
    for (; acptr != &me; acptr = cli_serv(acptr)->up)
slug@0
   436
    {
slug@0
   437
      if (IsBurst(acptr) || Protocol(acptr) < 10)
slug@0
   438
        break;
slug@0
   439
    }
slug@0
   440
    if (!IPcheck_remote_connect(sptr, (acptr != &me)))
slug@0
   441
    {
slug@0
   442
      /*
slug@0
   443
       * We ran out of bits to count this
slug@0
   444
       */
slug@0
   445
      sendcmdto_one(&me, CMD_KILL, sptr, "%C :%s (Too many connections from your host -- Ghost)",
slug@0
   446
                    sptr, cli_name(&me));
slug@0
   447
      return exit_client(cptr, sptr, &me,"Too many connections from your host -- throttled");
slug@0
   448
    }
paul@30
   449
paul@30
   450
    if(MyConnect(sptr) && feature_bool(FEAT_SETHOST_AUTO)) {
paul@30
   451
      if (conf_check_slines(sptr)) {
paul@30
   452
        send_reply(sptr, RPL_USINGSLINE);
paul@30
   453
        SetSetHost(sptr);
paul@30
   454
      }
paul@30
   455
    }
paul@30
   456
slug@0
   457
    SetUser(sptr);
slug@0
   458
  }
slug@0
   459
paul@101
   460
  /* If they get both +x and an account during registration, hide
paul@101
   461
   * their hostmask here.  Calling hide_hostmask() from IAuth's
paul@101
   462
   * account assignment causes a numeric reply during registration.
paul@101
   463
   */
paul@101
   464
  if (HasHiddenHost(sptr))
paul@101
   465
    hide_hostmask(sptr, FLAG_HIDDENHOST);
paul@126
   466
  if (IsInvisible(sptr))
paul@126
   467
    ++UserStats.inv_clients;
paul@126
   468
  if (IsOper(sptr))
paul@126
   469
    ++UserStats.opers;
slug@0
   470
paul@80
   471
  tmpstr = umode_str(sptr, 0);
paul@80
   472
paul@80
   473
  /* Do not send oper name and send full IP address to IPv6-grokking servers. */
slug@0
   474
  sendcmdto_flag_serv_butone(user->server, CMD_NICK, cptr,
paul@80
   475
                             FLAG_IPV6, FLAG_OPERNAME,
slug@0
   476
                             "%s %d %Tu %s %s %s%s%s%s %s%s :%s",
paul@33
   477
                             cli_name(sptr), cli_hopcount(sptr) + 1,
paul@33
   478
                             cli_lastnick(sptr),
paul@30
   479
                             user->realusername, user->realhost,
slug@0
   480
                             *tmpstr ? "+" : "", tmpstr, *tmpstr ? " " : "",
slug@0
   481
                             iptobase64(ip_base64, &cli_ip(sptr), sizeof(ip_base64), 1),
slug@0
   482
                             NumNick(sptr), cli_info(sptr));
paul@80
   483
  /* Do not send oper name and send fake IPv6 addresses to pre-IPv6 servers. */
paul@80
   484
  sendcmdto_flagarray_serv_butone(user->server, CMD_NICK, cptr,
paul@80
   485
                             NULL, 0, ipv6andopername, 2,
slug@0
   486
                             "%s %d %Tu %s %s %s%s%s%s %s%s :%s",
paul@33
   487
                             cli_name(sptr), cli_hopcount(sptr) + 1,
paul@33
   488
                             cli_lastnick(sptr),
paul@80
   489
                             user->realusername, user->realhost,
paul@80
   490
                             *tmpstr ? "+" : "", tmpstr, *tmpstr ? " " : "",
paul@80
   491
                             iptobase64(ip_base64, &cli_ip(sptr), sizeof(ip_base64), 0),
paul@80
   492
                             NumNick(sptr), cli_info(sptr));
paul@80
   493
paul@80
   494
  tmpstr = umode_str(sptr, 1);
paul@80
   495
  /* Send oper name and full IP address to IPv6-grokking servers. */
paul@80
   496
  sendcmdto_flagarray_serv_butone(user->server, CMD_NICK, cptr,
paul@80
   497
                             ipv6andopername, 2, NULL, 0,
paul@80
   498
                             "%s %d %Tu %s %s %s%s%s%s %s%s :%s",
paul@80
   499
                             cli_name(sptr), cli_hopcount(sptr) + 1,
paul@80
   500
                             cli_lastnick(sptr),
paul@80
   501
                             user->realusername, user->realhost,
paul@80
   502
                             *tmpstr ? "+" : "", tmpstr, *tmpstr ? " " : "",
paul@80
   503
                             iptobase64(ip_base64, &cli_ip(sptr), sizeof(ip_base64), 1),
paul@80
   504
                             NumNick(sptr), cli_info(sptr));
paul@80
   505
  /* Send oper name and fake IPv6 addresses to pre-IPv6 servers. */
slug@0
   506
  sendcmdto_flag_serv_butone(user->server, CMD_NICK, cptr,
paul@80
   507
                             FLAG_OPERNAME, FLAG_IPV6,
slug@0
   508
                             "%s %d %Tu %s %s %s%s%s%s %s%s :%s",
paul@33
   509
                             cli_name(sptr), cli_hopcount(sptr) + 1,
paul@33
   510
                             cli_lastnick(sptr),
paul@30
   511
                             user->realusername, user->realhost,
slug@0
   512
                             *tmpstr ? "+" : "", tmpstr, *tmpstr ? " " : "",
slug@0
   513
                             iptobase64(ip_base64, &cli_ip(sptr), sizeof(ip_base64), 0),
slug@0
   514
                             NumNick(sptr), cli_info(sptr));
slug@0
   515
slug@0
   516
  /* Send user mode to client */
slug@0
   517
  if (MyUser(sptr))
slug@0
   518
  {
slug@0
   519
    static struct Flags flags; /* automatically initialized to zeros */
paul@31
   520
    /* To avoid sending +r to the client due to auth-on-connect, set
paul@31
   521
     * the "old" FLAG_ACCOUNT bit to match the client's value.
paul@31
   522
     */
paul@31
   523
    if (IsAccount(cptr))
paul@31
   524
      FlagSet(&flags, FLAG_ACCOUNT);
paul@31
   525
    else
paul@31
   526
      FlagClr(&flags, FLAG_ACCOUNT);
paul@126
   527
    client_set_privs(sptr, NULL);
paul@80
   528
    send_umode(cptr, sptr, &flags, ALL_UMODES, 0);
slug@0
   529
    if ((cli_snomask(sptr) != SNO_DEFAULT) && HasFlag(sptr, FLAG_SERVNOTICE))
slug@0
   530
      send_reply(sptr, RPL_SNOMASK, cli_snomask(sptr), cli_snomask(sptr));
slug@0
   531
  }
slug@0
   532
  return 0;
slug@0
   533
}
slug@0
   534
slug@0
   535
/** List of user mode characters. */
slug@0
   536
static const struct UserMode {
slug@0
   537
  unsigned int flag; /**< User mode constant. */
slug@0
   538
  char         c;    /**< Character corresponding to the mode. */
slug@0
   539
} userModeList[] = {
slug@0
   540
  { FLAG_OPER,        'o' },
slug@0
   541
  { FLAG_LOCOP,       'O' },
slug@0
   542
  { FLAG_INVISIBLE,   'i' },
slug@0
   543
  { FLAG_WALLOP,      'w' },
slug@0
   544
  { FLAG_SERVNOTICE,  's' },
slug@0
   545
  { FLAG_DEAF,        'd' },
slug@0
   546
  { FLAG_CHSERV,      'k' },
slug@0
   547
  { FLAG_DEBUG,       'g' },
slug@0
   548
  { FLAG_ACCOUNT,     'r' },
paul@30
   549
  { FLAG_HIDDENHOST,  'x' },
paul@30
   550
  { FLAG_ACCOUNTONLY, 'R' },
paul@30
   551
  { FLAG_XTRAOP,      'X' },
paul@30
   552
  { FLAG_NOCHAN,      'n' },
paul@30
   553
  { FLAG_NOIDLE,      'I' },
paul@30
   554
  { FLAG_SETHOST,     'h' },
paul@30
   555
  { FLAG_PARANOID,    'P' }
slug@0
   556
};
slug@0
   557
slug@0
   558
/** Length of #userModeList. */
slug@0
   559
#define USERMODELIST_SIZE sizeof(userModeList) / sizeof(struct UserMode)
slug@0
   560
slug@0
   561
/*
slug@0
   562
 * XXX - find a way to get rid of this
slug@0
   563
 */
slug@0
   564
/** Nasty global buffer used for communications with umode_str() and others. */
slug@0
   565
static char umodeBuf[BUFSIZE];
slug@0
   566
slug@0
   567
/** Try to set a user's nickname.
slug@0
   568
 * If \a sptr is a server, the client is being introduced for the first time.
slug@0
   569
 * @param[in] cptr Client to set nickname.
slug@0
   570
 * @param[in] sptr Client sending the NICK.
slug@0
   571
 * @param[in] nick New nickname.
slug@0
   572
 * @param[in] parc Number of arguments to NICK.
slug@0
   573
 * @param[in] parv Argument list to NICK.
slug@0
   574
 * @return CPTR_KILLED if \a cptr was killed, else 0.
slug@0
   575
 */
slug@0
   576
int set_nick_name(struct Client* cptr, struct Client* sptr,
slug@0
   577
                  const char* nick, int parc, char* parv[])
slug@0
   578
{
slug@0
   579
  if (IsServer(sptr)) {
slug@0
   580
    /*
slug@0
   581
     * A server introducing a new client, change source
slug@0
   582
     */
slug@0
   583
    struct Client* new_client = make_client(cptr, STAT_UNKNOWN);
slug@0
   584
    assert(0 != new_client);
slug@0
   585
slug@0
   586
    cli_hopcount(new_client) = atoi(parv[2]);
slug@0
   587
    cli_lastnick(new_client) = atoi(parv[3]);
paul@115
   588
slug@0
   589
    /*
slug@0
   590
     * Set new nick name.
slug@0
   591
     */
slug@0
   592
    strcpy(cli_name(new_client), nick);
slug@0
   593
    cli_user(new_client) = make_user(new_client);
slug@0
   594
    cli_user(new_client)->server = sptr;
slug@0
   595
    SetRemoteNumNick(new_client, parv[parc - 2]);
slug@0
   596
    /*
slug@0
   597
     * IP# of remote client
slug@0
   598
     */
slug@0
   599
    base64toip(parv[parc - 3], &cli_ip(new_client));
slug@0
   600
slug@0
   601
    add_client_to_list(new_client);
slug@0
   602
    hAddClient(new_client);
slug@0
   603
slug@0
   604
    cli_serv(sptr)->ghost = 0;        /* :server NICK means end of net.burst */
slug@0
   605
    ircd_strncpy(cli_username(new_client), parv[4], USERLEN);
paul@33
   606
    ircd_strncpy(cli_user(new_client)->username, parv[4], USERLEN);
paul@30
   607
    ircd_strncpy(cli_user(new_client)->realusername, parv[4], USERLEN);
slug@0
   608
    ircd_strncpy(cli_user(new_client)->host, parv[5], HOSTLEN);
slug@0
   609
    ircd_strncpy(cli_user(new_client)->realhost, parv[5], HOSTLEN);
slug@0
   610
    ircd_strncpy(cli_info(new_client), parv[parc - 1], REALLEN);
paul@115
   611
paul@115
   612
    Count_newremoteclient(UserStats, sptr);
paul@115
   613
paul@115
   614
    if (parc > 7 && *parv[6] == '+') {
paul@115
   615
      /* (parc-4) -3 for the ip, numeric nick, realname */
paul@115
   616
      set_user_mode(cptr, new_client, parc-7, parv+4, ALLOWMODES_ANY);
slug@0
   617
    }
slug@0
   618
paul@33
   619
    return register_user(cptr, new_client);
slug@0
   620
  }
slug@0
   621
  else if ((cli_name(sptr))[0]) {
slug@0
   622
    /*
slug@0
   623
     * Client changing its nick
slug@0
   624
     *
slug@0
   625
     * If the client belongs to me, then check to see
slug@0
   626
     * if client is on any channels where it is currently
slug@0
   627
     * banned.  If so, do not allow the nick change to occur.
slug@0
   628
     */
slug@0
   629
    if (MyUser(sptr)) {
slug@0
   630
      const char* channel_name;
slug@0
   631
      struct Membership *member;
paul@30
   632
      if ((channel_name = find_no_nickchange_channel(sptr)) && !IsXtraOp(sptr)) {
slug@0
   633
        return send_reply(cptr, ERR_BANNICKCHANGE, channel_name);
slug@0
   634
      }
slug@0
   635
      /*
slug@0
   636
       * Refuse nick change if the last nick change was less
slug@0
   637
       * then 30 seconds ago. This is intended to get rid of
slug@0
   638
       * clone bots doing NICK FLOOD. -SeKs
slug@0
   639
       * If someone didn't change their nick for more then 60 seconds
slug@0
   640
       * however, allow to do two nick changes immediately after another
slug@0
   641
       * before limiting the nick flood. -Run
slug@0
   642
       */
slug@0
   643
      if (CurrentTime < cli_nextnick(cptr))
slug@0
   644
      {
slug@0
   645
        cli_nextnick(cptr) += 2;
slug@0
   646
        send_reply(cptr, ERR_NICKTOOFAST, parv[1],
slug@0
   647
                   cli_nextnick(cptr) - CurrentTime);
slug@0
   648
        /* Send error message */
slug@0
   649
        sendcmdto_one(cptr, CMD_NICK, cptr, "%s", cli_name(cptr));
slug@0
   650
        /* bounce NICK to user */
slug@0
   651
        return 0;                /* ignore nick change! */
slug@0
   652
      }
slug@0
   653
      else {
slug@0
   654
        /* Limit total to 1 change per NICK_DELAY seconds: */
slug@0
   655
        cli_nextnick(cptr) += NICK_DELAY;
slug@0
   656
        /* However allow _maximal_ 1 extra consecutive nick change: */
slug@0
   657
        if (cli_nextnick(cptr) < CurrentTime)
slug@0
   658
          cli_nextnick(cptr) = CurrentTime;
slug@0
   659
      }
slug@0
   660
      /* Invalidate all bans against the user so we check them again */
slug@0
   661
      for (member = (cli_user(cptr))->channel; member;
slug@0
   662
	   member = member->next_channel)
slug@0
   663
	ClearBanValid(member);
slug@0
   664
    }
slug@0
   665
    /*
slug@0
   666
     * Also set 'lastnick' to current time, if changed.
slug@0
   667
     */
slug@0
   668
    if (0 != ircd_strcmp(parv[0], nick))
slug@0
   669
      cli_lastnick(sptr) = (sptr == cptr) ? TStime() : atoi(parv[2]);
slug@0
   670
slug@0
   671
    /*
slug@0
   672
     * Client just changing his/her nick. If he/she is
slug@0
   673
     * on a channel, send note of change to all clients
slug@0
   674
     * on that channel. Propagate notice to other servers.
slug@0
   675
     */
slug@0
   676
    if (IsUser(sptr)) {
slug@0
   677
      sendcmdto_common_channels_butone(sptr, CMD_NICK, NULL, ":%s", nick);
slug@0
   678
      add_history(sptr, 1);
slug@0
   679
      sendcmdto_serv_butone(sptr, CMD_NICK, cptr, "%s %Tu", nick,
slug@0
   680
                            cli_lastnick(sptr));
slug@0
   681
    }
slug@0
   682
    else
slug@0
   683
      sendcmdto_one(sptr, CMD_NICK, sptr, ":%s", nick);
slug@0
   684
slug@0
   685
    if ((cli_name(sptr))[0])
slug@0
   686
      hRemClient(sptr);
slug@0
   687
    strcpy(cli_name(sptr), nick);
slug@0
   688
    hAddClient(sptr);
slug@0
   689
  }
slug@0
   690
  else {
slug@0
   691
    /* Local client setting NICK the first time */
slug@0
   692
    strcpy(cli_name(sptr), nick);
slug@0
   693
    hAddClient(sptr);
paul@33
   694
    return auth_set_nick(cli_auth(sptr), nick);
slug@0
   695
  }
slug@0
   696
  return 0;
slug@0
   697
}
slug@0
   698
slug@0
   699
/** Calculate the hash value for a target.
slug@0
   700
 * @param[in] target Pointer to target, cast to unsigned int.
slug@0
   701
 * @return Hash value constructed from the pointer.
slug@0
   702
 */
slug@0
   703
static unsigned char hash_target(unsigned int target)
slug@0
   704
{
slug@0
   705
  return (unsigned char) (target >> 16) ^ (target >> 8);
slug@0
   706
}
slug@0
   707
slug@0
   708
/** Records \a target as a recent target for \a sptr.
slug@0
   709
 * @param[in] sptr User who has sent to a new target.
slug@0
   710
 * @param[in] target Target to add.
slug@0
   711
 */
slug@0
   712
void
slug@0
   713
add_target(struct Client *sptr, void *target)
slug@0
   714
{
slug@0
   715
  /* Ok, this shouldn't work esp on alpha
slug@0
   716
  */
slug@0
   717
  unsigned char  hash = hash_target((unsigned long) target);
slug@0
   718
  unsigned char* targets;
slug@0
   719
  int            i;
slug@0
   720
  assert(0 != sptr);
slug@0
   721
  assert(cli_local(sptr));
slug@0
   722
slug@0
   723
  targets = cli_targets(sptr);
slug@0
   724
slug@0
   725
  /* 
slug@0
   726
   * Already in table?
slug@0
   727
   */
slug@0
   728
  for (i = 0; i < MAXTARGETS; ++i) {
slug@0
   729
    if (targets[i] == hash)
slug@0
   730
      return;
slug@0
   731
  }
slug@0
   732
  /*
slug@0
   733
   * New target
slug@0
   734
   */
slug@0
   735
  memmove(&targets[RESERVEDTARGETS + 1],
slug@0
   736
          &targets[RESERVEDTARGETS], MAXTARGETS - RESERVEDTARGETS - 1);
slug@0
   737
  targets[RESERVEDTARGETS] = hash;
slug@0
   738
}
slug@0
   739
slug@0
   740
/** Check whether \a sptr can send to or join \a target yet.
slug@0
   741
 * @param[in] sptr User trying to join a channel or send a message.
slug@0
   742
 * @param[in] target Target of the join or message.
slug@0
   743
 * @param[in] name Name of the target.
slug@0
   744
 * @param[in] created If non-zero, trying to join a new channel.
slug@0
   745
 * @return Non-zero if too many target changes; zero if okay to send.
slug@0
   746
 */
slug@0
   747
int check_target_limit(struct Client *sptr, void *target, const char *name,
slug@0
   748
    int created)
slug@0
   749
{
slug@0
   750
  unsigned char hash = hash_target((unsigned long) target);
slug@0
   751
  int            i;
slug@0
   752
  unsigned char* targets;
slug@0
   753
slug@0
   754
  assert(0 != sptr);
slug@0
   755
  assert(cli_local(sptr));
slug@0
   756
  targets = cli_targets(sptr);
slug@0
   757
slug@0
   758
  /* If user is invited to channel, give him/her a free target */
slug@0
   759
  if (IsChannelName(name) && IsInvited(sptr, target))
slug@0
   760
    return 0;
slug@0
   761
paul@30
   762
  /* opers always have a free target */
paul@30
   763
  if (IsAnOper(sptr))
paul@30
   764
    return 0;
paul@30
   765
slug@0
   766
  /*
slug@0
   767
   * Same target as last time?
slug@0
   768
   */
slug@0
   769
  if (targets[0] == hash)
slug@0
   770
    return 0;
slug@0
   771
  for (i = 1; i < MAXTARGETS; ++i) {
slug@0
   772
    if (targets[i] == hash) {
slug@0
   773
      memmove(&targets[1], &targets[0], i);
slug@0
   774
      targets[0] = hash;
slug@0
   775
      return 0;
slug@0
   776
    }
slug@0
   777
  }
slug@0
   778
  /*
slug@0
   779
   * New target
slug@0
   780
   */
slug@0
   781
  if (!created) {
slug@0
   782
    if (CurrentTime < cli_nexttarget(sptr)) {
slug@0
   783
      if (cli_nexttarget(sptr) - CurrentTime < TARGET_DELAY + 8) {
slug@0
   784
        /*
slug@0
   785
         * No server flooding
slug@0
   786
         */
slug@0
   787
        cli_nexttarget(sptr) += 2;
slug@0
   788
        send_reply(sptr, ERR_TARGETTOOFAST, name,
slug@0
   789
                   cli_nexttarget(sptr) - CurrentTime);
slug@0
   790
      }
slug@0
   791
      return 1;
slug@0
   792
    }
slug@0
   793
    else {
slug@0
   794
      cli_nexttarget(sptr) += TARGET_DELAY;
slug@0
   795
      if (cli_nexttarget(sptr) < CurrentTime - (TARGET_DELAY * (MAXTARGETS - 1)))
slug@0
   796
        cli_nexttarget(sptr) = CurrentTime - (TARGET_DELAY * (MAXTARGETS - 1));
slug@0
   797
    }
slug@0
   798
  }
slug@0
   799
  memmove(&targets[1], &targets[0], MAXTARGETS - 1);
slug@0
   800
  targets[0] = hash;
slug@0
   801
  return 0;
slug@0
   802
}
slug@0
   803
slug@0
   804
/** Allows a channel operator to avoid target change checks when
slug@0
   805
 * sending messages to users on their channel.
slug@0
   806
 * @param[in] source User sending the message.
slug@0
   807
 * @param[in] nick Destination of the message.
slug@0
   808
 * @param[in] channel Name of channel being sent to.
slug@0
   809
 * @param[in] text Message to send.
slug@0
   810
 * @param[in] is_notice If non-zero, use CNOTICE instead of CPRIVMSG.
slug@0
   811
 */
slug@0
   812
/* Added 971023 by Run. */
slug@0
   813
int whisper(struct Client* source, const char* nick, const char* channel,
slug@0
   814
            const char* text, int is_notice)
slug@0
   815
{
slug@0
   816
  struct Client*     dest;
slug@0
   817
  struct Channel*    chptr;
slug@0
   818
  struct Membership* membership;
slug@0
   819
slug@0
   820
  assert(0 != source);
slug@0
   821
  assert(0 != nick);
slug@0
   822
  assert(0 != channel);
slug@0
   823
  assert(MyUser(source));
slug@0
   824
slug@0
   825
  if (!(dest = FindUser(nick))) {
slug@0
   826
    return send_reply(source, ERR_NOSUCHNICK, nick);
slug@0
   827
  }
slug@0
   828
  if (!(chptr = FindChannel(channel))) {
slug@0
   829
    return send_reply(source, ERR_NOSUCHCHANNEL, channel);
slug@0
   830
  }
slug@0
   831
  /*
slug@0
   832
   * compare both users channel lists, instead of the channels user list
slug@0
   833
   * since the link is the same, this should be a little faster for channels
slug@0
   834
   * with a lot of users
slug@0
   835
   */
slug@0
   836
  for (membership = cli_user(source)->channel; membership; membership = membership->next_channel) {
slug@0
   837
    if (chptr == membership->channel)
slug@0
   838
      break;
slug@0
   839
  }
slug@0
   840
  if (0 == membership) {
slug@0
   841
    return send_reply(source, ERR_NOTONCHANNEL, chptr->chname);
slug@0
   842
  }
slug@0
   843
  if (!IsVoicedOrOpped(membership)) {
slug@0
   844
    return send_reply(source, ERR_VOICENEEDED, chptr->chname);
slug@0
   845
  }
slug@0
   846
  /*
slug@0
   847
   * lookup channel in destination
slug@0
   848
   */
slug@0
   849
  assert(0 != cli_user(dest));
slug@0
   850
  for (membership = cli_user(dest)->channel; membership; membership = membership->next_channel) {
slug@0
   851
    if (chptr == membership->channel)
slug@0
   852
      break;
slug@0
   853
  }
slug@0
   854
  if (0 == membership || IsZombie(membership)) {
slug@0
   855
    return send_reply(source, ERR_USERNOTINCHANNEL, cli_name(dest), chptr->chname);
slug@0
   856
  }
slug@0
   857
  if (is_silenced(source, dest))
slug@0
   858
    return 0;
paul@103
   859
paul@103
   860
  if (IsAccountOnly(dest) && !IsAccount(source) && !IsOper(source)) {
paul@103
   861
    if(!is_notice)
paul@103
   862
      send_reply(source, ERR_ACCOUNTONLY, cli_name(source), feature_str(FEAT_URLREG));
paul@103
   863
    return 0;
paul@103
   864
  }
slug@0
   865
          
slug@0
   866
  if (is_notice)
slug@0
   867
    sendcmdto_one(source, CMD_NOTICE, dest, "%C :%s", dest, text);
slug@0
   868
  else
paul@121
   869
  {
paul@121
   870
    if (cli_user(dest)->away)
paul@121
   871
      send_reply(source, RPL_AWAY, cli_name(dest), cli_user(dest)->away);
slug@0
   872
    sendcmdto_one(source, CMD_PRIVATE, dest, "%C :%s", dest, text);
paul@121
   873
  }
slug@0
   874
  return 0;
slug@0
   875
}
slug@0
   876
slug@0
   877
slug@0
   878
/** Send a user mode change for \a cptr to neighboring servers.
slug@0
   879
 * @param[in] cptr User whose mode is changing.
slug@0
   880
 * @param[in] sptr Client who sent us the mode change message.
slug@0
   881
 * @param[in] old Prior set of user flags.
slug@0
   882
 * @param[in] prop If non-zero, also include FLAG_OPER.
slug@0
   883
 */
slug@0
   884
void send_umode_out(struct Client *cptr, struct Client *sptr,
slug@0
   885
                    struct Flags *old, int prop)
slug@0
   886
{
slug@0
   887
  int i;
slug@0
   888
  struct Client *acptr;
slug@0
   889
paul@80
   890
  send_umode(NULL, sptr, old, prop ? SEND_UMODES : SEND_UMODES_BUT_OPER, 0);
slug@0
   891
slug@0
   892
  for (i = HighestFd; i >= 0; i--)
slug@0
   893
  {
slug@0
   894
    if ((acptr = LocalClientArray[i]) && IsServer(acptr) &&
paul@80
   895
        (acptr != cptr) && (acptr != sptr) && !IsSendOperName(acptr) && *umodeBuf)
paul@80
   896
        sendcmdto_one(sptr, CMD_MODE, acptr, "%s %s", cli_name(sptr), umodeBuf);
slug@0
   897
  }
paul@80
   898
paul@80
   899
  send_umode(NULL, sptr, old, prop ? SEND_UMODES : SEND_UMODES_BUT_OPER, 1);
paul@80
   900
paul@80
   901
  for (i = HighestFd; i >= 0; i--)
paul@80
   902
  {
paul@80
   903
    if ((acptr = LocalClientArray[i]) && IsServer(acptr) &&
paul@80
   904
        (acptr != cptr) && (acptr != sptr) && IsSendOperName(acptr) && *umodeBuf)
paul@80
   905
        sendcmdto_one(sptr, CMD_MODE, acptr, "%s %s", cli_name(sptr), umodeBuf);
paul@80
   906
  }
paul@80
   907
slug@0
   908
  if (cptr && MyUser(cptr))
paul@80
   909
    send_umode(cptr, sptr, old, ALL_UMODES, 0);
slug@0
   910
}
slug@0
   911
slug@0
   912
slug@0
   913
/** Call \a fmt for each Client named in \a names.
slug@0
   914
 * @param[in] sptr Client requesting information.
slug@0
   915
 * @param[in] names Space-delimited list of nicknames.
slug@0
   916
 * @param[in] rpl Base reply string for messages.
slug@0
   917
 * @param[in] fmt Formatting callback function.
slug@0
   918
 */
slug@0
   919
void send_user_info(struct Client* sptr, char* names, int rpl, InfoFormatter fmt)
slug@0
   920
{
slug@0
   921
  char*          name;
slug@0
   922
  char*          p = 0;
slug@0
   923
  int            arg_count = 0;
slug@0
   924
  int            users_found = 0;
slug@0
   925
  struct Client* acptr;
slug@0
   926
  struct MsgBuf* mb;
slug@0
   927
slug@0
   928
  assert(0 != sptr);
slug@0
   929
  assert(0 != names);
slug@0
   930
  assert(0 != fmt);
slug@0
   931
slug@0
   932
  mb = msgq_make(sptr, rpl_str(rpl), cli_name(&me), cli_name(sptr));
slug@0
   933
slug@0
   934
  for (name = ircd_strtok(&p, names, " "); name; name = ircd_strtok(&p, 0, " ")) {
slug@0
   935
    if ((acptr = FindUser(name))) {
slug@0
   936
      if (users_found++)
slug@0
   937
	msgq_append(0, mb, " ");
slug@0
   938
      (*fmt)(acptr, sptr, mb);
slug@0
   939
    }
slug@0
   940
    if (5 == ++arg_count)
slug@0
   941
      break;
slug@0
   942
  }
slug@0
   943
  send_buffer(sptr, mb, 0);
slug@0
   944
  msgq_clean(mb);
slug@0
   945
}
slug@0
   946
slug@0
   947
/** Set \a flag on \a cptr and possibly hide the client's hostmask.
slug@0
   948
 * @param[in,out] cptr User who is getting a new flag.
slug@0
   949
 * @param[in] flag Some flag that affects host-hiding (FLAG_HIDDENHOST, FLAG_ACCOUNT).
slug@0
   950
 * @return Zero.
slug@0
   951
 */
slug@0
   952
int
slug@0
   953
hide_hostmask(struct Client *cptr, unsigned int flag)
slug@0
   954
{
slug@0
   955
  struct Membership *chan;
slug@0
   956
slug@0
   957
  switch (flag) {
slug@0
   958
  case FLAG_HIDDENHOST:
slug@0
   959
    /* Local users cannot set +x unless FEAT_HOST_HIDING is true. */
slug@0
   960
    if (MyConnect(cptr) && !feature_bool(FEAT_HOST_HIDING))
slug@0
   961
      return 0;
slug@0
   962
    break;
slug@0
   963
  case FLAG_ACCOUNT:
slug@0
   964
    /* Invalidate all bans against the user so we check them again */
slug@0
   965
    for (chan = (cli_user(cptr))->channel; chan;
slug@0
   966
         chan = chan->next_channel)
slug@0
   967
      ClearBanValid(chan);
slug@0
   968
    break;
slug@0
   969
  default:
slug@0
   970
    return 0;
slug@0
   971
  }
slug@0
   972
slug@0
   973
  SetFlag(cptr, flag);
paul@50
   974
  if (!HasFlag(cptr, FLAG_HIDDENHOST) || !HasFlag(cptr, FLAG_ACCOUNT) || HasSetHost(cptr))
slug@0
   975
    return 0;
slug@0
   976
slug@0
   977
  sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Registered");
slug@0
   978
  ircd_snprintf(0, cli_user(cptr)->host, HOSTLEN, "%s.%s",
slug@0
   979
                cli_user(cptr)->account, feature_str(FEAT_HIDDEN_HOST));
slug@0
   980
slug@0
   981
  /* ok, the client is now fully hidden, so let them know -- hikari */
slug@0
   982
  if (MyConnect(cptr))
slug@0
   983
   send_reply(cptr, RPL_HOSTHIDDEN, cli_user(cptr)->host);
slug@0
   984
slug@0
   985
  /*
slug@0
   986
   * Go through all channels the client was on, rejoin him
slug@0
   987
   * and set the modes, if any
slug@0
   988
   */
slug@0
   989
  for (chan = cli_user(cptr)->channel; chan; chan = chan->next_channel)
slug@0
   990
  {
slug@0
   991
    if (IsZombie(chan))
slug@0
   992
      continue;
slug@0
   993
    /* Send a JOIN unless the user's join has been delayed. */
slug@0
   994
    if (!IsDelayedJoin(chan))
slug@0
   995
      sendcmdto_channel_butserv_butone(cptr, CMD_JOIN, chan->channel, cptr, 0,
slug@0
   996
                                         "%H", chan->channel);
slug@0
   997
    if (IsChanOp(chan) && HasVoice(chan))
slug@0
   998
      sendcmdto_channel_butserv_butone(&his, CMD_MODE, chan->channel, cptr, 0,
slug@0
   999
                                       "%H +ov %C %C", chan->channel, cptr,
slug@0
  1000
                                       cptr);
slug@0
  1001
    else if (IsChanOp(chan) || HasVoice(chan))
slug@0
  1002
      sendcmdto_channel_butserv_butone(&his, CMD_MODE, chan->channel, cptr, 0,
slug@0
  1003
        "%H +%c %C", chan->channel, IsChanOp(chan) ? 'o' : 'v', cptr);
slug@0
  1004
  }
slug@0
  1005
  return 0;
slug@0
  1006
}
slug@0
  1007
paul@30
  1008
/*
paul@30
  1009
 * set_hostmask() - derived from hide_hostmask()
paul@30
  1010
 *
paul@30
  1011
 */
paul@30
  1012
int set_hostmask(struct Client *cptr, char *hostmask, char *password)
paul@30
  1013
{
paul@30
  1014
  int restore = 0;
paul@30
  1015
  int freeform = 0;
paul@30
  1016
  char *host, *new_vhost, *vhost_pass;
paul@30
  1017
  char hiddenhost[USERLEN + HOSTLEN + 2];
paul@30
  1018
  struct Membership *chan;
paul@30
  1019
paul@30
  1020
  Debug((DEBUG_INFO, "set_hostmask() %C, %s, %s", cptr, hostmask, password));
paul@30
  1021
paul@30
  1022
  /* sethost enabled? */
paul@30
  1023
  if (MyConnect(cptr) && !feature_bool(FEAT_SETHOST)) {
paul@30
  1024
    send_reply(cptr, ERR_DISABLED, "SETHOST");
paul@30
  1025
    return 0;
paul@30
  1026
  }
paul@30
  1027
paul@30
  1028
  /* sethost enabled for users? */
paul@30
  1029
  if (MyConnect(cptr) && !IsAnOper(cptr) && !feature_bool(FEAT_SETHOST_USER)) {
paul@30
  1030
    send_reply(cptr, ERR_NOPRIVILEGES);
paul@30
  1031
    return 0;
paul@30
  1032
  }
paul@30
  1033
 
paul@30
  1034
  /* MODE_DEL: restore original hostmask */
paul@30
  1035
  if (EmptyString(hostmask)) {
paul@138
  1036
    /* is already sethost'ed? and only opers can remove a sethost */
paul@138
  1037
    if (IsSetHost(cptr) && IsAnOper(cptr)) {
paul@30
  1038
      restore = 1;
paul@30
  1039
      sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Host change");
paul@30
  1040
      /* If they are +rx, we need to return to their +x host, not their "real" host */
paul@30
  1041
      if (HasHiddenHost(cptr))
paul@30
  1042
        ircd_snprintf(0, cli_user(cptr)->host, HOSTLEN, "%s.%s",
paul@30
  1043
          cli_user(cptr)->account, feature_str(FEAT_HIDDEN_HOST));
paul@30
  1044
      else
paul@30
  1045
        strncpy(cli_user(cptr)->host, cli_user(cptr)->realhost, HOSTLEN);
paul@30
  1046
      strncpy(cli_user(cptr)->username, cli_user(cptr)->realusername, USERLEN);
paul@30
  1047
      /* log it */
paul@30
  1048
      if (MyConnect(cptr))
paul@30
  1049
        log_write(LS_SETHOST, L_INFO, LOG_NOSNOTICE,
paul@30
  1050
            "SETHOST (%s@%s) by (%#R): restoring real hostmask",
paul@30
  1051
            cli_user(cptr)->username, cli_user(cptr)->host, cptr);
paul@30
  1052
    } else
paul@30
  1053
      return 0;
paul@30
  1054
  /* MODE_ADD: set a new hostmask */
paul@30
  1055
  } else {
paul@30
  1056
    /* chop up ident and host.cc */
paul@103
  1057
    if ((host = strrchr(hostmask, '@'))) { /* oper can specifiy ident@host.cc */
paul@30
  1058
      *host++ = '\0';
paul@103
  1059
      if ( MyConnect(cptr) && (0 == strcmp(host, cli_user(cptr)->host)) && (0 == strcmp(hostmask, cli_user(cptr)->username))) {
paul@103
  1060
        ircd_snprintf(0, hiddenhost, HOSTLEN + USERLEN + 2, "%s@%s",
paul@103
  1061
            cli_user(cptr)->username, cli_user(cptr)->host);
paul@103
  1062
        send_reply(cptr, RPL_HOSTHIDDEN, hiddenhost);
paul@103
  1063
        return 0;
paul@103
  1064
      }
paul@103
  1065
    } else { /* user can only specifiy host.cc [password] */
paul@30
  1066
      host = hostmask;
paul@103
  1067
      if ( MyConnect(cptr) && (0 == strcmp(host, cli_user(cptr)->host))) {
paul@103
  1068
        ircd_snprintf(0, hiddenhost, HOSTLEN + USERLEN + 2, "%s@%s",
paul@103
  1069
            cli_user(cptr)->username, cli_user(cptr)->host);
paul@103
  1070
        send_reply(cptr, RPL_HOSTHIDDEN, hiddenhost);
paul@103
  1071
        return 0;
paul@103
  1072
      }
paul@103
  1073
    }
paul@30
  1074
    /*
paul@30
  1075
     * Oper sethost
paul@30
  1076
     */
paul@30
  1077
    if (MyConnect(cptr)) {
paul@30
  1078
      if (IsAnOper(cptr)) {
paul@30
  1079
        if ((new_vhost = IsVhost(host, 1)) == NULL) {
paul@52
  1080
          if (!HasPriv(cptr, PRIV_FREEFORM)) {
paul@30
  1081
            send_reply(cptr, ERR_HOSTUNAVAIL, hostmask);
paul@30
  1082
            log_write(LS_SETHOST, L_INFO, LOG_NOSNOTICE,
paul@30
  1083
                "SETHOST (%s@%s) by (%#R): no such s-line",
paul@30
  1084
                (host != hostmask) ? hostmask : cli_user(cptr)->username, host, cptr);
paul@30
  1085
            return 0;
paul@30
  1086
          } else /* freeform active, log and go */
paul@30
  1087
            freeform = 1;
paul@30
  1088
        }
paul@30
  1089
        sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Host change");
paul@30
  1090
        /* set the new ident and host */
paul@30
  1091
        if (host != hostmask) /* oper only specified host.cc */
paul@30
  1092
          strncpy(cli_user(cptr)->username, hostmask, USERLEN);
paul@30
  1093
        strncpy(cli_user(cptr)->host, host, HOSTLEN);
paul@30
  1094
        /* log it */
paul@30
  1095
        log_write(LS_SETHOST, (freeform) ? L_NOTICE : L_INFO,
paul@30
  1096
            (freeform) ? 0 : LOG_NOSNOTICE, "SETHOST (%s@%s) by (%#R)%s",
paul@30
  1097
            cli_user(cptr)->username, cli_user(cptr)->host, cptr,
paul@30
  1098
            (freeform) ? ": using freeform" : "");
paul@30
  1099
      /*
paul@30
  1100
       * plain user sethost, handled here
paul@30
  1101
       */
paul@30
  1102
      } else {
paul@30
  1103
        /* empty password? */
paul@30
  1104
        if (EmptyString(password)) {
paul@30
  1105
          send_reply(cptr, ERR_NEEDMOREPARAMS, "MODE");
paul@30
  1106
          return 0;
paul@30
  1107
        }
paul@30
  1108
        /* no such s-line */
paul@30
  1109
        if ((new_vhost = IsVhost(host, 0)) == NULL) {
paul@30
  1110
          send_reply(cptr, ERR_HOSTUNAVAIL, hostmask);
paul@30
  1111
          log_write(LS_SETHOST, L_INFO, LOG_NOSNOTICE, "SETHOST (%s@%s %s) by (%#R): no such s-line",
paul@30
  1112
              cli_user(cptr)->username, host, password, cptr);
paul@30
  1113
          return 0;
paul@30
  1114
        }
paul@30
  1115
        /* no password */
paul@30
  1116
        if ((vhost_pass = IsVhostPass(new_vhost)) == NULL) {
paul@30
  1117
          send_reply(cptr, ERR_PASSWDMISMATCH);
paul@30
  1118
          log_write(LS_SETHOST, L_INFO, 0, "SETHOST (%s@%s %s) by (%#R): trying to use an oper s-line",
paul@30
  1119
              cli_user(cptr)->username, host, password, cptr);
paul@30
  1120
          return 0;
paul@30
  1121
        }
paul@30
  1122
        /* incorrect password */
paul@30
  1123
        if (strCasediff(vhost_pass, password)) {
paul@30
  1124
          send_reply(cptr, ERR_PASSWDMISMATCH);
paul@30
  1125
          log_write(LS_SETHOST, L_NOTICE, 0, "SETHOST (%s@%s %s) by (%#R): incorrect password",
paul@30
  1126
              cli_user(cptr)->username, host, password, cptr);
paul@30
  1127
          return 0;
paul@30
  1128
        }
paul@30
  1129
        sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Host change");
paul@30
  1130
        /* set the new host */
paul@30
  1131
        strncpy(cli_user(cptr)->host, new_vhost, HOSTLEN);
paul@30
  1132
        /* log it */
paul@30
  1133
        log_write(LS_SETHOST, L_INFO, LOG_NOSNOTICE, "SETHOST (%s@%s) by (%#R)",
paul@30
  1134
            cli_user(cptr)->username, cli_user(cptr)->host, cptr);
paul@30
  1135
      }
paul@30
  1136
    } else { /* remote user */
paul@30
  1137
        sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Host change");
paul@30
  1138
        if (host != hostmask) /* oper only specified host.cc */
paul@30
  1139
          strncpy(cli_user(cptr)->username, hostmask, USERLEN);
paul@30
  1140
        strncpy(cli_user(cptr)->host, host, HOSTLEN);
paul@30
  1141
    }
paul@30
  1142
  }
paul@30
  1143
paul@30
  1144
  if (restore)
paul@30
  1145
    ClearSetHost(cptr);
paul@30
  1146
  else
paul@30
  1147
    SetSetHost(cptr);
paul@30
  1148
paul@30
  1149
  if (MyConnect(cptr)) {
paul@30
  1150
    ircd_snprintf(0, hiddenhost, HOSTLEN + USERLEN + 2, "%s@%s",
paul@30
  1151
      cli_user(cptr)->username, cli_user(cptr)->host);
paul@30
  1152
    send_reply(cptr, RPL_HOSTHIDDEN, hiddenhost);
paul@30
  1153
  }
paul@30
  1154
paul@30
  1155
#if 0
paul@30
  1156
  /* Code copied from hide_hostmask().  This is the old (pre-delayedjoin) 
paul@30
  1157
   * version.  Switch this in if you're not using the delayed join patch. */
paul@30
  1158
  /*
paul@30
  1159
   * Go through all channels the client was on, rejoin him
paul@30
  1160
   * and set the modes, if any
paul@30
  1161
   */
paul@30
  1162
  for (chan = cli_user(cptr)->channel; chan; chan = chan->next_channel) {
paul@30
  1163
    if (IsZombie(chan))
paul@30
  1164
      continue;
paul@30
  1165
    sendcmdto_channel_butserv_butone(cptr, CMD_JOIN, chan->channel, cptr,
paul@30
  1166
      "%H", chan->channel);
paul@30
  1167
    if (IsChanOp(chan) && HasVoice(chan)) {
paul@30
  1168
      sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan->channel, cptr,
paul@30
  1169
        "%H +ov %C %C", chan->channel, cptr, cptr);
paul@30
  1170
    } else if (IsChanOp(chan) || HasVoice(chan)) {
paul@30
  1171
      sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan->channel, cptr,
paul@30
  1172
        "%H +%c %C", chan->channel, IsChanOp(chan) ? 'o' : 'v', cptr);
paul@30
  1173
    }
paul@30
  1174
  }
paul@30
  1175
#endif
paul@30
  1176
paul@30
  1177
  /*
paul@30
  1178
   * Go through all channels the client was on, rejoin him
paul@30
  1179
   * and set the modes, if any
paul@30
  1180
   */
paul@30
  1181
  for (chan = cli_user(cptr)->channel; chan; chan = chan->next_channel) {
paul@30
  1182
    if (IsZombie(chan))
paul@30
  1183
      continue;
paul@30
  1184
    /* If this channel has delayed joins and the user has no modes, just set
paul@30
  1185
     * the delayed join flag rather than showing the join, even if the user
paul@30
  1186
     * was visible before */
paul@30
  1187
    if (!IsChanOp(chan) && !HasVoice(chan)
paul@30
  1188
        && (chan->channel->mode.mode & MODE_DELJOINS)) {
paul@30
  1189
      SetDelayedJoin(chan);
paul@30
  1190
    } else {
paul@30
  1191
      sendcmdto_channel_butserv_butone(cptr, CMD_JOIN, chan->channel, cptr, 0,
paul@30
  1192
        "%H", chan->channel);
paul@30
  1193
    }
paul@30
  1194
    if (IsChanOp(chan) && HasVoice(chan)) {
paul@75
  1195
      sendcmdto_channel_butserv_butone(&his, CMD_MODE, chan->channel, cptr, 0,
paul@30
  1196
        "%H +ov %C %C", chan->channel, cptr, cptr);
paul@30
  1197
    } else if (IsChanOp(chan) || HasVoice(chan)) {
paul@75
  1198
      sendcmdto_channel_butserv_butone(&his, CMD_MODE, chan->channel, cptr, 0,
paul@30
  1199
        "%H +%c %C", chan->channel, IsChanOp(chan) ? 'o' : 'v', cptr);
paul@30
  1200
    }
paul@30
  1201
  }
paul@30
  1202
  return 1;
paul@30
  1203
}
paul@30
  1204
slug@0
  1205
/** Set a user's mode.  This function checks that \a cptr is trying to
slug@0
  1206
 * set his own mode, prevents local users from setting inappropriate
slug@0
  1207
 * modes through this function, and applies any other side effects of
slug@0
  1208
 * a successful mode change.
slug@0
  1209
 *
slug@0
  1210
 * @param[in,out] cptr User setting someone's mode.
slug@0
  1211
 * @param[in] sptr Client who sent the mode change message.
slug@0
  1212
 * @param[in] parc Number of parameters in \a parv.
slug@0
  1213
 * @param[in] parv Parameters to MODE.
paul@115
  1214
 * @param[in] allow_modes ALLOWMODES_ANY for any mode, ALLOWMODES_DEFAULT for 
paul@115
  1215
 *                        only permitting legitimate default user modes.
slug@0
  1216
 * @return Zero.
slug@0
  1217
 */
paul@115
  1218
int set_user_mode(struct Client *cptr, struct Client *sptr, int parc, 
paul@115
  1219
		char *parv[], int allow_modes)
slug@0
  1220
{
slug@0
  1221
  char** p;
slug@0
  1222
  char*  m;
slug@0
  1223
  int what;
slug@0
  1224
  int i;
slug@0
  1225
  struct Flags setflags;
slug@0
  1226
  unsigned int tmpmask = 0;
slug@0
  1227
  int snomask_given = 0;
slug@0
  1228
  char buf[BUFSIZE];
paul@117
  1229
  char *hostmask, *password;
slug@0
  1230
  int prop = 0;
slug@0
  1231
  int do_host_hiding = 0;
paul@30
  1232
  int do_set_host = 0;
paul@80
  1233
  size_t opernamelen;
paul@116
  1234
  char *opername = 0;
paul@115
  1235
  char* account = NULL;
slug@0
  1236
paul@30
  1237
  hostmask = password = NULL;
slug@0
  1238
  what = MODE_ADD;
slug@0
  1239
slug@0
  1240
  if (parc < 3)
slug@0
  1241
  {
slug@0
  1242
    m = buf;
slug@0
  1243
    *m++ = '+';
slug@0
  1244
    for (i = 0; i < USERMODELIST_SIZE; i++)
slug@0
  1245
    {
slug@0
  1246
      if (HasFlag(sptr, userModeList[i].flag) &&
paul@30
  1247
          ((userModeList[i].flag != FLAG_ACCOUNT) &&
paul@30
  1248
          (userModeList[i].flag != FLAG_SETHOST)))
slug@0
  1249
        *m++ = userModeList[i].c;
slug@0
  1250
    }
slug@0
  1251
    *m = '\0';
slug@0
  1252
    send_reply(sptr, RPL_UMODEIS, buf);
slug@0
  1253
    if (HasFlag(sptr, FLAG_SERVNOTICE) && MyConnect(sptr)
slug@0
  1254
        && cli_snomask(sptr) !=
slug@0
  1255
        (unsigned int)(IsOper(sptr) ? SNO_OPERDEFAULT : SNO_DEFAULT))
slug@0
  1256
      send_reply(sptr, RPL_SNOMASK, cli_snomask(sptr), cli_snomask(sptr));
slug@0
  1257
    return 0;
slug@0
  1258
  }
slug@0
  1259
slug@0
  1260
  /*
slug@0
  1261
   * find flags already set for user
slug@0
  1262
   * why not just copy them?
slug@0
  1263
   */
slug@0
  1264
  setflags = cli_flags(sptr);
slug@0
  1265
slug@0
  1266
  if (MyConnect(sptr))
slug@0
  1267
    tmpmask = cli_snomask(sptr);
slug@0
  1268
slug@0
  1269
  /*
slug@0
  1270
   * parse mode change string(s)
slug@0
  1271
   */
paul@115
  1272
  for (p = &parv[2]; *p && p<&parv[parc]; p++) {       /* p is changed in loop too */
slug@0
  1273
    for (m = *p; *m; m++) {
slug@0
  1274
      switch (*m) {
slug@0
  1275
      case '+':
slug@0
  1276
        what = MODE_ADD;
slug@0
  1277
        break;
slug@0
  1278
      case '-':
slug@0
  1279
        what = MODE_DEL;
slug@0
  1280
        break;
slug@0
  1281
      case 's':
slug@0
  1282
        if (*(p + 1) && is_snomask(*(p + 1))) {
slug@0
  1283
          snomask_given = 1;
slug@0
  1284
          tmpmask = umode_make_snomask(tmpmask, *++p, what);
slug@0
  1285
          tmpmask &= (IsAnOper(sptr) ? SNO_ALL : SNO_USER);
slug@0
  1286
        }
slug@0
  1287
        else
slug@0
  1288
          tmpmask = (what == MODE_ADD) ?
slug@0
  1289
              (IsAnOper(sptr) ? SNO_OPERDEFAULT : SNO_DEFAULT) : 0;
slug@0
  1290
        if (tmpmask)
slug@0
  1291
	  SetServNotice(sptr);
slug@0
  1292
        else
slug@0
  1293
	  ClearServNotice(sptr);
slug@0
  1294
        break;
slug@0
  1295
      case 'w':
slug@0
  1296
        if (what == MODE_ADD)
slug@0
  1297
          SetWallops(sptr);
slug@0
  1298
        else
slug@0
  1299
          ClearWallops(sptr);
slug@0
  1300
        break;
slug@0
  1301
      case 'o':
paul@80
  1302
        if (what == MODE_ADD) {
slug@0
  1303
          SetOper(sptr);
paul@80
  1304
          if (IsServer(cptr) && IsSendOperName(cptr)) {
paul@80
  1305
            if (*(p + 1)) {
paul@80
  1306
              opername = *++p;
paul@80
  1307
              if (cli_user(sptr)->opername)
paul@80
  1308
                MyFree(cli_user(sptr)->opername);
paul@80
  1309
              if ((opername[0] == NOOPERNAMECHARACTER) && (opername[1] == '\0')) {
paul@80
  1310
                cli_user(sptr)->opername = NULL;
paul@80
  1311
              } else {
paul@80
  1312
                opernamelen = strlen(opername);
paul@80
  1313
                if (opernamelen > ACCOUNTLEN) {
paul@80
  1314
                  protocol_violation(cptr, "Received opername (%s) longer than %d for %s; ignoring.", opername, ACCOUNTLEN, cli_name(sptr));
paul@80
  1315
                  cli_user(sptr)->opername = NULL;
paul@80
  1316
                } else {
paul@80
  1317
                  cli_user(sptr)->opername = (char*) MyMalloc(opernamelen + 1);
paul@80
  1318
                  assert(0 != cli_user(sptr)->opername);
paul@80
  1319
                  ircd_strncpy(cli_user(sptr)->opername,opername,ACCOUNTLEN);
paul@80
  1320
                }
paul@80
  1321
              }
paul@80
  1322
            }
paul@80
  1323
          }
paul@80
  1324
        } else {
slug@0
  1325
          ClrFlag(sptr, FLAG_OPER);
slug@0
  1326
          ClrFlag(sptr, FLAG_LOCOP);
slug@0
  1327
          if (MyConnect(sptr))
slug@0
  1328
          {
slug@0
  1329
            tmpmask = cli_snomask(sptr) & ~SNO_OPER;
slug@0
  1330
            cli_handler(sptr) = CLIENT_HANDLER;
slug@0
  1331
          }
slug@0
  1332
        }
slug@0
  1333
        break;
slug@0
  1334
      case 'O':
slug@0
  1335
        if (what == MODE_ADD)
slug@0
  1336
          SetLocOp(sptr);
slug@0
  1337
        else
slug@0
  1338
        { 
slug@0
  1339
          ClrFlag(sptr, FLAG_OPER);
slug@0
  1340
          ClrFlag(sptr, FLAG_LOCOP);
slug@0
  1341
          if (MyConnect(sptr))
slug@0
  1342
          {
slug@0
  1343
            tmpmask = cli_snomask(sptr) & ~SNO_OPER;
slug@0
  1344
            cli_handler(sptr) = CLIENT_HANDLER;
slug@0
  1345
          }
slug@0
  1346
        }
slug@0
  1347
        break;
slug@0
  1348
      case 'i':
slug@0
  1349
        if (what == MODE_ADD)
slug@0
  1350
          SetInvisible(sptr);
slug@0
  1351
        else
paul@30
  1352
          if (!feature_bool(FEAT_AUTOINVISIBLE) || IsOper(sptr)) /* Don't allow non-opers to -i if FEAT_AUTOINVISIBLE is set */
paul@30
  1353
            ClearInvisible(sptr);
slug@0
  1354
        break;
slug@0
  1355
      case 'd':
slug@0
  1356
        if (what == MODE_ADD)
slug@0
  1357
          SetDeaf(sptr);
slug@0
  1358
        else
slug@0
  1359
          ClearDeaf(sptr);
slug@0
  1360
        break;
slug@0
  1361
      case 'k':
slug@0
  1362
        if (what == MODE_ADD)
slug@0
  1363
          SetChannelService(sptr);
slug@0
  1364
        else
slug@0
  1365
          ClearChannelService(sptr);
slug@0
  1366
        break;
paul@30
  1367
      case 'X':
paul@30
  1368
        if (what == MODE_ADD)
paul@30
  1369
          SetXtraOp(sptr);
paul@30
  1370
        else
paul@30
  1371
          ClearXtraOp(sptr);
paul@30
  1372
        break;
paul@30
  1373
      case 'n':
paul@30
  1374
        if (what == MODE_ADD)
paul@30
  1375
          SetNoChan(sptr);
paul@30
  1376
        else
paul@30
  1377
          ClearNoChan(sptr);
paul@30
  1378
        break;
paul@30
  1379
      case 'I':
paul@30
  1380
        if (what == MODE_ADD)
paul@30
  1381
          SetNoIdle(sptr);
paul@30
  1382
        else
paul@30
  1383
          ClearNoIdle(sptr);
paul@30
  1384
        break;
slug@0
  1385
      case 'g':
slug@0
  1386
        if (what == MODE_ADD)
slug@0
  1387
          SetDebug(sptr);
slug@0
  1388
        else
slug@0
  1389
          ClearDebug(sptr);
slug@0
  1390
        break;
slug@0
  1391
      case 'x':
slug@0
  1392
        if (what == MODE_ADD)
paul@30
  1393
          do_host_hiding = 1;
paul@30
  1394
         break;
paul@30
  1395
      case 'h':
paul@30
  1396
         if (what == MODE_ADD) {
paul@30
  1397
           if (*(p + 1) && is_hostmask(*(p + 1))) {
paul@30
  1398
             do_set_host = 1;
paul@30
  1399
             hostmask = *++p;
paul@30
  1400
             /* DON'T step p onto the trailing NULL in the parameter array! - splidge */
paul@30
  1401
             if (*(p+1))
paul@30
  1402
               password = *++p;
paul@30
  1403
             else
paul@30
  1404
               password = NULL;
paul@30
  1405
           } else {
paul@30
  1406
             if (!*(p+1))
paul@30
  1407
               send_reply(sptr, ERR_NEEDMOREPARAMS, "SETHOST");
paul@30
  1408
             else {
paul@30
  1409
               send_reply(sptr, ERR_BADHOSTMASK, *(p+1));
paul@30
  1410
               p++; /* Swallow the arg anyway */
paul@30
  1411
             }
paul@30
  1412
           }
paul@30
  1413
         } else { /* MODE_DEL */
paul@30
  1414
           do_set_host = 1;
paul@30
  1415
           hostmask = NULL;
paul@30
  1416
           password = NULL;
paul@30
  1417
         }
paul@30
  1418
         break;
paul@30
  1419
      case 'R':
paul@30
  1420
        if (what == MODE_ADD)
paul@30
  1421
          SetAccountOnly(sptr);
paul@30
  1422
        else
paul@30
  1423
          ClearAccountOnly(sptr);
paul@30
  1424
        break;
paul@30
  1425
      case 'P':
paul@30
  1426
	if (what == MODE_ADD)
paul@30
  1427
          SetParanoid(sptr);
paul@30
  1428
        else
paul@30
  1429
          ClearParanoid(sptr);
slug@0
  1430
	break;
paul@115
  1431
      case 'r':
chris@147
  1432
	if ((what == MODE_ADD) && *(p + 1)) {
paul@115
  1433
	  account = *(++p);
paul@115
  1434
	  SetAccount(sptr);
paul@115
  1435
	}
paul@115
  1436
	/* There is no -r */
paul@115
  1437
	break;
slug@0
  1438
      default:
slug@0
  1439
        send_reply(sptr, ERR_UMODEUNKNOWNFLAG, *m);
slug@0
  1440
        break;
slug@0
  1441
      }
slug@0
  1442
    }
slug@0
  1443
  }
slug@0
  1444
  /*
slug@0
  1445
   * Evaluate rules for new user mode
slug@0
  1446
   * Stop users making themselves operators too easily:
slug@0
  1447
   */
slug@0
  1448
  if (!IsServer(cptr))
slug@0
  1449
  {
slug@0
  1450
    if (!FlagHas(&setflags, FLAG_OPER) && IsOper(sptr))
slug@0
  1451
      ClearOper(sptr);
slug@0
  1452
    if (!FlagHas(&setflags, FLAG_LOCOP) && IsLocOp(sptr))
slug@0
  1453
      ClearLocOp(sptr);
paul@115
  1454
    if (!FlagHas(&setflags, FLAG_ACCOUNT) && IsAccount(sptr))
paul@115
  1455
      ClrFlag(sptr, FLAG_ACCOUNT);
slug@0
  1456
    /*
slug@0
  1457
     * new umode; servers can set it, local users cannot;
slug@0
  1458
     * prevents users from /kick'ing or /mode -o'ing
slug@0
  1459
     */
paul@52
  1460
    if (!FlagHas(&setflags, FLAG_CHSERV) && !(IsOper(sptr) && HasPriv(sptr, PRIV_CHANSERV)))
slug@0
  1461
      ClearChannelService(sptr);
paul@52
  1462
    if (!FlagHas(&setflags, FLAG_XTRAOP) && !(IsOper(sptr) && HasPriv(sptr, PRIV_XTRA_OPER)))
paul@30
  1463
      ClearXtraOp(sptr);
paul@30
  1464
    if (!FlagHas(&setflags, FLAG_NOCHAN) && !(IsOper(sptr) || feature_bool(FEAT_USER_HIDECHANS)))
paul@30
  1465
      ClearNoChan(sptr);
paul@52
  1466
    if (!FlagHas(&setflags, FLAG_NOIDLE) && !((IsOper(sptr) && HasPriv(sptr, PRIV_NOIDLE)) || feature_bool(FEAT_USER_HIDEIDLETIME)))
paul@30
  1467
      ClearNoIdle(sptr);
paul@52
  1468
    if (!FlagHas(&setflags, FLAG_PARANOID) && !(IsOper(sptr) && HasPriv(sptr, PRIV_PARANOID)))
paul@30
  1469
      ClearParanoid(sptr);
paul@30
  1470
slug@0
  1471
    /*
slug@0
  1472
     * only send wallops to opers
slug@0
  1473
     */
slug@0
  1474
    if (feature_bool(FEAT_WALLOPS_OPER_ONLY) && !IsAnOper(sptr) &&
slug@0
  1475
	!FlagHas(&setflags, FLAG_WALLOP))
slug@0
  1476
      ClearWallops(sptr);
slug@0
  1477
    if (feature_bool(FEAT_HIS_SNOTICES_OPER_ONLY) && MyConnect(sptr) &&
slug@0
  1478
        !IsAnOper(sptr) && !FlagHas(&setflags, FLAG_SERVNOTICE))
slug@0
  1479
    {
slug@0
  1480
      ClearServNotice(sptr);
slug@0
  1481
      set_snomask(sptr, 0, SNO_SET);
slug@0
  1482
    }
slug@0
  1483
    if (feature_bool(FEAT_HIS_DEBUG_OPER_ONLY) &&
slug@0
  1484
        !IsAnOper(sptr) && !FlagHas(&setflags, FLAG_DEBUG))
slug@0
  1485
      ClearDebug(sptr);
slug@0
  1486
  }
slug@0
  1487
  if (MyConnect(sptr))
slug@0
  1488
  {
slug@0
  1489
    if ((FlagHas(&setflags, FLAG_OPER) || FlagHas(&setflags, FLAG_LOCOP)) &&
slug@0
  1490
        !IsAnOper(sptr))
slug@0
  1491
      det_confs_butmask(sptr, CONF_CLIENT & ~CONF_OPERATOR);
slug@0
  1492
slug@0
  1493
    if (SendServNotice(sptr))
slug@0
  1494
    {
slug@0
  1495
      if (tmpmask != cli_snomask(sptr))
slug@0
  1496
	set_snomask(sptr, tmpmask, SNO_SET);
slug@0
  1497
      if (cli_snomask(sptr) && snomask_given)
slug@0
  1498
	send_reply(sptr, RPL_SNOMASK, cli_snomask(sptr), cli_snomask(sptr));
slug@0
  1499
    }
slug@0
  1500
    else
slug@0
  1501
      set_snomask(sptr, 0, SNO_SET);
slug@0
  1502
  }
slug@0
  1503
  /*
slug@0
  1504
   * Compare new flags with old flags and send string which
slug@0
  1505
   * will cause servers to update correctly.
slug@0
  1506
   */
paul@115
  1507
  if (!FlagHas(&setflags, FLAG_ACCOUNT) && IsAccount(sptr)) {
paul@115
  1508
      int len = ACCOUNTLEN;
paul@116
  1509
      char *pts, *ts;
paul@115
  1510
      if ((ts = strchr(account, ':'))) {
paul@115
  1511
	len = (ts++) - account;
paul@115
  1512
	cli_user(sptr)->acc_create = atoi(ts);
paul@116
  1513
        if ((pts = strchr(ts, ':')))
paul@117
  1514
	  cli_user(sptr)->acc_id = strtoul(pts + 1, NULL, 10);
paul@116
  1515
        Debug((DEBUG_DEBUG, "Received timestamped account in user mode; "
paul@116
  1516
	      "account \"%s\", timestamp %Tu, id %lu", account,
paul@116
  1517
	      cli_user(sptr)->acc_create,
paul@117
  1518
	      cli_user(sptr)->acc_id));
paul@115
  1519
      }
paul@115
  1520
      ircd_strncpy(cli_user(sptr)->account, account, len);
paul@115
  1521
  }
paul@115
  1522
  if (!FlagHas(&setflags, FLAG_HIDDENHOST) && do_host_hiding && allow_modes != ALLOWMODES_DEFAULT)
slug@0
  1523
    hide_hostmask(sptr, FLAG_HIDDENHOST);
paul@30
  1524
  if (do_set_host) {
paul@30
  1525
    /* We clear the flag in the old mask, so that the +h will be sent */
paul@30
  1526
    /* Only do this if we're SETTING +h and it succeeded */
paul@30
  1527
    if (set_hostmask(sptr, hostmask, password) && hostmask)
paul@30
  1528
      FlagClr(&setflags, FLAG_SETHOST);
paul@30
  1529
  }
paul@126
  1530
paul@126
  1531
  if (IsRegistered(sptr)) {
paul@126
  1532
    if (!FlagHas(&setflags, FLAG_OPER) && IsOper(sptr)) {
paul@126
  1533
      /* user now oper */
paul@126
  1534
      ++UserStats.opers;
paul@126
  1535
      client_set_privs(sptr, NULL); /* may set propagate privilege */
paul@126
  1536
    }
paul@126
  1537
    /* remember propagate privilege setting */
paul@126
  1538
    if (HasPriv(sptr, PRIV_PROPAGATE)) {
paul@126
  1539
      prop = 1;
paul@126
  1540
    }
paul@126
  1541
    if (FlagHas(&setflags, FLAG_OPER) && !IsOper(sptr)) {
paul@126
  1542
      /* user no longer oper */
paul@126
  1543
      assert(UserStats.opers > 0);
paul@126
  1544
      --UserStats.opers;
paul@126
  1545
      client_set_privs(sptr, NULL); /* will clear propagate privilege */
paul@127
  1546
      if (cli_user(sptr)->opername) {
paul@127
  1547
        MyFree(cli_user(sptr)->opername);
paul@127
  1548
        cli_user(sptr)->opername = NULL;
paul@127
  1549
      }
paul@126
  1550
    }
paul@126
  1551
    if (FlagHas(&setflags, FLAG_INVISIBLE) && !IsInvisible(sptr)) {
paul@126
  1552
      assert(UserStats.inv_clients > 0);
paul@126
  1553
      --UserStats.inv_clients;
paul@126
  1554
    }
paul@126
  1555
    if (!FlagHas(&setflags, FLAG_INVISIBLE) && IsInvisible(sptr)) {
paul@126
  1556
      ++UserStats.inv_clients;
paul@126
  1557
    }
paul@126
  1558
    assert(UserStats.opers <= UserStats.clients + UserStats.unknowns);
paul@126
  1559
    assert(UserStats.inv_clients <= UserStats.clients + UserStats.unknowns);
paul@101
  1560
    send_umode_out(cptr, sptr, &setflags, prop);
paul@126
  1561
  }
slug@0
  1562
slug@0
  1563
  return 0;
slug@0
  1564
}
slug@0
  1565
slug@0
  1566
/** Build a mode string to describe modes for \a cptr.
slug@0
  1567
 * @param[in] cptr Some user.
slug@0
  1568
 * @return Pointer to a static buffer.
slug@0
  1569
 */
paul@80
  1570
char *umode_str(struct Client *cptr, int opernames)
slug@0
  1571
{
slug@0
  1572
  /* Maximum string size: "owidgrx\0" */
slug@0
  1573
  char *m = umodeBuf;
slug@0
  1574
  int i;
slug@0
  1575
  struct Flags c_flags = cli_flags(cptr);
slug@0
  1576
slug@0
  1577
  if (!HasPriv(cptr, PRIV_PROPAGATE))
slug@0
  1578
    FlagClr(&c_flags, FLAG_OPER);
slug@0
  1579
slug@0
  1580
  for (i = 0; i < USERMODELIST_SIZE; ++i)
slug@0
  1581
  {
slug@0
  1582
    if (FlagHas(&c_flags, userModeList[i].flag) &&
slug@0
  1583
        userModeList[i].flag >= FLAG_GLOBAL_UMODES)
slug@0
  1584
      *m++ = userModeList[i].c;
slug@0
  1585
  }
slug@0
  1586
paul@80
  1587
  if (opernames && IsOper(cptr))
paul@80
  1588
  {
paul@80
  1589
    *m++ = ' ';
paul@80
  1590
    if (cli_user(cptr)->opername) {
paul@80
  1591
      char* t = cli_user(cptr)->opername;
paul@80
  1592
      while ((*m++ = *t++))
paul@80
  1593
        ; /* Empty loop */
paul@81
  1594
      m--; /* Step back over the '\0' */
paul@80
  1595
    } else {
paul@80
  1596
      *m++ = NOOPERNAMECHARACTER;
paul@80
  1597
    }
paul@80
  1598
  }
paul@80
  1599
slug@0
  1600
  if (IsAccount(cptr))
slug@0
  1601
  {
slug@0
  1602
    char* t = cli_user(cptr)->account;
slug@0
  1603
slug@0
  1604
    *m++ = ' ';
slug@0
  1605
    while ((*m++ = *t++))
slug@0
  1606
      ; /* Empty loop */
slug@0
  1607
slug@0
  1608
    if (cli_user(cptr)->acc_create) {
cruicky@83
  1609
      char nbuf[30];
slug@0
  1610
      Debug((DEBUG_DEBUG, "Sending timestamped account in user mode for "
slug@0
  1611
	     "account \"%s\"; timestamp %Tu", cli_user(cptr)->account,
slug@0
  1612
	     cli_user(cptr)->acc_create));
paul@38
  1613
      if(cli_user(cptr)->acc_id) {
paul@38
  1614
        ircd_snprintf(0, t = nbuf, sizeof(nbuf), ":%Tu:%lu",
paul@38
  1615
                      cli_user(cptr)->acc_create, cli_user(cptr)->acc_id);
paul@38
  1616
      } else {
paul@38
  1617
        ircd_snprintf(0, t = nbuf, sizeof(nbuf), ":%Tu",
paul@38
  1618
                      cli_user(cptr)->acc_create);
paul@38
  1619
      }
slug@0
  1620
      m--; /* back up over previous nul-termination */
slug@0
  1621
      while ((*m++ = *t++))
slug@0
  1622
	; /* Empty loop */
slug@0
  1623
    }
paul@30
  1624
    m--; /* Step back over the '\0' */
slug@0
  1625
  }
slug@0
  1626
paul@30
  1627
  if (IsSetHost(cptr)) {
paul@30
  1628
    *m++ = ' ';
paul@30
  1629
    ircd_snprintf(0, m, USERLEN + HOSTLEN + 2, "%s@%s", cli_user(cptr)->username,
paul@30
  1630
         cli_user(cptr)->host);
paul@30
  1631
  } else
paul@30
  1632
    *m = '\0';
slug@0
  1633
  return umodeBuf;                /* Note: static buffer, gets
slug@0
  1634
                                   overwritten by send_umode() */
slug@0
  1635
}
slug@0
  1636
slug@0
  1637
/** Send a mode change string for \a sptr to \a cptr.
slug@0
  1638
 * @param[in] cptr Destination of mode change message.
slug@0
  1639
 * @param[in] sptr User whose mode has changed.
slug@0
  1640
 * @param[in] old Pre-change set of modes for \a sptr.
slug@0
  1641
 * @param[in] sendset One of ALL_UMODES, SEND_UMODES_BUT_OPER,
slug@0
  1642
 * SEND_UMODES, to select which changed user modes to send.
slug@0
  1643
 */
slug@0
  1644
void send_umode(struct Client *cptr, struct Client *sptr, struct Flags *old,
paul@80
  1645
                int sendset, int opernames)
slug@0
  1646
{
slug@0
  1647
  int i;
slug@0
  1648
  int flag;
paul@30
  1649
  int needhost = 0;
paul@80
  1650
  int needoper = 0;
slug@0
  1651
  char *m;
slug@0
  1652
  int what = MODE_NULL;
slug@0
  1653
slug@0
  1654
  /*
slug@0
  1655
   * Build a string in umodeBuf to represent the change in the user's
slug@0
  1656
   * mode between the new (cli_flags(sptr)) and 'old', but skipping
slug@0
  1657
   * the modes indicated by sendset.
slug@0
  1658
   */
slug@0
  1659
  m = umodeBuf;
slug@0
  1660
  *m = '\0';
slug@0
  1661
  for (i = 0; i < USERMODELIST_SIZE; ++i)
slug@0
  1662
  {
slug@0
  1663
    flag = userModeList[i].flag;
slug@0
  1664
    if (FlagHas(old, flag)
slug@0
  1665
        == HasFlag(sptr, flag))
slug@0
  1666
      continue;
slug@0
  1667
    switch (sendset)
slug@0
  1668
    {
slug@0
  1669
    case ALL_UMODES:
slug@0
  1670
      break;
slug@0
  1671
    case SEND_UMODES_BUT_OPER:
slug@0
  1672
      if (flag == FLAG_OPER)
slug@0
  1673
        continue;
slug@0
  1674
      /* and fall through */
slug@0
  1675
    case SEND_UMODES:
slug@0
  1676
      if (flag < FLAG_GLOBAL_UMODES)
slug@0
  1677
        continue;
slug@0
  1678
      break;      
slug@0
  1679
    }
paul@80
  1680
    /* Special case for OPER.. */
paul@80
  1681
    if (flag == FLAG_OPER) {
paul@80
  1682
      /* If we're setting +o, add the opername later */
paul@80
  1683
      if (!FlagHas(old, flag))
paul@80
  1684
      	needoper++;
paul@80
  1685
    }
paul@30
  1686
    /* Special case for SETHOST.. */
paul@30
  1687
    if (flag == FLAG_SETHOST) {
paul@30
  1688
      /* Don't send to users */
paul@30
  1689
      if (cptr && MyUser(cptr))
paul@30
  1690
      	continue;
paul@30
  1691
      
paul@30
  1692
      /* If we're setting +h, add the parameter later */
paul@30
  1693
      if (!FlagHas(old, flag))	
paul@30
  1694
      	needhost++;    
paul@30
  1695
    }
slug@0
  1696
    if (FlagHas(old, flag))
slug@0
  1697
    {
slug@0
  1698
      if (what == MODE_DEL)
slug@0
  1699
        *m++ = userModeList[i].c;
slug@0
  1700
      else
slug@0
  1701
      {
slug@0
  1702
        what = MODE_DEL;
slug@0
  1703
        *m++ = '-';
slug@0
  1704
        *m++ = userModeList[i].c;
slug@0
  1705
      }
slug@0
  1706
    }
slug@0
  1707
    else /* !FlagHas(old, flag) */
slug@0
  1708
    {
slug@0
  1709
      if (what == MODE_ADD)
slug@0
  1710
        *m++ = userModeList[i].c;
slug@0
  1711
      else
slug@0
  1712
      {
slug@0
  1713
        what = MODE_ADD;
slug@0
  1714
        *m++ = '+';
slug@0
  1715
        *m++ = userModeList[i].c;
slug@0
  1716
      }
slug@0
  1717
    }
slug@0
  1718
  }
paul@80
  1719
  if (opernames && needoper) {
paul@80
  1720
    *m++ = ' ';
paul@80
  1721
    if (cli_user(sptr)->opername) {
paul@80
  1722
      char* t = cli_user(sptr)->opername;
paul@80
  1723
      while ((*m++ = *t++))
paul@80
  1724
        ; /* Empty loop */
cruicky@84
  1725
      m--; /* Step back over the '\0' */
paul@80
  1726
    } else {
paul@80
  1727
      *m++ = NOOPERNAMECHARACTER;
paul@80
  1728
    }
paul@80
  1729
  }
paul@30
  1730
  if (needhost) {
paul@30
  1731
    *m++ = ' ';
paul@30
  1732
    ircd_snprintf(0, m, USERLEN + HOSTLEN + 1, "%s@%s", cli_user(sptr)->username,
paul@30
  1733
         cli_user(sptr)->host);
paul@30
  1734
  } else
paul@30
  1735
    *m = '\0';
slug@0
  1736
  if (*umodeBuf && cptr)
paul@30
  1737
    sendcmdto_one(sptr, CMD_MODE, cptr, "%s %s", cli_name(sptr), umodeBuf);
slug@0
  1738
}
slug@0
  1739
slug@0
  1740
/**
slug@0
  1741
 * Check to see if this resembles a sno_mask.  It is if 1) there is
slug@0
  1742
 * at least one digit and 2) The first digit occurs before the first
slug@0
  1743
 * alphabetic character.
slug@0
  1744
 * @param[in] word Word to check for sno_mask-ness.
slug@0
  1745
 * @return Non-zero if \a word looks like a server notice mask; zero if not.
slug@0
  1746
 */
slug@0
  1747
int is_snomask(char *word)
slug@0
  1748
{
slug@0
  1749
  if (word)
slug@0
  1750
  {
slug@0
  1751
    for (; *word; word++)
slug@0
  1752
      if (IsDigit(*word))
slug@0
  1753
        return 1;
slug@0
  1754
      else if (IsAlpha(*word))
slug@0
  1755
        return 0;
slug@0
  1756
  }
slug@0
  1757
  return 0;
slug@0
  1758
}
slug@0
  1759
paul@30
  1760
 /*
paul@30
  1761
  * Check to see if it resembles a valid hostmask.
paul@30
  1762
  */
paul@30
  1763
int is_hostmask(char *word)
paul@30
  1764
{
paul@30
  1765
  int i = 0;
paul@30
  1766
  char *host;
paul@30
  1767
paul@30
  1768
  Debug((DEBUG_INFO, "is_hostmask() %s", word));
paul@30
  1769
paul@30
  1770
  if (strlen(word) > (HOSTLEN + USERLEN + 1) || strlen(word) <= 0)
paul@30
  1771
    return 0;
paul@30
  1772
paul@30
  1773
  /* if a host is specified, make sure it's valid */
paul@30
  1774
  host = strrchr(word, '@');
paul@30
  1775
  if (host) {
paul@30
  1776
     if (strlen(++host) < 1)
paul@30
  1777
       return 0;
paul@30
  1778
     if (strlen(host) > HOSTLEN)
paul@30
  1779
       return 0;
paul@30
  1780
  }
paul@30
  1781
paul@30
  1782
  if (word) {
paul@30
  1783
    if ('@' == *word)	/* no leading @'s */
paul@30
  1784
        return 0;
paul@30
  1785
paul@30
  1786
    if ('#' == *word) {	/* numeric index given? */
paul@30
  1787
      for (word++; *word; word++) {
paul@30
  1788
        if (!IsDigit(*word))
paul@30
  1789
          return 0;
paul@30
  1790
      }
paul@30
  1791
      return 1;
paul@30
  1792
    }
paul@30
  1793
paul@30
  1794
    /* normal hostmask, account for at most one '@' */
paul@30
  1795
    for (; *word; word++) {
paul@30
  1796
      if ('@' == *word) {
paul@30
  1797
        i++;
paul@30
  1798
        continue;
paul@30
  1799
      }
paul@30
  1800
      if (!IsHostChar(*word))
paul@30
  1801
        return 0;
paul@30
  1802
    }
paul@30
  1803
    return (1 < i) ? 0 : 1; /* no more than on '@' */
paul@30
  1804
  }
paul@30
  1805
  return 0;
paul@30
  1806
}
paul@30
  1807
paul@30
  1808
 /*
paul@30
  1809
  * IsVhost() - Check if given host is a valid spoofhost
paul@30
  1810
  * (ie: configured thru a S:line)
paul@30
  1811
  */
paul@30
  1812
static char *IsVhost(char *hostmask, int oper)
paul@30
  1813
{
paul@30
  1814
  unsigned int i = 0, y = 0;
paul@30
  1815
  struct sline *sconf;
paul@30
  1816
paul@30
  1817
  Debug((DEBUG_INFO, "IsVhost() %s", hostmask));
paul@30
  1818
paul@30
  1819
  if (EmptyString(hostmask))
paul@30
  1820
    return NULL;
paul@30
  1821
paul@30
  1822
  /* spoofhost specified as index, ie: #27 */
paul@30
  1823
  if ('#' == hostmask[0]) {
paul@30
  1824
    y = atoi(hostmask + 1);
paul@30
  1825
    for (i = 0, sconf = GlobalSList; sconf; sconf = sconf->next) {
paul@30
  1826
      if (!oper && EmptyString(sconf->passwd))
paul@30
  1827
        continue;
paul@30
  1828
      if (y == ++i)
paul@30
  1829
        return sconf->spoofhost;
paul@30
  1830
    }
paul@30
  1831
    return NULL;
paul@30
  1832
  }
paul@30
  1833
paul@30
  1834
  /* spoofhost specified as host, ie: host.cc */
paul@30
  1835
  for (sconf = GlobalSList; sconf; sconf = sconf->next)
paul@30
  1836
    if (strCasediff(hostmask, sconf->spoofhost) == 0)
paul@30
  1837
      return sconf->spoofhost;
paul@30
  1838
paul@30
  1839
  return NULL;
paul@30
  1840
}
paul@30
  1841
paul@30
  1842
 /*
paul@30
  1843
  * IsVhostPass() - Check if given spoofhost has a password
paul@30
  1844
  * associated with it, and if, return the password (cleartext)
paul@30
  1845
  */
paul@30
  1846
static char *IsVhostPass(char *hostmask)
paul@30
  1847
{
paul@30
  1848
  struct sline *sconf;
paul@30
  1849
paul@30
  1850
  Debug((DEBUG_INFO, "IsVhostPass() %s", hostmask));
paul@30
  1851
paul@30
  1852
  if (EmptyString(hostmask))
paul@30
  1853
    return NULL;
paul@30
  1854
paul@30
  1855
  for (sconf = GlobalSList; sconf; sconf = sconf->next)
paul@30
  1856
    if (strCasediff(hostmask, sconf->spoofhost) == 0) {
paul@30
  1857
      Debug((DEBUG_INFO, "sconf->passwd %s", sconf->passwd));
paul@30
  1858
      return EmptyString(sconf->passwd) ? NULL : sconf->passwd;
paul@30
  1859
    }
paul@30
  1860
paul@30
  1861
  return NULL;
paul@30
  1862
}
paul@30
  1863
slug@0
  1864
/** Update snomask \a oldmask according to \a arg and \a what.
slug@0
  1865
 * @param[in] oldmask Original user mask.
slug@0
  1866
 * @param[in] arg Update string (either a number or '+'/'-' followed by a number).
slug@0
  1867
 * @param[in] what MODE_ADD if adding the mask.
slug@0
  1868
 * @return New value of service notice mask.
slug@0
  1869
 */
slug@0
  1870
unsigned int umode_make_snomask(unsigned int oldmask, char *arg, int what)
slug@0
  1871
{
slug@0
  1872
  unsigned int sno_what;
slug@0
  1873
  unsigned int newmask;
slug@0
  1874
  if (*arg == '+')
slug@0
  1875
  {
slug@0
  1876
    arg++;
slug@0
  1877
    if (what == MODE_ADD)
slug@0
  1878
      sno_what = SNO_ADD;
slug@0
  1879
    else
slug@0
  1880
      sno_what = SNO_DEL;
slug@0
  1881
  }
slug@0
  1882
  else if (*arg == '-')
slug@0
  1883
  {
slug@0
  1884
    arg++;
slug@0
  1885
    if (what == MODE_ADD)
slug@0
  1886
      sno_what = SNO_DEL;
slug@0
  1887
    else
slug@0
  1888
      sno_what = SNO_ADD;
slug@0
  1889
  }
slug@0
  1890
  else
slug@0
  1891
    sno_what = (what == MODE_ADD) ? SNO_SET : SNO_DEL;
slug@0
  1892
  /* pity we don't have strtoul everywhere */
slug@0
  1893
  newmask = (unsigned int)atoi(arg);
slug@0
  1894
  if (sno_what == SNO_DEL)
slug@0
  1895
    newmask = oldmask & ~newmask;
slug@0
  1896
  else if (sno_what == SNO_ADD)
slug@0
  1897
    newmask |= oldmask;
slug@0
  1898
  return newmask;
slug@0
  1899
}
slug@0
  1900
slug@0
  1901
/** Remove \a cptr from the singly linked list \a list.
slug@0
  1902
 * @param[in] cptr Client to remove from list.
slug@0
  1903
 * @param[in,out] list Pointer to head of list containing \a cptr.
slug@0
  1904
 */
slug@0
  1905
static void delfrom_list(struct Client *cptr, struct SLink **list)
slug@0
  1906
{
slug@0
  1907
  struct SLink* tmp;
slug@0
  1908
  struct SLink* prv = NULL;
slug@0
  1909
slug@0
  1910
  for (tmp = *list; tmp; tmp = tmp->next) {
slug@0
  1911
    if (tmp->value.cptr == cptr) {
slug@0
  1912
      if (prv)
slug@0
  1913
        prv->next = tmp->next;
slug@0
  1914
      else
slug@0
  1915
        *list = tmp->next;
slug@0
  1916
      free_link(tmp);
slug@0
  1917
      break;
slug@0
  1918
    }
slug@0
  1919
    prv = tmp;
slug@0
  1920
  }
slug@0
  1921
}
slug@0
  1922
slug@0
  1923
/** Set \a cptr's server notice mask, according to \a what.
slug@0
  1924
 * @param[in,out] cptr Client whose snomask is updating.
slug@0
  1925
 * @param[in] newmask Base value for new snomask.
slug@0
  1926
 * @param[in] what One of SNO_ADD, SNO_DEL, SNO_SET, to choose operation.
slug@0
  1927
 */
slug@0
  1928
void set_snomask(struct Client *cptr, unsigned int newmask, int what)
slug@0
  1929
{
slug@0
  1930
  unsigned int oldmask, diffmask;        /* unsigned please */
slug@0
  1931
  int i;
slug@0
  1932
  struct SLink *tmp;
slug@0
  1933
slug@0
  1934
  oldmask = cli_snomask(cptr);
slug@0
  1935
slug@0
  1936
  if (what == SNO_ADD)
slug@0
  1937
    newmask |= oldmask;
slug@0
  1938
  else if (what == SNO_DEL)
slug@0
  1939
    newmask = oldmask & ~newmask;
slug@0
  1940
  else if (what != SNO_SET)        /* absolute set, no math needed */
slug@0
  1941
    sendto_opmask_butone(0, SNO_OLDSNO, "setsnomask called with %d ?!", what);
slug@0
  1942
slug@0
  1943
  newmask &= (IsAnOper(cptr) ? SNO_ALL : SNO_USER);
slug@0
  1944
slug@0
  1945
  diffmask = oldmask ^ newmask;
slug@0
  1946
slug@0
  1947
  for (i = 0; diffmask >> i; i++) {
slug@0
  1948
    if (((diffmask >> i) & 1))
slug@0
  1949
    {
slug@0
  1950
      if (((newmask >> i) & 1))
slug@0
  1951
      {
slug@0
  1952
        tmp = make_link();
slug@0
  1953
        tmp->next = opsarray[i];
slug@0
  1954
        tmp->value.cptr = cptr;
slug@0
  1955
        opsarray[i] = tmp;
slug@0
  1956
      }
slug@0
  1957
      else
slug@0
  1958
        /* not real portable :( */
slug@0
  1959
        delfrom_list(cptr, &opsarray[i]);
slug@0
  1960
    }
slug@0
  1961
  }
slug@0
  1962
  cli_snomask(cptr) = newmask;
slug@0
  1963
}
slug@0
  1964
slug@0
  1965
/** Check whether \a sptr is allowed to send a message to \a acptr.
slug@0
  1966
 * If \a sptr is a remote user, it means some server has an outdated
slug@0
  1967
 * SILENCE list for \a acptr, so send the missing SILENCE mask(s) back
slug@0
  1968
 * in the direction of \a sptr.  Skip the check if \a sptr is a server.
slug@0
  1969
 * @param[in] sptr Client trying to send a message.
slug@0
  1970
 * @param[in] acptr Destination of message.
slug@0
  1971
 * @return Non-zero if \a sptr is SILENCEd by \a acptr, zero if not.
slug@0
  1972
 */
slug@0
  1973
int is_silenced(struct Client *sptr, struct Client *acptr)
slug@0
  1974
{
slug@0
  1975
  struct Ban *found;
slug@0
  1976
  struct User *user;
slug@0
  1977
  size_t buf_used, slen;
slug@0
  1978
  char buf[BUFSIZE];
slug@0
  1979
slug@0
  1980
  if (IsServer(sptr) || !(user = cli_user(acptr))
slug@0
  1981
      || !(found = find_ban(sptr, user->silence)))
slug@0
  1982
    return 0;
slug@0
  1983
  assert(!(found->flags & BAN_EXCEPTION));
slug@0
  1984
  if (!MyConnect(sptr)) {
slug@0
  1985
    /* Buffer positive silence to send back. */
slug@0
  1986
    buf_used = strlen(found->banstr);
slug@0
  1987
    memcpy(buf, found->banstr, buf_used);
slug@0
  1988
    /* Add exceptions to buffer. */
slug@0
  1989
    for (found = user->silence; found; found = found->next) {
slug@0
  1990
      if (!(found->flags & BAN_EXCEPTION))
slug@0
  1991
        continue;
slug@0
  1992
      slen = strlen(found->banstr);
slug@0
  1993
      if (buf_used + slen + 4 > 400) {
slug@0
  1994
        buf[buf_used] = '\0';
slug@0
  1995
        sendcmdto_one(acptr, CMD_SILENCE, cli_from(sptr), "%C %s", sptr, buf);
slug@0
  1996
        buf_used = 0;
slug@0
  1997
      }
slug@0
  1998
      if (buf_used)
slug@0
  1999
        buf[buf_used++] = ',';
slug@0
  2000
      buf[buf_used++] = '+';
slug@0
  2001
      buf[buf_used++] = '~';
slug@0
  2002
      memcpy(buf + buf_used, found->banstr, slen);
slug@0
  2003
      buf_used += slen;
slug@0
  2004
    }
slug@0
  2005
    /* Flush silence buffer. */
slug@0
  2006
    if (buf_used) {
slug@0
  2007
      buf[buf_used] = '\0';
slug@0
  2008
      sendcmdto_one(acptr, CMD_SILENCE, cli_from(sptr), "%C %s", sptr, buf);
slug@0
  2009
      buf_used = 0;
slug@0
  2010
    }
slug@0
  2011
  }
slug@0
  2012
  return 1;
slug@0
  2013
}
slug@0
  2014
slug@0
  2015
/** Send RPL_ISUPPORT lines to \a cptr.
slug@0
  2016
 * @param[in] cptr Client to send ISUPPORT to.
slug@0
  2017
 * @return Zero.
slug@0
  2018
 */
slug@0
  2019
int
slug@0
  2020
send_supported(struct Client *cptr)
slug@0
  2021
{
slug@0
  2022
  char featurebuf[512];
slug@0
  2023
slug@0
  2024
  ircd_snprintf(0, featurebuf, sizeof(featurebuf), FEATURES1, FEATURESVALUES1);
slug@0
  2025
  send_reply(cptr, RPL_ISUPPORT, featurebuf);
slug@0
  2026
  ircd_snprintf(0, featurebuf, sizeof(featurebuf), FEATURES2, FEATURESVALUES2);
slug@0
  2027
  send_reply(cptr, RPL_ISUPPORT, featurebuf);
slug@0
  2028
slug@0
  2029
  return 0; /* convenience return, if it's ever needed */
slug@0
  2030
}
paul@115
  2031
paul@115
  2032
/* vim: shiftwidth=2 
paul@115
  2033
 */