Client - Broke the client out into separate files
[tpg/opendispense2.git] / src / client / protocol.c
1 /*
2  * OpenDispense 2 
3  * UCC (University [of WA] Computer Club) Electronic Accounting System
4  * - Dispense Client
5  *
6  * protocol.c
7  * - Client/Server communication
8  *
9  * This file is licenced under the 3-clause BSD Licence. See the file
10  * COPYING for full details.
11  */
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <netdb.h>      // gethostbyname
16 #include <sys/socket.h>
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
19 //#include <openssl/sha.h>      // SHA1
20 #include <pwd.h>        // getpwuids
21 #include <unistd.h>     // close/getuid
22 #include <limits.h>     // INT_MIN/INT_MAX
23 #include <stdarg.h>
24 #include "common.h"
25
26 // === PROTOTYPES ===
27 char    *ReadLine(int Socket);
28  int    sendf(int Socket, const char *Format, ...);
29
30 // ---------------------
31 // --- Coke Protocol ---
32 // ---------------------
33 int OpenConnection(const char *Host, int Port)
34 {
35         struct hostent  *host;
36         struct sockaddr_in      serverAddr;
37          int    sock;
38         
39         host = gethostbyname(Host);
40         if( !host ) {
41                 fprintf(stderr, "Unable to look up '%s'\n", Host);
42                 return -1;
43         }
44         
45         memset(&serverAddr, 0, sizeof(serverAddr));
46         
47         serverAddr.sin_family = AF_INET;        // IPv4
48         // NOTE: I have a suspicion that IPv6 will play sillybuggers with this :)
49         serverAddr.sin_addr.s_addr = *((unsigned long *) host->h_addr_list[0]);
50         serverAddr.sin_port = htons(Port);
51         
52         sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
53         if( sock < 0 ) {
54                 fprintf(stderr, "Failed to create socket\n");
55                 return -1;
56         }
57
58 //      printf("geteuid() = %i, getuid() = %i\n", geteuid(), getuid());
59         
60         if( geteuid() == 0 || getuid() == 0 )
61         {
62                  int    i;
63                 struct sockaddr_in      localAddr;
64                 memset(&localAddr, 0, sizeof(localAddr));
65                 localAddr.sin_family = AF_INET; // IPv4
66                 
67                 // Loop through all the top ports until one is avaliable
68                 for( i = 512; i < 1024; i ++)
69                 {
70                         localAddr.sin_port = htons(i);  // IPv4
71                         // Attempt to bind to low port for autoauth
72                         if( bind(sock, (struct sockaddr*)&localAddr, sizeof(localAddr)) == 0 )
73                                 break;
74                 }
75                 if( i == 1024 )
76                         printf("Warning: AUTOAUTH unavaliable\n");
77 //              else
78 //                      printf("Bound to 0.0.0.0:%i\n", i);
79         }
80         
81         if( connect(sock, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0 ) {
82                 fprintf(stderr, "Failed to connect to server\n");
83                 return -1;
84         }
85
86         // We're not authenticated if the connection has just opened
87         gbIsAuthenticated = 0;
88         
89         return sock;
90 }
91
92 int Authenticate_AutoAuth(int Socket, const char *Username)
93 {
94         char    *buf;
95          int    responseCode;
96          int    ret = -1;
97         
98         // Attempt automatic authentication
99         sendf(Socket, "AUTOAUTH %s\n", Username);
100         
101         // Check if it worked
102         buf = ReadLine(Socket);
103         
104         responseCode = atoi(buf);
105         switch( responseCode )
106         {
107         case 200:       // Autoauth succeeded, return
108                 ret = 0;
109                 break;
110         
111         case 401:       // Untrusted
112 //              fprintf(stderr, "Untrusted host, AUTOAUTH unavaliable\n");
113                 ret = RV_PERMISSIONS;
114                 break;
115         case 404:       // Bad Username
116                 fprintf(stderr, "Bad Username '%s'\n", Username);
117                 ret = RV_INVALID_USER;
118                 break;
119         
120         default:
121                 fprintf(stderr, "Unkown response code %i from server\n", responseCode);
122                 printf("%s\n", buf);
123                 ret = RV_UNKNOWN_ERROR;
124                 break;;
125         }
126         
127         free(buf);
128         return ret;
129 }
130
131 int Authenticate_AuthIdent(int Socket)
132 {
133         char    *buf;
134          int    responseCode;
135          int    ret = -1;
136         
137         // Attempt automatic authentication
138         sendf(Socket, "AUTHIDENT\n");
139         
140         // Check if it worked
141         buf = ReadLine(Socket);
142         
143         responseCode = atoi(buf);
144         switch( responseCode )
145         {
146         case 200:       // Autoauth succeeded, return
147                 ret = 0;
148                 break;
149         
150         case 401:       // Untrusted
151 //              fprintf(stderr, "Untrusted host, AUTHIDENT unavaliable\n");
152                 ret = RV_PERMISSIONS;
153                 break;
154         
155         default:
156                 fprintf(stderr, "Unkown response code %i from server\n", responseCode);
157                 printf("%s\n", buf);
158                 ret = RV_UNKNOWN_RESPONSE;
159                 break;
160         }
161         
162         free(buf);
163
164         return ret;
165 }
166
167 int Authenticate_Password(int Socket, const char *Username)
168 {
169         #if USE_PASSWORD_AUTH
170         char    *buf;
171          int    responseCode;   
172         char    salt[32];
173          int    i;
174         regmatch_t      matches[4];
175
176         sendf(Socket, "USER %s\n", Username);
177         printf("Using username %s\n", Username);
178         
179         buf = ReadLine(Socket);
180         
181         // TODO: Get Salt
182         // Expected format: 100 SALT <something> ...
183         // OR             : 100 User Set
184         RunRegex(&gSaltRegex, buf, 4, matches, "Malformed server response");
185         responseCode = atoi(buf);
186         if( responseCode != 100 ) {
187                 fprintf(stderr, "Unknown repsonse code %i from server\n%s\n", responseCode, buf);
188                 free(buf);
189                 return RV_UNKNOWN_ERROR;        // ERROR
190         }
191         
192         // Check for salt
193         if( memcmp( buf+matches[2].rm_so, "SALT", matches[2].rm_eo - matches[2].rm_so) == 0) {
194                 // Store it for later
195                 memcpy( salt, buf + matches[3].rm_so, matches[3].rm_eo - matches[3].rm_so );
196                 salt[ matches[3].rm_eo - matches[3].rm_so ] = 0;
197         }
198         free(buf);
199         
200         // Give three attempts
201         for( i = 0; i < 3; i ++ )
202         {
203                  int    ofs = strlen(Username)+strlen(salt);
204                 char    tmpBuf[42];
205                 char    tmp[ofs+20];
206                 char    *pass = getpass("Password: ");
207                 uint8_t h[20];
208                 
209                 // Create hash string
210                 // <username><salt><hash>
211                 strcpy(tmp, Username);
212                 strcat(tmp, salt);
213                 SHA1( (unsigned char*)pass, strlen(pass), h );
214                 memcpy(tmp+ofs, h, 20);
215                 
216                 // Hash all that
217                 SHA1( (unsigned char*)tmp, ofs+20, h );
218                 sprintf(tmpBuf, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
219                         h[ 0], h[ 1], h[ 2], h[ 3], h[ 4], h[ 5], h[ 6], h[ 7], h[ 8], h[ 9],
220                         h[10], h[11], h[12], h[13], h[14], h[15], h[16], h[17], h[18], h[19]
221                         );
222         
223                 // Send password
224                 sendf(Socket, "PASS %s\n", tmpBuf);
225                 buf = ReadLine(Socket);
226         
227                 responseCode = atoi(buf);
228                 // Auth OK?
229                 if( responseCode == 200 )       break;
230                 // Bad username/password
231                 if( responseCode == 401 )       continue;
232                 
233                 fprintf(stderr, "Unknown repsonse code %i from server\n%s\n", responseCode, buf);
234                 free(buf);
235                 return -1;
236         }
237         free(buf);
238         if( i == 3 )
239                 return RV_INVALID_USER; // 2 = Bad Password
240
241         return 0;
242         #else
243         return RV_INVALID_USER;
244         #endif
245 }
246
247 /**
248  * \brief Authenticate with the server
249  * \return Boolean Failure
250  */
251 int Authenticate(int Socket)
252 {
253         struct passwd   *pwd;
254         
255         if( gbIsAuthenticated ) return 0;
256         
257         // Get user name
258         pwd = getpwuid( getuid() );
259
260         // Attempt AUTOAUTH
261         if( Authenticate_AutoAuth(Socket, pwd->pw_name) == 0 )
262                 ;
263         else if( Authenticate_AuthIdent(Socket) == 0 )
264                 ;
265         else if( Authenticate_Password(Socket, pwd->pw_name) == 0 )
266                 return RV_INVALID_USER;
267
268         // Set effective user
269         if( gsEffectiveUser ) {
270                 char    *buf;
271                  int    responseCode;
272                 sendf(Socket, "SETEUSER %s\n", gsEffectiveUser);
273                 
274                 buf = ReadLine(Socket);
275                 responseCode = atoi(buf);
276                 
277                 switch(responseCode)
278                 {
279                 case 200:
280                         printf("Running as '%s' by '%s'\n", gsEffectiveUser, pwd->pw_name);
281                         break;
282                 
283                 case 403:
284                         printf("Only coke members can use `dispense -u`\n");
285                         free(buf);
286                         return RV_PERMISSIONS;
287                 
288                 case 404:
289                         printf("Invalid user selected\n");
290                         free(buf);
291                         return RV_INVALID_USER;
292                 
293                 default:
294                         fprintf(stderr, "Unkown response code %i from server\n", responseCode);
295                         printf("%s\n", buf);
296                         free(buf);
297                         return RV_UNKNOWN_ERROR;
298                 }
299                 
300                 free(buf);
301         }
302         
303         gbIsAuthenticated = 1;
304         
305         return 0;
306 }
307
308 int GetUserBalance(int Socket)
309 {
310         regmatch_t      matches[6];
311         struct passwd   *pwd;
312         char    *buf;
313          int    responseCode;
314         
315         if( !gsUserName )
316         {
317                 if( gsEffectiveUser ) {
318                         gsUserName = gsEffectiveUser;
319                 }
320                 else {
321                         pwd = getpwuid( getuid() );
322                         gsUserName = strdup(pwd->pw_name);
323                 }
324         }
325         
326         sendf(Socket, "USER_INFO %s\n", gsUserName);
327         buf = ReadLine(Socket);
328         responseCode = atoi(buf);
329         switch(responseCode)
330         {
331         case 202:       break;  // Ok
332         
333         case 404:
334                 printf("Invalid user? (USER_INFO failed)\n");
335                 free(buf);
336                 return RV_INVALID_USER;
337         
338         default:
339                 fprintf(stderr, "Unkown response code %i from server\n", responseCode);
340                 printf("%s\n", buf);
341                 free(buf);
342                 return RV_UNKNOWN_ERROR;
343         }
344
345         RunRegex(&gUserInfoRegex, buf, 6, matches, "Malformed server response");
346         
347         giUserBalance = atoi( buf + matches[4].rm_so );
348         gsUserFlags = strdup( buf + matches[5].rm_so );
349         
350         free(buf);
351         
352         return 0;
353 }
354
355 /**
356  * \brief Read an item info response from the server
357  * \param Dest  Destination for the read item (strings will be on the heap)
358  */
359 int ReadItemInfo(int Socket, tItem *Dest)
360 {
361         char    *buf;
362          int    responseCode;
363         
364         regmatch_t      matches[8];
365         char    *statusStr;
366         
367         // Get item info
368         buf = ReadLine(Socket);
369         responseCode = atoi(buf);
370         
371         switch(responseCode)
372         {
373         case 202:       break;
374         
375         case 406:
376                 printf("Bad item name\n");
377                 free(buf);
378                 return RV_BAD_ITEM;
379         
380         default:
381                 fprintf(stderr, "Unknown response from dispense server (Response Code %i)\n%s", responseCode, buf);
382                 free(buf);
383                 return RV_UNKNOWN_ERROR;
384         }
385         
386         RunRegex(&gItemRegex, buf, 8, matches, "Malformed server response");
387         
388         buf[ matches[3].rm_eo ] = '\0';
389         buf[ matches[5].rm_eo ] = '\0';
390         buf[ matches[7].rm_eo ] = '\0';
391         
392         statusStr = &buf[ matches[5].rm_so ];
393         
394         Dest->ID = atoi( buf + matches[4].rm_so );
395         
396         if( strcmp(statusStr, "avail") == 0 )
397                 Dest->Status = 0;
398         else if( strcmp(statusStr, "sold") == 0 )
399                 Dest->Status = 1;
400         else if( strcmp(statusStr, "error") == 0 )
401                 Dest->Status = -1;
402         else {
403                 fprintf(stderr, "Unknown response from dispense server (status '%s')\n",
404                         statusStr);
405                 return RV_UNKNOWN_ERROR;
406         }
407         Dest->Price = atoi( buf + matches[6].rm_so );
408         
409         // Hack a little to reduce heap fragmentation
410         {
411                 char    tmpType[strlen(buf + matches[3].rm_so) + 1];
412                 char    tmpDesc[strlen(buf + matches[7].rm_so) + 1];
413                 strcpy(tmpType, buf + matches[3].rm_so);
414                 strcpy(tmpDesc, buf + matches[7].rm_so);
415                 free(buf);
416                 Dest->Type = strdup( tmpType );
417                 Dest->Desc = strdup( tmpDesc );
418         }
419         
420         return 0;
421 }
422
423 /**
424  * \brief Fill the item information structure
425  * \return Boolean Failure
426  */
427 void PopulateItemList(int Socket)
428 {
429         char    *buf;
430          int    responseCode;
431         
432         char    *arrayType;
433          int    count, i;
434         regmatch_t      matches[4];
435         
436         // Ask server for stock list
437         send(Socket, "ENUM_ITEMS\n", 11, 0);
438         buf = ReadLine(Socket);
439         
440         //printf("Output: %s\n", buf);
441         
442         responseCode = atoi(buf);
443         if( responseCode != 201 ) {
444                 fprintf(stderr, "Unknown response from dispense server (Response Code %i)\n", responseCode);
445                 exit(RV_UNKNOWN_ERROR);
446         }
447         
448         // - Get item list -
449         
450         // Expected format:
451         //  201 Items <count>
452         //  202 Item <count>
453         RunRegex(&gArrayRegex, buf, 4, matches, "Malformed server response");
454                 
455         arrayType = &buf[ matches[2].rm_so ];   buf[ matches[2].rm_eo ] = '\0';
456         count = atoi( &buf[ matches[3].rm_so ] );
457                 
458         // Check array type
459         if( strcmp(arrayType, "Items") != 0 ) {
460                 // What the?!
461                 fprintf(stderr, "Unexpected array type, expected 'Items', got '%s'\n",
462                         arrayType);
463                 exit(RV_UNKNOWN_ERROR);
464         }
465         free(buf);
466         
467         giNumItems = count;
468         gaItems = malloc( giNumItems * sizeof(tItem) );
469         
470         // Fetch item information
471         for( i = 0; i < giNumItems; i ++ )
472         {
473                 ReadItemInfo( Socket, &gaItems[i] );
474         }
475         
476         // Read end of list
477         buf = ReadLine(Socket);
478         responseCode = atoi(buf);
479                 
480         if( responseCode != 200 ) {
481                 fprintf(stderr, "Unknown response from dispense server %i\n'%s'",
482                         responseCode, buf
483                         );
484                 exit(-1);
485         }
486         
487         free(buf);
488 }
489
490
491 /**
492  * \brief Get information on an item
493  * \return Boolean Failure
494  */
495 int Dispense_ItemInfo(int Socket, const char *Type, int ID)
496 {
497         tItem   item;
498          int    ret;
499         
500         // Query
501         sendf(Socket, "ITEM_INFO %s:%i\n", Type, ID);
502         
503         ret = ReadItemInfo(Socket, &item);
504         if(ret) return ret;
505         
506         printf("%8s:%-2i %2i.%02i %s\n",
507                 item.Type, item.ID,
508                 item.Price/100, item.Price%100,
509                 item.Desc);
510         
511         free(item.Type);
512         free(item.Desc);
513         
514         return 0;
515 }
516
517 /**
518  * \brief Dispense an item
519  * \return Boolean Failure
520  */
521 int DispenseItem(int Socket, const char *Type, int ID)
522 {
523          int    ret, responseCode;
524         char    *buf;
525         
526         // Check for a dry run
527         if( gbDryRun ) {
528                 printf("Dry Run - No action\n");
529                 return 0;
530         }
531         
532         // Dispense!
533         sendf(Socket, "DISPENSE %s:%i\n", Type, ID);
534         buf = ReadLine(Socket);
535         
536         responseCode = atoi(buf);
537         switch( responseCode )
538         {
539         case 200:
540                 printf("Dispense OK\n");
541                 ret = 0;
542                 break;
543         case 401:
544                 printf("Not authenticated\n");
545                 ret = RV_PERMISSIONS;
546                 break;
547         case 402:
548                 printf("Insufficient balance\n");
549                 ret = RV_BALANCE;
550                 break;
551         case 406:
552                 printf("Bad item name\n");
553                 ret = RV_BAD_ITEM;
554                 break;
555         case 500:
556                 printf("Item failed to dispense, is the slot empty?\n");
557                 ret = RV_SERVER_ERROR;
558                 break;
559         case 501:
560                 printf("Dispense not possible (slot empty/permissions)\n");
561                 ret = RV_SERVER_ERROR;
562                 break;
563         default:
564                 printf("Unknown response code %i ('%s')\n", responseCode, buf);
565                 ret = RV_UNKNOWN_ERROR;
566                 break;
567         }
568         
569         free(buf);
570         return ret;
571 }
572
573 /**
574  * \brief Alter a user's balance
575  */
576 int Dispense_AlterBalance(int Socket, const char *Username, int Ammount, const char *Reason)
577 {
578         char    *buf;
579          int    responseCode, rv = -1;
580         
581         // Check for a dry run
582         if( gbDryRun ) {
583                 printf("Dry Run - No action\n");
584                 return 0;
585         }
586
587         // Sanity
588         if( Ammount == 0 ) {
589                 printf("An amount would be nice\n");
590                 return RV_ARGUMENTS;
591         }
592         
593         sendf(Socket, "ADD %s %i %s\n", Username, Ammount, Reason);
594         buf = ReadLine(Socket);
595         
596         responseCode = atoi(buf);
597         
598         switch(responseCode)
599         {
600         case 200:
601                 rv = 0; // OK
602                 break;
603         case 402:
604                 fprintf(stderr, "Insufficient balance\n");
605                 rv = RV_BAD_ITEM;
606                 break;
607         case 403:       // Not in coke
608                 fprintf(stderr, "You are not in coke (sucker)\n");
609                 rv = RV_PERMISSIONS;
610                 break;
611         case 404:       // Unknown user
612                 fprintf(stderr, "Unknown user '%s'\n", Username);
613                 rv = RV_INVALID_USER;
614                 break;
615         default:
616                 fprintf(stderr, "Unknown response code %i\n'%s'\n", responseCode, buf);
617                 rv = RV_UNKNOWN_RESPONSE;
618                 break;
619         }
620         free(buf);
621         
622         return rv;
623 }
624
625 /**
626  * \brief Set a user's balance
627  * \note Only avaliable to dispense admins
628  */
629 int Dispense_SetBalance(int Socket, const char *Username, int Balance, const char *Reason)
630 {
631         char    *buf;
632          int    responseCode;
633         
634         // Check for a dry run
635         if( gbDryRun ) {
636                 printf("Dry Run - No action\n");
637                 return 0;
638         }
639         
640         sendf(Socket, "SET %s %i %s\n", Username, Balance, Reason);
641         buf = ReadLine(Socket);
642         
643         responseCode = atoi(buf);
644         free(buf);
645         
646         switch(responseCode)
647         {
648         case 200:       return 0;       // OK
649         case 403:       // Not in coke
650                 fprintf(stderr, "You are not an admin\n");
651                 return RV_PERMISSIONS;
652         case 404:       // Unknown user
653                 fprintf(stderr, "Unknown user '%s'\n", Username);
654                 return RV_INVALID_USER;
655         default:
656                 fprintf(stderr, "Unknown response code %i\n", responseCode);
657                 return RV_UNKNOWN_RESPONSE;
658         }
659         
660         return -1;
661 }
662
663 /**
664  * \brief Give money to another user
665  */
666 int Dispense_Give(int Socket, const char *Username, int Ammount, const char *Reason)
667 {
668         char    *buf;
669          int    responseCode;
670         
671         if( Ammount < 0 ) {
672                 printf("Sorry, you can only give, you can't take.\n");
673                 return RV_ARGUMENTS;
674         }
675         
676         // Fast return on zero
677         if( Ammount == 0 ) {
678                 printf("Are you actually going to give any?\n");
679                 return RV_ARGUMENTS;
680         }
681         
682         // Check for a dry run
683         if( gbDryRun ) {
684                 printf("Dry Run - No action\n");
685                 return 0;
686         }
687         
688         sendf(Socket, "GIVE %s %i %s\n", Username, Ammount, Reason);
689
690         buf = ReadLine(Socket);
691         responseCode = atoi(buf);
692         free(buf);      
693         switch(responseCode)
694         {
695         case 200:
696                 printf("Give succeeded\n");
697                 return RV_SUCCESS;      // OK
698         
699         case 402:       
700                 fprintf(stderr, "Insufficient balance\n");
701                 return RV_BALANCE;
702         
703         case 404:       // Unknown user
704                 fprintf(stderr, "Unknown user '%s'\n", Username);
705                 return RV_INVALID_USER;
706         
707         default:
708                 fprintf(stderr, "Unknown response code %i\n", responseCode);
709                 return RV_UNKNOWN_RESPONSE;
710         }
711         
712         return -1;
713 }
714
715 int Dispense_Refund(int Socket, const char *Username, const char *Item, int PriceOverride)
716 {
717         char    *buf;
718          int    responseCode, ret = -1;
719         
720         // Check item id
721         if( RunRegex(&gUserItemIdentRegex, Item, 0, NULL, NULL) != 0 )
722         {
723                 fprintf(stderr, "Error: Invalid item ID passed (should be <type>:<num>)\n");
724                 return RV_ARGUMENTS;
725         }
726
727         // Check username (quick)
728         if( strchr(Username, ' ') || strchr(Username, '\n') )
729         {
730                 fprintf(stderr, "Error: Username is invalid (no spaces or newlines please)\n");
731                 return RV_ARGUMENTS;
732         }
733
734         // Send the query
735         sendf(Socket, "REFUND %s %s %i\n", Username, Item, PriceOverride);
736
737         buf = ReadLine(Socket);
738         responseCode = atoi(buf);
739         switch(responseCode)
740         {
741         case 200:
742                 Dispense_ShowUser(Socket, Username);    // Show destination account
743                 ret = 0;
744                 break;
745         case 403:
746                 fprintf(stderr, "Refund access is only avaliable to coke members\n");
747                 ret = RV_PERMISSIONS;
748                 break;
749         case 404:
750                 fprintf(stderr, "Unknown user '%s' passed\n", Username);
751                 ret = RV_INVALID_USER;
752                 break;
753         case 406:
754                 fprintf(stderr, "Invalid item '%s' passed\n", Item);
755                 ret = RV_BAD_ITEM;
756                 break;
757         default:
758                 fprintf(stderr, "Unknown response from server %i\n%s\n", responseCode, buf);
759                 ret = -1;
760                 break;
761         }
762         free(buf);
763         return ret;
764 }
765
766 /**
767  * \brief Donate money to the club
768  */
769 int Dispense_Donate(int Socket, int Ammount, const char *Reason)
770 {
771         char    *buf;
772          int    responseCode;
773         
774         if( Ammount < 0 ) {
775                 printf("Sorry, you can only give, you can't take.\n");
776                 return -1;
777         }
778         
779         // Fast return on zero
780         if( Ammount == 0 ) {
781                 printf("Are you actually going to give any?\n");
782                 return 1;
783         }
784         
785         // Check for a dry run
786         if( gbDryRun ) {
787                 printf("Dry Run - No action\n");
788                 return 0;
789         }
790         
791         sendf(Socket, "DONATE %i %s\n", Ammount, Reason);
792         buf = ReadLine(Socket);
793         
794         responseCode = atoi(buf);
795         free(buf);
796         
797         switch(responseCode)
798         {
799         case 200:       return 0;       // OK
800         
801         case 402:       
802                 fprintf(stderr, "Insufficient balance\n");
803                 return 1;
804         
805         default:
806                 fprintf(stderr, "Unknown response code %i\n", responseCode);
807                 return -1;
808         }
809         
810         return -1;
811 }
812
813 /**
814  * \brief Enumerate users
815  */
816 int Dispense_EnumUsers(int Socket)
817 {
818         char    *buf;
819          int    responseCode;
820          int    nUsers;
821         regmatch_t      matches[4];
822         
823         if( giMinimumBalance != INT_MIN ) {
824                 if( giMaximumBalance != INT_MAX ) {
825                         sendf(Socket, "ENUM_USERS min_balance:%i max_balance:%i\n", giMinimumBalance, giMaximumBalance);
826                 }
827                 else {
828                         sendf(Socket, "ENUM_USERS min_balance:%i\n", giMinimumBalance);
829                 }
830         }
831         else {
832                 if( giMaximumBalance != INT_MAX ) {
833                         sendf(Socket, "ENUM_USERS max_balance:%i\n", giMaximumBalance);
834                 }
835                 else {
836                         sendf(Socket, "ENUM_USERS\n");
837                 }
838         }
839         buf = ReadLine(Socket);
840         responseCode = atoi(buf);
841         
842         switch(responseCode)
843         {
844         case 201:       break;  // Ok, length follows
845         
846         default:
847                 fprintf(stderr, "Unknown response code %i\n%s\n", responseCode, buf);
848                 free(buf);
849                 return -1;
850         }
851         
852         // Get count (not actually used)
853         RunRegex(&gArrayRegex, buf, 4, matches, "Malformed server response");
854         nUsers = atoi( buf + matches[3].rm_so );
855         printf("%i users returned\n", nUsers);
856         
857         // Free string
858         free(buf);
859         
860         // Read returned users
861         do {
862                 buf = ReadLine(Socket);
863                 responseCode = atoi(buf);
864                 
865                 if( responseCode != 202 )       break;
866                 
867                 _PrintUserLine(buf);
868                 free(buf);
869         } while(responseCode == 202);
870         
871         // Check final response
872         if( responseCode != 200 ) {
873                 fprintf(stderr, "Unknown response code %i\n%s\n", responseCode, buf);
874                 free(buf);
875                 return -1;
876         }
877         
878         free(buf);
879         
880         return 0;
881 }
882
883 int Dispense_ShowUser(int Socket, const char *Username)
884 {
885         char    *buf;
886          int    responseCode, ret;
887         
888         sendf(Socket, "USER_INFO %s\n", Username);
889         buf = ReadLine(Socket);
890         
891         responseCode = atoi(buf);
892         
893         switch(responseCode)
894         {
895         case 202:
896                 _PrintUserLine(buf);
897                 ret = 0;
898                 break;
899         
900         case 404:
901                 printf("Unknown user '%s'\n", Username);
902                 ret = 1;
903                 break;
904         
905         default:
906                 fprintf(stderr, "Unknown response code %i '%s'\n", responseCode, buf);
907                 ret = -1;
908                 break;
909         }
910         
911         free(buf);
912         
913         return ret;
914 }
915
916 void _PrintUserLine(const char *Line)
917 {
918         regmatch_t      matches[6];
919          int    bal;
920         
921         RunRegex(&gUserInfoRegex, Line, 6, matches, "Malformed server response");
922         // 3: Username
923         // 4: Balance
924         // 5: Flags
925         {
926                  int    usernameLen = matches[3].rm_eo - matches[3].rm_so;
927                 char    username[usernameLen + 1];
928                  int    flagsLen = matches[5].rm_eo - matches[5].rm_so;
929                 char    flags[flagsLen + 1];
930                 
931                 memcpy(username, Line + matches[3].rm_so, usernameLen);
932                 username[usernameLen] = '\0';
933                 memcpy(flags, Line + matches[5].rm_so, flagsLen);
934                 flags[flagsLen] = '\0';
935                 
936                 bal = atoi(Line + matches[4].rm_so);
937                 printf("%-15s: $%8.02f (%s)\n", username, ((float)bal)/100, flags);
938         }
939 }
940
941 int Dispense_AddUser(int Socket, const char *Username)
942 {
943         char    *buf;
944          int    responseCode, ret;
945         
946         // Check for a dry run
947         if( gbDryRun ) {
948                 printf("Dry Run - No action\n");
949                 return 0;
950         }
951         
952         sendf(Socket, "USER_ADD %s\n", Username);
953         
954         buf = ReadLine(Socket);
955         responseCode = atoi(buf);
956         
957         switch(responseCode)
958         {
959         case 200:
960                 printf("User '%s' added\n", Username);
961                 ret = 0;
962                 break;
963                 
964         case 403:
965                 printf("Only wheel can add users\n");
966                 ret = 1;
967                 break;
968                 
969         case 404:
970                 printf("User '%s' already exists\n", Username);
971                 ret = 0;
972                 break;
973         
974         default:
975                 fprintf(stderr, "Unknown response code %i '%s'\n", responseCode, buf);
976                 ret = -1;
977                 break;
978         }
979         
980         free(buf);
981         
982         return ret;
983 }
984
985 int Dispense_SetUserType(int Socket, const char *Username, const char *TypeString, const char *Reason)
986 {
987         char    *buf;
988          int    responseCode, ret;
989         
990         // Check for a dry run
991         if( gbDryRun ) {
992                 printf("Dry Run - No action\n");
993                 return 0;
994         }
995         
996         // TODO: Pre-validate the string
997         
998         sendf(Socket, "USER_FLAGS %s %s %s\n", Username, TypeString, Reason);
999         
1000         buf = ReadLine(Socket);
1001         responseCode = atoi(buf);
1002         
1003         switch(responseCode)
1004         {
1005         case 200:
1006                 printf("User '%s' updated\n", Username);
1007                 ret = 0;
1008                 break;
1009                 
1010         case 403:
1011                 printf("Only dispense admins can modify users\n");
1012                 ret = RV_PERMISSIONS;
1013                 break;
1014         
1015         case 404:
1016                 printf("User '%s' does not exist\n", Username);
1017                 ret = RV_INVALID_USER;
1018                 break;
1019         
1020         case 407:
1021                 printf("Flag string is invalid\n");
1022                 ret = RV_ARGUMENTS;
1023                 break;
1024         
1025         default:
1026                 fprintf(stderr, "Unknown response code %i '%s'\n", responseCode, buf);
1027                 ret = RV_UNKNOWN_RESPONSE;
1028                 break;
1029         }
1030         
1031         free(buf);
1032         
1033         return ret;
1034 }
1035
1036 int Dispense_SetItem(int Socket, const char *Type, int ID, int NewPrice, const char *NewName)
1037 {
1038         char    *buf;
1039          int    responseCode, ret;
1040         
1041         // Check for a dry run
1042         if( gbDryRun ) {
1043                 printf("Dry Run - No action\n");
1044                 return 0;
1045         }
1046         
1047         sendf(Socket, "UPDATE_ITEM %s:%i %i %s\n", Type, ID, NewPrice, NewName);
1048         
1049         buf = ReadLine(Socket);
1050         responseCode = atoi(buf);
1051         
1052         switch(responseCode)
1053         {
1054         case 200:
1055                 printf("Item %s:%i updated\n", Type, ID);
1056                 ret = 0;
1057                 break;
1058                 
1059         case 403:
1060                 printf("Only coke members can modify the slots\n");
1061                 ret = RV_PERMISSIONS;
1062                 break;
1063         
1064         case 406:
1065                 printf("Invalid item passed\n");
1066                 ret = RV_BAD_ITEM;
1067                 break;
1068         
1069         default:
1070                 fprintf(stderr, "Unknown response code %i '%s'\n", responseCode, buf);
1071                 ret = -1;
1072                 break;
1073         }
1074         
1075         free(buf);
1076         
1077         return ret;
1078 }
1079
1080 // ===
1081 // Helpers
1082 // ===
1083 char *ReadLine(int Socket)
1084 {
1085         static char     buf[BUFSIZ];
1086         static int      bufPos = 0;
1087         static int      bufValid = 0;
1088          int    len;
1089         char    *newline = NULL;
1090          int    retLen = 0;
1091         char    *ret = malloc(10);
1092         
1093         #if DEBUG_TRACE_SERVER
1094         printf("ReadLine: ");
1095         fflush(stdout);
1096         #endif
1097         
1098         ret[0] = '\0';
1099         
1100         while( !newline )
1101         {
1102                 if( bufValid ) {
1103                         len = bufValid;
1104                 }
1105                 else {
1106                         len = recv(Socket, buf+bufPos, BUFSIZ-1-bufPos, 0);
1107                         if( len <= 0 ) {
1108                                 free(ret);
1109                                 return strdup("599 Client Connection Error\n");
1110                         }
1111                 }
1112                 buf[bufPos+len] = '\0';
1113                 
1114                 newline = strchr( buf+bufPos, '\n' );
1115                 if( newline ) {
1116                         *newline = '\0';
1117                 }
1118                 
1119                 retLen += strlen(buf+bufPos);
1120                 ret = realloc(ret, retLen + 1);
1121                 strcat( ret, buf+bufPos );
1122                 
1123                 if( newline ) {
1124                          int    newLen = newline - (buf+bufPos) + 1;
1125                         bufValid = len - newLen;
1126                         len = newLen;
1127                 }
1128                 if( len + bufPos == BUFSIZ - 1 )        bufPos = 0;
1129                 else    bufPos += len;
1130         }
1131         
1132         #if DEBUG_TRACE_SERVER
1133         printf("%i '%s'\n", retLen, ret);
1134         #endif
1135         
1136         return ret;
1137 }
1138
1139 int sendf(int Socket, const char *Format, ...)
1140 {
1141         va_list args;
1142          int    len;
1143         
1144         va_start(args, Format);
1145         len = vsnprintf(NULL, 0, Format, args);
1146         va_end(args);
1147         
1148         {
1149                 char    buf[len+1];
1150                 va_start(args, Format);
1151                 vsnprintf(buf, len+1, Format, args);
1152                 va_end(args);
1153                 
1154                 #if DEBUG_TRACE_SERVER
1155                 printf("sendf: %s", buf);
1156                 #endif
1157                 
1158                 return send(Socket, buf, len, 0);
1159         }
1160 }
1161

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