ns_recover.cpp

Go to the documentation of this file.
00001 /* NickServ core functions
00002  *
00003  * (C) 2003-2013 Anope Team
00004  * Contact us at team@anope.org
00005  *
00006  * Please read COPYING and README for further details.
00007  *
00008  * Based on the original code of Epona by Lara.
00009  * Based on the original code of Services by Andy Church.
00010  */
00011 
00012 /*************************************************************************/
00013 
00014 #include "module.h"
00015 
00016 struct NSRecoverExtensibleInfo : ExtensibleItem, std::map<Anope::string, ChannelStatus> { };
00017 
00018 class NSRecoverRequest : public IdentifyRequest
00019 {
00020         CommandSource source;
00021         Command *cmd;
00022         Anope::string user;
00023  
00024  public:
00025         NSRecoverRequest(Module *o, CommandSource &src, Command *c, const Anope::string &nick, const Anope::string &pass) : IdentifyRequest(o, nick, pass), source(src), cmd(c), user(nick) { }
00026 
00027         void OnSuccess() anope_override
00028         {
00029                 User *u = User::Find(user, true);
00030                 if (!source.GetUser() || !source.service)
00031                         return;
00032 
00033                 NickAlias *na = NickAlias::Find(user);
00034                 if (!na)
00035                         return;
00036 
00037                 Log(LOG_COMMAND, source, cmd) << "for " << na->nick;
00038 
00039                 /* Nick is being held by us, release it */
00040                 if (na->HasExt("HELD"))
00041                 {
00042                         na->Release();
00043                         source.Reply(_("Service's hold on \002%s\002 has been released."), na->nick.c_str());
00044                 }
00045                 else if (!u)
00046                 {
00047                         source.Reply(_("No one is using your nick, and services are not holding it."));
00048                 }
00049                 // If the user being recovered is identified for the account of the nick then the user is the
00050                 // same person that is executing the command, so kill them off (old GHOST command).
00051                 else if (u->Account() == na->nc)
00052                 {
00053                         if (!source.GetAccount() && na->nc->HasExt("SECURE"))
00054                         {
00055                                 source.GetUser()->Login(u->Account());
00056                                 Log(LOG_COMMAND, source, cmd) << "and was automatically identified to " << u->Account()->display;
00057                         }
00058 
00059                         if (Config->NSRestoreOnRecover)
00060                         {
00061                                 if (!u->chans.empty())
00062                                 {
00063                                         NSRecoverExtensibleInfo *ei = new NSRecoverExtensibleInfo;
00064                                         for (User::ChanUserList::iterator it = u->chans.begin(), it_end = u->chans.end(); it != it_end; ++it)
00065                                                 (*ei)[(*it)->chan->name] = (*it)->status;
00066 
00067                                         source.GetUser()->Extend("ns_recover_info", ei);
00068                                 }
00069                         }
00070 
00071                         u->SendMessage(NickServ, _("This nickname has been recovered by %s. If you did not do\n"
00072                                                         "this then %s may have your password, and you should change it.\n"),
00073                                                         source.GetNick().c_str(), source.GetNick().c_str());
00074 
00075                         Anope::string buf = source.command.upper() + " command used by " + source.GetNick();
00076                         u->Kill(source.service->nick, buf);
00077 
00078                         source.Reply(_("Ghost with your nick has been killed."));
00079 
00080                         if (IRCD->CanSVSNick)
00081                                 IRCD->SendForceNickChange(source.GetUser(), GetAccount(), Anope::CurTime);
00082                 }
00083                 /* User is not identified or not identified to the same account as the person using this command */
00084                 else
00085                 {
00086                         if (!source.GetAccount() && na->nc->HasExt("SECURE"))
00087                         {
00088                                 source.GetUser()->Login(na->nc); // Identify the user using the command if they arent identified
00089                                 Log(LOG_COMMAND, source, cmd) << "and was automatically identified to " << na->nick << " (" << na->nc->display << ")";
00090                         }
00091 
00092                         u->SendMessage(NickServ, _("This nickname has been recovered by %s."), source.GetNick().c_str());
00093                         u->Collide(na);
00094 
00095                         if (IRCD->CanSVSNick)
00096                         {
00097                                 /* If we can svsnick then release our hold and svsnick the user using the command */
00098                                 na->Release();
00099                                 IRCD->SendForceNickChange(source.GetUser(), GetAccount(), Anope::CurTime);
00100                         }
00101                         else
00102                                 source.Reply(_("The user with your nick has been removed. Use this command again\n"
00103                                                 "to release services's hold on your nick."));
00104                 }
00105         }
00106 
00107         void OnFail() anope_override
00108         {
00109                 if (NickAlias::Find(GetAccount()) != NULL)
00110                 {
00111                         source.Reply(ACCESS_DENIED);
00112                         if (!GetPassword().empty())
00113                         {
00114                                 Log(LOG_COMMAND, source, cmd) << "with an invalid password for " << GetAccount();
00115                                 if (source.GetUser())
00116                                         source.GetUser()->BadPassword();
00117                         }
00118                 }
00119                 else
00120                         source.Reply(NICK_X_NOT_REGISTERED, GetAccount().c_str());
00121         }
00122 };
00123 
00124 class CommandNSRecover : public Command
00125 {
00126  public:
00127         CommandNSRecover(Module *creator) : Command(creator, "nickserv/recover", 1, 2)
00128         {
00129                 this->SetDesc(_("Regains control of your nick"));
00130                 this->SetSyntax("\037nickname\037 [\037password\037]");
00131                 this->AllowUnregistered(true);
00132         }
00133 
00134         void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
00135         {
00136                 const Anope::string &nick = params[0];
00137                 const Anope::string &pass = params.size() > 1 ? params[1] : "";
00138 
00139                 User *user = User::Find(nick, true);
00140 
00141                 if (user && source.GetUser() == user)
00142                 {
00143                         source.Reply(_("You can't %s yourself!"), source.command.lower().c_str());
00144                         return;
00145                 }
00146 
00147                 const NickAlias *na = NickAlias::Find(nick);
00148 
00149                 if (!na)
00150                 {
00151                         source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
00152                         return;
00153                 }
00154                 else if (na->nc->HasExt("SUSPENDED"))
00155                 {
00156                         source.Reply(NICK_X_SUSPENDED, na->nick.c_str());
00157                         return;
00158                 }
00159 
00160                 bool ok = false;
00161                 if (source.GetAccount() == na->nc)
00162                         ok = true;
00163                 else if (!na->nc->HasExt("SECURE") && source.GetUser() && na->nc->IsOnAccess(source.GetUser()))
00164                         ok = true;
00165                 else if (source.GetUser() && !source.GetUser()->fingerprint.empty() && na->nc->FindCert(source.GetUser()->fingerprint))
00166                         ok = true;
00167 
00168                 if (ok == false && !pass.empty())
00169                 {
00170                         NSRecoverRequest *req = new NSRecoverRequest(owner, source, this, na->nick, pass);
00171                         FOREACH_MOD(I_OnCheckAuthentication, OnCheckAuthentication(source.GetUser(), req));
00172                         req->Dispatch();
00173                 }
00174                 else
00175                 {
00176                         NSRecoverRequest req(owner, source, this, na->nick, pass);
00177 
00178                         if (ok)
00179                                 req.OnSuccess();
00180                         else
00181                                 req.OnFail();
00182                 }
00183         }
00184 
00185         bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
00186         {
00187                 this->SendSyntax(source);
00188                 source.Reply(" ");
00189                 source.Reply(_("Recovers your nick from another user or from services.\n"
00190                                 "If services are currently holding your nick, the hold\n"
00191                                 "will be released. If another user is holding your nick\n"
00192                                 "and is identified they will be killed (similar to the old\n"
00193                                 "GHOST command). If they are not identified they will be\n"
00194                                 "forced off of the nick."));
00195                 return true;
00196         }
00197 };
00198 
00199 class NSRecover : public Module
00200 {
00201         CommandNSRecover commandnsrecover;
00202 
00203  public:
00204         NSRecover(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE),
00205                 commandnsrecover(this)
00206         {
00207                 this->SetAuthor("Anope");
00208 
00209                 if (Config->NoNicknameOwnership)
00210                         throw ModuleException(modname + " can not be used with options:nonicknameownership enabled");
00211 
00212                 Implementation i[] = { I_OnUserNickChange, I_OnJoinChannel, I_OnShutdown, I_OnRestart };
00213                 ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
00214         }
00215 
00216         ~NSRecover()
00217         {
00218                 for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
00219                         it->second->Shrink("ns_recover_info");
00220 
00221                 OnShutdown();
00222         }
00223 
00224         void OnShutdown() anope_override
00225         {
00226                 /* On shutdown, restart, or mod unload, remove all of our holds for nicks (svshold or qlines)
00227                  * because some IRCds do not allow us to have these automatically expire
00228                  */
00229                 for (nickalias_map::const_iterator it = NickAliasList->begin(); it != NickAliasList->end(); ++it)
00230                         it->second->Release();
00231         }
00232 
00233         void OnRestart() anope_override { OnShutdown(); }
00234 
00235         void OnUserNickChange(User *u, const Anope::string &oldnick) anope_override
00236         {
00237                 if (Config->NSRestoreOnRecover)
00238                 {
00239                         NSRecoverExtensibleInfo *ei = u->GetExt<NSRecoverExtensibleInfo *>("ns_recover_info");
00240 
00241                         if (ei != NULL)
00242                                 for (std::map<Anope::string, ChannelStatus>::iterator it = ei->begin(), it_end = ei->end(); it != it_end;)
00243                                 {
00244                                         Channel *c = Channel::Find(it->first);
00245                                         const Anope::string &cname = it->first;
00246                                         ++it;
00247 
00248                                         /* User might already be on the channel */
00249                                         if (u->FindChannel(c))
00250                                                 this->OnJoinChannel(u, c);
00251                                         else if (IRCD->CanSVSJoin)
00252                                                 IRCD->SendSVSJoin(NickServ, u, cname, "");
00253                                 }
00254                 }
00255         }
00256 
00257         void OnJoinChannel(User *u, Channel *c) anope_override
00258         {
00259                 if (Config->NSRestoreOnRecover)
00260                 {
00261                         NSRecoverExtensibleInfo *ei = u->GetExt<NSRecoverExtensibleInfo *>("ns_recover_info");
00262 
00263                         if (ei != NULL)
00264                         {
00265                                 std::map<Anope::string, ChannelStatus>::iterator it = ei->find(c->name);
00266                                 if (it != ei->end())
00267                                 {
00268                                         for (std::set<Anope::string>::iterator it2 = it->second.modes.begin(), it2_end = it->second.modes.end(); it2 != it2_end; ++it2)
00269                                                 c->SetMode(c->ci->WhoSends(), ModeManager::FindChannelModeByName(*it2), u->GetUID());
00270 
00271                                         ei->erase(it);
00272                                         if (ei->empty())
00273                                                 u->Shrink("ns_recover_info");
00274                                 }
00275                         }
00276                 }
00277         }
00278 };
00279 
00280 MODULE_INIT(NSRecover)