b75841a805345549b3cb3a247165359b6a0ae5f7
[ngircd-alex.git] / src / ngircd / irc-op.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2014 Alexander Barton (alex@barton.de) and Contributors.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * Please read the file COPYING, README and AUTHORS for more information.
10  */
11
12 #include "portab.h"
13
14 /**
15  * @file
16  * Channel operator commands
17  */
18
19 #include <assert.h>
20 #include <string.h>
21
22 #include "conn.h"
23 #include "channel.h"
24 #include "irc-macros.h"
25 #include "irc-write.h"
26 #include "lists.h"
27 #include "log.h"
28 #include "messages.h"
29 #include "parse.h"
30
31 #include "irc-op.h"
32
33 /* Local functions */
34
35 static bool
36 try_kick(CLIENT *peer, CLIENT* from, const char *nick, const char *channel,
37          const char *reason)
38 {
39         CLIENT *target = Client_Search(nick);
40
41         if (!target)
42                 return IRC_WriteErrClient(from, ERR_NOSUCHNICK_MSG,
43                                           Client_ID(from), nick);
44
45         Channel_Kick(peer, target, from, channel, reason);
46         return true;
47 }
48
49 /* Global functions */
50
51 /**
52  * Handler for the IRC command "KICK".
53  *
54  * @param Client The client from which this command has been received.
55  * @param Req Request structure with prefix and all parameters.
56  * @return CONNECTED or DISCONNECTED.
57  */
58 GLOBAL bool
59 IRC_KICK(CLIENT *Client, REQUEST *Req)
60 {
61         CLIENT *from;
62         char *itemList = Req->argv[0];
63         const char* currentNick, *currentChannel, *reason;
64         unsigned int channelCount = 1;
65         unsigned int nickCount = 1;
66
67         assert( Client != NULL );
68         assert( Req != NULL );
69
70         _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
71
72         while (*itemList) {
73                 if (*itemList == ',') {
74                         *itemList = '\0';
75                         channelCount++;
76                 }
77                 itemList++;
78         }
79
80         itemList = Req->argv[1];
81         while (*itemList) {
82                 if (*itemList == ',') {
83                         *itemList = '\0';
84                         nickCount++;
85                 }
86                 itemList++;
87         }
88
89         reason = Req->argc == 3 ? Req->argv[2] : Client_ID(from);
90         currentNick = Req->argv[1];
91         currentChannel = Req->argv[0];
92         if (channelCount == 1) {
93                 while (nickCount > 0) {
94                         if (!try_kick(Client, from, currentNick,
95                                       currentChannel, reason))
96                                 return false;
97
98                         while (*currentNick)
99                                 currentNick++;
100
101                         currentNick++;
102                         nickCount--;
103                 }
104         } else if (channelCount == nickCount) {
105                 while (nickCount > 0) {
106                         if (!try_kick(Client, from, currentNick,
107                                       currentChannel, reason))
108                                 return false;
109
110                         while (*currentNick)
111                                 currentNick++;
112
113                         while (*currentChannel)
114                                 currentChannel++;
115
116                         currentNick++;
117                         currentChannel++;
118                         nickCount--;
119                 }
120         } else {
121                 return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
122                                         Client_ID(Client), Req->command);
123         }
124         return true;
125 } /* IRC_KICK */
126
127 /**
128  * Handler for the IRC command "INVITE".
129  *
130  * @param Client The client from which this command has been received.
131  * @param Req Request structure with prefix and all parameters.
132  * @return CONNECTED or DISCONNECTED.
133  */
134 GLOBAL bool
135 IRC_INVITE(CLIENT *Client, REQUEST *Req)
136 {
137         CHANNEL *chan;
138         CLIENT *target, *from;
139         const char *colon_if_necessary;
140         bool remember = false;
141
142         assert( Client != NULL );
143         assert( Req != NULL );
144
145         _IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
146
147         /* Search user */
148         target = Client_Search(Req->argv[0]);
149         if (!target || (Client_Type(target) != CLIENT_USER))
150                 return IRC_WriteErrClient(from, ERR_NOSUCHNICK_MSG,
151                                           Client_ID(Client), Req->argv[0]);
152
153         chan = Channel_Search(Req->argv[1]);
154         if (chan) {
155                 /* Channel exists. Is the user a valid member of the channel? */
156                 if (!Channel_IsMemberOf(chan, from))
157                         return IRC_WriteErrClient(from, ERR_NOTONCHANNEL_MSG,
158                                                   Client_ID(Client),
159                                                   Req->argv[1]);
160
161                 /* Is the channel "invite-disallow"? */
162                 if (Channel_HasMode(chan, 'V'))
163                         return IRC_WriteErrClient(from, ERR_NOINVITE_MSG,
164                                                   Client_ID(from),
165                                                   Channel_Name(chan));
166
167                 /* Is the channel "invite-only"? */
168                 if (Channel_HasMode(chan, 'i')) {
169                         /* Yes. The user issuing the INVITE command must be
170                          * channel owner/admin/operator/halfop! */
171                         if (!Channel_UserHasMode(chan, from, 'q') &&
172                             !Channel_UserHasMode(chan, from, 'a') &&
173                             !Channel_UserHasMode(chan, from, 'o') &&
174                             !Channel_UserHasMode(chan, from, 'h'))
175                                 return IRC_WriteErrClient(from,
176                                                           ERR_CHANOPRIVSNEEDED_MSG,
177                                                           Client_ID(from),
178                                                           Channel_Name(chan));
179                         remember = true;
180                 }
181
182                 /* Is the target user already member of the channel? */
183                 if (Channel_IsMemberOf(chan, target))
184                         return IRC_WriteErrClient(from, ERR_USERONCHANNEL_MSG,
185                                                   Client_ID(from),
186                                                   Req->argv[0], Req->argv[1]);
187
188                 /* If the target user is banned on that channel: remember invite */
189                 if (Lists_Check(Channel_GetListBans(chan), target))
190                         remember = true;
191
192                 if (remember) {
193                         /* We must remember this invite */
194                         if (!Channel_AddInvite(chan, Client_MaskCloaked(target),
195                                                 true))
196                                 return CONNECTED;
197                 }
198         }
199
200         LogDebug("User \"%s\" invites \"%s\" to \"%s\" ...", Client_Mask(from),
201                  Req->argv[0], Req->argv[1]);
202
203         /*
204          * RFC 2812 states:
205          * 'There is no requirement that the channel [..] must exist or be a
206          * valid channel'. The problem with this is that this allows the
207          * "channel" to contain spaces, in which case we must prefix its name
208          * with a colon to make it clear that it is only a single argument.
209          */
210         colon_if_necessary = strchr(Req->argv[1], ' ') ? ":":"";
211         /* Inform target client */
212         IRC_WriteStrClientPrefix(target, from, "INVITE %s %s%s", Req->argv[0],
213                                   colon_if_necessary, Req->argv[1]);
214
215         if (Client_Conn(target) > NONE) {
216                 /* The target user is local, so we have to send the status code */
217                 if (!IRC_WriteStrClientPrefix(from, target, RPL_INVITING_MSG,
218                                                Client_ID(from), Req->argv[0],
219                                                colon_if_necessary, Req->argv[1]))
220                         return DISCONNECTED;
221
222                 if (Client_HasMode(target, 'a') &&
223                         !IRC_WriteStrClient(from, RPL_AWAY_MSG, Client_ID(from),
224                                         Client_ID(target), Client_Away(target)))
225                                 return DISCONNECTED;
226         }
227         return CONNECTED;
228 } /* IRC_INVITE */
229
230 /* -eof- */