Client work
[tpg/opendispense2.git] / src / client / main.c
1 /*
2  * OpenDispense 2 
3  * UCC (University [of WA] Computer Club) Electronic Accounting System
4  * - Dispense Client
5  *
6  * main.c - Core and Initialisation
7  *
8  * This file is licenced under the 3-clause BSD Licence. See the file
9  * COPYING for full details.
10  */
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <ctype.h>      // isspace
15 #include <stdarg.h>
16 #include <regex.h>
17
18 #include <unistd.h>     // close
19 #include <netdb.h>      // gethostbyname
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23
24 // === TYPES ===
25 typedef struct sItem {
26         char    *Ident;
27         char    *Desc;
28          int    Price;
29 }       tItem;
30
31 // === PROTOTYPES ===
32  int    sendf(int Socket, const char *Format, ...);
33  int    OpenConnection(const char *Host, int Port);
34 char    *trim(char *string);
35  int    RunRegex(regex_t *regex, const char *string, int nMatches, regmatch_t *matches, const char *errorMessage);
36 void    CompileRegex(regex_t *regex, const char *pattern, int flags);
37
38 // === GLOBALS ===
39 char    *gsDispenseServer = "localhost";
40  int    giDispensePort = 11020;
41 tItem   *gaItems;
42  int    giNumItems;
43 regex_t gArrayRegex;
44 regex_t gItemRegex;
45
46 // === CODE ===
47 int main(int argc, char *argv[])
48 {
49          int    sock;
50          int    i, responseCode, len;
51         char    buffer[BUFSIZ];
52         
53         // -- Create regular expressions
54         // > Code Type Count ...
55         CompileRegex(&gArrayRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([0-9]+)", REG_EXTENDED);     //
56         // > Code Type Ident Price Desc
57         CompileRegex(&gItemRegex, "^([0-9]{3})\\s+(.+?)\\s+(.+?)\\s+([0-9]+)\\s+(.+)$", REG_EXTENDED);
58         
59         // Connect to server
60         sock = OpenConnection(gsDispenseServer, giDispensePort);
61         if( sock < 0 )  return -1;
62
63         // Determine what to do
64         if( argc > 1 )
65         {
66                 if( strcmp(argv[1], "acct") == 0 )
67                 {
68                         // Alter account
69                         // List accounts
70                         return 0;
71                 }
72         }
73
74         // Ask server for stock list
75         send(sock, "ENUM_ITEMS\n", 11, 0);
76         len = recv(sock, buffer, BUFSIZ-1, 0);
77         buffer[len] = '\0';
78         
79         trim(buffer);
80         
81         printf("Output: %s\n", buffer);
82         
83         responseCode = atoi(buffer);
84         if( responseCode != 201 )
85         {
86                 fprintf(stderr, "Unknown response from dispense server (Response Code %i)\n", responseCode);
87                 return -1;
88         }
89         
90         // Get item list
91         {
92                 char    *itemType, *itemStart;
93                  int    count;
94                 regmatch_t      matches[4];
95                 
96                 // Expected format: 201 Items <count> <item1> <item2> ...
97                 RunRegex(&gArrayRegex, buffer, 4, matches, "Malformed server response");
98                 
99                 itemType = &buffer[ matches[2].rm_so ]; buffer[ matches[2].rm_eo ] = '\0';
100                 count = atoi( &buffer[ matches[3].rm_so ] );
101                 
102                 // Check array type
103                 if( strcmp(itemType, "Items") != 0 ) {
104                         // What the?!
105                         fprintf(stderr, "Unexpected array type, expected 'Items', got '%s'\n",
106                                 itemType);
107                         return -1;
108                 }
109                 
110                 itemStart = &buffer[ matches[3].rm_eo ];
111                 
112                 gaItems = malloc( count * sizeof(tItem) );
113                 
114                 for( giNumItems = 0; giNumItems < count && itemStart; giNumItems ++ )
115                 {
116                         char    *next = strchr( ++itemStart, ' ' );
117                         if( next )      *next = '\0';
118                         gaItems[giNumItems].Ident = strdup(itemStart);
119                         itemStart = next;
120                 }
121         }
122         
123         
124         // Display the list for the user
125         for( i = 0; i < giNumItems; i ++ )
126         {
127                 regmatch_t      matches[6];
128                 
129                 // Print item Ident
130                 printf("%2i %s\t", i, gaItems[i].Ident);
131                 
132                 // Get item info
133                 sendf(sock, "ITEM_INFO %s\n", gaItems[i].Ident);
134                 len = recv(sock, buffer, BUFSIZ-1, 0);
135                 buffer[len] = '\0';
136                 trim(buffer);
137                 
138                 responseCode = atoi(buffer);
139                 if( responseCode != 202 ) {
140                         fprintf(stderr, "Unknown response from dispense server (Response Code %i)\n", responseCode);
141                         return -1;
142                 }
143                 
144                 RunRegex(&gItemRegex, buffer, 6, matches, "Malformed server response");
145                 
146                 buffer[ matches[3].rm_eo ] = '\0';
147                 
148                 gaItems[i].Price = atoi( buffer + matches[4].rm_so );
149                 gaItems[i].Desc = strdup( buffer + matches[5].rm_so );
150                 
151                 printf("%3i %s\n", gaItems[i].Price, gaItems[i].Desc);
152         }
153         
154         
155         // and choose what to dispense
156         for(;;)
157         {
158                 char    *buf;
159                 
160                 fgets(buffer, BUFSIZ, stdin);
161                 
162                 buf = trim(buffer);
163                 
164                 if( buf[0] == 'q' )     break;
165                 
166                 i = atoi(buf);
167                 
168                 printf("buf = '%s', atoi(buf) = %i\n", buf, i);
169                 
170                 if( i != 0 || buf[0] == '0' )
171                 {
172                         printf("i = %i\n", i);
173                         
174                         if( i < 0 || i >= giNumItems ) {
175                                 printf("Bad item (should be between 0 and %i)\n", giNumItems);
176                                 continue;
177                         }
178                         
179                         sendf(sock, "DISPENSE %s\n", gaItems[i].Ident);
180                         
181                         len = recv(sock, buffer, BUFSIZ-1, 0);
182                         buffer[len] = '\0';
183                         trim(buffer);
184                         
185                         responseCode = atoi(buffer);
186                         switch( responseCode )
187                         {
188                         case 200:
189                                 printf("Dispense OK\n");
190                                 break;
191                         case 401:
192                                 printf("Not authenticated\n");
193                                 break;
194                         case 402:
195                                 printf("Insufficient balance\n");
196                                 break;
197                         case 406:
198                                 printf("Bad item name, bug report\n");
199                                 break;
200                         case 500:
201                                 printf("Item failed to dispense, is the slot empty?\n");
202                                 break;
203                         default:
204                                 printf("Unknown response code %i\n", responseCode);
205                                 break;
206                         }
207                         
208                         break;
209                 }
210         }
211
212         close(sock);
213
214         return 0;
215 }
216
217 // === HELPERS ===
218 int sendf(int Socket, const char *Format, ...)
219 {
220         va_list args;
221          int    len;
222         
223         va_start(args, Format);
224         len = vsnprintf(NULL, 0, Format, args);
225         va_end(args);
226         
227         {
228                 char    buf[len+1];
229                 va_start(args, Format);
230                 vsnprintf(buf, len+1, Format, args);
231                 va_end(args);
232                 
233                 return send(Socket, buf, len, 0);
234         }
235 }
236
237 int OpenConnection(const char *Host, int Port)
238 {
239         struct hostent  *host;
240         struct sockaddr_in      serverAddr;
241          int    sock;
242         
243         host = gethostbyname(Host);
244         if( !host ) {
245                 fprintf(stderr, "Unable to look up '%s'\n", Host);
246                 return -1;
247         }
248         
249         memset(&serverAddr, 0, sizeof(serverAddr));
250         
251         serverAddr.sin_family = AF_INET;        // IPv4
252         // NOTE: I have a suspicion that IPv6 will play sillybuggers with this :)
253         serverAddr.sin_addr.s_addr = *((unsigned long *) host->h_addr_list[0]);
254         serverAddr.sin_port = htons(Port);
255         
256         sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
257         if( sock < 0 ) {
258                 fprintf(stderr, "Failed to create socket\n");
259                 return -1;
260         }
261         
262         if( connect(sock, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0 ) {
263                 fprintf(stderr, "Failed to connect to server\n");
264                 return -1;
265         }
266         
267         return sock;
268 }
269
270 char *trim(char *string)
271 {
272          int    i;
273         
274         while( isspace(*string) )
275                 string ++;
276         
277         for( i = strlen(string); i--; )
278         {
279                 if( isspace(string[i]) )
280                         string[i] = '\0';
281                 else
282                         break;
283         }
284         
285         return string;
286 }
287
288 int RunRegex(regex_t *regex, const char *string, int nMatches, regmatch_t *matches, const char *errorMessage)
289 {
290          int    ret;
291         
292         ret = regexec(regex, string, nMatches, matches, 0);
293         if( ret ) {
294                 size_t  len = regerror(ret, regex, NULL, 0);
295                 char    errorStr[len];
296                 regerror(ret, regex, errorStr, len);
297                 printf("string = '%s'\n", string);
298                 fprintf(stderr, "%s\n%s", errorMessage, errorStr);
299                 exit(-1);
300         }
301         
302         return ret;
303 }
304
305 void CompileRegex(regex_t *regex, const char *pattern, int flags)
306 {
307          int    ret = regcomp(regex, pattern, flags);
308         if( ret ) {
309                 size_t  len = regerror(ret, regex, NULL, 0);
310                 char    errorStr[len];
311                 regerror(ret, regex, errorStr, len);
312                 fprintf(stderr, "Regex compilation failed - %s\n", errorStr);
313                 exit(-1);
314         }
315 }

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