IRC Client - Added Readline support, error catching
[tpg/acess2.git] / Usermode / Applications / irc_src / main.c
1 /*\r
2  * Acess2 IRC Client\r
3  */\r
4 #include <acess/sys.h>\r
5 #include <stdlib.h>\r
6 #include <stdio.h>\r
7 #include <string.h>\r
8 #include <net.h>\r
9 #include <readline.h>\r
10 \r
11 #define BUFSIZ  1023\r
12 \r
13 // === TYPES ===\r
14 typedef struct sServer {\r
15          int    FD;\r
16         char    InBuf[BUFSIZ+1];\r
17          int    ReadPos;\r
18 } tServer;\r
19 \r
20 // === PROTOTYPES ===\r
21  int    ParseArguments(int argc, const char *argv[]);\r
22  int    ProcessIncoming(tServer *Server);\r
23  int    writef(int FD, const char *Format, ...);\r
24  int    OpenTCP(const char *AddressString, short PortNumber);\r
25 \r
26 // === GLOBALS ===\r
27 char    *gsUsername = "root";\r
28 char    *gsHostname = "acess";\r
29 char    *gsRemoteAddress = NULL;\r
30 char    *gsRealName = "Acess2 IRC Client";\r
31 char    *gsNickname = "acess";\r
32 short   giRemotePort = 6667;\r
33 \r
34 // ==== CODE ====\r
35 int main(int argc, const char *argv[], const char *envp[])\r
36 {\r
37          int    tmp;\r
38         tServer srv;\r
39         tReadline       *readline_info;\r
40         \r
41         memset(&srv, 0, sizeof(srv));\r
42         \r
43         // Parse Command line\r
44         // - Sets the server configuration globals\r
45         if( (tmp = ParseArguments(argc, argv)) ) {\r
46                 return tmp;\r
47         }\r
48         \r
49         // Connect to the remove server\r
50         srv.FD = OpenTCP( gsRemoteAddress, giRemotePort );\r
51         if( srv.FD == -1 ) {\r
52                 fprintf(stderr, "Unable to create socket\n");\r
53                 return -1;\r
54         }\r
55         \r
56         printf("Connection opened\n");\r
57         ProcessIncoming(&srv);\r
58         \r
59         writef(srv.FD, "USER %s %s %s : %s\n", gsUsername, gsHostname, gsRemoteAddress, gsRealName);\r
60         writef(srv.FD, "NICK %s\n", gsNickname);\r
61         \r
62         printf("Processing\n");\r
63         \r
64         readline_info = Readline_Init(1);\r
65         \r
66         for( ;; )\r
67         {\r
68                 fd_set  readfds;\r
69                  int    rv;\r
70                 \r
71                 FD_ZERO(&readfds);\r
72                 FD_SET(0, &readfds);    // stdin\r
73                 FD_SET(srv.FD, &readfds);\r
74                 \r
75                 rv = select(srv.FD+1, &readfds, 0, 0, NULL);\r
76                 if( rv == -1 )  break;\r
77                 \r
78                 if(FD_ISSET(0, &readfds))\r
79                 {\r
80                         // User input\r
81                         char    *cmd = Readline_NonBlock(readline_info);\r
82                         if( cmd )\r
83                         {\r
84                                 // TODO: Implement windows / proper commands, but meh\r
85                                 if( cmd[0] )\r
86                                         writef(srv.FD, "%s\n", cmd);\r
87                                 free(cmd);\r
88                         }\r
89                 }\r
90                 \r
91                 // Server response\r
92                 if(FD_ISSET(srv.FD, &readfds))\r
93                 {\r
94                         if( ProcessIncoming(&srv) != 0 ) {\r
95                                 // Oops, error\r
96                                 break;\r
97                         }\r
98                 }\r
99         }\r
100         \r
101         close(srv.FD);\r
102         return 0;\r
103 }\r
104 \r
105 /**\r
106  * \todo Actually implement correctly :)\r
107  */\r
108 int ParseArguments(int argc, const char *argv[])\r
109 {\r
110         gsRemoteAddress = "130.95.13.18";       // irc.ucc.asn.au\r
111         \r
112         return 0;\r
113 }\r
114 \r
115 void Cmd_PRIVMSG(tServer *Server, const char *Dest, const char *Src, const char *Message)\r
116 {\r
117         printf("%p: %s => %s\t%s\n", Server, Src, Dest, Message);\r
118 }\r
119 \r
120 /**\r
121  * \brief Read a space-separated value from a string\r
122  */\r
123 char *GetValue(char *Src, int *Ofs)\r
124 {\r
125          int    pos = *Ofs;\r
126         char    *ret = Src + pos;\r
127         char    *end;\r
128         \r
129         if( !Src )      return NULL;\r
130         \r
131         while( *ret == ' ' )    ret ++;\r
132         \r
133         end = strchr(ret, ' ');\r
134         if( end ) {\r
135                 *end = '\0';\r
136         }\r
137         else {\r
138                 end = ret + strlen(ret) - 1;\r
139         }\r
140         *Ofs = end - Src + 1;\r
141         \r
142         return ret;\r
143 }\r
144 \r
145 /**\r
146  */\r
147 void ParseServerLine(tServer *Server, char *Line)\r
148 {\r
149          int    pos = 0;\r
150         char    *ident, *cmd;\r
151         if( *Line == ':' ) {\r
152                 // Message\r
153                 ident = GetValue(Line, &pos);\r
154                 pos ++; // Space\r
155                 cmd = GetValue(Line, &pos);\r
156                 \r
157                 if( strcmp(cmd, "PRIVMSG") == 0 ) {\r
158                         char    *dest, *message;\r
159                         pos ++;\r
160                         dest = GetValue(Line, &pos);\r
161                         pos ++;\r
162                         if( Line[pos] == ':' ) {\r
163                                 message = Line + pos + 1;\r
164                         }\r
165                         else {\r
166                                 message = GetValue(Line, &pos);\r
167                         }\r
168                         Cmd_PRIVMSG(Server, dest, ident, message);\r
169                 }\r
170         }\r
171         else {\r
172                 // Command to client\r
173         }\r
174 }\r
175 \r
176 /**\r
177  * \brief Process incoming lines from the server\r
178  */\r
179 int ProcessIncoming(tServer *Server)\r
180 {       \r
181         char    *ptr, *newline;\r
182          int    len;\r
183         \r
184         // While there is data in the buffer, read it into user memory and \r
185         // process it line by line\r
186         // ioctl#8 on a TCP client gets the number of bytes in the recieve buffer\r
187         // - Used to avoid blocking\r
188         #if NON_BLOCK_READ\r
189         while( (len = ioctl(Server->FD, 8, NULL)) > 0 )\r
190         {\r
191         #endif\r
192                 // Read data\r
193                 len = read(Server->FD, BUFSIZ - Server->ReadPos, &Server->InBuf[Server->ReadPos]);\r
194                 if( len == -1 ) {\r
195                         return -1;\r
196                 }\r
197                 Server->InBuf[Server->ReadPos + len] = '\0';\r
198                 \r
199                 // Break into lines\r
200                 ptr = Server->InBuf;\r
201                 while( (newline = strchr(ptr, '\n')) )\r
202                 {\r
203                         *newline = '\0';\r
204                         printf("%s\n", ptr);\r
205                         ParseServerLine(Server, ptr);\r
206                         ptr = newline + 1;\r
207                 }\r
208                 \r
209                 // Handle incomplete lines\r
210                 if( ptr - Server->InBuf < len + Server->ReadPos ) {\r
211                         // Update the read position\r
212                         // InBuf ReadPos    ptr          ReadPos+len\r
213                         // | old | new used | new unused |\r
214                         Server->ReadPos = len + Server->ReadPos - (ptr - Server->InBuf);\r
215                         // Copy stuff back (moving "new unused" to the start of the buffer)\r
216                         memcpy(Server->InBuf, ptr, Server->ReadPos);\r
217                 }\r
218                 else {\r
219                         Server->ReadPos = 0;\r
220                 }\r
221         #if NON_BLOCK_READ\r
222         }\r
223         #endif\r
224         \r
225         return 0;\r
226 }\r
227 \r
228 /**\r
229  * \brief Write a formatted string to a file descriptor\r
230  * \r
231  */\r
232 int writef(int FD, const char *Format, ...)\r
233 {\r
234         va_list args;\r
235          int    len;\r
236         \r
237         va_start(args, Format);\r
238         len = vsnprintf(NULL, 1000, Format, args);\r
239         va_end(args);\r
240         \r
241         {\r
242                 char    buf[len+1];\r
243                 va_start(args, Format);\r
244                 vsnprintf(buf, len+1, Format, args);\r
245                 va_end(args);\r
246                 \r
247                 return write(FD, len, buf);\r
248         }\r
249 }\r
250 \r
251 /**\r
252  * \brief Initialise a TCP connection to \a AddressString on port \a PortNumber\r
253  */\r
254 int OpenTCP(const char *AddressString, short PortNumber)\r
255 {\r
256          int    fd, addrType;\r
257         char    *iface;\r
258         char    addrBuffer[8];\r
259         \r
260         // Parse IP Address\r
261         addrType = Net_ParseAddress(AddressString, addrBuffer);\r
262         if( addrType == 0 ) {\r
263                 fprintf(stderr, "Unable to parse '%s' as an IP address\n", AddressString);\r
264                 return -1;\r
265         }\r
266         \r
267         // Finds the interface for the destination address\r
268         iface = Net_GetInterface(addrType, addrBuffer);\r
269         if( iface == NULL ) {\r
270                 fprintf(stderr, "Unable to find a route to '%s'\n", AddressString);\r
271                 return -1;\r
272         }\r
273         \r
274         printf("iface = '%s'\n", iface);\r
275         \r
276         // Open client socket\r
277         // TODO: Move this out to libnet?\r
278         {\r
279                  int    len = snprintf(NULL, 100, "/Devices/ip/%s/tcpc", iface);\r
280                 char    path[len+1];\r
281                 snprintf(path, 100, "/Devices/ip/%s/tcpc", iface);\r
282                 fd = open(path, OPENFLAG_READ|OPENFLAG_WRITE);\r
283         }\r
284         \r
285         free(iface);\r
286         \r
287         if( fd == -1 ) {\r
288                 fprintf(stderr, "Unable to open TCP Client for reading\n");\r
289                 return -1;\r
290         }\r
291         \r
292         // Set remote port and address\r
293         printf("Setting port and remote address\n");\r
294         ioctl(fd, 5, &PortNumber);\r
295         ioctl(fd, 6, addrBuffer);\r
296         \r
297         // Connect\r
298         printf("Initiating connection\n");\r
299         if( ioctl(fd, 7, NULL) == 0 ) {\r
300                 // Shouldn't happen :(\r
301                 fprintf(stderr, "Unable to start connection\n");\r
302                 return -1;\r
303         }\r
304         \r
305         // Return descriptor\r
306         return fd;\r
307 }\r

UCC git Repository :: git.ucc.asn.au