]> jfr.im git - irc/quakenet/snircd-patchqueue.git/blame - staffpriv.patch
Update patchset for latest ircu changes
[irc/quakenet/snircd-patchqueue.git] / staffpriv.patch
CommitLineData
48200f14 1staffpriv
2
3give staff certain privs
4usermode +S (internally +S <staffname> just like usermode +o)
5add /STAFF command, mirrored after /OPER
6add priv STAFF used to determine whether the client becomes an OPER or STAFF
7
8add privs
9CLAIM_NICK allows staff to use /KILL staffname to free their own staffnick? (not implemented yet)
10GLINE_LOOKUP allows staff to lookup gline info using /GLINE
11HIDE_CHANS allows staff/oper to set usermode +n
12CHECK_CHANNEL allows staff to use /CHECK #channel (staff version respects hiddenhost, sethost, viewable topic, visible key, etc.)
13
14by default, staff gets all of the above privs + CHAN_LIMIT (no channel limit) and NOIDLE (set usermode +I)
15
16shows staff member count in /lusers
17shows staff in /trace, /stats l, /who <query> o,
18shows staff status in /WHOIS - show to IRC Operators the staffname
19staff can do /PRIVS to view their own privs - cannot view other user's privs
37dcceec 20allow staff to set usermode -h (remove sethost)
21allow staff to see through usermode +I (so they can see idle/signon time on fellow staff/opers)
48200f14 22
23TODO: treat staff a bit more favourable with regards to excess flood? add feature STAFF_CLIENT_FLOOD?
24TODO: allow staff to access oper configured spoof blocks with /SETHOST?
25TODO: set default staff spoof host in feature STAFF_SETHOST to e.g. staff.quakenet.org - set upon login
48200f14 26TODO: 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)
27TODO: allow a search in /GLINE instead of requiring a perfect match?
28TODO: logging of /CHECK (for all)
29
7daac0f2 30diff -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
48200f14 33@@ -45,5 +45,6 @@
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);
38
39 #endif /* INCLUDED_check_h */
7daac0f2 40diff -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
48200f14 43@@ -90,7 +90,7 @@
44 #define FlagClr(set,flag) ((set)->bits[FLAGSET_INDEX(flag)] &= ~FLAGSET_MASK(flag))
45
46 /** String containing valid user modes, in no particular order. */
47-#define infousermodes "dioOswkgxRXInPq"
48+#define infousermodes "dioOswkgxRXInPqS"
49
50 /** Character to indicate no oper name available */
51 #define NOOPERNAMECHARACTER '-'
52@@ -144,6 +144,11 @@
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 */
62 };
63
64@@ -178,6 +183,7 @@
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 */
72@@ -591,6 +597,10 @@
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. */
83@@ -660,6 +670,8 @@
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). */
92@@ -717,6 +729,8 @@
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. */
7daac0f2 101diff -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
48200f14 104@@ -100,6 +100,7 @@
105 */
106
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[]);
109
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*[]);
127@@ -179,6 +182,7 @@
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*[]);
135@@ -233,6 +237,7 @@
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*[]);
7daac0f2 143diff -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
48200f14 146@@ -116,6 +116,10 @@
147 #define TOK_STATS "R"
148 #define CMD_STATS MSG_STATS, TOK_STATS
149
150+#define MSG_STAFF "STAFF" /* STAF */
151+#define TOK_STAFF "STAFF"
152+#define CMD_STAFF MSG_STAFF, TOK_STAFF
153+
154 #define MSG_HELP "HELP" /* HELP */
155 #define TOK_HELP "HELP"
156 #define CMD_HELP MSG_HELP, TOK_HELP
7daac0f2 157diff -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
48200f14 160@@ -29,6 +29,7 @@
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. */
165
166 /* Misc: */
167 unsigned int channels; /**< Existing channels. */
7daac0f2 168diff -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
48200f14 171@@ -95,6 +95,7 @@
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 */
176 };
177
178 #endif /* INCLUDED_struct_h */
7daac0f2 179diff -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
48200f14 182@@ -174,6 +174,7 @@
183 m_settime.c \
184 m_silence.c \
185 m_squit.c \
186+ m_staff.c \
187 m_stats.c \
188 m_time.c \
189 m_topic.c \
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 \
204+ ../include/send.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 \
7daac0f2 208diff -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
48200f14 211@@ -123,6 +123,8 @@
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;
219
220@@ -146,7 +148,7 @@
221 /* Clear out client's privileges. */
222 memset(cli_privs(client), 0, sizeof(struct Privs));
223
224- if (!IsAnOper(client) || !oper)
225+ if (!IsStaffOrAnOper(client) || !oper)
226 return;
227
228 if (!privs_defaults_set)
229@@ -163,6 +165,10 @@
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);
237
238 memset(&privs_local, 0, sizeof(privs_local));
239 FlagSet(&privs_local, PRIV_CHAN_LIMIT);
37dcceec 240@@ -178,17 +184,33 @@
48200f14 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);
248+
37dcceec 249+ /* set the default for privs for staff */
48200f14 250+ memset(&privs_staff, 0, sizeof(privs_staff));
48200f14 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);
257
258 privs_defaults_set = 1;
259 }
260
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;
37dcceec 271 else {
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");
274 return;
275 }
276
277@@ -226,6 +248,17 @@
48200f14 278 ClrPriv(client, PRIV_OPKICK);
279 ClrPriv(client, PRIV_BADCHAN);
280 }
37dcceec 281+ /* TODO: better way than this? */
48200f14 282+ /* do not let staff have privs they should not have */
283+ if (HasPriv(client, PRIV_STAFF)) {
37dcceec 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)
288+ continue;
289+ ClrPriv(client, priv);
290+ }
48200f14 291+ }
292 }
293
294 /** Array mapping privilege values to names and vice versa. */
37dcceec 295@@ -248,6 +281,8 @@
48200f14 296 P(PARANOID), P(CHECK), P(WALL), P(CLOSE),
297 P(ROUTE), P(ROUTEINFO), P(SERVERINFO), P(CHANNEL_PRIVACY),
298 P(USER_PRIVACY),
299+ P(STAFF), P(CLAIM_NICK), P(GLINE_LOOKUP), P(HIDE_CHANS),
300+ P(CHECK_CHANNEL),
301 #undef P
302 { 0, 0 }
303 };
7daac0f2 304diff -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
48200f14 307@@ -168,6 +168,11 @@
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 },
316 { NULL, 0 }
317 };
318 static int ntokens;
7daac0f2 319diff -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
48200f14 322@@ -189,7 +189,9 @@
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... */
331 %type <num> sizespec
332 %type <num> timespec timefactor factoredtimes factoredtime
333@@ -593,8 +595,10 @@
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);
346@@ -704,6 +708,11 @@
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; };
357
7daac0f2 358diff -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
48200f14 361@@ -94,7 +94,12 @@
362 struct Client *acptr;
363 int flags = CHECK_SHOWUSERS, i;
364
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);
369+
370+ /* no privs */
371+ if (!HasPriv(sptr, PRIV_CHECK) && !HasPriv(sptr, PRIV_CHECK_CHANNEL))
372 return send_reply(sptr, ERR_NOPRIVILEGES);
373
374 if (parc < 2) {
375@@ -104,7 +109,9 @@
376
377 if ( parc>=4 ||
378 (parc==3 && parv[2][0] != '-')) {
379- /* remote query */
380+ /* remote query - no need to restrict staff,
381+ * hunt_server_cmd is told here that sptr needs to be an oper
382+ */
383 if (hunt_server_cmd(sptr, CMD_CHECK, cptr, 0, parc==4 ? "%C %s %s" : "%C %s", 1, parc, parv) != HUNTED_ISME)
384 return 0;
385 parv++; parc--;
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);
393+ else {
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, " ");
397+ }
398 }
399 else
400 send_reply(sptr, ERR_SEARCHNOMATCH, "CHECK", parv[1]);
401 }
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]);
408@@ -177,6 +193,36 @@
409 }
410
411
412+/* test if a client can CHECK a channel
413+ * return 1 when allowed, else 0
414+ */
415+int client_can_check_channel(struct Client *sptr, struct Channel *chptr) {
416+ struct Membership *lp;
417+
418+ /* opers can */
419+ if (IsAnOper(sptr))
420+ return 1;
421+
422+ /* look at the channel modes
423+ * channel is not secret, private, invite only, or keyed
424+ */
425+ if (!(chptr->mode.mode & (MODE_SECRET | MODE_PRIVATE | MODE_INVITEONLY)) && (!*chptr->mode.key))
426+ return 1;
427+
428+ /* the client is on the channel */
429+ if (find_channel_member(sptr, chptr))
430+ return 1;
431+
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))
435+ return 0;
436+ }
437+
438+ /* we made it, so it is allowed */
439+ return 1;
440+}
441+
442
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 @@
446
447 if ((flags & CHECK_SHOWUSERS) || ((flags & CHECK_OPSONLY) && opped)) {
448 ircd_snprintf(0, outbuf, sizeof(outbuf), "%s%c", acptr->cli_info, COLOR_OFF);
449+
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*/
454+ if (IsStaff(sptr))
455+ ircd_snprintf(0, outbuf2, sizeof(outbuf2), " [%s]",
456+ (HasHiddenHost(acptr) || HasSetHost(acptr)) ?
457+ feature_str(FEAT_HIDDEN_IP) : ircd_ntoa(&(cli_ip(acptr))));
458+ else
459+ ircd_snprintf(0, outbuf2, sizeof(outbuf2), " [%s]", ircd_ntoa(&(cli_ip(acptr))));
460 }
461- send_reply(sptr, RPL_CHANUSER, ustat, acptr->cli_name, cli_user(acptr)->realusername,
462+ /* staff - do not show real hostnames or IPs */
463+ if (IsStaff(sptr))
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,
474+ /* show account */
475+ (c ? cli_user(acptr)->account : ""));
476+ else
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 : ""));
480 }
481@@ -379,6 +448,11 @@
482 /* Topic */
483 if (strlen(chptr->topic) <= 0)
484 send_reply(sptr, RPL_DATASTR, " Topic:: <none>");
485+
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>");
489+
490 else {
491 ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Topic:: %s", chptr->topic);
492 send_reply(sptr, RPL_DATASTR, outbuf);
493@@ -401,6 +475,7 @@
494 modebuf[0] = '\0';
495 parabuf[0] = '\0';
496
497+ /* no need to check modes - this will hide the key from non-opers */
498 channel_modes(sptr, modebuf, parabuf, sizeof(modebuf), chptr, NULL);
499
500 if(modebuf[1] == '\0')
501@@ -464,6 +539,9 @@
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);
508 } else
509 send_reply(sptr, RPL_DATASTR, " Status:: Client");
510
7daac0f2 511diff -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
48200f14 514@@ -669,11 +669,13 @@
515 int
516 m_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
517 {
518- if (feature_bool(FEAT_HIS_USERGLINE))
519- return send_reply(sptr, ERR_DISABLED, "GLINE");
520+
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);
524
525 if (parc < 2)
526- return send_reply(sptr, ERR_NOSUCHGLINE, "");
527+ return send_reply(sptr, ERR_NOSUCHGLINE, "*");
528
529 return gline_list(sptr, parv[1]);
530 }
7daac0f2 531diff -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
48200f14 534@@ -120,6 +120,8 @@
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)
543@@ -154,6 +156,8 @@
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)
7daac0f2 552diff -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
48200f14 555@@ -143,6 +143,10 @@
556 name = parc > 1 ? parv[1] : 0;
557 password = parc > 2 ? parv[2] : 0;
558
559+ /* staff doing OPER */
560+ if (IsStaff(sptr))
561+ return send_reply(sptr, SND_EXPLICIT | RPL_YOUREOPER, ":You are now IRC Staff");
562+
563 if (EmptyString(name) || EmptyString(password))
564 return need_more_params(sptr, "OPER");
565
566@@ -169,6 +173,18 @@
567 }
568 SetLocOp(sptr);
569 client_set_privs(sptr, aconf);
570+
571+ /* staff - clear flag and privs, and reject it */
572+ if (HasPriv(sptr, PRIV_STAFF)) {
573+ ClearLocOp(sptr);
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);
578+ return 0;
579+ }
580+
581+ /* oper */
582 if (HasPriv(sptr, PRIV_PROPAGATE))
583 {
584 ClearLocOp(sptr);
7daac0f2 585diff -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
48200f14 588@@ -38,6 +38,27 @@
589 #include "numnicks.h"
590 #include "send.h"
591
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
598+ */
599+int m_privs(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
600+{
601+ /* not staff */
602+ if (!IsStaff(sptr))
603+ return send_reply(sptr, ERR_NOPRIVILEGES);
604+
605+ /* no parameters given, show own privs */
606+ if (parc < 2)
607+ return client_report_privs(sptr, sptr);
608+
609+ /* staff not allowed to view privs on other users */
610+ return send_reply(sptr, ERR_NOPRIVILEGES);
611+}
612+
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.
7daac0f2 616diff -r b6cf63c513c5 ircd/m_staff.c
48200f14 617--- /dev/null Thu Jan 01 00:00:00 1970 +0000
7daac0f2 618+++ b/ircd/m_staff.c Thu Feb 12 14:18:31 2009 +0100
48200f14 619@@ -0,0 +1,252 @@
620+/*
621+ * IRC - Internet Relay Chat, ircd/m_oper.c
622+ * Copyright (C) 1990 Jarkko Oikarinen and
623+ * University of Oulu, Computing Center
624+ *
625+ * See file AUTHORS in IRC package for additional names of
626+ * the programmers.
627+ *
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.
632+ *
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.
637+ *
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.
641+ *
642+ * $Id: m_oper.c 1327 2005-03-19 22:52:33Z entrope $
643+ */
644+
645+/*
646+ * m_functions execute protocol messages on this server:
647+ *
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...).
653+ *
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.
657+ *
658+ * (!IsServer(cptr)) => (cptr == sptr), because
659+ * prefixes are taken *only* from servers...
660+ *
661+ * (IsServer(cptr))
662+ * (sptr == cptr) => the message didn't
663+ * have the prefix.
664+ *
665+ * (sptr != cptr && IsServer(sptr) means
666+ * the prefix specified servername. (?)
667+ *
668+ * (sptr != cptr && !IsServer(sptr) means
669+ * that message originated from a remote
670+ * user (not local).
671+ *
672+ * combining
673+ *
674+ * (!IsServer(sptr)) means that, sptr can safely
675+ * taken as defining the target structure of the
676+ * message in this server.
677+ *
678+ * *Always* true (if 'parse' and others are working correct):
679+ *
680+ * 1) sptr->from == cptr (note: cptr->from == cptr)
681+ *
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 ]
686+ *
687+ * parc number of variable parameter strings (if zero,
688+ * parv is allowed to be NULL)
689+ *
690+ * parv a NULL terminated list of parameter pointers,
691+ *
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*
697+ *
698+ * note: it is guaranteed that parv[0]..parv[parc-1] are all
699+ * non-NULL pointers.
700+ */
701+#include "config.h"
702+
703+#include "client.h"
704+#include "hash.h"
705+#include "ircd.h"
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"
712+#include "msg.h"
713+#include "numeric.h"
714+#include "numnicks.h"
715+#include "querycmds.h"
716+#include "s_conf.h"
717+#include "s_debug.h"
718+#include "s_user.h"
719+#include "s_misc.h"
720+#include "send.h"
721+
722+/* #include <assert.h> -- Now using assert in ircd_log.h */
723+#include <stdlib.h>
724+#include <string.h>
725+
726+
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)
729+{
730+ char *crypted;
731+ int res;
732+ /*
733+ * use first two chars of the password they send in as salt
734+ *
735+ * passwd may be NULL. Head it off at the pass...
736+ */
737+ if (!to_match || !passwd)
738+ return 0;
739+
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);
744+
745+ if (!crypted)
746+ return 0;
747+ res = strcmp(crypted, passwd);
748+ MyFree(crypted);
749+ return 0 == res;
750+}
751+
752+
753+/*
754+ * m_staff - generic message handler
755+ */
756+int m_staff(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
757+{
758+ struct ConfItem* aconf;
759+ char* name;
760+ char* password;
761+
762+ assert(0 != cptr);
763+ assert(cptr == sptr);
764+
765+ name = parc > 1 ? parv[1] : 0;
766+ password = parc > 2 ? parv[2] : 0;
767+
768+ /* staff doing STAFF */
769+ if (IsStaff(sptr))
770+ return send_reply(sptr, SND_EXPLICIT | RPL_YOUREOPER, ":You are now IRC Staff");
771+
772+ /* check input */
773+ if (EmptyString(name) || EmptyString(password))
774+ return need_more_params(sptr, "STAFF");
775+
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);
782+ return 0;
783+ }
784+
785+ /* we should have it by now */
786+ assert(0 != (aconf->status & CONF_OPERATOR));
787+
788+ /* compare passwords */
789+ if (staff_password_match(password, aconf->passwd)) {
790+ struct Flags old_mode = cli_flags(sptr);
791+
792+ /* mismatch */
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);
798+ return 0;
799+ }
800+
801+ /* set privs */
802+ SetStaff(sptr);
803+ client_set_privs(sptr, aconf);
804+
805+ /* are they staff? */
806+ if (HasPriv(sptr, PRIV_STAFF)) {
807+ ++UserStats.staff;
808+
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);
815+
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");
822+
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);
828+
829+ /* not staff - clear flag and privs, and reject */
830+ } else {
831+ client_set_privs(sptr, NULL);
832+ ClearStaff(sptr);
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);
836+ return 0;
837+ }
838+
839+ /* failed */
840+ } else {
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);
844+ }
845+ return 0;
846+}
847+
848+
849+/*
850+ * ms_staff - server message handler
851+ */
852+int ms_staff(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
853+{
854+ assert(0 != cptr);
855+ assert(IsServer(cptr));
856+ /* TODO: KILL sptr (if user KILL, if server SQUIT?) */
857+ protocol_violation(sptr, "Received STAFF message from %C", sptr);
858+ return 0;
859+}
860+
861+
862+/*
863+ * mo_staff - oper message handler
864+ */
865+int mo_staff(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
866+{
867+ assert(0 != cptr);
868+ assert(cptr == sptr);
869+ send_reply(sptr, RPL_YOUREOPER);
870+ return 0;
871+}
7daac0f2 872diff -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
48200f14 875@@ -198,7 +198,7 @@
876 if (!(acptr = LocalClientArray[i])) /* Local Connection? */
877 continue;
878 if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
879- !IsAnOper(acptr) && (acptr != sptr))
880+ !IsStaffOrAnOper(acptr) && (acptr != sptr))
881 continue;
882 if (!doall && wilds && match(tname, cli_name(acptr)))
883 continue;
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)) {
890 if (IsAnOper(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));
898 else
899 send_reply(sptr, RPL_TRACEUSER, conClass,
900 get_client_name(acptr, SHOW_IP),
7daac0f2 901diff -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
48200f14 904@@ -319,7 +319,7 @@
905 for (member = chptr->members; member; member = member->next_member)
906 {
907 acptr = member->user;
908- if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr))
909+ if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr) && !IsStaff(acptr))
910 continue;
911 if ((acptr != sptr)
912 && ((member->status & CHFL_ZOMBIE)
913@@ -341,7 +341,7 @@
914 else
915 {
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))
920 {
921 do_who(sptr, acptr, 0, fields, qrt);
922@@ -389,7 +389,7 @@
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))
928 continue;
929 if ((mask) &&
930 ((!(matchsel & WHO_FIELD_NIC))
931@@ -426,7 +426,7 @@
932 {
933 if (!(IsUser(acptr) && Process(acptr)))
934 continue;
935- if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr))
936+ if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr) && !IsStaff(acptr))
937 continue;
938 if (!(SEE_USER(sptr, acptr, bitsel)))
939 continue;
7daac0f2 940diff -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
48200f14 943@@ -216,6 +216,20 @@
944 send_reply(sptr, RPL_WHOISOPERNAME, name, user->opername);
945 }
946
947+ /* user is staff */
948+ if (IsStaff(acptr)) {
949+ /* TODO: use new numeric for this?
950+ */
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()
37dcceec 954+ * TODO: is user->staffname not always set?
48200f14 955+ * TODO: "is staff as" ..?
956+ */
957+ if (IsAnOper(sptr) && user->staffname)
958+ send_reply(sptr, SND_EXPLICIT | RPL_WHOISOPERNAME, "%s %s :is staff as", name, user->staffname);
959+ }
960+
961 if (IsAccount(acptr))
962 send_reply(sptr, RPL_WHOISACCOUNT, name, user->account);
963
37dcceec 964@@ -223,16 +237,24 @@
965 send_reply(sptr, RPL_WHOISACTUALLY, name, user->realusername,
966 user->realhost, ircd_ntoa(&cli_ip(acptr)));
967
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));
972
973 /* Hint: if your looking to add more flags to a user, eg +h, here's
974 * probably a good place to add them :)
975 */
976-
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
984+ */
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));
7daac0f2 992diff -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
48200f14 995@@ -443,6 +443,13 @@
996 { m_unregistered, m_stats, m_stats, m_stats, m_ignore, mh_stats }
997 },
998 {
999+ MSG_STAFF,
1000+ TOK_STAFF,
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 }
1004+ },
1005+ {
1006 MSG_LINKS,
1007 TOK_LINKS,
1008 0, MAXPARA, MFLG_SLOW, 0, NULL,
1009@@ -608,7 +615,7 @@
1010 TOK_PRIVS,
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 }
1015 },
1016 {
1017 MSG_ACCOUNT,
1018@@ -654,7 +661,7 @@
1019 MSG_CHECK,
1020 TOK_CHECK,
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 }
1024 },
1025
1026 /*
7daac0f2 1027diff -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);
1032 --UserStats.opers;
1033 }
1034+ if (IsStaff(bcptr)) {
1035+ assert(UserStats.staff > 0);
1036+ --UserStats.staff;
1037+ }
1038 if (MyConnect(bcptr))
1039 Count_clientdisconnects(bcptr, UserStats);
1040 else
1041diff -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
48200f14 1044@@ -339,7 +339,7 @@
1045 if (!name && IsUser(acptr))
1046 continue;
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) &&
1050 (acptr != sptr))
1051 continue;
1052 /* Only show the ones that match the given mask - if any */
7daac0f2 1053diff -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
48200f14 1056@@ -115,6 +115,8 @@
1057 MyFree(user->away);
1058 if (user->opername)
1059 MyFree(user->opername);
1060+ if (user->staffname)
1061+ MyFree(user->staffname);
1062 /*
1063 * sanity check
1064 */
1065@@ -460,6 +462,8 @@
1066 ++UserStats.inv_clients;
1067 if (IsOper(sptr))
1068 ++UserStats.opers;
1069+ if (IsStaff(sptr))
1070+ ++UserStats.staff;
1071
1072 tmpstr = umode_str(sptr, UMODE_ALL_PARAMS_BUT_OPERID);
1073
1074@@ -547,7 +551,8 @@
1075 { FLAG_NOIDLE, 'I' },
1076 { FLAG_SETHOST, 'h' },
1077 { FLAG_PARANOID, 'P' },
1078- { FLAG_COMMONCHANSONLY, 'q' }
1079+ { FLAG_COMMONCHANSONLY, 'q' },
1080+ { FLAG_STAFF, 'S' }
1081 };
1082
1083 /** Length of #userModeList. */
37dcceec 1084@@ -1024,15 +1029,15 @@
1085 }
1086
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);
1091 return 0;
1092 }
1093
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)) {
1100 restore = 1;
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 */
48200f14 1103@@ -1228,8 +1233,8 @@
1104 int prop = 0;
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;
1112
1113 hostmask = password = NULL;
1114@@ -1445,6 +1450,35 @@
1115 }
1116 /* There is no -r */
1117 break;
1118+
1119+ /* Staff - +S mode */
1120+ case 'S':
1121+ if (what == MODE_ADD) {
1122+ SetStaff(sptr);
1123+ if (IsServer(cptr)) {
1124+ if (*(p + 1)) {
1125+ staffname = *++p;
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;
1132+ } else {
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);
1136+ }
1137+ } else {
1138+ /* TODO: KILL sptr to resolve desynch? */
1139+ protocol_violation(cptr, "Received usermode +S for %C but no staffname parameter; ignoring.", sptr);
1140+ ClearStaff(sptr);
1141+ }
1142+ }
1143+ } else
1144+ ClearStaff(sptr);
1145+ break;
1146+
1147 default:
1148 send_reply(sptr, ERR_UMODEUNKNOWNFLAG, *m);
1149 break;
1150@@ -1461,6 +1495,8 @@
1151 ClearOper(sptr);
1152 if (!FlagHas(&setflags, FLAG_LOCOP) && IsLocOp(sptr))
1153 ClearLocOp(sptr);
1154+ if (!FlagHas(&setflags, FLAG_STAFF) && IsStaff(sptr))
1155+ ClearStaff(sptr);
1156 if (!FlagHas(&setflags, FLAG_ACCOUNT) && IsAccount(sptr))
1157 ClrFlag(sptr, FLAG_ACCOUNT);
1158 /*
1159@@ -1471,9 +1507,9 @@
1160 ClearChannelService(sptr);
1161 if (!FlagHas(&setflags, FLAG_XTRAOP) && !(IsOper(sptr) && HasPriv(sptr, PRIV_XTRA_OPER)))
1162 ClearXtraOp(sptr);
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)))
1165 ClearNoChan(sptr);
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)))
1168 ClearNoIdle(sptr);
1169 if (!FlagHas(&setflags, FLAG_PARANOID) && !(IsOper(sptr) && HasPriv(sptr, PRIV_PARANOID)))
1170 ClearParanoid(sptr);
1171@@ -1496,8 +1532,8 @@
1172 }
1173 if (MyConnect(sptr))
1174 {
1175- if ((FlagHas(&setflags, FLAG_OPER) || FlagHas(&setflags, FLAG_LOCOP)) &&
1176- !IsAnOper(sptr))
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);
1180
1181 if (SendServNotice(sptr))
1182@@ -1578,6 +1614,43 @@
1183 cli_user(sptr)->opername = NULL;
1184 }
1185 }
1186+
1187+
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 */
1192+
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));
1198+ }
1199+
1200+ /* no longer staff */
1201+ if (FlagHas(&setflags, FLAG_STAFF) && !IsStaff(sptr)) {
1202+ assert(UserStats.staff > 0);
1203+ --UserStats.staff;
1204+
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);
1210+ else
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));
1214+
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;
1219+ }
1220+ }
1221+
1222+
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;
1228 }
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);
1233 }
1234@@ -1628,6 +1702,19 @@
1235 }
1236 }
1237
1238+ /* staffname is wanted */
1239+ if ((type != UMODE_AND_ACCOUNT && type != UMODE_AND_ACCOUNT_SHORT) && IsStaff(cptr)) {
1240+ *m++ = ' ';
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' */
1246+ } else {
1247+ *m++ = NOOPERNAMECHARACTER;
1248+ }
1249+ }
1250+
1251 if (IsAccount(cptr))
1252 {
1253 char *t, nbuf[64+ACCOUNTLEN];
1254@@ -1670,6 +1757,7 @@
1255 int flag;
1256 int needhost = 0;
1257 int needoper = 0;
1258+ int needstaff = 0;
1259 char *m;
1260 int what = MODE_NULL;
1261
1262@@ -1705,6 +1793,12 @@
1263 if (!FlagHas(old, flag))
1264 needoper++;
1265 }
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))
1270+ needstaff++;
1271+ }
1272 /* Special case for SETHOST.. */
1273 if (flag == FLAG_SETHOST) {
1274 /* Don't send to users */
1275@@ -1749,6 +1843,17 @@
1276 *m++ = NOOPERNAMECHARACTER;
1277 }
1278 }
1279+ if (sptr != cptr && needstaff) {
1280+ *m++ = ' ';
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' */
1286+ } else {
1287+ *m++ = NOOPERNAMECHARACTER;
1288+ }
1289+ }
1290 if (needhost) {
1291 *m++ = ' ';
1292 ircd_snprintf(0, m, USERLEN + HOSTLEN + 1, "%s@%s", cli_user(sptr)->username,