Пример #1
0
	void Execute()
	{
		if (CurrentVote.Voting)
		{
			Player->PrintToClient(PRINT_HIGH, "Vote already in progress.");
			return;
		}

		if (ArgCount() != 3)
		{
			Player->PrintToClient (PRINT_HIGH, "Syntax: vote nextmap mapname\n");
			return;
		}

		CVoteMapData *voteData = QNew(TAG_GENERIC) CVoteMapData(true, ArgGets(2));
		CurrentVote.StartVote(VOTE_NEXTMAP, voteData, Player->Client.Persistent.Name);
	}
Пример #2
0
/**
 * ArgRejoinArray
 *
 * "Rejoins" arguments in an array.
 *
 * @param ArgV the array
 * @param Index the index of the first argument which is to be rejoined
 */
void ArgRejoinArray(const char **ArgV, int Index) {
	int Count = ArgCount(ArgV[0]);

	if (Count - 1 <= Index) {
		return;
	}

	for (int i = Index + 1; i < Count; i++) {
		char *Arg = const_cast<char *>(ArgV[i]);

		if (strchr(Arg, ' ') != NULL || *(Arg - 1) == ':') {
			*(Arg - 1) = ':';
			*(Arg - 2) = ' ';
		} else {
			*(Arg - 1) = ' ';
		}
	}
}
Пример #3
0
/**
 * ParseLineArgV
 *
 * Parses and processes a line which was sent by the server.
 *
 * @param argc number of tokens
 * @param argv the tokens
 */
