3 * UCC (University [of WA] Computer Club) Electronic Accounting System
6 * main.c - Core and Initialisation
8 * This file is licenced under the 3-clause BSD Licence. See the file
9 * COPYING for full details.
14 #include <ctype.h> // isspace
18 #include <unistd.h> // close
19 #include <netdb.h> // gethostbyname
20 #include <pwd.h> // getpwuids
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
26 typedef struct sItem {
33 int sendf(int Socket, const char *Format, ...);
34 int OpenConnection(const char *Host, int Port);
35 void Authenticate(int Socket);
36 char *trim(char *string);
37 int RunRegex(regex_t *regex, const char *string, int nMatches, regmatch_t *matches, const char *errorMessage);
38 void CompileRegex(regex_t *regex, const char *pattern, int flags);
41 char *gsDispenseServer = "localhost";
42 int giDispensePort = 11020;
49 int main(int argc, char *argv[])
52 int i, responseCode, len;
55 // -- Create regular expressions
56 // > Code Type Count ...
57 CompileRegex(&gArrayRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([0-9]+)", REG_EXTENDED); //
58 // > Code Type Ident Price Desc
59 CompileRegex(&gItemRegex, "^([0-9]{3})\\s+(.+?)\\s+(.+?)\\s+([0-9]+)\\s+(.+)$", REG_EXTENDED);
62 sock = OpenConnection(gsDispenseServer, giDispensePort);
63 if( sock < 0 ) return -1;
65 // Determine what to do
68 if( strcmp(argv[1], "acct") == 0 )
76 // Ask server for stock list
77 send(sock, "ENUM_ITEMS\n", 11, 0);
78 len = recv(sock, buffer, BUFSIZ-1, 0);
83 printf("Output: %s\n", buffer);
85 responseCode = atoi(buffer);
86 if( responseCode != 201 )
88 fprintf(stderr, "Unknown response from dispense server (Response Code %i)\n", responseCode);
94 char *itemType, *itemStart;
96 regmatch_t matches[4];
98 // Expected format: 201 Items <count> <item1> <item2> ...
99 RunRegex(&gArrayRegex, buffer, 4, matches, "Malformed server response");
101 itemType = &buffer[ matches[2].rm_so ]; buffer[ matches[2].rm_eo ] = '\0';
102 count = atoi( &buffer[ matches[3].rm_so ] );
105 if( strcmp(itemType, "Items") != 0 ) {
107 fprintf(stderr, "Unexpected array type, expected 'Items', got '%s'\n",
112 itemStart = &buffer[ matches[3].rm_eo ];
114 gaItems = malloc( count * sizeof(tItem) );
116 for( giNumItems = 0; giNumItems < count && itemStart; giNumItems ++ )
118 char *next = strchr( ++itemStart, ' ' );
119 if( next ) *next = '\0';
120 gaItems[giNumItems].Ident = strdup(itemStart);
125 // Get item information
126 for( i = 0; i < giNumItems; i ++ )
128 regmatch_t matches[6];
131 printf("%2i %s\t", i, gaItems[i].Ident);
134 sendf(sock, "ITEM_INFO %s\n", gaItems[i].Ident);
135 len = recv(sock, buffer, BUFSIZ-1, 0);
139 responseCode = atoi(buffer);
140 if( responseCode != 202 ) {
141 fprintf(stderr, "Unknown response from dispense server (Response Code %i)\n", responseCode);
145 RunRegex(&gItemRegex, buffer, 6, matches, "Malformed server response");
147 buffer[ matches[3].rm_eo ] = '\0';
149 gaItems[i].Price = atoi( buffer + matches[4].rm_so );
150 gaItems[i].Desc = strdup( buffer + matches[5].rm_so );
152 printf("%3i %s\n", gaItems[i].Price, gaItems[i].Desc);
157 // and choose what to dispense
158 // TODO: ncurses interface (with separation between item classes)
159 // - Hmm... that would require standardising the item ID to be <class>:<index>
166 fgets(buffer, BUFSIZ, stdin);
170 if( buf[0] == 'q' ) break;
174 printf("buf = '%s', atoi(buf) = %i\n", buf, i);
176 if( i != 0 || buf[0] == '0' )
178 printf("i = %i\n", i);
180 if( i < 0 || i >= giNumItems ) {
181 printf("Bad item (should be between 0 and %i)\n", giNumItems);
185 sendf(sock, "DISPENSE %s\n", gaItems[i].Ident);
187 len = recv(sock, buffer, BUFSIZ-1, 0);
191 responseCode = atoi(buffer);
192 switch( responseCode )
195 printf("Dispense OK\n");
198 printf("Not authenticated\n");
201 printf("Insufficient balance\n");
204 printf("Bad item name, bug report\n");
207 printf("Item failed to dispense, is the slot empty?\n");
210 printf("Unknown response code %i\n", responseCode);
224 int sendf(int Socket, const char *Format, ...)
229 va_start(args, Format);
230 len = vsnprintf(NULL, 0, Format, args);
235 va_start(args, Format);
236 vsnprintf(buf, len+1, Format, args);
239 return send(Socket, buf, len, 0);
243 int OpenConnection(const char *Host, int Port)
245 struct hostent *host;
246 struct sockaddr_in serverAddr;
249 host = gethostbyname(Host);
251 fprintf(stderr, "Unable to look up '%s'\n", Host);
255 memset(&serverAddr, 0, sizeof(serverAddr));
257 serverAddr.sin_family = AF_INET; // IPv4
258 // NOTE: I have a suspicion that IPv6 will play sillybuggers with this :)
259 serverAddr.sin_addr.s_addr = *((unsigned long *) host->h_addr_list[0]);
260 serverAddr.sin_port = htons(Port);
262 sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
264 fprintf(stderr, "Failed to create socket\n");
270 struct sockaddr_in localAddr;
271 memset(&localAddr, 0, sizeof(localAddr));
272 localAddr.sin_family = AF_INET; // IPv4
273 localAddr.sin_port = 1023; // IPv4
274 // Attempt to bind to low port for autoauth
275 bind(sock, &localAddr, sizeof(localAddr));
279 if( connect(sock, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0 ) {
280 fprintf(stderr, "Failed to connect to server\n");
287 void Authenticate(int Socket)
294 pwd = getpwuid( getuid() );
296 // Attempt automatic authentication
297 sendf(Socket, "AUTOAUTH %s\n", pwd->pw_name);
299 // Check if it worked
300 recv(Socket, buf, 511, 0);
303 responseCode = atoi(buf);
304 switch( responseCode )
306 case 200: // Authenticated, return :)
308 case 401: // Untrusted, attempt password authentication
310 case 404: // Bad Username
311 fprintf(stderr, "Bad Username '%s'\n", pwd->pw_name);
314 fprintf(stderr, "Unkown response code %i from server\n", responseCode);
322 char *trim(char *string)
326 while( isspace(*string) )
329 for( i = strlen(string); i--; )
331 if( isspace(string[i]) )
340 int RunRegex(regex_t *regex, const char *string, int nMatches, regmatch_t *matches, const char *errorMessage)
344 ret = regexec(regex, string, nMatches, matches, 0);
346 size_t len = regerror(ret, regex, NULL, 0);
348 regerror(ret, regex, errorStr, len);
349 printf("string = '%s'\n", string);
350 fprintf(stderr, "%s\n%s", errorMessage, errorStr);
357 void CompileRegex(regex_t *regex, const char *pattern, int flags)
359 int ret = regcomp(regex, pattern, flags);
361 size_t len = regerror(ret, regex, NULL, 0);
363 regerror(ret, regex, errorStr, len);
364 fprintf(stderr, "Regex compilation failed - %s\n", errorStr);