]> jfr.im git - irc/rqf/shadowircd.git/commitdiff
Add ircu-like /who format options.
authorJilles Tjoelker <redacted>
Sat, 8 Nov 2008 23:12:56 +0000 (00:12 +0100)
committerJilles Tjoelker <redacted>
Sat, 8 Nov 2008 23:12:56 +0000 (00:12 +0100)
Use  /quote help who  for details.
The format code "o" (oplevel) is not documented,
but implemented showing dummy information (999 for
chanop, n/a otherwise).

help/opers/who
include/numeric.h
modules/m_who.c
src/messages.tab

index 43b1ef7bdada00585b29c17ce0ff1d952ffc99da..d7c2729a51cd8b1a93dd1bcd694bd92530f65f82 100644 (file)
@@ -1,4 +1,4 @@
-WHO <#channel|nick|mask> [o]
+WHO <#channel|nick|mask> [o][%format]
 
 The WHO command displays information about a user, 
 such as their GECOS information, their user@host, 
@@ -44,4 +44,26 @@ on the same channel as you are not shown.
 A second parameter of a lowercase letter o ensures
 only IRC operators are displayed.
 
+The second parameter may also contain a format
+specification starting with a percent sign.
+This causes the output to use numeric 354,
+with the selected fields:
+
+t       -       Query type. Outputs the given number in each reply.
+c       -       Channel.
+u       -       Username.
+i       -       IP address.
+h       -       Host.
+s       -       Server.
+n       -       Nickname.
+f       -       Status.
+d       -       Hop count.
+l       -       Idle time or 0 for users on other servers.
+a       -       Services account name or 0 if none.
+r       -       GECOS information.
+
+"WHO #lamers %tuhnf,42" would generate a brief listing
+of channel members and include the number 42 in each
+line.
+
 See also: whois, userhost, cmode, umode
index b80c200d2b7e54aa2a53bf0e1867f14934b31288..6b449e48d3051545778d477d3c62c8ce3eb3c6d8 100644 (file)
@@ -175,6 +175,7 @@ extern const char *form_str(int);
 #define RPL_VERSION          351
 
 #define RPL_WHOREPLY         352
+#define RPL_WHOSPCRPL        354 /* from ircu -- jilles */
 #define RPL_ENDOFWHO         315
 #define RPL_NAMREPLY         353
 #define RPL_WHOWASREAL       360
index b0b76f56d5b13452729e22ff4cd714e8c7a30e6b..1af6b0a6719bf5659294cf9ceba13d17d310a9c8 100644 (file)
 #include "packet.h"
 #include "s_newconf.h"
 
+#define FIELD_CHANNEL    0x0001
+#define FIELD_HOP        0x0002
+#define FIELD_FLAGS      0x0004
+#define FIELD_HOST       0x0008
+#define FIELD_IP         0x0010
+#define FIELD_IDLE       0x0020
+#define FIELD_NICK       0x0040
+#define FIELD_INFO       0x0080
+#define FIELD_SERVER     0x0100
+#define FIELD_QUERYTYPE  0x0200 /* cookie for client */
+#define FIELD_USER       0x0400
+#define FIELD_ACCOUNT    0x0800
+#define FIELD_OPLEVEL    0x1000 /* meaningless and stupid, but whatever */
+
+struct who_format
+{
+       int fields;
+       const char *querytype;
+};
+
 static int m_who(struct Client *, struct Client *, int, const char **);
 
 struct Message who_msgtab = {
@@ -52,19 +72,20 @@ mapi_clist_av1 who_clist[] = { &who_msgtab, NULL };
 DECLARE_MODULE_AV1(who, NULL, NULL, who_clist, NULL, NULL, "$Revision: 3350 $");
 
 static void do_who_on_channel(struct Client *source_p, struct Channel *chptr,
-                             int server_oper, int member);
+                             int server_oper, int member,
+                             struct who_format *fmt);
 
