3 give staff certain privs
4 usermode +S (internally +S <staffname> just like usermode +o)
5 add /STAFF command, mirrored after /OPER
6 add priv STAFF used to determine whether the client becomes an OPER or STAFF
9 CLAIM_NICK allows staff to use /KILL staffname to free their own staffnick? (not implemented yet)
10 GLINE_LOOKUP allows staff to lookup gline info using /GLINE
11 HIDE_CHANS allows staff/oper to set usermode +n
12 CHECK_CHANNEL allows staff to use /CHECK #channel (staff version respects hiddenhost, sethost, viewable topic, visible key, etc.)
14 by default, staff gets all of the above privs + CHAN_LIMIT (no channel limit) and NOIDLE (set usermode +I)
16 shows staff member count in /lusers
17 shows staff in /trace, /stats l, /who <query> o,
18 shows staff status in /WHOIS - show to IRC Operators the staffname
19 staff can do /PRIVS to view their own privs - cannot view other user's privs
20 allow staff to set usermode -h (remove sethost)
21 allow staff to see through usermode +I (so they can see idle/signon time on fellow staff/opers)
23 TODO: treat staff a bit more favourable with regards to excess flood? add feature STAFF_CLIENT_FLOOD?
24 TODO: allow staff to access oper configured spoof blocks with /SETHOST?
25 TODO: set default staff spoof host in feature STAFF_SETHOST to e.g. staff.quakenet.org - set upon login
26 TODO: allow staff free'ing their nick? or else allow /NICK even when hit by a gline (that way we can reserve nicks using a nick!*@* gline)
27 TODO: allow a search in /GLINE instead of requiring a perfect match?
28 TODO: logging of /CHECK (for all)
30 diff -r b6cf63c513c5 include/check.h
31 --- a/include/check.h Thu Feb 12 14:05:21 2009 +0100
32 +++ b/include/check.h Thu Feb 12 14:18:31 2009 +0100
34 extern void checkClient(struct Client *sptr, struct Client *acptr);
35 extern void checkServer(struct Client *sptr, struct Client *acptr);
36 extern signed int checkHostmask(struct Client *sptr, char *hoststr, int flags);
37 +extern int client_can_check_channel(struct Client *sptr, struct Channel *chptr);
39 #endif /* INCLUDED_check_h */
40 diff -r b6cf63c513c5 include/client.h
41 --- a/include/client.h Thu Feb 12 14:05:21 2009 +0100
42 +++ b/include/client.h Thu Feb 12 14:18:31 2009 +0100
44 #define FlagClr(set,flag) ((set)->bits[FLAGSET_INDEX(flag)] &= ~FLAGSET_MASK(flag))
46 /** String containing valid user modes, in no particular order. */
47 -#define infousermodes "dioOswkgxRXInPq"
48 +#define infousermodes "dioOswkgxRXInPqS"
50 /** Character to indicate no oper name available */
51 #define NOOPERNAMECHARACTER '-'
53 PRIV_USER_PRIVACY, /* oper can bypass user privacy +x etc gives i.e. see real ip's */
54 PRIV_CHANNEL_PRIVACY, /* oper can bypass channel privacy i.e. can see modes on channels they are not on and channel keys */
55 PRIV_SERVERINFO, /* oper can use /get, /stats, /hash, retrieve remote information */
56 + PRIV_STAFF, /* staff */
57 + PRIV_CLAIM_NICK, /* staff can /KILL staffname to free own nick */
58 + PRIV_GLINE_LOOKUP, /* staff can use /GLINE to lookup a gline */
59 + PRIV_HIDE_CHANS, /* oper can set usermode +n */
60 + PRIV_CHECK_CHANNEL, /* staff can /CHECK #channel */
61 PRIV_LAST_PRIV /**< number of privileges */
65 FLAG_LOCOP, /**< Local operator -- SRB */
66 FLAG_SERVNOTICE, /**< server notices such as kill */
67 FLAG_OPER, /**< Operator */
68 + FLAG_STAFF, /**< snircd: Staff - mode +S */
69 FLAG_INVISIBLE, /**< makes user invisible */
70 FLAG_WALLOP, /**< send wallops to them */
71 FLAG_DEAF, /**< Makes user deaf */
73 #define IsLocOp(x) (MyUser(x) && HasFlag(x, FLAG_LOCOP))
74 /** Return non-zero if the client has set mode +o (global operator). */
75 #define IsOper(x) HasFlag(x, FLAG_OPER)
76 +/** Return non-zero if the client has set mode +S (staff). */
77 +#define IsStaff(x) HasFlag(x, FLAG_STAFF)
78 +/** Return non-zero if the client has set mode +S, +O or +o.*/
79 +#define IsStaffOrAnOper(x) (IsStaff(x) || IsAnOper(x))
80 /** Return non-zero if the client has an active UDP ping request. */
81 #define IsUPing(x) HasFlag(x, FLAG_UPING)
82 /** Return non-zero if the client has no '\n' in its buffer. */
84 #define SetLocOp(x) SetFlag(x, FLAG_LOCOP)
85 /** Mark a client as having mode +o (global operator). */
86 #define SetOper(x) SetFlag(x, FLAG_OPER)
87 +/** Mark a client as having mode +S (staff). */
88 +#define SetStaff(x) SetFlag(x, FLAG_STAFF)
89 /** Mark a client as having a pending UDP ping. */
90 #define SetUPing(x) SetFlag(x, FLAG_UPING)
91 /** Mark a client as having mode +w (wallops). */
93 #define ClearLocOp(x) ClrFlag(x, FLAG_LOCOP)
94 /** Remove mode +o (global operator) from the client. */
95 #define ClearOper(x) ClrFlag(x, FLAG_OPER)
96 +/** Remove mode +S (staff) from the client. */
97 +#define ClearStaff(x) ClrFlag(x, FLAG_STAFF)
98 /** Clear the client's pending UDP ping flag. */
99 #define ClearUPing(x) ClrFlag(x, FLAG_UPING)
100 /** Remove mode +w (wallops) from the client. */
101 diff -r b6cf63c513c5 include/handlers.h
102 --- a/include/handlers.h Thu Feb 12 14:05:21 2009 +0100
103 +++ b/include/handlers.h Thu Feb 12 14:18:31 2009 +0100
107 extern int m_check(struct Client *cptr, struct Client *sptr, int parc, char *parv[]);
108 +extern int mo_check(struct Client *cptr, struct Client *sptr, int parc, char *parv[]);
110 extern int m_cap(struct Client*, struct Client*, int, char*[]);
111 extern int m_cnotice(struct Client*, struct Client*, int, char*[]);
112 @@ -133,12 +134,14 @@
113 extern int m_pong(struct Client*, struct Client*, int, char*[]);
114 extern int m_private(struct Client*, struct Client*, int, char*[]);
115 extern int m_privmsg(struct Client*, struct Client*, int, char*[]);
116 +extern int m_privs(struct Client*, struct Client*, int, char*[]);
117 extern int m_proto(struct Client*, struct Client*, int, char*[]);
118 extern int m_pseudo(struct Client*, struct Client*, int, char*[]);
119 extern int m_quit(struct Client*, struct Client*, int, char*[]);
120 extern int m_registered(struct Client*, struct Client*, int, char*[]);
121 extern int m_sethost(struct Client*, struct Client*, int, char*[]);
122 extern int m_silence(struct Client*, struct Client*, int, char*[]);
123 +extern int m_staff(struct Client*, struct Client*, int, char*[]);
124 extern int m_stats(struct Client*, struct Client*, int, char*[]);
125 extern int m_time(struct Client*, struct Client*, int, char*[]);
126 extern int m_topic(struct Client*, struct Client*, int, char*[]);
128 extern int mo_set(struct Client*, struct Client*, int, char*[]);
129 extern int mo_settime(struct Client*, struct Client*, int, char*[]);
130 extern int mo_squit(struct Client*, struct Client*, int, char*[]);
131 +extern int mo_staff(struct Client*, struct Client*, int, char*[]);
132 extern int mo_stats(struct Client*, struct Client*, int, char*[]);
133 extern int mo_trace(struct Client*, struct Client*, int, char*[]);
134 extern int mo_uping(struct Client*, struct Client*, int, char*[]);
136 extern int ms_settime(struct Client*, struct Client*, int, char*[]);
137 extern int ms_silence(struct Client*, struct Client*, int, char*[]);
138 extern int ms_squit(struct Client*, struct Client*, int, char*[]);
139 +extern int ms_staff(struct Client*, struct Client*, int, char*[]);
140 extern int ms_stats(struct Client*, struct Client*, int, char*[]);
141 extern int ms_topic(struct Client*, struct Client*, int, char*[]);
142 extern int ms_trace(struct Client*, struct Client*, int, char*[]);
143 diff -r b6cf63c513c5 include/msg.h
144 --- a/include/msg.h Thu Feb 12 14:05:21 2009 +0100
145 +++ b/include/msg.h Thu Feb 12 14:18:31 2009 +0100
147 #define TOK_STATS "R"
148 #define CMD_STATS MSG_STATS, TOK_STATS
150 +#define MSG_STAFF "STAFF" /* STAF */
151 +#define TOK_STAFF "STAFF"
152 +#define CMD_STAFF MSG_STAFF, TOK_STAFF
154 #define MSG_HELP "HELP" /* HELP */
155 #define TOK_HELP "HELP"
156 #define CMD_HELP MSG_HELP, TOK_HELP
157 diff -r b6cf63c513c5 include/querycmds.h
158 --- a/include/querycmds.h Thu Feb 12 14:05:21 2009 +0100
159 +++ b/include/querycmds.h Thu Feb 12 14:18:31 2009 +0100
161 /* Global user mode changes: */
162 unsigned int inv_clients; /**< Registered invisible users. */
163 unsigned int opers; /**< Registered IRC operators. */
164 + unsigned int staff; /**< Registered Staff members. */
167 unsigned int channels; /**< Existing channels. */
168 diff -r b6cf63c513c5 include/struct.h
169 --- a/include/struct.h Thu Feb 12 14:05:21 2009 +0100
170 +++ b/include/struct.h Thu Feb 12 14:18:31 2009 +0100
172 unsigned long acc_id; /**< IRC account unique id */
173 uint64_t acc_flags; /**< IRC account flags */
174 char* opername; /**< IRC Oper Account name */
175 + char* staffname; /**< IRC Staff Account name */
178 #endif /* INCLUDED_struct_h */
179 diff -r b6cf63c513c5 ircd/Makefile.in
180 --- a/ircd/Makefile.in Thu Feb 12 14:05:21 2009 +0100
181 +++ b/ircd/Makefile.in Thu Feb 12 14:18:31 2009 +0100
190 @@ -1060,6 +1061,17 @@
191 ../include/ircd_reply.h ../include/ircd_string.h ../include/numeric.h \
192 ../include/numnicks.h ../include/match.h ../include/s_debug.h \
193 ../include/s_misc.h ../include/s_user.h ../include/send.h
194 +m_staff.o: m_staff.c ../config.h ../include/client.h ../include/ircd_defs.h \
195 + ../include/dbuf.h ../include/msgq.h ../include/ircd_events.h \
196 + ../config.h ../include/ircd_handler.h ../include/res.h \
197 + ../include/capab.h ../include/hash.h ../include/ircd.h \
198 + ../include/struct.h ../include/ircd_alloc.h ../include/ircd_features.h \
199 + ../include/ircd_log.h ../include/ircd_reply.h ../include/ircd_string.h \
200 + ../include/ircd_chattr.h ../include/ircd_crypt.h ../include/msg.h \
201 + ../include/numeric.h ../include/numnicks.h ../include/querycmds.h \
202 + ../include/ircd_features.h ../include/s_conf.h ../include/client.h \
203 + ../include/s_debug.h ../include/s_user.h ../include/s_misc.h \
205 m_stats.o: m_stats.c ../config.h ../include/client.h \
206 ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
207 ../include/ircd_events.h ../config.h ../include/ircd_handler.h \
208 diff -r b6cf63c513c5 ircd/client.c
209 --- a/ircd/client.c Thu Feb 12 14:05:21 2009 +0100
210 +++ b/ircd/client.c Thu Feb 12 14:18:31 2009 +0100
212 static struct Privs privs_global;
213 /** Default privilege set for local operators. */
214 static struct Privs privs_local;
215 +/** Default privilege set for staff. */
216 +static struct Privs privs_staff;
217 /** Non-zero if #privs_global and #privs_local have been initialized. */
218 static int privs_defaults_set;
221 /* Clear out client's privileges. */
222 memset(cli_privs(client), 0, sizeof(struct Privs));
224 - if (!IsAnOper(client) || !oper)
225 + if (!IsStaffOrAnOper(client) || !oper)
228 if (!privs_defaults_set)
230 FlagClr(&privs_global, PRIV_DIE);
231 FlagClr(&privs_global, PRIV_RESTART);
232 FlagClr(&privs_global, PRIV_JUPE);
233 + FlagClr(&privs_global, PRIV_STAFF);
234 + FlagClr(&privs_global, PRIV_CLAIM_NICK);
235 + FlagClr(&privs_global, PRIV_GLINE_LOOKUP);
236 + FlagClr(&privs_global, PRIV_CHECK_CHANNEL);
238 memset(&privs_local, 0, sizeof(privs_local));
239 FlagSet(&privs_local, PRIV_CHAN_LIMIT);
240 @@ -178,17 +184,33 @@
241 FlagSet(&privs_local, PRIV_WHOX);
242 FlagSet(&privs_local, PRIV_DISPLAY);
243 FlagSet(&privs_local, PRIV_FORCE_LOCAL_OPMODE);
244 + FlagClr(&privs_local, PRIV_STAFF);
245 + FlagClr(&privs_local, PRIV_CLAIM_NICK);
246 + FlagClr(&privs_local, PRIV_GLINE_LOOKUP);
247 + FlagClr(&privs_local, PRIV_CHECK_CHANNEL);
249 + /* set the default for privs for staff */
250 + memset(&privs_staff, 0, sizeof(privs_staff));
251 + FlagSet(&privs_staff, PRIV_CHAN_LIMIT);
252 + FlagSet(&privs_staff, PRIV_CLAIM_NICK);
253 + FlagSet(&privs_staff, PRIV_GLINE_LOOKUP);
254 + FlagSet(&privs_staff, PRIV_NOIDLE);
255 + FlagSet(&privs_staff, PRIV_HIDE_CHANS);
256 + FlagSet(&privs_staff, PRIV_CHECK_CHANNEL);
258 privs_defaults_set = 1;
261 - /* Decide whether to use global or local oper defaults. */
262 - if (FlagHas(&oper->privs_dirty, PRIV_PROPAGATE))
263 + /* Decide whether to use staff, global or local oper defaults. */
264 + if (FlagHas(&oper->privs_dirty, PRIV_STAFF) ||
265 + FlagHas(&oper->conn_class->privs_dirty, PRIV_STAFF))
266 + defaults = &privs_staff;
267 + else if (FlagHas(&oper->privs_dirty, PRIV_PROPAGATE))
268 defaults = FlagHas(&oper->privs, PRIV_PROPAGATE) ? &privs_global : &privs_local;
269 else if (FlagHas(&oper->conn_class->privs_dirty, PRIV_PROPAGATE))
270 defaults = FlagHas(&oper->conn_class->privs, PRIV_PROPAGATE) ? &privs_global : &privs_local;
272 - assert(0 && "Oper has no propagation and neither does connection class");
273 + assert(0 && "Oper has no propagation/staff and neither does connection class");
278 ClrPriv(client, PRIV_OPKICK);
279 ClrPriv(client, PRIV_BADCHAN);
281 + /* TODO: better way than this? */
282 + /* do not let staff have privs they should not have */
283 + if (HasPriv(client, PRIV_STAFF)) {
284 + for (priv = 0; priv < PRIV_LAST_PRIV; ++priv) {
285 + if (priv == PRIV_STAFF || priv == PRIV_CHAN_LIMIT || priv == PRIV_CLAIM_NICK ||
286 + priv == PRIV_GLINE_LOOKUP || priv == PRIV_NOIDLE || priv == PRIV_HIDE_CHANS ||
287 + priv == PRIV_CHECK_CHANNEL)
289 + ClrPriv(client, priv);
294 /** Array mapping privilege values to names and vice versa. */
296 P(PARANOID), P(CHECK), P(WALL), P(CLOSE),
297 P(ROUTE), P(ROUTEINFO), P(SERVERINFO), P(CHANNEL_PRIVACY),
299 + P(STAFF), P(CLAIM_NICK), P(GLINE_LOOKUP), P(HIDE_CHANS),
304 diff -r b6cf63c513c5 ircd/ircd_lexer.l
305 --- a/ircd/ircd_lexer.l Thu Feb 12 14:05:21 2009 +0100
306 +++ b/ircd/ircd_lexer.l Thu Feb 12 14:18:31 2009 +0100
308 { "serverinfo", TPRIV_SERVERINFO },
309 { "user_privacy", TPRIV_USER_PRIVACY },
310 { "channel_privacy", TPRIV_CHANNEL_PRIVACY },
311 + { "staff", TPRIV_STAFF },
312 + { "claim_nick", TPRIV_CLAIM_NICK },
313 + { "gline_lookup", TPRIV_GLINE_LOOKUP },
314 + { "hide_chans", TPRIV_HIDE_CHANS },
315 + { "check_channel", TPRIV_CHECK_CHANNEL },
319 diff -r b6cf63c513c5 ircd/ircd_parser.y
320 --- a/ircd/ircd_parser.y Thu Feb 12 14:05:21 2009 +0100
321 +++ b/ircd/ircd_parser.y Thu Feb 12 14:18:31 2009 +0100
323 %token TPRIV_FORCE_OPMODE TPRIV_FORCE_LOCAL_OPMODE TPRIV_APASS_OPMODE
324 %token TPRIV_CHANSERV TPRIV_XTRA_OPER TPRIV_NOIDLE TPRIV_FREEFORM TPRIV_PARANOID
325 %token TPRIV_CHECK TPRIV_WALL TPRIV_CLOSE TPRIV_ROUTE TPRIV_ROUTEINFO TPRIV_SERVERINFO
326 -%token TPRIV_CHANNEL_PRIVACY TPRIV_USER_PRIVACY TPRIV_LIST_CHAN
327 +%token TPRIV_CHANNEL_PRIVACY TPRIV_USER_PRIVACY TPRIV_LIST_CHAN
328 +%token TPRIV_STAFF TPRIV_CLAIM_NICK TPRIV_GLINE_LOOKUP TPRIV_HIDE_CHANS
329 +%token TPRIV_CHECK_CHANNEL
330 /* and some types... */
332 %type <num> timespec timefactor factoredtimes factoredtime
334 else if (c_class == NULL)
335 parse_error("Invalid or missing class in operator block");
336 else if (!FlagHas(&privs_dirty, PRIV_PROPAGATE)
337 - && !FlagHas(&c_class->privs_dirty, PRIV_PROPAGATE))
338 - parse_error("Operator block for %s and class %s have no LOCAL setting", name, c_class->cc_name);
339 + && !FlagHas(&c_class->privs_dirty, PRIV_PROPAGATE)
340 + && !FlagHas(&privs_dirty, PRIV_STAFF)
341 + && !FlagHas(&c_class->privs_dirty, PRIV_STAFF))
342 + parse_error("Operator block for %s and class %s have no LOCAL or STAFF setting", name, c_class->cc_name);
343 else for (link = hosts; link != NULL; link = link->next) {
344 aconf = make_conf(CONF_OPERATOR);
345 DupString(aconf->name, name);
347 TPRIV_SERVERINFO { $$ = PRIV_SERVERINFO ; } |
348 TPRIV_CHANNEL_PRIVACY { $$ = PRIV_CHANNEL_PRIVACY ; } |
349 TPRIV_USER_PRIVACY { $$ = PRIV_USER_PRIVACY ; } |
350 + TPRIV_STAFF { $$ = PRIV_STAFF; } |
351 + TPRIV_CLAIM_NICK { $$ = PRIV_CLAIM_NICK; } |
352 + TPRIV_GLINE_LOOKUP { $$ = PRIV_GLINE_LOOKUP; } |
353 + TPRIV_CHECK_CHANNEL { $$ = PRIV_CHECK_CHANNEL; } |
354 + TPRIV_HIDE_CHANS { $$ = PRIV_HIDE_CHANS; } |
355 TPRIV_PARANOID { $$ = PRIV_PARANOID; } ;
356 yesorno: YES { $$ = 1; } | NO { $$ = 0; };
358 diff -r b6cf63c513c5 ircd/m_check.c
359 --- a/ircd/m_check.c Thu Feb 12 14:05:21 2009 +0100
360 +++ b/ircd/m_check.c Thu Feb 12 14:18:31 2009 +0100
362 struct Client *acptr;
363 int flags = CHECK_SHOWUSERS, i;
365 - if (!HasPriv(sptr, PRIV_CHECK))
366 + /* not an oper or staff */
367 + if (!IsAnOper(sptr) && !IsStaff(sptr))
368 + return send_reply(sptr, ERR_NOPRIVILEGES);
371 + if (!HasPriv(sptr, PRIV_CHECK) && !HasPriv(sptr, PRIV_CHECK_CHANNEL))
372 return send_reply(sptr, ERR_NOPRIVILEGES);
378 (parc==3 && parv[2][0] != '-')) {
380 + /* remote query - no need to restrict staff,
381 + * hunt_server_cmd is told here that sptr needs to be an oper
383 if (hunt_server_cmd(sptr, CMD_CHECK, cptr, 0, parc==4 ? "%C %s %s" : "%C %s", 1, parc, parv) != HUNTED_ISME)
386 @@ -152,11 +159,20 @@
387 if (IsChannelName(parv[1])) { /* channel */
388 if ((chptr = FindChannel(parv[1]))) {
389 checkChannel(sptr, chptr);
390 - checkUsers(sptr, chptr, flags);
391 + if (client_can_check_channel(sptr, chptr))
392 + checkUsers(sptr, chptr, flags);
394 + send_reply(sptr, SND_EXPLICIT | RPL_DATASTR,
395 + ":Aborting - Channel %s has an IRC Operator on it.", chptr->chname);
396 + send_reply(sptr, RPL_ENDOFCHECK, " ");
400 send_reply(sptr, ERR_SEARCHNOMATCH, "CHECK", parv[1]);
402 + /* staff can only check channel - stop here */
403 + else if (IsStaff(sptr))
404 + return send_reply(sptr, ERR_NOPRIVILEGES);
405 else if ((acptr = FindClient(parv[1])) && !(FindServer(parv[1]))) { /* client and not a server */
406 if (!IsRegistered(acptr)) {
407 send_reply(sptr, ERR_SEARCHNOMATCH, "CHECK", parv[1]);
412 +/* test if a client can CHECK a channel
413 + * return 1 when allowed, else 0
415 +int client_can_check_channel(struct Client *sptr, struct Channel *chptr) {
416 + struct Membership *lp;
419 + if (IsAnOper(sptr))
422 + /* look at the channel modes
423 + * channel is not secret, private, invite only, or keyed
425 + if (!(chptr->mode.mode & (MODE_SECRET | MODE_PRIVATE | MODE_INVITEONLY)) && (!*chptr->mode.key))
428 + /* the client is on the channel */
429 + if (find_channel_member(sptr, chptr))
432 + /* look for opers on the channel - ignore channel services */
433 + for (lp = chptr->members; lp; lp = lp->next_member) {
434 + if (IsAnOper(lp->user) && !IsRealChannelService(lp->user))
438 + /* we made it, so it is allowed */
443 /* return number of clients from same IP on the channel */
444 static int checkClones(struct Channel *chptr, struct Client *cptr) {
445 @@ -313,10 +359,33 @@
447 if ((flags & CHECK_SHOWUSERS) || ((flags & CHECK_OPSONLY) && opped)) {
448 ircd_snprintf(0, outbuf, sizeof(outbuf), "%s%c", acptr->cli_info, COLOR_OFF);
450 + /* show IPs and hostnames */
451 if (flags & CHECK_SHOWHOSTIP) {
452 - ircd_snprintf(0, outbuf2, sizeof(outbuf2), " [%s]", ircd_ntoa(&(cli_ip(acptr))));
453 + /* staff - do not show real IPs*/
455 + ircd_snprintf(0, outbuf2, sizeof(outbuf2), " [%s]",
456 + (HasHiddenHost(acptr) || HasSetHost(acptr)) ?
457 + feature_str(FEAT_HIDDEN_IP) : ircd_ntoa(&(cli_ip(acptr))));
459 + ircd_snprintf(0, outbuf2, sizeof(outbuf2), " [%s]", ircd_ntoa(&(cli_ip(acptr))));
461 - send_reply(sptr, RPL_CHANUSER, ustat, acptr->cli_name, cli_user(acptr)->realusername,
462 + /* staff - do not show real hostnames or IPs */
464 + send_reply(sptr, RPL_CHANUSER, ustat, acptr->cli_name, cli_user(acptr)->username,
465 + /* show IPs instead of hostnames - respect hiddenhost and sethost */
466 + ((flags & CHECK_SHOWIPS) ?
467 + ((HasHiddenHost(acptr) || HasSetHost(acptr)) ?
468 + feature_str(FEAT_HIDDEN_IP) : ircd_ntoa(&(cli_ip(acptr)))) : cli_user(acptr)->host),
469 + /* show IPs too - set above */
470 + (flags & CHECK_SHOWHOSTIP) ? outbuf2 : "",
471 + /* show servernames instead of realnames - respect HIS */
472 + ((flags & CHECK_SHOWSERVER) && !feature_bool(FEAT_HIS_WHO_SERVERNAME)) ?
473 + cli_name(cli_user(acptr)->server) : outbuf,
475 + (c ? cli_user(acptr)->account : ""));
477 + send_reply(sptr, RPL_CHANUSER, ustat, acptr->cli_name, cli_user(acptr)->realusername,
478 ((flags & CHECK_SHOWIPS) ? ircd_ntoa(&(cli_ip(acptr))) : cli_user(acptr)->realhost), (flags & CHECK_SHOWHOSTIP) ? outbuf2 : "", (flags & CHECK_SHOWSERVER) ? cli_name(cli_user(acptr)->server) : outbuf,
479 (c ? cli_user(acptr)->account : ""));
483 if (strlen(chptr->topic) <= 0)
484 send_reply(sptr, RPL_DATASTR, " Topic:: <none>");
486 + /* channel is +s - do not show the topic to staff, unless they are there */
487 + else if (SecretChannel(chptr) && IsStaff(sptr) && !find_channel_member(sptr, chptr))
488 + send_reply(sptr, RPL_DATASTR, " Topic:: <hidden>");
491 ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Topic:: %s", chptr->topic);
492 send_reply(sptr, RPL_DATASTR, outbuf);
497 + /* no need to check modes - this will hide the key from non-opers */
498 channel_modes(sptr, modebuf, parabuf, sizeof(modebuf), chptr, NULL);
500 if(modebuf[1] == '\0')
502 } else if (IsAnOper(acptr)) {
503 ircd_snprintf(0, outbuf, sizeof(outbuf), " Status:: IRC Operator (ID: %s)", cli_user(acptr)->opername ? cli_user(acptr)->opername : "<unknown>");
504 send_reply(sptr, RPL_DATASTR, outbuf);
505 + } else if (IsStaff(acptr)) {
506 + ircd_snprintf(0, outbuf, sizeof(outbuf), " Status:: IRC Staff (ID: %s)", cli_user(acptr)->staffname ? cli_user(acptr)->staffname : "<unknown>");
507 + send_reply(sptr, RPL_DATASTR, outbuf);
509 send_reply(sptr, RPL_DATASTR, " Status:: Client");
511 diff -r b6cf63c513c5 ircd/m_gline.c
512 --- a/ircd/m_gline.c Thu Feb 12 14:05:21 2009 +0100
513 +++ b/ircd/m_gline.c Thu Feb 12 14:18:31 2009 +0100
514 @@ -669,11 +669,13 @@
516 m_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
518 - if (feature_bool(FEAT_HIS_USERGLINE))
519 - return send_reply(sptr, ERR_DISABLED, "GLINE");
521 + /* check feature HIS_USERGLINE, Staff and priv GLINE_LOOKUP */
522 + if (feature_bool(FEAT_HIS_USERGLINE) && (!IsStaff(sptr) || !HasPriv(sptr, PRIV_GLINE_LOOKUP)))
523 + return send_reply(sptr, ERR_NOPRIVILEGES);
526 - return send_reply(sptr, ERR_NOSUCHGLINE, "");
527 + return send_reply(sptr, ERR_NOSUCHGLINE, "*");
529 return gline_list(sptr, parv[1]);
531 diff -r b6cf63c513c5 ircd/m_lusers.c
532 --- a/ircd/m_lusers.c Thu Feb 12 14:05:21 2009 +0100
533 +++ b/ircd/m_lusers.c Thu Feb 12 14:18:31 2009 +0100
535 UserStats.inv_clients, UserStats.servers);
536 if (longoutput && UserStats.opers)
537 send_reply(sptr, RPL_LUSEROP, UserStats.opers);
538 + if (longoutput && UserStats.staff)
539 + send_reply(sptr, SND_EXPLICIT | RPL_LUSEROP, "%d :staff member(s) online", UserStats.staff);
540 if (UserStats.unknowns > 0)
541 send_reply(sptr, RPL_LUSERUNKNOWN, UserStats.unknowns);
542 if (longoutput && UserStats.channels > 0)
544 UserStats.inv_clients, UserStats.servers);
545 if (longoutput && UserStats.opers)
546 send_reply(sptr, RPL_LUSEROP, UserStats.opers);
547 + if (longoutput && UserStats.staff)
548 + send_reply(sptr, SND_EXPLICIT | RPL_LUSEROP, "%d :staff member(s) online", UserStats.staff);
549 if (UserStats.unknowns > 0)
550 send_reply(sptr, RPL_LUSERUNKNOWN, UserStats.unknowns);
551 if (longoutput && UserStats.channels > 0)
552 diff -r b6cf63c513c5 ircd/m_oper.c
553 --- a/ircd/m_oper.c Thu Feb 12 14:05:21 2009 +0100
554 +++ b/ircd/m_oper.c Thu Feb 12 14:18:31 2009 +0100
556 name = parc > 1 ? parv[1] : 0;
557 password = parc > 2 ? parv[2] : 0;
559 + /* staff doing OPER */
561 + return send_reply(sptr, SND_EXPLICIT | RPL_YOUREOPER, ":You are now IRC Staff");
563 if (EmptyString(name) || EmptyString(password))
564 return need_more_params(sptr, "OPER");
569 client_set_privs(sptr, aconf);
571 + /* staff - clear flag and privs, and reject it */
572 + if (HasPriv(sptr, PRIV_STAFF)) {
574 + client_set_privs(sptr, NULL);
575 + send_reply(sptr, ERR_NOOPERHOST);
576 + sendto_opmask_butone(0, SNO_OLDREALOP, "Failed OPER attempt by %s (%s@%s) as %s",
577 + parv[0], cli_user(sptr)->realusername, cli_sockhost(sptr), name);
582 if (HasPriv(sptr, PRIV_PROPAGATE))
585 diff -r b6cf63c513c5 ircd/m_privs.c
586 --- a/ircd/m_privs.c Thu Feb 12 14:05:21 2009 +0100
587 +++ b/ircd/m_privs.c Thu Feb 12 14:18:31 2009 +0100
589 #include "numnicks.h"
592 +/** Handle a local staff's privilege query.
593 + * @param[in] cptr Client that sent us the message.
594 + * @param[in] sptr Original source of message.
595 + * @param[in] parc Number of arguments.
596 + * @param[in] parv Argument vector.
597 + * @see \ref m_functions
599 +int m_privs(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
602 + if (!IsStaff(sptr))
603 + return send_reply(sptr, ERR_NOPRIVILEGES);
605 + /* no parameters given, show own privs */
607 + return client_report_privs(sptr, sptr);
609 + /* staff not allowed to view privs on other users */
610 + return send_reply(sptr, ERR_NOPRIVILEGES);
613 /** Handle a local operator's privilege query.
614 * @param[in] cptr Client that sent us the message.
615 * @param[in] sptr Original source of message.
616 diff -r b6cf63c513c5 ircd/m_staff.c
617 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
618 +++ b/ircd/m_staff.c Thu Feb 12 14:18:31 2009 +0100
621 + * IRC - Internet Relay Chat, ircd/m_oper.c
622 + * Copyright (C) 1990 Jarkko Oikarinen and
623 + * University of Oulu, Computing Center
625 + * See file AUTHORS in IRC package for additional names of
628 + * This program is free software; you can redistribute it and/or modify
629 + * it under the terms of the GNU General Public License as published by
630 + * the Free Software Foundation; either version 1, or (at your option)
631 + * any later version.
633 + * This program is distributed in the hope that it will be useful,
634 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
635 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
636 + * GNU General Public License for more details.
638 + * You should have received a copy of the GNU General Public License
639 + * along with this program; if not, write to the Free Software
640 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
642 + * $Id: m_oper.c 1327 2005-03-19 22:52:33Z entrope $
646 + * m_functions execute protocol messages on this server:
648 + * cptr is always NON-NULL, pointing to a *LOCAL* client
649 + * structure (with an open socket connected!). This
650 + * identifies the physical socket where the message
651 + * originated (or which caused the m_function to be
652 + * executed--some m_functions may call others...).
654 + * sptr is the source of the message, defined by the
655 + * prefix part of the message if present. If not
656 + * or prefix not found, then sptr==cptr.
658 + * (!IsServer(cptr)) => (cptr == sptr), because
659 + * prefixes are taken *only* from servers...
662 + * (sptr == cptr) => the message didn't
665 + * (sptr != cptr && IsServer(sptr) means
666 + * the prefix specified servername. (?)
668 + * (sptr != cptr && !IsServer(sptr) means
669 + * that message originated from a remote
670 + * user (not local).
674 + * (!IsServer(sptr)) means that, sptr can safely
675 + * taken as defining the target structure of the
676 + * message in this server.
678 + * *Always* true (if 'parse' and others are working correct):
680 + * 1) sptr->from == cptr (note: cptr->from == cptr)
682 + * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
683 + * *cannot* be a local connection, unless it's
684 + * actually cptr!). [MyConnect(x) should probably
685 + * be defined as (x == x->from) --msa ]
687 + * parc number of variable parameter strings (if zero,
688 + * parv is allowed to be NULL)
690 + * parv a NULL terminated list of parameter pointers,
692 + * parv[0], sender (prefix string), if not present
693 + * this points to an empty string.
694 + * parv[1]...parv[parc-1]
695 + * pointers to additional parameters
696 + * parv[parc] == NULL, *always*
698 + * note: it is guaranteed that parv[0]..parv[parc-1] are all
699 + * non-NULL pointers.
706 +#include "ircd_alloc.h"
707 +#include "ircd_features.h"
708 +#include "ircd_log.h"
709 +#include "ircd_reply.h"
710 +#include "ircd_string.h"
711 +#include "ircd_crypt.h"
713 +#include "numeric.h"
714 +#include "numnicks.h"
715 +#include "querycmds.h"
717 +#include "s_debug.h"
722 +/* #include <assert.h> -- Now using assert in ircd_log.h */
727 +/* TODO: from m_oper.c - use it from there? and not have a copy here? */
728 +int staff_password_match(const char* to_match, const char* passwd)
733 + * use first two chars of the password they send in as salt
735 + * passwd may be NULL. Head it off at the pass...
737 + if (!to_match || !passwd)
740 + /* we no longer do a CRYPT_OPER_PASSWORD check because a clear
741 + text passwords just handled by a fallback mechanism called
742 + crypt_clear if it's enabled -- hikari */
743 + crypted = ircd_crypt(to_match, passwd);
747 + res = strcmp(crypted, passwd);
754 + * m_staff - generic message handler
756 +int m_staff(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
758 + struct ConfItem* aconf;
763 + assert(cptr == sptr);
765 + name = parc > 1 ? parv[1] : 0;
766 + password = parc > 2 ? parv[2] : 0;
768 + /* staff doing STAFF */
770 + return send_reply(sptr, SND_EXPLICIT | RPL_YOUREOPER, ":You are now IRC Staff");
773 + if (EmptyString(name) || EmptyString(password))
774 + return need_more_params(sptr, "STAFF");
776 + /* attempt to find the operator block */
777 + aconf = find_conf_exact(name, sptr, CONF_OPERATOR);
778 + if (!aconf || IsIllegal(aconf)) {
779 + send_reply(sptr, ERR_NOOPERHOST);
780 + sendto_opmask_butone(0, SNO_OLDREALOP, "Failed STAFF attempt by %s (%s@%s) as %s",
781 + parv[0], cli_user(sptr)->realusername, cli_sockhost(sptr), name);
785 + /* we should have it by now */
786 + assert(0 != (aconf->status & CONF_OPERATOR));
788 + /* compare passwords */
789 + if (staff_password_match(password, aconf->passwd)) {
790 + struct Flags old_mode = cli_flags(sptr);
793 + if (ACR_OK != attach_conf(sptr, aconf)) {
794 + send_reply(sptr, ERR_NOOPERHOST);
795 + sendto_opmask_butone(0, SNO_OLDREALOP, "Failed STAFF attempt by %s "
796 + "(%s@%s) as %s", parv[0], cli_user(sptr)->realusername,
797 + cli_sockhost(sptr), aconf->name);
803 + client_set_privs(sptr, aconf);
805 + /* are they staff? */
806 + if (HasPriv(sptr, PRIV_STAFF)) {
809 + /* set staffname */
810 + if (cli_user(sptr)->staffname)
811 + MyFree(cli_user(sptr)->staffname);
812 + cli_user(sptr)->staffname = (char*) MyMalloc(strlen(name) + 1);
813 + assert(0 != cli_user(sptr)->staffname);
814 + ircd_strncpy(cli_user(sptr)->staffname, aconf->name, ACCOUNTLEN);
816 + /* TODO: check sendQ */
817 + /* send out the mode and confirmation */
818 + cli_max_sendq(sptr) = 0; /* Get the sendq from the oper's class */
819 + send_umode_out(cptr, sptr, &old_mode, 0);
820 + /* TODO: create own numeric for this? */
821 + send_reply(sptr, SND_EXPLICIT | RPL_YOUREOPER, ":You are now IRC Staff");
823 + /* inform ops and log it */
824 + sendto_opmask_butone(0, SNO_OLDSNO, "%s (%s@%s) is now staff (S) as %s",
825 + parv[0], cli_user(sptr)->realusername, cli_sockhost(sptr),
826 + cli_user(sptr)->staffname);
827 + log_write(LS_OPER, L_INFO, 0, "STAFF (%s) by (%#R)", name, sptr);
829 + /* not staff - clear flag and privs, and reject */
831 + client_set_privs(sptr, NULL);
833 + send_reply(sptr, ERR_NOOPERHOST);
834 + sendto_opmask_butone(0, SNO_OLDREALOP, "Failed STAFF attempt by %s (%s@%s) as %s",
835 + parv[0], cli_user(sptr)->realusername, cli_sockhost(sptr), name);
841 + send_reply(sptr, ERR_PASSWDMISMATCH);
842 + sendto_opmask_butone(0, SNO_OLDREALOP, "Failed STAFF attempt by %s (%s@%s) as %s",
843 + parv[0], cli_user(sptr)->realusername, cli_sockhost(sptr), aconf->name);
850 + * ms_staff - server message handler
852 +int ms_staff(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
855 + assert(IsServer(cptr));
856 + /* TODO: KILL sptr (if user KILL, if server SQUIT?) */
857 + protocol_violation(sptr, "Received STAFF message from %C", sptr);
863 + * mo_staff - oper message handler
865 +int mo_staff(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
868 + assert(cptr == sptr);
869 + send_reply(sptr, RPL_YOUREOPER);
872 diff -r b6cf63c513c5 ircd/m_trace.c
873 --- a/ircd/m_trace.c Thu Feb 12 14:05:21 2009 +0100
874 +++ b/ircd/m_trace.c Thu Feb 12 14:18:31 2009 +0100
876 if (!(acptr = LocalClientArray[i])) /* Local Connection? */
878 if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
879 - !IsAnOper(acptr) && (acptr != sptr))
880 + !IsStaffOrAnOper(acptr) && (acptr != sptr))
882 if (!doall && wilds && match(tname, cli_name(acptr)))
884 @@ -232,11 +232,15 @@
885 /* Only opers see users if there is a wildcard
886 but anyone can see all the opers. */
887 if ((IsAnOper(sptr) && (MyUser(sptr) ||
888 - !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr)) {
889 + !(dow && IsInvisible(acptr)))) || !dow || IsStaffOrAnOper(acptr)) {
891 send_reply(sptr, RPL_TRACEOPERATOR, conClass,
892 get_client_name(acptr, SHOW_IP),
893 CurrentTime - cli_lasttime(acptr));
894 + else if (IsStaff(acptr))
895 + send_reply(sptr, SND_EXPLICIT | RPL_TRACEOPERATOR, "Staff %s %s %ld ", conClass,
896 + get_client_name(acptr, SHOW_IP),
897 + CurrentTime - cli_lasttime(acptr));
899 send_reply(sptr, RPL_TRACEUSER, conClass,
900 get_client_name(acptr, SHOW_IP),
901 diff -r b6cf63c513c5 ircd/m_who.c
902 --- a/ircd/m_who.c Thu Feb 12 14:05:21 2009 +0100
903 +++ b/ircd/m_who.c Thu Feb 12 14:18:31 2009 +0100
905 for (member = chptr->members; member; member = member->next_member)
907 acptr = member->user;
908 - if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr))
909 + if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr) && !IsStaff(acptr))
912 && ((member->status & CHFL_ZOMBIE)
916 if ((acptr = FindUser(nick)) &&
917 - ((!(bitsel & WHOSELECT_OPER)) || SeeOper(sptr,acptr)) &&
918 + ((!(bitsel & WHOSELECT_OPER)) || SeeOper(sptr,acptr) || IsStaff(acptr)) &&
919 Process(acptr) && SHOW_MORE(sptr, counter))
921 do_who(sptr, acptr, 0, fields, qrt);
923 if (!(IsUser(acptr) && Process(acptr)))
924 continue; /* Now Process() is at the beginning, if we fail
925 we'll never have to show this acptr in this query */
926 - if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr))
927 + if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr) && !IsStaff(acptr))
930 ((!(matchsel & WHO_FIELD_NIC))
933 if (!(IsUser(acptr) && Process(acptr)))
935 - if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr))
936 + if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr) && !IsStaff(acptr))
938 if (!(SEE_USER(sptr, acptr, bitsel)))
940 diff -r b6cf63c513c5 ircd/m_whois.c
941 --- a/ircd/m_whois.c Thu Feb 12 14:05:21 2009 +0100
942 +++ b/ircd/m_whois.c Thu Feb 12 14:18:31 2009 +0100
944 send_reply(sptr, RPL_WHOISOPERNAME, name, user->opername);
947 + /* user is staff */
948 + if (IsStaff(acptr)) {
949 + /* TODO: use new numeric for this?
951 + send_reply(sptr, SND_EXPLICIT | RPL_WHOISOPERATOR, "%s :is %s Staff",
952 + name, feature_str(FEAT_NETWORK));
953 + /* TODO: allow staff to see eachother's staffname? IsStaffOrAnOper()
954 + * TODO: is user->staffname not always set?
955 + * TODO: "is staff as" ..?
957 + if (IsAnOper(sptr) && user->staffname)
958 + send_reply(sptr, SND_EXPLICIT | RPL_WHOISOPERNAME, "%s %s :is staff as", name, user->staffname);
961 if (IsAccount(acptr))
962 send_reply(sptr, RPL_WHOISACCOUNT, name, user->account);
964 @@ -223,16 +237,24 @@
965 send_reply(sptr, RPL_WHOISACTUALLY, name, user->realusername,
966 user->realhost, ircd_ntoa(&cli_ip(acptr)));
968 - if (!IsAnOper(sptr) && IsParanoid(acptr) && IsAnOper(acptr))
969 + /* TODO: exclude staff from paranoia? */
970 + if (!IsStaffOrAnOper(sptr) && IsParanoid(acptr) && IsAnOper(acptr))
971 sendcmdto_one(&me, CMD_NOTICE, acptr, "%C :whois: %s performed a /WHOIS on you.", acptr, cli_name(sptr));
973 /* Hint: if your looking to add more flags to a user, eg +h, here's
974 * probably a good place to add them :)
977 + /* TODO: allow staff to see through +I?
978 + * (this is priv stuff is getting out of hand,
979 + * oper needs priv USER_PRIVACY to see through +I ???)
980 + * remove that priv requirement for opers to keep things consistent
981 + * also 'user_privacy' makes not much sense here, +I is for privileged users
982 + * opers and staff, to hide idle time from ordinary users
983 + * not from eachother
985 if (MyConnect(acptr) &&
986 ((IsAnOper(sptr) && HasPriv(sptr, PRIV_USER_PRIVACY)) ||
987 - (!IsNoIdle(acptr) && (!feature_bool(FEAT_HIS_WHOIS_IDLETIME) ||
988 + ((!IsNoIdle(acptr) || IsStaff(sptr)) && (!feature_bool(FEAT_HIS_WHOIS_IDLETIME) ||
989 sptr == acptr || parc >= 3))))
990 send_reply(sptr, RPL_WHOISIDLE, name, CurrentTime - user->last,
991 cli_firsttime(acptr));
992 diff -r b6cf63c513c5 ircd/parse.c
993 --- a/ircd/parse.c Thu Feb 12 14:05:21 2009 +0100
994 +++ b/ircd/parse.c Thu Feb 12 14:18:31 2009 +0100
996 { m_unregistered, m_stats, m_stats, m_stats, m_ignore, mh_stats }
1001 + 0, MAXPARA, MFLG_SLOW, 0, NULL,
1002 + /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
1003 + { m_unregistered, m_staff, ms_staff, mo_staff, m_ignore, mh_nohelp }
1008 0, MAXPARA, MFLG_SLOW, 0, NULL,
1011 0, MAXPARA, MFLG_SLOW, 0, NULL,
1012 /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
1013 - { m_unregistered, m_not_oper, ms_privs, mo_privs, m_ignore, mh_privs }
1014 + { m_unregistered, m_privs, ms_privs, mo_privs, m_ignore, mh_privs }
1021 0, MAXPARA, MFLG_SLOW, 0, NULL,
1022 - { m_unregistered, m_not_oper, m_check, m_check, m_ignore, mh_check }
1023 + { m_unregistered, m_check, m_check, m_check, m_ignore, mh_check }
1027 diff -r b6cf63c513c5 ircd/s_misc.c
1028 --- a/ircd/s_misc.c Thu Feb 12 14:05:21 2009 +0100
1029 +++ b/ircd/s_misc.c Thu Feb 12 14:18:31 2009 +0100
1030 @@ -245,6 +245,10 @@
1031 assert(UserStats.opers > 0);
1034 + if (IsStaff(bcptr)) {
1035 + assert(UserStats.staff > 0);
1036 + --UserStats.staff;
1038 if (MyConnect(bcptr))
1039 Count_clientdisconnects(bcptr, UserStats);
1041 diff -r b6cf63c513c5 ircd/s_stats.c
1042 --- a/ircd/s_stats.c Thu Feb 12 14:05:21 2009 +0100
1043 +++ b/ircd/s_stats.c Thu Feb 12 14:18:31 2009 +0100
1045 if (!name && IsUser(acptr))
1047 /* Don't show invisible people to non opers unless they know the nick */
1048 - if (IsInvisible(acptr) && (!name || wilds) && !IsAnOper(acptr) &&
1049 + if (IsInvisible(acptr) && (!name || wilds) && !IsStaffOrAnOper(acptr) &&
1052 /* Only show the ones that match the given mask - if any */
1053 diff -r b6cf63c513c5 ircd/s_user.c
1054 --- a/ircd/s_user.c Thu Feb 12 14:05:21 2009 +0100
1055 +++ b/ircd/s_user.c Thu Feb 12 14:18:31 2009 +0100
1059 MyFree(user->opername);
1060 + if (user->staffname)
1061 + MyFree(user->staffname);
1066 ++UserStats.inv_clients;
1069 + if (IsStaff(sptr))
1070 + ++UserStats.staff;
1072 tmpstr = umode_str(sptr, UMODE_ALL_PARAMS_BUT_OPERID);
1075 { FLAG_NOIDLE, 'I' },
1076 { FLAG_SETHOST, 'h' },
1077 { FLAG_PARANOID, 'P' },
1078 - { FLAG_COMMONCHANSONLY, 'q' }
1079 + { FLAG_COMMONCHANSONLY, 'q' },
1080 + { FLAG_STAFF, 'S' }
1083 /** Length of #userModeList. */
1084 @@ -1024,15 +1029,15 @@
1087 /* sethost enabled for users? */
1088 - if (MyConnect(cptr) && !IsAnOper(cptr) && !feature_bool(FEAT_SETHOST_USER)) {
1089 + if (MyConnect(cptr) && !IsStaffOrAnOper(cptr) && !feature_bool(FEAT_SETHOST_USER)) {
1090 send_reply(cptr, ERR_NOPRIVILEGES);
1094 /* MODE_DEL: restore original hostmask */
1095 if (EmptyString(hostmask)) {
1096 - /* is already sethost'ed? and only opers can remove a sethost */
1097 - if (IsSetHost(cptr) && IsAnOper(cptr)) {
1098 + /* is already sethost'ed? and only opers and staff can remove a sethost */
1099 + if (IsSetHost(cptr) && IsStaffOrAnOper(cptr)) {
1101 sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Host change");
1102 /* If they are +rx, we need to return to their +x host, not their "real" host */
1103 @@ -1228,8 +1233,8 @@
1105 int do_host_hiding = 0;
1106 int do_set_host = 0;
1107 - size_t opernamelen;
1108 - char *opername = 0;
1109 + size_t opernamelen, staffnamelen;
1110 + char *opername = 0, *staffname = 0;
1111 char* account = NULL;
1113 hostmask = password = NULL;
1114 @@ -1445,6 +1450,35 @@
1116 /* There is no -r */
1119 + /* Staff - +S mode */
1121 + if (what == MODE_ADD) {
1123 + if (IsServer(cptr)) {
1126 + if (cli_user(sptr)->staffname)
1127 + MyFree(cli_user(sptr)->staffname);
1128 + staffnamelen = strlen(staffname);
1129 + if (staffnamelen > ACCOUNTLEN) {
1130 + protocol_violation(cptr, "Received staffname (%s) longer than %d for %s; ignoring.", staffname, ACCOUNTLEN, cli_name(sptr));
1131 + cli_user(sptr)->staffname = NULL;
1133 + cli_user(sptr)->staffname = (char*) MyMalloc(staffnamelen + 1);
1134 + assert(0 != cli_user(sptr)->staffname);
1135 + ircd_strncpy(cli_user(sptr)->staffname, staffname, ACCOUNTLEN);
1138 + /* TODO: KILL sptr to resolve desynch? */
1139 + protocol_violation(cptr, "Received usermode +S for %C but no staffname parameter; ignoring.", sptr);
1148 send_reply(sptr, ERR_UMODEUNKNOWNFLAG, *m);
1150 @@ -1461,6 +1495,8 @@
1152 if (!FlagHas(&setflags, FLAG_LOCOP) && IsLocOp(sptr))
1154 + if (!FlagHas(&setflags, FLAG_STAFF) && IsStaff(sptr))
1156 if (!FlagHas(&setflags, FLAG_ACCOUNT) && IsAccount(sptr))
1157 ClrFlag(sptr, FLAG_ACCOUNT);
1159 @@ -1471,9 +1507,9 @@
1160 ClearChannelService(sptr);
1161 if (!FlagHas(&setflags, FLAG_XTRAOP) && !(IsOper(sptr) && HasPriv(sptr, PRIV_XTRA_OPER)))
1163 - if (!FlagHas(&setflags, FLAG_NOCHAN) && !(IsOper(sptr) || feature_bool(FEAT_USER_HIDECHANS)))
1164 + if (!FlagHas(&setflags, FLAG_NOCHAN) && !((IsStaffOrAnOper(sptr) && HasPriv(sptr, PRIV_HIDE_CHANS)) || feature_bool(FEAT_USER_HIDECHANS)))
1166 - if (!FlagHas(&setflags, FLAG_NOIDLE) && !((IsOper(sptr) && HasPriv(sptr, PRIV_NOIDLE)) || feature_bool(FEAT_USER_HIDEIDLETIME)))
1167 + if (!FlagHas(&setflags, FLAG_NOIDLE) && !((IsStaffOrAnOper(sptr) && HasPriv(sptr, PRIV_NOIDLE)) || feature_bool(FEAT_USER_HIDEIDLETIME)))
1169 if (!FlagHas(&setflags, FLAG_PARANOID) && !(IsOper(sptr) && HasPriv(sptr, PRIV_PARANOID)))
1170 ClearParanoid(sptr);
1171 @@ -1496,8 +1532,8 @@
1173 if (MyConnect(sptr))
1175 - if ((FlagHas(&setflags, FLAG_OPER) || FlagHas(&setflags, FLAG_LOCOP)) &&
1177 + if ((FlagHas(&setflags, FLAG_OPER) || FlagHas(&setflags, FLAG_LOCOP) || FlagHas(&setflags, FLAG_STAFF)) &&
1178 + !IsStaffOrAnOper(sptr))
1179 det_confs_butmask(sptr, CONF_CLIENT & ~CONF_OPERATOR);
1181 if (SendServNotice(sptr))
1182 @@ -1578,6 +1614,43 @@
1183 cli_user(sptr)->opername = NULL;
1188 + /* user becomes staff */
1189 + if (!FlagHas(&setflags, FLAG_STAFF) && IsStaff(sptr)) {
1190 + ++UserStats.staff;
1191 + client_set_privs(sptr, NULL); /* may set propagate privilege */
1193 + /* notify my operators a user has STAFFed on a remote server */
1194 + if (!MyConnect(sptr))
1195 + sendto_opmask_butone(0, SNO_OLDSNO, "%s (%s@%s) is now staff (S) as %s on %s",
1196 + cli_name(sptr), cli_user(sptr)->realusername, cli_user(sptr)->realhost,
1197 + cli_user(sptr)->staffname, cli_name(cli_user(sptr)->server));
1200 + /* no longer staff */
1201 + if (FlagHas(&setflags, FLAG_STAFF) && !IsStaff(sptr)) {
1202 + assert(UserStats.staff > 0);
1203 + --UserStats.staff;
1205 + /* notify my operators an staff member has deSTAFFed on the network */
1206 + if (MyConnect(sptr))
1207 + sendto_opmask_butone(0, SNO_OLDSNO, "%s (%s@%s) is no longer staff (S) as %s",
1208 + cli_name(sptr), cli_user(sptr)->realusername, cli_user(sptr)->realhost,
1209 + cli_user(sptr)->staffname);
1211 + sendto_opmask_butone(0, SNO_OLDSNO, "%s (%s@%s) is no longer staff (S) as %s on %s",
1212 + cli_name(sptr), cli_user(sptr)->realusername, cli_user(sptr)->realhost,
1213 + cli_user(sptr)->staffname, cli_name(cli_user(sptr)->server));
1215 + client_set_privs(sptr, NULL); /* will clear propagate privilege */
1216 + if (cli_user(sptr)->staffname) {
1217 + MyFree(cli_user(sptr)->staffname);
1218 + cli_user(sptr)->staffname = NULL;
1223 if (FlagHas(&setflags, FLAG_INVISIBLE) && !IsInvisible(sptr)) {
1224 assert(UserStats.inv_clients > 0);
1225 --UserStats.inv_clients;
1226 @@ -1586,6 +1659,7 @@
1227 ++UserStats.inv_clients;
1229 assert(UserStats.opers <= UserStats.clients + UserStats.unknowns);
1230 + assert(UserStats.staff <= UserStats.clients + UserStats.unknowns);
1231 assert(UserStats.inv_clients <= UserStats.clients + UserStats.unknowns);
1232 send_umode_out(cptr, sptr, &setflags, prop);
1234 @@ -1628,6 +1702,19 @@
1238 + /* staffname is wanted */
1239 + if ((type != UMODE_AND_ACCOUNT && type != UMODE_AND_ACCOUNT_SHORT) && IsStaff(cptr)) {
1241 + if (cli_user(cptr)->staffname) {
1242 + char* t = cli_user(cptr)->staffname;
1243 + while ((*m++ = *t++))
1244 + ; /* Empty loop */
1245 + m--; /* Step back over the '\0' */
1247 + *m++ = NOOPERNAMECHARACTER;
1251 if (IsAccount(cptr))
1253 char *t, nbuf[64+ACCOUNTLEN];
1254 @@ -1670,6 +1757,7 @@
1258 + int needstaff = 0;
1260 int what = MODE_NULL;
1262 @@ -1705,6 +1793,12 @@
1263 if (!FlagHas(old, flag))
1266 + /* Special case for STAFF.. */
1267 + if (flag == FLAG_STAFF) {
1268 + /* If we're setting +S, add the staffname later */
1269 + if (!FlagHas(old, flag))
1272 /* Special case for SETHOST.. */
1273 if (flag == FLAG_SETHOST) {
1274 /* Don't send to users */
1275 @@ -1749,6 +1843,17 @@
1276 *m++ = NOOPERNAMECHARACTER;
1279 + if (sptr != cptr && needstaff) {
1281 + if (cli_user(sptr)->staffname) {
1282 + char* t = cli_user(sptr)->staffname;
1283 + while ((*m++ = *t++))
1284 + ; /* Empty loop */
1285 + m--; /* Step back over the '\0' */
1287 + *m++ = NOOPERNAMECHARACTER;
1292 ircd_snprintf(0, m, USERLEN + HOSTLEN + 1, "%s@%s", cli_user(sptr)->username,