8 #include <ctype.h> // isdigit
9 #include <stdio.h> // vsnprintf
12 tServer *Server_Connect(const char *Name, const char *AddressString, short PortNumber);
13 int Server_HandleIncoming(tServer *Server);
14 void ParseServerLine(tServer *Server, char *Line);
17 const char *gsUsername = "user";
18 const char *gsHostname = "acess";
19 const char *gsRealName = "Acess2 IRC Client";
20 const char *gsNickname = "acess";
24 void Servers_FillSelect(int *nfds, fd_set *rfds, fd_set *efds)
26 for( tServer *srv = gpServers; srv; srv = srv->Next )
28 FD_SET(srv->FD, rfds);
29 FD_SET(srv->FD, efds);
30 if( srv->FD >= *nfds )
35 void Servers_HandleSelect(int nfds, const fd_set *rfds, const fd_set *efds)
37 for( tServer *srv = gpServers; srv; srv = srv->Next )
39 if(FD_ISSET(srv->FD, rfds))
41 int rv = Server_HandleIncoming(srv);
45 _SysDebug("ProcessIncoming failed on FD%i (Server %p %s)",
46 srv->FD, srv, srv->Name);
47 Exit("Processing error");
51 if(FD_ISSET(srv->FD, efds))
53 _SysDebug("Error on FD%i (Server %p %s)",
54 srv->FD, srv, srv->Name);
60 void Servers_CloseAll(const char *QuitMessage)
64 tServer *srv = gpServers;
65 gpServers = srv->Next;
67 Server_SendCommand(srv, "QUIT :%s", QuitMessage);
74 * \brief Connect to a server
76 tServer *Server_Connect(const char *Name, const char *AddressString, short PortNumber)
80 ret = calloc(1, sizeof(tServer) + strlen(Name) + 1);
82 strcpy(ret->Name, Name);
84 // Connect to the remove server
85 ret->FD = OpenTCP( AddressString, PortNumber );
88 Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Name, "Unable to create socket");
92 ret->Nick = strdup(gsNickname);
94 // Append to open list
95 ret->Next = gpServers;
98 // Read some initial data
99 Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Name, "Unable to create socket");
100 Server_HandleIncoming(ret);
103 Server_SendCommand(ret, "USER %s %s %s : %s", gsUsername, gsHostname, AddressString, gsRealName);
104 Server_SendCommand(ret, "NICK %s", ret->Nick);
105 Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Name, "Identified");
110 const char *Server_GetNick(const tServer *Server)
115 void Server_SendCommand(tServer *Server, const char *Format, ...)
120 va_start(args, Format);
121 len = vsnprintf(NULL, 0, Format, args);
125 va_start(args, Format);
126 vsnprintf(buf, len+1, Format, args);
129 _SysWrite(Server->FD, buf, len);
130 _SysWrite(Server->FD, "\n", 1);
133 void Cmd_PRIVMSG(tServer *Server, const char *Dest, const char *Src, const char *Message)
136 if( strcmp(Dest, Server->Nick) == 0 ) {
137 win = Windows_GetByName(Server, Src);
139 win = Window_Create(Server, Src);
142 win = Windows_GetByName(Server, Dest);
144 win = Window_Create(Server, Dest);
146 Window_AppendMessage(win, MSG_CLASS_MESSAGE, Src, "%s", Message);
150 * \brief Process incoming lines from the server
152 int Server_HandleIncoming(tServer *Server)
157 // While there is data in the buffer, read it into user memory and
158 // process it line by line
159 // ioctl#8 on a TCP client gets the number of bytes in the recieve buffer
160 // - Used to avoid blocking
162 while( (len = _SysIOCtl(Server->FD, 8, NULL)) > 0 )
166 len = _SysRead(Server->FD, &Server->InBuf[Server->ReadPos], sizeof(Server->InBuf) - Server->ReadPos);
170 Server->InBuf[Server->ReadPos + len] = '\0';
174 while( (newline = strchr(ptr, '\n')) )
177 if( newline[-1] == '\r' ) newline[-1] = '\0';
178 ParseServerLine(Server, ptr);
182 // Handle incomplete lines
183 if( ptr - Server->InBuf < len + Server->ReadPos ) {
184 // Update the read position
185 // InBuf ReadPos ptr ReadPos+len
186 // | old | new used | new unused |
187 Server->ReadPos = len + Server->ReadPos - (ptr - Server->InBuf);
188 // Copy stuff back (moving "new unused" to the start of the buffer)
189 memcpy(Server->InBuf, ptr, Server->ReadPos);
201 void ParseServerLine_Numeric(tServer *Server, const char *ident, int Num, char *Line)
205 const char *user = GetValue(Line, &pos);
206 const char *timestamp;
208 if( Line[pos] == ':' ) {
209 message = Line + pos + 1;
212 message = GetValue(Line, &pos);
218 user = message; // Channel
219 message = Line + pos + 1; // Topic
220 Window_AppendMsg_Topic( Windows_GetByNameOrCreate(Server, user), message );
222 case 333: // Topic set by
223 user = message; // Channel
224 message = GetValue(Line, &pos); // User
225 timestamp = GetValue(Line, &pos); // Timestamp
226 Window_AppendMsg_TopicTime( Windows_GetByNameOrCreate(Server, user), message, timestamp );
228 case 353: // /NAMES list
229 // <user> = <channel> :list
230 // '=' was eaten in and set to message
231 user = GetValue(Line, &pos); // Actually channel
232 message = Line + pos + 1; // List
233 // TODO: parse and store
234 Window_AppendMessage( Windows_GetByNameOrCreate(Server, user), MSG_CLASS_CLIENT, "NAMES", message );
236 case 366: // end of /NAMES list
237 // <user> <channel> :msg
240 case 372: // MOTD Data
241 case 375: // MOTD Start
242 case 376: // MOTD End
245 //printf("[%s] %i %s\n", Server->Name, num, message);
246 Window_AppendMessage( WINDOW_STATUS, MSG_CLASS_WALL, Server->Name, "Unknown %i %s", Num, message);
251 void ParseServerLine_String(tServer *Server, const char *ident, const char *cmd, char *Line)
254 _SysDebug("ident=%s,cmd=%s,Line=%s", ident, cmd, Line);
255 if( strcmp(cmd, "NOTICE") == 0 )
257 const char *class = GetValue(Line, &pos);
258 _SysDebug("NOTICE class='%s'", class);
260 const char *message = (Line[pos] == ':') ? Line + pos + 1 : GetValue(Line, &pos);
262 //printf("[%s] NOTICE %s: %s\n", Server->Name, ident, message);
263 char *ident_bang = strchr(ident, '!');
267 // TODO: Colour codes
268 Window_AppendMessage( WINDOW_STATUS, MSG_CLASS_WALL, Server->Name, "%s %s", ident, message);
270 else if( strcmp(cmd, "PRIVMSG") == 0 )
272 const char *dest = GetValue(Line, &pos);
273 const char *message = (Line[pos] == ':') ? Line + pos + 1 : GetValue(Line, &pos);
275 char *ident_bang = strchr(ident, '!');
279 Cmd_PRIVMSG(Server, dest, ident, message);
281 else if( strcmp(cmd, "JOIN" ) == 0 )
283 const char *channel = Line + pos + 1;
285 Window_AppendMsg_Join( Windows_GetByNameOrCreate(Server, channel), ident );
287 else if( strcmp(cmd, "PART" ) == 0 )
289 const char *channel = Line + pos + 1;
291 Window_AppendMsg_Part( Windows_GetByNameOrCreate(Server, channel), ident, "" );
295 Window_AppendMessage( WINDOW_STATUS, MSG_CLASS_BARE, Server->Name, "Unknown command '%s' %s", cmd, Line);
301 void ParseServerLine(tServer *Server, char *Line)
305 _SysDebug("[%s] %s", Server->Name, Line);
311 const char *ident = GetValue(Line, &pos); // Ident (user or server)
312 const char *cmd = GetValue(Line, &pos);
315 if( isdigit(cmd[0]) && isdigit(cmd[1]) && isdigit(cmd[2]) )
318 num = (cmd[0] - '0') * 100;
319 num += (cmd[1] - '0') * 10;
320 num += (cmd[2] - '0') * 1;
322 ParseServerLine_Numeric(Server, ident, num, Line+pos);
326 ParseServerLine_String(Server, ident, cmd, Line+pos);
330 const char *cmd = GetValue(Line, &pos);
332 if( strcmp(cmd, "PING") == 0 ) {
333 Server_SendCommand(Server, "PONG %s", Line+pos);
337 Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Server->Name, "UNK Client Command: %s", Line);