1 /**
2 * DChannel
3 *
4 * Represents a channel and its
5 * associated information such
6 * as its name, topic, members
7 */
8 
9 module dnetd.dchannel;
10 
11 import dnetd.dconnection : DConnection;
12 import core.sync.mutex : Mutex;
13 import std.conv : to;
14 import std.stdio : writeln;
15 import gogga;
16 
17 public class DChannel
18 {
19 	/**
20 	* Channel information
21 	*/
22 	private string name;
23 	//private string topic;
24 
25 	/**
26 	* Users in this channel
27 	*/
28 	private DConnection[] members;
29 	private Mutex memberLock;
30 
31 	this(string name)
32 	{
33 		/* Initialize the lock */
34 		memberLock = new Mutex();
35 
36 		this.name = name;
37 	}
38 
39 	public string getName()
40 	{
41 		return name;
42 	}
43 
44 	/**
45 	* Joins the given client to this channel
46 	*/
47 	public bool join(DConnection client)
48 	{
49 		/* Send a message stating the user has joined (TODO: This should be done later, possibly, how defensive should we program) */
50 		broadcastJoin(client);
51 
52 		/* Lock the members list */
53 		gprintln("join: mutex lock (about to call)");
54 		memberLock.lock();
55 		gprintln("join: mutex lock (completed)");
56 
57 		/**
58 		* Don't allow the user to join a channel he
59 		* is already in
60 		*/
61 		bool isPresent = false;
62 		
63 		foreach(DConnection member; members)
64 		{
65 			if(client is member)
66 			{
67 				isPresent = true;
68 				break;
69 			}
70 		}
71 
72 		/**
73 		* TODO: Error handling if the calling DConnection fails midway 
74 		* and doesn't unlock it
75 		*/
76 
77 		/* Only join channel if not already joined */
78 		if(!isPresent)
79 		{
80 			/* Add the client */
81 			members ~= client;			
82 		}
83 
84 		/* Unlock the members list */
85 		gprintln("join: mutex unlock (about to call)");
86 		memberLock.unlock();
87 		gprintln("join: mutex unlock (completed)");
88 
89 		return isPresent;
90 	}
91 
92 	/**
93 	* Returns the number of members in this channel
94 	*/
95 	public ulong getMemberCount()
96 	{
97 		/* The count of members */
98 		ulong memberCount;
99 		
100 		/* Lock the members list */
101 		memberLock.lock();
102 
103 		/* Get the member count */
104 		memberCount = members.length;
105 
106 		/* Unlock the members list */
107 		memberLock.unlock();
108 
109 		return memberCount;
110 	}
111 
112 	public bool isMember(DConnection client)
113 	{
114 		/* Whether or not you are a member */
115 		bool isMem;
116 
117 		/* Lock the members list */
118 		memberLock.lock();
119 
120 		/* CHeck if you are in this channel */
121 		foreach(DConnection member; members)
122 		{
123 			if(member is client)
124 			{
125 				isMem = true;
126 				break;
127 			}
128 		}
129 
130 		/* Unlock the members list */
131 		memberLock.unlock();
132 
133 		return isMem;
134 	}
135 
136 	/**
137 	* Removes the given client from this channel
138 	*/
139 	public void leave(DConnection client)
140 	{
141 		/* Lock the members list */
142 		memberLock.lock();
143 
144 		/* TODO: Get a better implementation */
145 
146 		/* Create a new list without the `client` */
147 		DConnection[] newMembers;
148 		foreach(DConnection currentMember; members)
149 		{
150 			if(!(currentMember is client))
151 			{
152 				newMembers ~= currentMember;	
153 			}
154 		}
155 
156 		/* Set it as the new list */
157 		members = newMembers;
158 
159 		/* Unlock the members list */
160 		memberLock.unlock();
161 
162 		/* Send broadcast leave message */
163 		broadcastLeave(client);
164 	}
165 
166 	/**
167 	* Sends a message to all users of this
168 	* channel that the given user has left
169 	*/
170 	private void broadcastLeave(DConnection left)
171 	{
172 		/* Lock the members list */
173 		memberLock.lock();
174 		
175 		/* Send left message here */
176 		foreach(DConnection currentMember; members)
177 		{
178 			sendLeaveMessage(currentMember, left);
179 		}
180 
181 		/* Unlock the members list */
182 		memberLock.unlock();
183 	}
184 
185 	/**
186 	* Sends a message to the user stating the given
187 	* (other) user has left the channel
188 	*/
189 	private void sendLeaveMessage(DConnection member, DConnection left)
190 	{
191 		/* The protocol data to send */
192 		byte[] protocolData;
193 
194 		/* Set the notificaiton type to `channel status` */
195 		protocolData ~= [1];
196 
197 		/* Set the sub-type to leave */
198 		protocolData ~= [0];
199 
200 		/* Set the channel notificaiton type to `member leave` */
201 
202 		/* LeaveInfo: <channel>,<username> */
203 		string leaveInfo = name~","~left.getUsername();
204 		protocolData ~= cast(byte[])leaveInfo;
205 
206 		/* Write the notification */
207 		member.writeSocket(0, protocolData);
208 	}
209 
210 	/**
211 	* Sends a message to all users of this
212 	* channel that the given user has joined
213 	*/
214 	private void broadcastJoin(DConnection joined)
215 	{
216 		/* Lock the members list */
217 		memberLock.lock();
218 		
219 		/* Send join message here */
220 		foreach(DConnection currentMember; members)
221 		{
222 			sendJoinMessage(currentMember, joined);
223 		}
224 
225 		/* Unlock the members list */
226 		memberLock.unlock();
227 	}
228 
229 	/**
230 	* Sends a message to the user stating the given
231 	* (other) user has joined the channel
232 	*/
233 	private void sendJoinMessage(DConnection member, DConnection joined)
234 	{
235 		/* The protocol data to send */
236 		byte[] protocolData;
237 
238 		/* Set the notificaiton type to `channel status` */
239 		protocolData ~= [1];
240 
241 		/* Set the sub-type to join */
242 		protocolData ~= [1];
243 
244 		/* Set the channel notificaiton type to `member join` */
245 
246 		/* JoinInfo: <channel>,<username> */
247 		string joinInfo = name~","~joined.getUsername();
248 		protocolData ~= cast(byte[])joinInfo;
249 
250 		/* Write the notification */
251 		member.writeSocket(0, protocolData);
252 	}
253 
254 
255 
256 	public bool sendMessage(DConnection sender, string message)
257 	{
258 		bool status;
259 
260 		/* The protocol data to send */
261 		byte[] msg;
262 
263 		/* Set the notificaiton type to `message notification` */
264 		msg ~= [0];
265 
266 		/**
267 		* Format
268 		* 0 - dm
269 		* 1 - channel (this case)
270 		* byte length of name of channel/person (dm case)
271 		* message-bytes
272 		*/
273 
274 		/* Set mode to channel message */
275 		msg ~= [cast(byte)0];
276 		
277 		/* Encode the [usernameLength, username] */
278 		msg ~= [(cast(byte)sender.getUsername().length)];
279 		msg ~= cast(byte[])sender.getUsername();
280 		
281 		/* Encode the [channelLength, channel] */
282 		msg ~= [(cast(byte)name.length)];
283 		msg ~= cast(byte[])name;
284 
285 		/* Encode the message */
286 		msg ~= cast(byte[])message;
287 		
288 		/* Send the message to everyone else in the channel */
289 		foreach(DConnection member; members)
290 		{
291 			/* Skip sending to self */
292 			if(!(member is sender))
293 			{
294 				/* Send the message */
295 				gprintln("Delivering message '"~message~"' for channel '"~name~"' to user '"~member.getUsername()~"'...");
296 				status = member.writeSocket(0, msg);
297 
298 				if(status)
299 				{
300 					gprintln("Delivered message '"~message~"' for channel '"~name~"' to user '"~member.getUsername()~"'!");	
301 				}
302 				else
303 				{
304 					gprintln("Failed to deliver message '"~message~"' for channel '"~name~"' to user '"~member.getUsername()~"'!", DebugType.ERROR);	
305 				}
306 			}
307 		}
308 
309 
310 		/* TODO: Don't, retur true */
311 		return true;
312 	}
313 
314 	/**
315 	* Returns a list of all the members
316 	*/
317 	public DConnection[] getMembers()
318 	{
319 		/* Members list */
320 		DConnection[] memberList;
321 
322 		/* Lock the members list */
323 		memberLock.lock();
324 		
325 		memberList = members;
326 
327 		/* Unlock the members list */
328 		memberLock.unlock();
329 
330 		return memberList;
331 	}
332 
333 
334 	public override string toString()
335 	{
336 		string toStr;
337 
338 		/* Lock the members list */
339 		memberLock.lock();
340 		
341 		toStr = "DChannel [Name: "~name~", Members: "~to!(string)(members)~"]";
342 
343 		/* Unlock the members list */
344 		memberLock.unlock();
345 
346 		return toStr;
347 	}
348 	
349 }