-static void who_global(struct Client *source_p, const char *mask, int server_oper, int operspy);
+static void who_global(struct Client *source_p, const char *mask, int server_oper, int operspy, struct who_format *fmt);
 
 static void do_who(struct Client *source_p,
-                  struct Client *target_p, const char *chname, const char *op_flags);
+                  struct Client *target_p, const char *chname, const char *op_flags, struct who_format *fmt);
 
 
 /*
 ** m_who
 **      parv[0] = sender prefix
 **      parv[1] = nickname mask list
-**      parv[2] = additional selection flag, only 'o' for now.
+**      parv[2] = additional selection flag and format options
 */
 static int
 m_who(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
@@ -78,6 +99,42 @@ m_who(struct Client *client_p, struct Client *source_p, int parc, const char *pa
        int server_oper = parc > 2 ? (*parv[2] == 'o') : 0;     /* Show OPERS only */
        int member;
        int operspy = 0;
+       struct who_format fmt;
+       const char *s;
+
+       fmt.fields = 0;
+       fmt.querytype = NULL;
+       if (parc > 2 && (s = strchr(parv[2], '%')) != NULL)
+       {
+               s++;
+               for (; *s != '\0'; s++)
+               {
+                       switch (*s)
+                       {
+                               case 'c': fmt.fields |= FIELD_CHANNEL; break;
+                               case 'd': fmt.fields |= FIELD_HOP; break;
+                               case 'f': fmt.fields |= FIELD_FLAGS; break;
+                               case 'h': fmt.fields |= FIELD_HOST; break;
+                               case 'i': fmt.fields |= FIELD_IP; break;
+                               case 'l': fmt.fields |= FIELD_IDLE; break;
+                               case 'n': fmt.fields |= FIELD_NICK; break;
+                               case 'r': fmt.fields |= FIELD_INFO; break;
+                               case 's': fmt.fields |= FIELD_SERVER; break;
+                               case 't': fmt.fields |= FIELD_QUERYTYPE; break;
+                               case 'u': fmt.fields |= FIELD_USER; break;
+                               case 'a': fmt.fields |= FIELD_ACCOUNT; break;
+                               case 'o': fmt.fields |= FIELD_OPLEVEL; break;
+                               case ',':
+                                         s++;
+                                         fmt.querytype = s;
+                                         s += strlen(s);
+                                         s--;
+                                         break;
+                       }
+               }
+               if (EmptyString(fmt.querytype) || strlen(fmt.querytype) > 3)
+                       fmt.querytype = "0";
+       }
 
        mask = LOCAL_COPY(parv[1]);
 
@@ -92,7 +149,7 @@ m_who(struct Client *client_p, struct Client *source_p, int parc, const char *pa
                if((lp = source_p->user->channel.head) != NULL)
                {
                        msptr = lp->data;
-                       do_who_on_channel(source_p, msptr->chptr, server_oper, YES);
+                       do_who_on_channel(source_p, msptr->chptr, server_oper, YES, &fmt);
                }
 
                sendto_one(source_p, form_str(RPL_ENDOFWHO),
@@ -124,9 +181,9 @@ m_who(struct Client *client_p, struct Client *source_p, int parc, const char *pa
                                report_operspy(source_p, "WHO", chptr->chname);
 
                        if(IsMember(source_p, chptr) || operspy)
-                               do_who_on_channel(source_p, chptr, server_oper, YES);
+                               do_who_on_channel(source_p, chptr, server_oper, YES, &fmt);
                        else if(!SecretChannel(chptr))
-                               do_who_on_channel(source_p, chptr, server_oper, NO);
+                               do_who_on_channel(source_p, chptr, server_oper, NO, &fmt);
                }
                sendto_one(source_p, form_str(RPL_ENDOFWHO),
                           me.name, source_p->name, parv[1] + operspy);
@@ -160,9 +217,9 @@ m_who(struct Client *client_p, struct Client *source_p, int parc, const char *pa
                 */
                if(lp != NULL)
                        do_who(source_p, target_p, chptr->chname,
-                              find_channel_status(lp->data, IsCapable(source_p, CLICAP_MULTI_PREFIX)));
+                              find_channel_status(lp->data, IsCapable(source_p, CLICAP_MULTI_PREFIX)), &fmt);
                else
-                       do_who(source_p, target_p, NULL, "");
+                       do_who(source_p, target_p, NULL, "", &fmt);
 
                sendto_one(source_p, form_str(RPL_ENDOFWHO), 
                           me.name, source_p->name, mask);
@@ -197,9 +254,9 @@ m_who(struct Client *client_p, struct Client *source_p, int parc, const char *pa
         * with "/who" ;) --fl
         */
        if((*(mask + 1) == '\0') && (*mask == '0'))
-               who_global(source_p, NULL, server_oper, 0);
+               who_global(source_p, NULL, server_oper, 0, &fmt);
        else
-               who_global(source_p, mask, server_oper, operspy);
+               who_global(source_p, mask, server_oper, operspy, &fmt);
 
        sendto_one(source_p, form_str(RPL_ENDOFWHO),
                   me.name, source_p->name, mask);
@@ -213,13 +270,15 @@ m_who(struct Client *client_p, struct Client *source_p, int parc, const char *pa
  *             - char * mask to match
  *             - int if oper on a server or not
  *             - pointer to int maxmatches
+ *             - format options
  * output      - NONE
  * side effects - lists matching invisible clients on specified channel,
  *               marks matched clients.
  */
 static void
 who_common_channel(struct Client *source_p, struct Channel *chptr,
-                  const char *mask, int server_oper, int *maxmatches)
+                  const char *mask, int server_oper, int *maxmatches,
+                  struct who_format *fmt)
 {
        struct membership *msptr;
        struct Client *target_p;
@@ -246,7 +305,7 @@ who_common_channel(struct Client *source_p, struct Channel *chptr,
                                        (IsOper(source_p) && match(mask, target_p->orighost)) ||
                                        match(mask, target_p->info))
                        {
-                               do_who(source_p, target_p, NULL, "");
+                               do_who(source_p, target_p, NULL, "", fmt);
                                --(*maxmatches);
                        }
                }
@@ -259,6 +318,8 @@ who_common_channel(struct Client *source_p, struct Channel *chptr,
  * inputs      - pointer to client requesting who
  *             - char * mask to match
  *             - int if oper on a server or not
+ *             - int if operspy or not
+ *             - format options
  * output      - NONE
  * side effects - do a global scan of all clients looking for match
  *               this is slightly expensive on EFnet ...
@@ -266,7 +327,7 @@ who_common_channel(struct Client *source_p, struct Channel *chptr,
  *               and will be left cleared on return
  */
 static void
-who_global(struct Client *source_p, const char *mask, int server_oper, int operspy)
+who_global(struct Client *source_p, const char *mask, int server_oper, int operspy, struct who_format *fmt)
 {
        struct membership *msptr;
        struct Client *target_p;
@@ -281,7 +342,7 @@ who_global(struct Client *source_p, const char *mask, int server_oper, int opers
                RB_DLINK_FOREACH(lp, source_p->user->channel.head)
                {
                        msptr = lp->data;
-                       who_common_channel(source_p, msptr->chptr, mask, server_oper, &maxmatches);
+                       who_common_channel(source_p, msptr->chptr, mask, server_oper, &maxmatches, fmt);
                }
        }
        else if (!ConfigFileEntry.operspy_dont_care_user_info)
@@ -315,7 +376,7 @@ who_global(struct Client *source_p, const char *mask, int server_oper, int opers
                                        (IsOper(source_p) && match(mask, target_p->orighost)) ||
                                        match(mask, target_p->info))
                        {
-                               do_who(source_p, target_p, NULL, "");
+                               do_who(source_p, target_p, NULL, "", fmt);
                                --maxmatches;
                        }
                }
@@ -335,12 +396,13 @@ who_global(struct Client *source_p, const char *mask, int server_oper, int opers
  *             - The "real name" of this channel
  *             - int if source_p is a server oper or not
  *             - int if client is member or not
+ *             - format options
  * output      - NONE
  * side effects - do a who on given channel
  */
 static void
 do_who_on_channel(struct Client *source_p, struct Channel *chptr,
-                 int server_oper, int member)
+                 int server_oper, int member, struct who_format *fmt)
 {
        struct Client *target_p;
        struct membership *msptr;
@@ -357,7 +419,7 @@ do_who_on_channel(struct Client *source_p, struct Channel *chptr,
 
                if(member || !IsInvisible(target_p))
                        do_who(source_p, target_p, chptr->chname,
-                              find_channel_status(msptr, combine));
+                              find_channel_status(msptr, combine), fmt);
        }
 }
 
@@ -368,23 +430,77 @@ do_who_on_channel(struct Client *source_p, struct Channel *chptr,
  *             - pointer to client to do who on
  *             - The reported name
  *             - channel flags
+ *             - format options
  * output      - NONE
  * side effects - do a who on given person
  */
 
 static void
-do_who(struct Client *source_p, struct Client *target_p, const char *chname, const char *op_flags)
+do_who(struct Client *source_p, struct Client *target_p, const char *chname, const char *op_flags, struct who_format *fmt)
 {
        char status[5];
+       char str[512], *p, *end;
+       const char *q;
 
        rb_sprintf(status, "%c%s%s",
                   target_p->user->away ? 'G' : 'H', IsOper(target_p) ? "*" : "", op_flags);
 
-       sendto_one(source_p, form_str(RPL_WHOREPLY), me.name, source_p->name,
-                  (chname) ? (chname) : "*",
-                  target_p->username,
-                  target_p->host, target_p->servptr->name, target_p->name,
-                  status, 
-                  ConfigServerHide.flatten_links ? 0 : target_p->hopcount, 
-                  target_p->info);
+       if (fmt->fields == 0)
+               sendto_one(source_p, form_str(RPL_WHOREPLY), me.name,
+                          source_p->name, (chname) ? (chname) : "*",
+                          target_p->username, target_p->host,
+                          target_p->servptr->name, target_p->name, status,
+                          ConfigServerHide.flatten_links ? 0 : target_p->hopcount, 
+                          target_p->info);
+       else
+       {
+               str[0] = '\0';
+               p = str;
+               end = str + sizeof str;
+               if (fmt->fields & FIELD_QUERYTYPE)
+                       p += rb_snprintf(p, end - p, " %s", fmt->querytype);
+               if (fmt->fields & FIELD_CHANNEL)
+                       p += rb_snprintf(p, end - p, " %s", (chname) ? (chname) : "*");
+               if (fmt->fields & FIELD_USER)
+                       p += rb_snprintf(p, end - p, " %s", target_p->username);
+               if (fmt->fields & FIELD_IP)
+               {
+                       if (show_ip(source_p, target_p) && !EmptyString(target_p->sockhost) && strcmp(target_p->sockhost, "0"))
+                               p += rb_snprintf(p, end - p, " %s", target_p->sockhost);
+                       else
+                               p += rb_snprintf(p, end - p, " %s", "255.255.255.255");
+               }
+               if (fmt->fields & FIELD_HOST)
+                       p += rb_snprintf(p, end - p, " %s", target_p->host);
+               if (fmt->fields & FIELD_SERVER)
+                       p += rb_snprintf(p, end - p, " %s", target_p->servptr->name);
+               if (fmt->fields & FIELD_NICK)
+                       p += rb_snprintf(p, end - p, " %s", target_p->name);
+               if (fmt->fields & FIELD_FLAGS)
+                       p += rb_snprintf(p, end - p, " %s", status);
+               if (fmt->fields & FIELD_HOP)
+                       p += rb_snprintf(p, end - p, " %d", ConfigServerHide.flatten_links ? 0 : target_p->hopcount);
+               if (fmt->fields & FIELD_IDLE)
+                       p += rb_snprintf(p, end - p, " %d", MyClient(target_p) ? rb_current_time() - target_p->localClient->last : 0);
+               if (fmt->fields & FIELD_ACCOUNT)
+               {
+                       /* display as in whois */
+                       q = target_p->user->suser;
+                       if (!EmptyString(q))
+                       {
+                               while(IsDigit(*q))
+                                       q++;
+                               if(*q == '\0')
+                                       q = target_p->user->suser;
+                       }
+                       else
+                               q = "0";
+                       p += rb_snprintf(p, end - p, " %s", q);
+               }
+               if (fmt->fields & FIELD_OPLEVEL)
+                       p += rb_snprintf(p, end - p, " %s", *op_flags == '@' ? "999" : "n/a");
+               if (fmt->fields & FIELD_INFO)
+                       p += rb_snprintf(p, end - p, " :%s", target_p->info);
+               sendto_one_numeric(source_p, RPL_WHOSPCRPL, "%s", str + 1);
+       }
 }
index e1b95d9b84ecf873f759392ce1954efd9f558d04..07b6fe9a4622eb5da8ee817486a68c894732efc2 100644 (file)
@@ -375,7 +375,7 @@ static  const char *  replies[] = {
 /* 351 RPL_VERSION, */          "%s(%s). %s :%s TS%dow %s",
 /* 352 RPL_WHOREPLY, */         ":%s 352 %s %s %s %s %s %s %s :%d %s",
 /* 353 RPL_NAMREPLY, */         ":%s 353 %s %s %s :",
-/* 354 */       NULL,
+/* 354 RPL_WHOSPCRPL */         NULL,
 /* 355 */       NULL,
 /* 356 */       NULL,
 /* 357 */       NULL,