-
Notifications
You must be signed in to change notification settings - Fork 0
/
irc-server-worker.adb
209 lines (184 loc) · 8.29 KB
/
irc-server-worker.adb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
private with Ada.Streams,
Ada.Strings.Fixed,
Ada.Strings.Hash,
Ada.Strings.Maps,
Ada.Text_IO,
GNAT.Sockets,
Ada.Exceptions,
IRC.Proto;
package body IRC.Server.Worker is
use GNAT.Sockets,
Ada.Strings.Fixed,
Ada.Strings.Maps,
Ada.Text_IO,
Ada.Exceptions;
-- We need a Hash function to make sure our Hashed_Maps.Map container can
-- proeprly create the hash map. This function will just rely on the
-- Ada.Strings.Hash function and pass in the string representation of the
-- Task_Id
function Hash (Key : Ada.Task_Identification.Task_Id) return Ada.Containers.Hash_Type is
begin
return Ada.Strings.Hash (Ada.Task_Identification.Image (Key));
end Hash;
-- Procedure to print debug.
procedure Debug (Message : String) is
begin
Put_Line ("Worker: " & Message);
end Debug;
-- A worker task to handle an IRC client.
task body Worker is
use Ada.Streams;
Client_Sock : Socket_Type;
begin
accept Serve (Sock : Socket_Type) do
Debug ("Start called.");
Client_Sock := Sock;
end Serve;
declare
Channel : Stream_Access := Stream (Client_Sock);
U : User;
URef : User_Ref := new User;
Serv_Pass : Boolean := False;
Nick_Sent : Boolean := False;
Next_Char : Character;
Ctr : Long_Long_Integer := 0;
begin
--URef.all := U;
Event_Loop:
while True loop
declare
Msg : String(1..500000);
Msg_Len : Natural := 0;
package Parser is new IRC.Proto;
begin
-- Read in full lines before processing protocol commands
declare
Loop_Index : Integer := 0;
begin
Line_Loop :
loop
Loop_Index := Loop_Index + 1; -- Increment loop index
Next_Char := Character'Input (Channel); -- Read next character
Msg(Loop_Index) := Next_Char; -- Append next character to message
exit Line_Loop when Next_Char = Ascii.LF; -- End loop if newline
Msg_Len := Msg_Len + 1; -- Increment message length
end loop Line_Loop;
-- Let our parser know how long the message is
Parser.Msg_Len := Msg_Len;
end;
-- Print line
Debug ( "Client message: " & Msg(Msg'First..Msg_Len) );
-- Test Parser
Parser.Parse_Message(Msg);
-- -- Get first part
-- Parser.First_Part(Msg);
--
-- Debug ( "Part: *" & Parser.Msg_Part_Str & "*" );
-- declare
-- Command : String := Parser.Msg_Part_Str(1..Parser.Msg_Part_Len);
-- begin
-- -- Quit if the client qants to
-- if Command = "QUIT" then
-- Parser.Next_Part(Msg => Msg,
-- Empty_Valid => True,
-- Eat_Colon => True,
-- To_End => True);
-- Debug ("Client quit: " & Parser.Msg_Part_Str);
-- exit Event_Loop;
-- end if;
--
-- -- Start IRC processing
-- if not U.ConnRegd then
--
-- -- TODO: Check for password
-- if ( Command = "PASS" ) and (Serv_Pass = True) then
-- Debug ("Recieved PASS but we don't do anything with it yet.");
-- end if;
-- -- Check for nickname
-- -- Command: NICK
-- -- Parameters: <nickname> [ <hopcount> ]
-- if ( Command = "NICK" ) and ( not Nick_Sent ) then
--
-- -- Get nick
-- Parser.Next_Part(Msg);
-- --Parser.Put_Part(URef.Username);
-- U.Nickname(1..Parser.Msg_Part_Len) := Parser.Get_Part;
-- Debug ("Got nickname: " & U.Nickname);
--
-- Nick_Sent := True;
-- end if;
-- -- Check for username
-- -- Command: USER
-- -- Parameters: <username> <hostname> <servername> <realname>
-- if ( Command = "USER" ) and ( Nick_Sent ) then
--
-- -- Get username
-- Parser.Next_Part(Msg);
-- U.Username(1..Parser.Msg_Part_Len) := Parser.Get_Part;
-- Debug ("Got username: " & U.Username);
--
-- -- Get hostname
-- Parser.Next_Part(Msg, True);
-- U.Hostname(1..Parser.Msg_Part_Len) := Parser.Get_Part;
-- Debug ("Got hostname: " & U.Hostname);
--
-- -- Get servname
-- Parser.Next_Part(Msg, True);
-- U.Servname(1..Parser.Msg_Part_Len) := Parser.Get_Part;
-- Debug ("Got servname: " & U.Servname);
--
-- -- Get realname
-- Parser.Next_Part(Msg => Msg,
-- Eat_Colon => True,
-- To_End => True);
-- U.Realname(1..Parser.Msg_Part_Len) := Parser.Get_Part;
-- Debug ("Got realname: " & U.Realname);
--
-- U.ConnRegd := True;
-- end if;
-- else
-- -- No-op for now
-- null;
-- end if;
-- end;
<<Next_Event>>
Ctr := Ctr + 1;
Debug ("Client message number: " & Long_Long_Integer'Image(Ctr));
end;
end loop Event_Loop;
Debug (".. closing connection");
Close_Socket (Client_Sock);
end;
exception when E : others =>
Debug ("Threw exception!" & Ascii.LF & Exception_Information (E));
end Worker;
protected body Coordinator is
procedure Last_Wish (C : Ada.Task_Termination.Cause_Of_Termination;
T : Ada.Task_Identification.Task_Id;
X : Ada.Exceptions.Exception_Occurrence) is
W : Worker_Ptr := Tasks.Element (T);
begin
-- First, let's make sure we remove the task object from our Tasks
-- map
Tasks.Delete (Key => T);
-- Then we deallocate it
Free_Worker (W);
Ada.Text_IO.Put_Line ("Task (" & Ada.Task_Identification.Image (T) & ") deallocated");
end Last_Wish;
procedure Track (Ptr : in Worker_Ptr) is
-- The Task_Id for a task can be found in the Identity attribute,
-- but since we're receiving a Worker_Ptr type, we first need to
-- dereference it into a Worker again
Key : constant Ada.Task_Identification.Task_Id := Ptr.all'Identity;
begin
Ada.Text_IO.Put_Line ("Adding task (" & Ada.Task_Identification.Image (Key) & ") to Coordinator.Tasks");
-- Add our Worker pointer into our hash map to hold onto it for
-- later
Tasks.Insert (Key => Key,
New_Item => Ptr );
-- We need to set a task termination handler (introduced in Ada
-- 2005) in order to get called when the Worker (W) terminates
Ada.Task_Termination.Set_Specific_Handler (Key, Last_Wish'Access);
end Track;
end Coordinator;
end IRC.Server.Worker;