]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/cmd_who.cpp
OOPS! We try again, since I'm smoking craq. LF is 0x0a NOT CR.
[user/henk/code/inspircd.git] / src / cmd_who.cpp
index 6054351d9819191d4f04bc86c736afdd3a63b3f5..31e8030f5d0fc470396fdaf95e8c098f8c7ea3a1 100644 (file)
@@ -1 +1,328 @@
-/*       +------------------------------------+\r *       | Inspire Internet Relay Chat Daemon |\r *       +------------------------------------+\r *\r *  InspIRCd: (C) 2002-2007 InspIRCd Development Team\r * See: http://www.inspircd.org/wiki/index.php/Credits\r *\r * This program is free but copyrighted software; see\r *        the file COPYING for details.\r *\r * ---------------------------------------------------\r */\r\r#include "inspircd.h"\r#include "configreader.h"\r#include "users.h"\r#include "modules.h"\r#include "wildcard.h"\r#include "commands/cmd_who.h"\r\r/* get the last 'visible' chan of a user */\rstatic char *getlastchanname(userrec *u)\r{\r   UCListIter i = u->chans.begin();\r       if (i != u->chans.end())\r       {\r              if (!i->first->IsModeSet('s'))\r                 return i->first->name;\r }\r\r     return "*";\r}\r\rbool cmd_who::whomatch(userrec* user, const char* matchtext)\r{\r  bool realhost = false;\r bool realname = false;\r bool positive = true;\r  bool metadata = false;\r bool ident = false;\r    bool away = false;\r     bool port = false;\r     char* dummy = NULL;\r\r   if (user->registered != REG_ALL)\r               return false;\r\r if (opt_local && !IS_LOCAL(user))\r              return false;\r  else if (opt_far && IS_LOCAL(user))\r            return false;\r\r if (opt_mode)\r  {\r              for (const char* n = matchtext; *n; n++)\r               {\r                      if (*n == '+')\r                 {\r                              positive = true;\r                               continue;\r                      }\r                      else if (*n == '-')\r                    {\r                              positive = false;\r                              continue;\r                      }\r                      if (user->IsModeSet(*n) != positive)\r                           return false;\r          }\r              return true;\r   }\r      else\r   {\r\r             if (opt_metadata)\r                      metadata = user->GetExt(matchtext, dummy);\r             else\r           {\r                      if (opt_realname)\r                              realname = match(user->fullname, matchtext);\r                   else\r                   {\r                              if (opt_showrealhost)\r                                  realhost = match(user->host, matchtext);\r                               else\r                           {\r                                      if (opt_ident)\r                                         ident = match(user->ident, matchtext);\r                                 else\r                                   {\r                                              if (opt_port)\r                                          {\r                                                      irc::portparser portrange(matchtext, false);\r                                                   long portno = -1;\r                                                      while ((portno = portrange.GetToken()))\r                                                                if (portno == user->GetPort())\r                                                                 port = true;\r                                           }\r                                              else\r                                           {\r                                                      if (opt_away)\r                                                          away = match(user->awaymsg, matchtext);\r                                                }\r                                      }\r                              }\r                      }\r              }\r              return ((port) || (away) || (ident) || (metadata) || (realname) || (realhost) || (match(user->dhost, matchtext)) || (match(user->nick, matchtext)) || (match(user->server, matchtext)));\r       }\r}\r\r\r\rextern "C" DllExport command_t* init_command(InspIRCd* Instance)\r{\r      return new cmd_who(Instance);\r}\r\rbool cmd_who::CanView(chanrec* chan, userrec* user)\r{\r if (!user || !chan)\r            return false;\r\r /* Execute items in fastest-to-execute first order */\r\r /* Opers see all */\r    if (IS_OPER(user))\r             return true;\r   else if (!chan->IsModeSet('s') && !chan->IsModeSet('p'))\r               return true;\r   else if (chan->HasUser(user))\r          return true;\r\r  return false;\r}\r\rvoid cmd_who::SendWhoLine(userrec* user, const std::string &initial, chanrec* ch, userrec* u, std::vector<std::string> &whoresults)\r{\r std::string lcn = getlastchanname(u);\r  chanrec* chlast = ServerInstance->FindChan(lcn);\r\r      /* Not visible to this user */\r if (u->Visibility && !u->Visibility->VisibleTo(user))\r          return;\r\r       std::string wholine =   initial + (ch ? ch->name : lcn) + " " + u->ident + " " + (opt_showrealhost ? u->host : u->dhost) + " " +\r                               ((*ServerInstance->Config->HideWhoisServer && !IS_OPER(user)) ? ServerInstance->Config->HideWhoisServer : u->server) +\r                         " " + u->nick + " ";\r\r  /* away? */\r    if (IS_AWAY(u))\r        {\r              wholine.append("G");\r   }\r      else\r   {\r              wholine.append("H");\r   }\r\r     /* oper? */\r    if (IS_OPER(u))\r        {\r              wholine.append("*");\r   }\r\r     wholine = wholine + (ch ? ch->GetPrefixChar(u) : (chlast ? chlast->GetPrefixChar(u) : "")) + " :0 " + u->fullname;\r     whoresults.push_back(wholine);\r}\r\rCmdResult cmd_who::Handle (const char** parameters, int pcnt, userrec *user)\r{\r       /*\r      * XXX - RFC says:\r      *   The <name> passed to WHO is matched against users' host, server, real\r      *   name and nickname\r  * Currently, we support WHO #chan, WHO nick, WHO 0, WHO *, and the addition of a 'o' flag, as per RFC.\r         */\r\r   /* WHO options */\r      opt_viewopersonly = false;\r     opt_showrealhost = false;\r      opt_unlimit = false;\r   opt_realname = false;\r  opt_mode = false;\r      opt_ident = false;\r     opt_metadata = false;\r  opt_port = false;\r      opt_away = false;\r      opt_local = false;\r     opt_far = false;\r\r      chanrec *ch = NULL;\r    std::vector<std::string> whoresults;\r   std::string initial = "352 " + std::string(user->nick) + " ";\r\r const char* matchtext = NULL;\r\r /* Change '0' into '*' so the wildcard matcher can grok it */\r  matchtext = parameters[0];\r     if (!strcmp(matchtext,"0"))\r            matchtext = "*";\r\r      if (pcnt > 1)\r  {\r              /* parse flags */\r              const char *iter = parameters[1];\r\r             while (*iter)\r          {\r                      switch (*iter)\r                 {\r                              case 'o':\r                                      opt_viewopersonly = true;\r                              break;\r                         case 'h':\r                                      if (IS_OPER(user))\r                                             opt_showrealhost = true;\r                               break;\r                         case 'u':\r                                      if (IS_OPER(user))\r                                             opt_unlimit = true;\r                            break;\r                         case 'r':\r                                      opt_realname = true;\r                           break;\r                         case 'm':\r                                      opt_mode = true;\r                               break;\r                         case 'M':\r                                      opt_metadata = true;\r                           break;\r                         case 'i':\r                                      opt_ident = true;\r                              break;\r                         case 'p':\r                                      opt_port = true;\r                               break;\r                         case 'a':\r                                      opt_away = true;\r                               break;\r                         case 'l':\r                                      opt_local = true;\r                              break;\r                         case 'f':\r                                      opt_far = true;\r                                break;\r                 }\r\r                     *iter++;\r               }\r      }\r\r\r    /* who on a channel? */\r        ch = ServerInstance->FindChan(matchtext);\r\r     if (ch)\r        {\r              if (CanView(ch,user))\r          {\r                      bool inside = ch->HasUser(user);\r       \r                       /* who on a channel. */\r                        CUList *cu = ch->GetUsers();\r   \r                       for (CUList::iterator i = cu->begin(); i != cu->end(); i++)\r                    {\r                              /* opers only, please */\r                               if (opt_viewopersonly && !IS_OPER(i->first))\r                                   continue;\r      \r                               /* If we're not inside the channel, hide +i users */\r                           if (i->first->IsModeSet('i') && !inside)\r                                       continue;\r      \r                               SendWhoLine(user, initial, ch, i->first, whoresults);\r                  }\r              }\r      }\r      else\r   {\r              /* Match against wildcard of nick, server or host */\r\r          if (opt_viewopersonly)\r         {\r                      /* Showing only opers */\r                       for (std::vector<userrec*>::iterator i = ServerInstance->all_opers.begin(); i != ServerInstance->all_opers.end(); i++)\r                 {\r                              userrec* oper = *i;\r\r                           if (whomatch(oper, matchtext))\r                         {\r                                      if ((!oper->IsModeSet('i')) && (!IS_OPER(user)))\r                                               continue;\r\r                                     SendWhoLine(user, initial, NULL, oper, whoresults);\r                            }\r                      }\r              }\r              else\r           {\r                      for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++)\r                 {\r                              if (whomatch(i->second, matchtext))\r                            {\r                                      if ((i->second->IsModeSet('i')) && (!IS_OPER(user)))\r                                           continue;\r\r                                     SendWhoLine(user, initial, NULL, i->second, whoresults);\r                               }\r                      }\r              }\r      }\r      /* Send the results out */\r     if ((ServerInstance->Config->MaxWhoResults && (whoresults.size() <= (size_t)ServerInstance->Config->MaxWhoResults)) || opt_unlimit)\r    {\r              for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++)\r                      user->WriteServ(*n);\r           user->WriteServ("315 %s %s :End of /WHO list.",user->nick, *parameters[0] ? parameters[0] : "*");\r              return CMD_SUCCESS;\r    }\r      else\r   {\r              /* BZZT! Too many results. */\r          user->WriteServ("315 %s %s :Too many results",user->nick, parameters[0]);\r              return CMD_FAILURE;\r    }\r}\r
\ No newline at end of file
+/*       +------------------------------------+
+ *       | Inspire Internet Relay Chat Daemon |
+ *       +------------------------------------+
+ *
+ *  InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ *         the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "configreader.h"
+#include "users.h"
+#include "modules.h"
+#include "wildcard.h"
+#include "commands/cmd_who.h"
+
+/* get the last 'visible' chan of a user */
+static char *getlastchanname(userrec *u)
+{
+       UCListIter i = u->chans.begin();
+       if (i != u->chans.end())
+       {
+               if (!i->first->IsModeSet('s'))
+                       return i->first->name;
+       }
+
+       return "*";
+}
+
+bool cmd_who::whomatch(userrec* user, const char* matchtext)
+{
+       bool realhost = false;
+       bool realname = false;
+       bool positive = true;
+       bool metadata = false;
+       bool ident = false;
+       bool away = false;
+       bool port = false;
+       char* dummy = NULL;
+
+       if (user->registered != REG_ALL)
+               return false;
+
+       if (opt_local && !IS_LOCAL(user))
+               return false;
+       else if (opt_far && IS_LOCAL(user))
+               return false;
+
+       if (opt_mode)
+       {
+               for (const char* n = matchtext; *n; n++)
+               {
+                       if (*n == '+')
+                       {
+                               positive = true;
+                               continue;
+                       }
+                       else if (*n == '-')
+                       {
+                               positive = false;
+                               continue;
+                       }
+                       if (user->IsModeSet(*n) != positive)
+                               return false;
+               }
+               return true;
+       }
+       else
+       {
+
+               if (opt_metadata)
+                       metadata = user->GetExt(matchtext, dummy);
+               else
+               {
+                       if (opt_realname)
+                               realname = match(user->fullname, matchtext);
+                       else
+                       {
+                               if (opt_showrealhost)
+                                       realhost = match(user->host, matchtext);
+                               else
+                               {
+                                       if (opt_ident)
+                                               ident = match(user->ident, matchtext);
+                                       else
+                                       {
+                                               if (opt_port)
+                                               {
+                                                       irc::portparser portrange(matchtext, false);
+                                                       long portno = -1;
+                                                       while ((portno = portrange.GetToken()))
+                                                               if (portno == user->GetPort())
+                                                                       port = true;
+                                               }
+                                               else
+                                               {
+                                                       if (opt_away)
+                                                               away = match(user->awaymsg, matchtext);
+                                               }
+                                       }
+                               }
+                       }
+               }
+               return ((port) || (away) || (ident) || (metadata) || (realname) || (realhost) || (match(user->dhost, matchtext)) || (match(user->nick, matchtext)) || (match(user->server, matchtext)));
+       }
+}
+
+
+
+extern "C" DllExport command_t* init_command(InspIRCd* Instance)
+{
+       return new cmd_who(Instance);
+}
+
+bool cmd_who::CanView(chanrec* chan, userrec* user)
+{
+       if (!user || !chan)
+               return false;
+
+       /* Execute items in fastest-to-execute first order */
+
+       /* Opers see all */
+       if (IS_OPER(user))
+               return true;
+       else if (!chan->IsModeSet('s') && !chan->IsModeSet('p'))
+               return true;
+       else if (chan->HasUser(user))
+               return true;
+
+       return false;
+}
+
+void cmd_who::SendWhoLine(userrec* user, const std::string &initial, chanrec* ch, userrec* u, std::vector<std::string> &whoresults)
+{
+       std::string lcn = getlastchanname(u);
+       chanrec* chlast = ServerInstance->FindChan(lcn);
+
+       /* Not visible to this user */
+       if (u->Visibility && !u->Visibility->VisibleTo(user))
+               return;
+
+       std::string wholine =   initial + (ch ? ch->name : lcn) + " " + u->ident + " " + (opt_showrealhost ? u->host : u->dhost) + " " +
+                               ((*ServerInstance->Config->HideWhoisServer && !IS_OPER(user)) ? ServerInstance->Config->HideWhoisServer : u->server) +
+                               " " + u->nick + " ";
+
+       /* away? */
+       if (IS_AWAY(u))
+       {
+               wholine.append("G");
+       }
+       else
+       {
+               wholine.append("H");
+       }
+
+       /* oper? */
+       if (IS_OPER(u))
+       {
+               wholine.append("*");
+       }
+
+       wholine = wholine + (ch ? ch->GetPrefixChar(u) : (chlast ? chlast->GetPrefixChar(u) : "")) + " :0 " + u->fullname;
+       whoresults.push_back(wholine);
+}
+
+CmdResult cmd_who::Handle (const char** parameters, int pcnt, userrec *user)
+{
+       /*
+        * XXX - RFC says:
+        *   The <name> passed to WHO is matched against users' host, server, real
+        *   name and nickname
+        * Currently, we support WHO #chan, WHO nick, WHO 0, WHO *, and the addition of a 'o' flag, as per RFC.
+        */
+
+       /* WHO options */
+       opt_viewopersonly = false;
+       opt_showrealhost = false;
+       opt_unlimit = false;
+       opt_realname = false;
+       opt_mode = false;
+       opt_ident = false;
+       opt_metadata = false;
+       opt_port = false;
+       opt_away = false;
+       opt_local = false;
+       opt_far = false;
+
+       chanrec *ch = NULL;
+       std::vector<std::string> whoresults;
+       std::string initial = "352 " + std::string(user->nick) + " ";
+
+       const char* matchtext = NULL;
+
+       /* Change '0' into '*' so the wildcard matcher can grok it */
+       matchtext = parameters[0];
+       if (!strcmp(matchtext,"0"))
+               matchtext = "*";
+
+       if (pcnt > 1)
+       {
+               /* parse flags */
+               const char *iter = parameters[1];
+
+               while (*iter)
+               {
+                       switch (*iter)
+                       {
+                               case 'o':
+                                       opt_viewopersonly = true;
+                               break;
+                               case 'h':
+                                       if (IS_OPER(user))
+                                               opt_showrealhost = true;
+                               break;
+                               case 'u':
+                                       if (IS_OPER(user))
+                                               opt_unlimit = true;
+                               break;
+                               case 'r':
+                                       opt_realname = true;
+                               break;
+                               case 'm':
+                                       opt_mode = true;
+                               break;
+                               case 'M':
+                                       opt_metadata = true;
+                               break;
+                               case 'i':
+                                       opt_ident = true;
+                               break;
+                               case 'p':
+                                       opt_port = true;
+                               break;
+                               case 'a':
+                                       opt_away = true;
+                               break;
+                               case 'l':
+                                       opt_local = true;
+                               break;
+                               case 'f':
+                                       opt_far = true;
+                               break;
+                       }
+
+                       *iter++;
+               }
+       }
+
+
+       /* who on a channel? */
+       ch = ServerInstance->FindChan(matchtext);
+
+       if (ch)
+       {
+               if (CanView(ch,user))
+               {
+                       bool inside = ch->HasUser(user);
+       
+                       /* who on a channel. */
+                       CUList *cu = ch->GetUsers();
+       
+                       for (CUList::iterator i = cu->begin(); i != cu->end(); i++)
+                       {
+                               /* opers only, please */
+                               if (opt_viewopersonly && !IS_OPER(i->first))
+                                       continue;
+       
+                               /* If we're not inside the channel, hide +i users */
+                               if (i->first->IsModeSet('i') && !inside)
+                                       continue;
+       
+                               SendWhoLine(user, initial, ch, i->first, whoresults);
+                       }
+               }
+       }
+       else
+       {
+               /* Match against wildcard of nick, server or host */
+
+               if (opt_viewopersonly)
+               {
+                       /* Showing only opers */
+                       for (std::vector<userrec*>::iterator i = ServerInstance->all_opers.begin(); i != ServerInstance->all_opers.end(); i++)
+                       {
+                               userrec* oper = *i;
+
+                               if (whomatch(oper, matchtext))
+                               {
+                                       if ((!oper->IsModeSet('i')) && (!IS_OPER(user)))
+                                               continue;
+
+                                       SendWhoLine(user, initial, NULL, oper, whoresults);
+                               }
+                       }
+               }
+               else
+               {
+                       for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++)
+                       {
+                               if (whomatch(i->second, matchtext))
+                               {
+                                       if ((i->second->IsModeSet('i')) && (!IS_OPER(user)))
+                                               continue;
+
+                                       SendWhoLine(user, initial, NULL, i->second, whoresults);
+                               }
+                       }
+               }
+       }
+       /* Send the results out */
+       if ((ServerInstance->Config->MaxWhoResults && (whoresults.size() <= (size_t)ServerInstance->Config->MaxWhoResults)) || opt_unlimit)
+       {
+               for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++)
+                       user->WriteServ(*n);
+               user->WriteServ("315 %s %s :End of /WHO list.",user->nick, *parameters[0] ? parameters[0] : "*");
+               return CMD_SUCCESS;
+       }
+       else
+       {
+               /* BZZT! Too many results. */
+               user->WriteServ("315 %s %s :Too many results",user->nick, parameters[0]);
+               return CMD_FAILURE;
+       }
+}