bool CIRCConnection::ParseLineArgV(int argc, const char **argv) {
	CChannel *Channel;
	CClientConnection *Client;

	m_LastResponse = g_CurrentTime;

	if (argc < 2) {
		return true;
	}

	const char *Reply = argv[0];
	const char *Raw = argv[1];
	char *Nick = ::NickFromHostmask(Reply);
	int iRaw = atoi(Raw);

	bool b_Me = false;
	if (m_CurrentNick != NULL && Nick != NULL && strcasecmp(Nick, m_CurrentNick) == 0) {
		b_Me = true;
	}

	free(Nick);

	Client = GetOwner()->GetClientConnectionMultiplexer();

	// HASH values
	CHashCompare hashRaw(argv[1]);
	static CHashCompare hashPrivmsg("PRIVMSG");
	static CHashCompare hashNotice("NOTICE");
	static CHashCompare hashJoin("JOIN");
	static CHashCompare hashPart("PART");
	static CHashCompare hashKick("KICK");
	static CHashCompare hashNick("NICK");
	static CHashCompare hashQuit("QUIT");
	static CHashCompare hashMode("MODE");
	static CHashCompare hashTopic("TOPIC");
	static CHashCompare hashPong("PONG");
	// END of HASH values

	if (argc > 3 && iRaw == 433) {
		bool ReturnValue = ModuleEvent(argc, argv);

		if (ReturnValue) {
			if (GetCurrentNick() == NULL) {
				WriteLine("NICK :%s_", argv[3]);
			}

			if (m_NickCatchTimer == NULL) {
				m_NickCatchTimer = new CTimer(30, false, NickCatchTimer, this);
			}
		}

		return ReturnValue;
	} else if (argc > 3 && hashRaw == hashPrivmsg && Client == NULL) {
		const char *Host;
		const char *Dest = argv[2];
		char *Nick = ::NickFromHostmask(Reply);

		Channel = GetChannel(Dest);

		if (Channel != NULL) {
			CNick *User = Channel->GetNames()->Get(Nick);

			if (User != NULL) {
				User->SetIdleSince(g_CurrentTime);
			}

			Channel->AddBacklogLine(argv[0], argv[3]);
		}

		if (!ModuleEvent(argc, argv)) {
			free(Nick);
			return false;
		}

		/* don't log ctcp requests */
		if (argv[3][0] != '\1' && argv[3][strlen(argv[3]) - 1] != '\1' && Dest != NULL &&
				Nick != NULL && m_CurrentNick != NULL && strcasecmp(Dest, m_CurrentNick) == 0 &&
				strcasecmp(Nick, m_CurrentNick) != 0) {
			char *Dup;
			char *Delim;

			Dup = strdup(Reply);

			if (AllocFailed(Dup)) {
				free(Nick);

				return true;
			}

			Delim = strchr(Dup, '!');

			if (Delim != NULL) {
				*Delim = '\0';

				Host = Delim + 1;
			}

			GetOwner()->Log("%s (%s): %s", Dup, Delim ? Host : "<unknown host>", argv[3]);

			free(Dup);
		}

		free(Nick);

		UpdateHostHelper(Reply);

		return true;
	} else if (argc > 3 && hashRaw == hashPrivmsg && Client != NULL) {
		Channel = GetChannel(argv[2]);

		if (Channel != NULL) {
			Channel->AddBacklogLine(argv[0], argv[3]);
		}
	} else if (argc > 3 && hashRaw == hashNotice && Client == NULL) {
		const char *Dest = argv[2];
		char *Nick;
		
		if (!ModuleEvent(argc, argv)) {
			return false;
		}

		Nick = ::NickFromHostmask(Reply);

		/* don't log ctcp replies */
		if (argv[3][0] != '\1' && argv[3][strlen(argv[3]) - 1] != '\1' && Dest != NULL &&
				Nick != NULL && m_CurrentNick != NULL && strcasecmp(Dest, m_CurrentNick) == 0 &&
				strcasecmp(Nick, m_CurrentNick) != 0) {
			GetOwner()->Log("%s (notice): %s", Reply, argv[3]);
		}

		free(Nick);

		return true;
	} else if (argc > 2 && hashRaw == hashJoin) {
		if (b_Me) {
			AddChannel(argv[2]);

			/* GetOwner() can be NULL if AddChannel failed */
			if (GetOwner() != NULL && Client == NULL) {
				WriteLine("MODE %s", argv[2]);
			}
		}

		Channel = GetChannel(argv[2]);

		if (Channel != NULL) {
			Nick = NickFromHostmask(Reply);

			if (AllocFailed(Nick)) {
				return false;
			}

			Channel->AddUser(Nick, '\0');
			free(Nick);
		}

		UpdateHostHelper(Reply);
	} else if (argc > 2 && hashRaw == hashPart) {
		bool bRet = ModuleEvent(argc, argv);

		if (b_Me) {
			RemoveChannel(argv[2]);
		} else {
			Channel = GetChannel(argv[2]);

			if (Channel != NULL) {
				Nick = ::NickFromHostmask(Reply);

				if (AllocFailed(Nick)) {
					return false;
				}

				Channel->RemoveUser(Nick);

				free(Nick);
			}
		}

		UpdateHostHelper(Reply);

		return bRet;
	} else if (argc > 3 && hashRaw == hashKick) {
		bool bRet = ModuleEvent(argc, argv);

		if (m_CurrentNick != NULL && strcasecmp(argv[3], m_CurrentNick) == 0) {
			RemoveChannel(argv[2]);

			if (Client == NULL) {
				char *Dup = strdup(Reply);

				if (AllocFailed(Dup)) {
					return bRet;
				}

				char *Delim = strchr(Dup, '!');
				const char *Host = NULL;

				if (Delim) {
					*Delim = '\0';

					Host = Delim + 1;
				}

				GetOwner()->Log("%s (%s) kicked you from %s (%s)", Dup, Delim ? Host : "<unknown host>", argv[2], argc > 4 ? argv[4] : "");

				free(Dup);
			}
		} else {
			Channel = GetChannel(argv[2]);

			if (Channel != NULL) {
				Channel->RemoveUser(argv[3]);
			}
		}

		UpdateHostHelper(Reply);

		return bRet;
	} else if (argc > 2 && iRaw == 1) {
		if (Client != NULL) {
			if (strcmp(Client->GetNick(), argv[2]) != 0) {
				Client->WriteLine(":%s!%s NICK :%s", Client->GetNick(), m_Site ? m_Site : "*****@*****.**", argv[2]);
			}
		}

		free(m_CurrentNick);
		m_CurrentNick = strdup(argv[2]);

		free(m_Server);
		m_Server = strdup(Reply);
	} else if (argc > 2 && hashRaw == hashNick) {
		if (b_Me) {
			free(m_CurrentNick);
			m_CurrentNick = strdup(argv[2]);
		}

		Nick = NickFromHostmask(argv[0]);

		int i = 0;

		if (!b_Me && GetOwner()->GetClientConnectionMultiplexer() == NULL) {
			const char *AwayNick = GetOwner()->GetAwayNick();

			if (AwayNick != NULL && strcasecmp(AwayNick, Nick) == 0) {
				WriteLine("NICK %s", AwayNick);
			}
		}

		while (hash_t<CChannel *> *ChannelHash = m_Channels->Iterate(i++)) {
			ChannelHash->Value->RenameUser(Nick, argv[2]);
		}

		free(Nick);
	} else if (argc > 1 && hashRaw == hashQuit) {
		bool bRet = ModuleEvent(argc, argv);

		Nick = NickFromHostmask(argv[0]);

		int i = 0;

		while (hash_t<CChannel *> *ChannelHash = m_Channels->Iterate(i++)) {
			ChannelHash->Value->RemoveUser(Nick);
		}

		free(Nick);

		return bRet;
	} else if (argc > 1 && (iRaw == 422 || iRaw == 376)) {
		int DelayJoin = GetOwner()->GetDelayJoin();
		if (m_State != State_Connected) {
			const CVector<CModule *> *Modules = g_Bouncer->GetModules();

			for (int i = 0; i < Modules->GetLength(); i++) {
				(*Modules)[i]->ServerLogon(GetOwner()->GetUsername());
			}

			const char *ClientNick;

			if (Client != NULL) {
				ClientNick = Client->GetNick();

				if (strcmp(m_CurrentNick, ClientNick) != 0) {
					Client->ChangeNick(m_CurrentNick);
				}
			}

			GetOwner()->Log("You were successfully connected to an IRC server.");
			g_Bouncer->Log("User %s connected to an IRC server.",
				GetOwner()->GetUsername());
		}

		if (DelayJoin == 1) {
			m_DelayJoinTimer = g_Bouncer->CreateTimer(5, false, DelayJoinTimer, this);
		} else if (DelayJoin == 0) {
			JoinChannels();
		}

		if (Client == NULL) {
			bool AppendTS = (GetOwner()->GetConfig()->ReadInteger("user.ts") != 0);
			const char *AwayReason = GetOwner()->GetAwayText();

			if (AwayReason != NULL) {
				WriteLine(AppendTS ? "AWAY :%s (Away since the dawn of time)" : "AWAY :%s", AwayReason);
			}
		}

		const char *AutoModes = GetOwner()->GetAutoModes();
		const char *DropModes = GetOwner()->GetDropModes();

		if (AutoModes != NULL) {
			WriteLine("MODE %s +%s", GetCurrentNick(), AutoModes);
		}

		if (DropModes != NULL && Client == NULL) {
			WriteLine("MODE %s -%s", GetCurrentNick(), DropModes);
		}

		m_State = State_Connected;
	} else if (argc > 1 && strcasecmp(Reply, "ERROR") == 0) {
		if (strstr(Raw, "throttle") != NULL) {
			GetOwner()->ScheduleReconnect(120);
		} else {
			GetOwner()->ScheduleReconnect(5);
		}

		if (GetCurrentNick() != NULL && GetSite() != NULL) {
			g_Bouncer->LogUser(GetUser(), "Error received for user %s [%s!%s]: %s",
				GetOwner()->GetUsername(), GetCurrentNick(), GetSite(), argv[1]);
		} else {
			g_Bouncer->LogUser(GetUser(), "Error received for user %s: %s",
				GetOwner()->GetUsername(), argv[1]);
		}
	} else if (argc > 3 && iRaw == 465) {
		if (GetCurrentNick() != NULL && GetSite() != NULL) {
			g_Bouncer->LogUser(GetUser(), "G/K-line reason for user %s [%s!%s]: %s",
				GetOwner()->GetUsername(), GetCurrentNick(), GetSite(), argv[3]);
		} else {
			g_Bouncer->LogUser(GetUser(), "G/K-line reason for user %s: %s",
				GetOwner()->GetUsername(), argv[3]);
		}
	} else if (argc > 5 && iRaw == 351) {
		free(m_ServerVersion);
		m_ServerVersion = strdup(argv[3]);

		free(m_ServerFeat);
		m_ServerFeat = strdup(argv[5]);
	} else if (argc > 3 && iRaw == 5) {
		for (int i = 3; i < argc - 1; i++) {
			char *Dup = strdup(argv[i]);

			if (AllocFailed(Dup)) {
				return false;
			}

			char *Eq = strchr(Dup, '=');

			if (strcasecmp(Dup, "NAMESX") == 0) {
				WriteLine("PROTOCTL NAMESX");
			}

			char *Value;

			if (Eq) {
				*Eq = '\0';

				Value = strdup(++Eq);
			} else {
				Value = strdup("");
			}

			m_ISupport->Add(Dup, Value);

			free(Dup);
		}
	} else if (argc > 4 && iRaw == 324) {
		Channel = GetChannel(argv[3]);

		if (Channel != NULL) {
			Channel->ClearModes();
			Channel->ParseModeChange(argv[0], argv[4], argc - 5, &argv[5]);
			Channel->SetModesValid(true);
		}
	} else if (argc > 3 && hashRaw == hashMode) {
		Channel = GetChannel(argv[2]);

		if (Channel != NULL) {
			Channel->ParseModeChange(argv[0], argv[3], argc - 4, &argv[4]);
		} else if (strcmp(m_CurrentNick, argv[2]) == 0) {
			bool Flip = true, WasNull;
			const char *Modes = argv[3];
			size_t Length = strlen(Modes) + 1;

			if (m_Usermodes != NULL) {
				Length += strlen(m_Usermodes);
			}

			WasNull = (m_Usermodes != NULL) ? false : true;
			m_Usermodes = (char *)realloc(m_Usermodes, Length);

			if (AllocFailed(m_Usermodes)) {
				return false;
			}

			if (WasNull) {
				m_Usermodes[0] = '\0';
			}

			while (*Modes != '\0') {
				if (*Modes == '+') {
					Flip = true;
				} else if (*Modes == '-') {
					Flip = false;
				} else {
					if (Flip) {
						size_t Position = strlen(m_Usermodes);
						m_Usermodes[Position] = *Modes;
						m_Usermodes[Position + 1] = '\0';
					} else {
						char *CurrentModes = m_Usermodes;
						size_t a = 0;

						while (*CurrentModes != '\0') {
							*CurrentModes = m_Usermodes[a];

							if (*CurrentModes != *Modes) {
								CurrentModes++;
							}

							a++;
						}
					}
				}

				Modes++;
			}
		}

		UpdateHostHelper(Reply);
	} else if (argc > 4 && iRaw == 329) {
		Channel = GetChannel(argv[3]);

		if (Channel != NULL) {
			Channel->SetCreationTime(atoi(argv[4]));
		}
	} else if (argc > 4 && iRaw == 332) {
		Channel = GetChannel(argv[3]);

		if (Channel != NULL) {
			Channel->SetTopic(argv[4]);
		}
	} else if (argc > 5 && iRaw == 333) {
		Channel = GetChannel(argv[3]);

		if (Channel != NULL) {
			Channel->SetTopicNick(argv[4]);
			Channel->SetTopicStamp(atoi(argv[5]));
		}
	} else if (argc > 3 && iRaw == 331) {
		Channel = GetChannel(argv[3]);

		if (Channel != NULL) {
			Channel->SetNoTopic();
		}
	} else if (argc > 3 && hashRaw == hashTopic) {
		Channel = GetChannel(argv[2]);

		if (Channel != NULL) {
			Channel->SetTopic(argv[3]);
			Channel->SetTopicStamp(g_CurrentTime);
			Channel->SetTopicNick(argv[0]);
		}

		UpdateHostHelper(Reply);
	} else if (argc > 5 && iRaw == 353) {
		Channel = GetChannel(argv[4]);

		if (Channel != NULL) {
			const char *nicks;
			const char **nickv;

			nicks = ArgTokenize(argv[5]);

			if (AllocFailed(nicks)) {
				return false;
			}

			nickv = ArgToArray(nicks);

			if (AllocFailed(nickv)) {
				ArgFree(nicks);

				return false;
			}

			int nickc = ArgCount(nicks);

			for (int i = 0; i < nickc; i++) {
				char *Nick = strdup(nickv[i]);
				char *BaseNick = Nick;

				if (AllocFailed(Nick)) {
					ArgFree(nicks);

					return false;
				}

				StrTrim(Nick, ' ');

				while (IsNickPrefix(*Nick)) {
					Nick++;
				}

				char *Modes = NULL;

				if (BaseNick != Nick) {
					Modes = (char *)malloc(Nick - BaseNick + 1);

					if (!AllocFailed(Modes)) {
						strmcpy(Modes, BaseNick, Nick - BaseNick + 1);
					}
				}

				Channel->AddUser(Nick, Modes);

				free(BaseNick);
				free(Modes);
			}

			ArgFreeArray(nickv);
			ArgFree(nicks);
		}
	} else if (argc > 3 && iRaw == 366) {
		Channel = GetChannel(argv[3]);

		if (Channel != NULL) {
			Channel->SetHasNames();
		}
	} else if (argc > 9 && iRaw == 352) {
		const char *Ident = argv[4];
		const char *Host = argv[5];
		const char *Server = argv[6];
		const char *Nick = argv[7];
		const char *Realname = argv[9];
		char *Mask;

		int rc = asprintf(&Mask, "%s!%s@%s", Nick, Ident, Host);

		if (!RcFailed(rc)) {
			UpdateHostHelper(Mask);
			UpdateWhoHelper(Nick, Realname, Server);

			free(Mask);
		}
	} else if (argc > 6 && iRaw == 367) {
		Channel = GetChannel(argv[3]);

		if (Channel != NULL) {
			Channel->GetBanlist()->SetBan(argv[4], argv[5], atoi(argv[6]));
		}
	} else if (argc > 3 && iRaw == 368) {
		Channel = GetChannel(argv[3]);

		if (Channel != NULL) {
			Channel->SetHasBans();
		}
	} else if (argc > 3 && iRaw == 396) {
		free(m_Site);
		m_Site = strdup(argv[3]);

		if (AllocFailed(m_Site)) {}
	} else if (argc > 3 && hashRaw == hashPong && m_Server != NULL && strcasecmp(argv[2], m_Server) == 0 && m_EatPong) {
		m_EatPong = false;

		return false;
	} else if (argc > 3 && iRaw == 421) {
		m_FloodControl->Unplug();

		return false;
	}

	if (GetOwner() != NULL) {
		return ModuleEvent(argc, argv);
	} else {
		return true;
	}
}
Пример #4
0
	void Execute()
	{
		if (CurrentVote.Voting)
		{
			Player->PrintToClient(PRINT_HIGH, "Vote already in progress.");
			return;
		}

		if (ArgCount() != 3)
		{
			Player->PrintToClient (PRINT_HIGH, "Use \"players\" to check the player IDs for kick-by-ID. Syntax:\n  vote ban/kick n:id\n  vote ban/kick p:playerName\n\n  Example: vote ban/kick n:8\n  Example: vote ban/kick p:Paril\n");
			return;
		}

		String str = ArgGets(2);

		if (str.Count() < 3 ||
			str[1] != ':' ||
			(str[0] != 'p' && str[0] != 'n'))
		{
			Player->PrintToClient (PRINT_HIGH, "Syntax error. Type \"vote ban\" or \"vote kick\" to see syntax.\n");
			return;
		}

		int playerToKick = -1;

		if (str[0] == 'p')
		{
			String playerName = str.Substring(2).ToLower();

			if (playerName.IsNullOrEmpty())	
			{
				Player->PrintToClient (PRINT_HIGH, "Syntax error. Type \"vote ban\" or \"vote kick\" to see syntax.\n");
				return;
			}

			for (int i = 1; i <= Game.MaxClients; ++i)
			{
				if (entity_cast<CPlayerEntity>(Game.Entities[i].Entity)->Client.Persistent.Name.Clone().ToLower() == playerName)
				{
					if (playerToKick != -1)
					{
						Player->PrintToClient (PRINT_HIGH, "Multiple players exist by that name. Type \"vote ban\" or \"vote kick\" to see how to ban by player number instead.\n");
						return;
					}

					playerToKick = i;
				}
			}

			if (playerToKick == -1)
			{
				Player->PrintToClient (PRINT_HIGH, "Player does not exist.\n");
				return;
			}
		}
		else
		{
			String playerNum = str.Substring(2);

			for (size_t i = 0; i < playerNum.Count(); ++i)
			{
				if (playerNum[i] < '0' || playerNum[i] > '9')
				{
					Player->PrintToClient (PRINT_HIGH, "Invalid player number.\n");
					return;
				}
			}

			playerToKick = atoi(str.Substring(2).CString());

			if (playerToKick <= 0 || playerToKick > Game.MaxClients)
			{
				Player->PrintToClient (PRINT_HIGH, "Invalid player number.\n");
				return;
			}
		}

		CVoteKickBanData *voteData = QNew(TAG_GENERIC) CVoteKickBanData(ban, playerToKick);
		CurrentVote.StartVote(voteType, voteData, Player->Client.Persistent.Name);
	}
