Make sure that the target user is able to join a local channel
[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         if (Req->argv[1][0] == '&') {
154                 /* Local channel. Make sure the target user is on this server! */
155                 if (Client_Conn(target) == NONE)
156                         return IRC_WriteErrClient(from, ERR_USERNOTONSERV_MSG,
157                                                   Client_ID(Client),
158                                                   Req->argv[0]);
159         }
160
161         chan = Channel_Search(Req->argv[1]);
162         if (chan) {
163                 /* Channel exists. Is the user a valid member of the channel? */
164                 if (!Channel_IsMemberOf(chan, from))
165                         return IRC_WriteErrClient(from, ERR_NOTONCHANNEL_MSG,
166                                                   Client_ID(Client),
167                                                   Req->argv[1]);
168
169                 /* Is the channel "invite-disallow"? */
170                 if (Channel_HasMode(chan, 'V'))
171                         return IRC_WriteErrClient(from, ERR_NOINVITE_MSG,
172                                                   Client_ID(from),
173                                                   Channel_Name(chan));
174
175                 /* Is the channel "invite-only"? */
176                 if (Channel_HasMode(chan, 'i')) {
177                         /* Yes. The user issuing the INVITE command must be
178                          * channel owner/admin/operator/halfop! */
179                         if (!Channel_UserHasMode(chan, from, 'q') &&
180                             !Channel_UserHasMode(chan, from, 'a') &&
181                             !Channel_UserHasMode(chan, from, 'o') &&
182                             !Channel_UserHasMode(chan, from, 'h'))
183                                 return IRC_WriteErrClient(from,
184                                                           ERR_CHANOPRIVSNEEDED_MSG,
185                                                           Client_ID(from),
186                                                           Channel_Name(chan));
187                         remember = true;
188                 }
189
190                 /* Is the target user already member of the channel? */
191                 if (Channel_IsMemberOf(chan, target))
192                         return IRC_WriteErrClient(from, ERR_USERONCHANNEL_MSG,
193                                                   Client_ID(from),
194                                                   Req->argv[0], Req->argv[1]);
195
196                 /* If the target user is banned on that channel: remember invite */
197                 if (Lists_Check(Channel_GetListBans(chan), target))
198                         remember = true;
199
200                 if (remember) {
201                         /* We must remember this invite */
202                         if (!Channel_AddInvite(chan, Client_MaskCloaked(target),
203                                                 true))
204                                 return CONNECTED;
205                 }
206         }
207
208         LogDebug("User \"%s\" invites \"%s\" to \"%s\" ...", Client_Mask(from),
209                  Req->argv[0], Req->argv[1]);
210
211         /*
212          * RFC 2812 states:
213          * 'There is no requirement that the channel [..] must exist or be a
214          * valid channel'. The problem with this is that this allows the
215          * "channel" to contain spaces, in which case we must prefix its name
216          * with a colon to make it clear that it is only a single argument.
217          */
218         colon_if_necessary = strchr(Req->argv[1], ' ') ? ":":"";
219         /* Inform target client */
220         IRC_WriteStrClientPrefix(target, from, "INVITE %s %s%s", Req->argv[0],
221                                   colon_if_necessary, Req->argv[1]);
222
223         if (Client_Conn(target) > NONE) {
224                 /* The target user is local, so we have to send the status code */
225                 if (!IRC_WriteStrClientPrefix(from, target, RPL_INVITING_MSG,
226                                                Client_ID(from), Req->argv[0],
227                                                colon_if_necessary, Req->argv[1]))
228                         return DISCONNECTED;
229
230                 if (Client_HasMode(target, 'a') &&
231                         !IRC_WriteStrClient(from, RPL_AWAY_MSG, Client_ID(from),
232                                         Client_ID(target), Client_Away(target)))
233                                 return DISCONNECTED;
234         }
235         return CONNECTED;
236 } /* IRC_INVITE */
237
238 /* -eof- */