Пример #5
0
/**
 * CCore
 *
 * Creates a new shroudBNC application object.
 *
 * @param Config the main config object
 * @param argc argument counts
 * @param argv program arguments
 */
CCore::CCore(CConfig *Config, int argc, char **argv) {
	int i;

	m_Log = NULL;

	m_PidFile = NULL;

	WritePidFile();

	m_Config = Config;

	m_SSLContext = NULL;
	m_SSLClientContext = NULL;

	m_Status = Status_Running; 

	CacheInitialize(m_ConfigCache, Config, "system.");

	char *SourcePath = strdup(BuildPathLog("sbnc.log"));
	rename(SourcePath, BuildPathLog("sbnc.log.old"));
	free(SourcePath);

	m_PollFds.Preallocate(SFD_SETSIZE);

	m_Log = new CLog("sbnc.log", true);

	if (m_Log == NULL) {
		printf("Log system could not be initialized. Shutting down.");

		exit(EXIT_FAILURE);
	}

	m_Log->Clear();
	Log("Log system initialized.");

	g_Bouncer = this;

	m_Config = Config;

	m_Args.SetList(argv, argc);

	m_Ident = new CIdentSupport();

	m_Config = new CConfig("sbnc.conf", NULL);
	CacheInitialize(m_ConfigCache, m_Config, "system.");

	const char *Users;
	CUser *User;

	if ((Users = m_Config->ReadString("system.users")) == NULL) {
		if (!MakeConfig()) {
			Log("Configuration file could not be created.");

			Fatal();
		}

		printf("Configuration has been successfully saved. Please restart shroudBNC now.\n");
		exit(EXIT_SUCCESS);
	}

	const char *Args;
	int Count;

	Args = ArgTokenize(Users);

	if (AllocFailed(Args)) {
		Fatal();
	}

	Count = ArgCount(Args);

	for (i = 0; i < Count; i++) {
		const char *Name = ArgGet(Args, i + 1);

		User = new CUser(Name);

		if (AllocFailed(User)) {
			Fatal();
		}

		m_Users.Add(Name, User);
	}

	ArgFree(Args);

	m_Listener = NULL;
	m_ListenerV6 = NULL;
	m_SSLListener = NULL;
	m_SSLListenerV6 = NULL;

	time(&m_Startup);

	m_LoadingModules = false;
	m_LoadingListeners = false;

	InitializeSocket();

	m_Capabilities = new CVector<const char *>();
	m_Capabilities->Insert("multi-prefix");
	m_Capabilities->Insert("znc.in/server-time-iso");
}
Пример #6
0
void
GameEvent(CORE_DATA *cd)
{
	switch (cd->event) {
	case EVENT_START:
		RegisterPlugin(OPENCORE_VERSION, "rabbit", "cycad", "1.0", __DATE__, __TIME__, "A rabbit bot", sizeof(USER_DATA), 0);

		/* set rabbit pid to nobody */
		ud(cd)->rabbit_pid = PID_NONE;

		/* register rabbit command */
		RegisterCommand(COMMAND_RABBIT, "!rabbit", "Rabbit", 2, CMD_PRIVATE, "<name>:<bounty>[:<special>]", "Start the rabbit game", NULL); break;
	case EVENT_LOGIN:
		/* set the bot's location to center */
		SetPosition(16 * 512, 16 * 512, 0, 0);
		break;
	case EVENT_COMMAND:
		switch (cd->cmd_id) {
		case COMMAND_RABBIT: {
			int show_usage = 0;

			PLAYER_ID rpid = ud(cd)->rabbit_pid;

			if (rpid == PID_NONE) {	/* rabbit game is not runnig */
				if (cd->cmd_argc <= 1) {
					show_usage = 1;
				} else {
					char *cmd = cd->cmd_argr[1];
					int nargs = ArgCount(cmd, ':');

					if (nargs != 2 && nargs != 3) {
						show_usage = 1;
					} else {	/* args to command are valid */
						/* copy name out of command */
						char rname[20];
						DelimArgs(rname, sizeof(rname), cmd, 0, ':', 0);

						/* copy bounty out of command */
						int bty = AtoiArg(cmd, 1, ':');

						/* copy special out of command if it was specified */
						int special = 0;
						if (nargs == 3)
							special = AtoiArg(cmd, 2, ':');

						/* find the player specified on the command line */
						PLAYER *rabbit = FindPlayerName(rname, MATCH_HERE | MATCH_PREFIX);
						if (rabbit) {
							/* player found, start the game */
							start_rabbit_game(cd, rabbit, bty, special);
						} else {
							RmtMessageFmt(cd->cmd_name, "Player not found: %s", rname);
						}
					}
				}
			} else {
				/* rabbit game is already running */
				PLAYER *p = FindPlayerPid(rpid, MATCH_HERE);

				RmtMessageFmt(cd->cmd_name, "Rabbit is already running (%s is the rabbit)",
				    p ? p->name : "**UNKNOWN**");
			}

			/* display usage if necessary */
			if (show_usage) RmtMessageFmt(cd->cmd_name, "Usage: %s <name>:<bounty>[:<special>]", cd->cmd_argv[0]);
		}
		default: break;
		}
		break;
	case EVENT_UPDATE:
		if (ud(cd)->rabbit_pid == cd->p1->pid) {	/* if the update is for the rabbit */
			if (cd->p1->status & STATUS_SAFE) {	/* check if the rabbit is in safe */
				/* the rabbit entered safe, end the game */
				ArenaMessageFmt("%s has lost the rabbit game by entering safe!", cd->p1->name);
				ud(cd)->rabbit_pid = PID_NONE;
			}
		}
		break;
	case EVENT_TIMER:
		if (ud(cd)->rabbit_pid != PID_NONE) {	/* if the rabbit game is running */
			/* find the rabbit */
			PLAYER *p = FindPlayerPid(ud(cd)->rabbit_pid, MATCH_HERE);
			if (p) {
				if (ud(cd)->where_timer == cd->timer_id) {
					/* a *where timer expired, send the next one and look for response */
					PrivMessage(p, "*where");
					ud(cd)->expecting_where = 1;
				} else if (ud(cd)->gameover_timer == cd->timer_id) {
					/* the game over timer expired, the rabbit didnt die and he won */
					ArenaMessageFmt("%s wins the rabbit game by staying alive!", p->name);
					ud(cd)->rabbit_pid = PID_NONE;
				}
			} else {
				/* rabbit not found, this should never happen! */
			}
		}
	case EVENT_MESSAGE:
		/*
		 * Only look for a response of the game is running, the bot is expecting *where,
		 * and the message type is an arena message.
		 */
		if (ud(cd)->rabbit_pid != PID_NONE && ud(cd)->expecting_where && 
		    cd->msg_type == MSG_ARENA) {
			/* message is a possible *where response */

			/* find the rabbit */
			PLAYER *p = FindPlayerPid(ud(cd)->rabbit_pid, MATCH_HERE);
			if (p) {
				char where_prefix[32];
				snprintf(where_prefix, 32, "%s: ", p->name);
				where_prefix[32 - 1] = '\0';

				/*
				 * Verify that this is *where output by looking for "rabbit: " at the
				 * beginning of the string.
				 */
				if (strncasecmp(where_prefix, cd->msg, strlen(where_prefix)) == 0) {
					/* this must be a *where response, process it */
					char loc[4];
					snprintf(loc, 4, "%s", &cd->msg[strlen(where_prefix)]);
					loc[4 - 1] = '\0';

					/* announce the rabbits location */
					ArenaMessageFmt(">>> Rabbit %s is at %-3s <<<", p->name, loc);

					/* set the next *where timer */
					ud(cd)->where_timer = SetTimer(30 * 1000, 0, 0);

					/*
					 * The bot wont be looking for *where responses until the
					 * next time it sends the command, so turn parsing off
					 * for now.
					 */
					ud(cd)->expecting_where = 0;
				}
			}
		}
		break;
	case EVENT_KILL:
		if (cd->p1->pid == ud(cd)->rabbit_pid) {
			/* the rabbit died */
			ArenaMessageFmt("%s wins the rabbit game by killing Rabbit %s!", cd->p2->name, cd->p1->name);
			ud(cd)->rabbit_pid = PID_NONE;
		}
		break;
	case EVENT_LEAVE:
		if (ud(cd)->rabbit_pid == cd->p1->pid) {
			/* the rabbit left */
			ArenaMessageFmt("%s has lost the rabbit game by leaving the arena!", cd->p1->name);
			ud(cd)->rabbit_pid = PID_NONE;
		}
		break;
	case EVENT_CHANGE:
		if (ud(cd)->rabbit_pid == cd->p1->pid) {
			/* the rabbit changed ship */
			ArenaMessageFmt("%s has lost the rabbit game by changing ship/freq!", cd->p1->name);
			ud(cd)->rabbit_pid = PID_NONE;
		}
		break;
	}
}