X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=src%2Fclient%2Fmain.c;h=c82d12d793f1bad6334452d23df3f3b0139d5b8f;hb=dce0ef0f3c15f9e38e57d4a14c904c339affac4e;hp=ee93227ce39fec34540561ce2cb9dc2a39f93bd6;hpb=cbc8b9bd4b01261475453a5dfbecf2d508edc730;p=tpg%2Fopendispense2.git diff --git a/src/client/main.c b/src/client/main.c index ee93227..c82d12d 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -14,6 +14,7 @@ #include // isspace #include #include +#include #include // close #include // gethostbyname @@ -21,6 +22,7 @@ #include #include #include +#include // SHA1 // === TYPES === typedef struct sItem { @@ -30,6 +32,9 @@ typedef struct sItem { } tItem; // === PROTOTYPES === + int ShowNCursesUI(void); +void PrintAlign(int Row, int Col, int Width, const char *Left, char Pad1, const char *Mid, char Pad2, const char *Right, ...); + int sendf(int Socket, const char *Format, ...); int OpenConnection(const char *Host, int Port); void Authenticate(int Socket); @@ -42,8 +47,7 @@ char *gsDispenseServer = "localhost"; int giDispensePort = 11020; tItem *gaItems; int giNumItems; -regex_t gArrayRegex; -regex_t gItemRegex; +regex_t gArrayRegex, gItemRegex, gSaltRegex; // === CODE === int main(int argc, char *argv[]) @@ -57,6 +61,8 @@ int main(int argc, char *argv[]) CompileRegex(&gArrayRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([0-9]+)", REG_EXTENDED); // // > Code Type Ident Price Desc CompileRegex(&gItemRegex, "^([0-9]{3})\\s+(.+?)\\s+(.+?)\\s+([0-9]+)\\s+(.+)$", REG_EXTENDED); + // > Code 'SALT' salt + CompileRegex(&gSaltRegex, "^([0-9]{3})\\s+(.+)\\s+(.+)$", REG_EXTENDED); // Connect to server sock = OpenConnection(gsDispenseServer, giDispensePort); @@ -152,13 +158,15 @@ int main(int argc, char *argv[]) printf("%3i %s\n", gaItems[i].Price, gaItems[i].Desc); } - Authenticate(sock); - // and choose what to dispense // TODO: ncurses interface (with separation between item classes) // - Hmm... that would require standardising the item ID to be : // Oh, why not :) + #if 1 + i = ShowNCursesUI(); + #else + for(;;) { char *buf; @@ -181,36 +189,42 @@ int main(int argc, char *argv[]) printf("Bad item (should be between 0 and %i)\n", giNumItems); continue; } - - sendf(sock, "DISPENSE %s\n", gaItems[i].Ident); - - len = recv(sock, buffer, BUFSIZ-1, 0); - buffer[len] = '\0'; - trim(buffer); - - responseCode = atoi(buffer); - switch( responseCode ) - { - case 200: - printf("Dispense OK\n"); - break; - case 401: - printf("Not authenticated\n"); - break; - case 402: - printf("Insufficient balance\n"); - break; - case 406: - printf("Bad item name, bug report\n"); - break; - case 500: - printf("Item failed to dispense, is the slot empty?\n"); - break; - default: - printf("Unknown response code %i\n", responseCode); - break; - } - + break; + } + } + #endif + + Authenticate(sock); + + if( i >= 0 ) + { + // Dispense! + sendf(sock, "DISPENSE %s\n", gaItems[i].Ident); + + len = recv(sock, buffer, BUFSIZ-1, 0); + buffer[len] = '\0'; + trim(buffer); + + responseCode = atoi(buffer); + switch( responseCode ) + { + case 200: + printf("Dispense OK\n"); + break; + case 401: + printf("Not authenticated\n"); + break; + case 402: + printf("Insufficient balance\n"); + break; + case 406: + printf("Bad item name, bug report\n"); + break; + case 500: + printf("Item failed to dispense, is the slot empty?\n"); + break; + default: + printf("Unknown response code %i\n", responseCode); break; } } @@ -220,6 +234,188 @@ int main(int argc, char *argv[]) return 0; } +void ShowItemAt(int Row, int Col, int Width, int Index) +{ + int _x, _y, times; + + move( Row, Col ); + + if( Index < 0 || Index >= giNumItems ) { + printw("%02i OOR", Index); + return ; + } + printw("%02i %s", Index, gaItems[Index].Desc); + + getyx(stdscr, _y, _x); + // Assumes max 4 digit prices + times = Width - 4 - (_x - Col); // TODO: Better handling for large prices + while(times--) addch(' '); + printw("%4i", gaItems[Index].Price); +} + +/** + */ +int ShowNCursesUI(void) +{ + int ch; + int i, times; + int xBase, yBase; + const int displayMinWidth = 34; + const int displayMinItems = 8; + char *titleString = "Dispense"; + int itemCount = displayMinItems; + int itemBase = 0; + + int height = itemCount + 3; + int width = displayMinWidth; + + // Enter curses mode + initscr(); + raw(); noecho(); + + xBase = COLS/2 - width/2; + yBase = LINES/2 - height/2; + + for( ;; ) + { + // Header + PrintAlign(yBase, xBase, width, "/", '-', titleString, '-', "\\"); + + // Items + for( i = 0; i < itemCount; i ++ ) + { + move( yBase + 1 + i, xBase ); + addch('|'); + addch(' '); + + // Check for ... row + if( i == 0 && itemBase > 0 ) { + printw(" ..."); + times = width - 1 - 8; + while(times--) addch(' '); + } + else if( i == itemCount - 1 && itemBase < giNumItems - itemCount ) { + printw(" ..."); + times = width - 1 - 8; + while(times--) addch(' '); + } + // Show an item + else { + ShowItemAt( yBase + 1 + i, xBase + 2, width - 4, itemBase + i); + addch(' '); + } + + // Scrollbar (if needed) + if( giNumItems > itemCount ) { + if( i == 0 ) { + addch('A'); + } + else if( i == itemCount - 1 ) { + addch('V'); + } + else { + int percentage = itemBase * 100 / (giNumItems-itemCount); + if( i-1 == percentage*(itemCount-3)/100 ) { + addch('#'); + } + else { + addch('|'); + } + } + } + else { + addch('|'); + } + } + + // Footer + PrintAlign(yBase+height-2, xBase, width, "\\", '-', "", '-', "/"); + + // Get input + ch = getch(); + + if( ch == '\x1B' ) { + ch = getch(); + if( ch == '[' ) { + ch = getch(); + + switch(ch) + { + case 'B': + if( itemBase < giNumItems - (itemCount) ) + itemBase ++; + break; + case 'A': + if( itemBase > 0 ) + itemBase --; + break; + } + } + else { + + } + } + else { + break; + } + + } + + + // Leave + endwin(); + return -1; +} + +void PrintAlign(int Row, int Col, int Width, const char *Left, char Pad1, const char *Mid, char Pad2, const char *Right, ...) +{ + int lLen, mLen, rLen; + int times; + + va_list args; + + // Get the length of the strings + va_start(args, Right); + lLen = vsnprintf(NULL, 0, Left, args); + mLen = vsnprintf(NULL, 0, Mid, args); + rLen = vsnprintf(NULL, 0, Right, args); + va_end(args); + + // Sanity check + if( lLen + mLen/2 > Width/2 || mLen/2 + rLen > Width/2 ) { + return ; // TODO: What to do? + } + + move(Row, Col); + + // Render strings + va_start(args, Right); + // - Left + { + char tmp[lLen+1]; + vsnprintf(tmp, lLen+1, Left, args); + addstr(tmp); + } + // - Left padding + times = Width/2 - mLen/2 - lLen; + while(times--) addch(Pad1); + // - Middle + { + char tmp[mLen+1]; + vsnprintf(tmp, mLen+1, Mid, args); + addstr(tmp); + } + // - Right Padding + times = Width/2 - mLen/2 - rLen; + while(times--) addch(Pad2); + // - Right + { + char tmp[rLen+1]; + vsnprintf(tmp, rLen+1, Right, args); + addstr(tmp); + } +} + // === HELPERS === int sendf(int Socket, const char *Format, ...) { @@ -289,6 +485,8 @@ void Authenticate(int Socket) struct passwd *pwd; char buf[512]; int responseCode; + char salt[32]; + regmatch_t matches[4]; // Get user name pwd = getpwuid( getuid() ); @@ -306,6 +504,50 @@ void Authenticate(int Socket) case 200: // Authenticated, return :) return ; case 401: // Untrusted, attempt password authentication + sendf(Socket, "USER %s\n", pwd->pw_name); + printf("Using username %s\n", pwd->pw_name); + + recv(Socket, buf, 511, 0); + trim(buf); + // TODO: Get Salt + // Expected format: 100 SALT ... + // OR : 100 User Set + printf("string = '%s'\n", buf); + RunRegex(&gSaltRegex, buf, 4, matches, "Malformed server response"); + if( atoi(buf) != 100 ) { + exit(-1); // ERROR + } + if( memcmp( buf+matches[2].rm_so, "SALT", matches[2].rm_eo - matches[2].rm_so) == 0) { + // Set salt + memcpy( salt, buf + matches[3].rm_so, matches[3].rm_eo - matches[3].rm_so ); + salt[ matches[3].rm_eo - matches[3].rm_so ] = 0; + printf("Salt: '%s'\n", salt); + } + + fflush(stdout); + { + int ofs = strlen(pwd->pw_name)+strlen(salt); + char tmp[ofs+20]; + char *pass = getpass("Password: "); + uint8_t h[20]; + + strcpy(tmp, pwd->pw_name); + strcat(tmp, salt); + SHA1( (unsigned char*)pass, strlen(pass), h ); + memcpy(tmp+ofs, h, 20); + + // Hash all that + SHA1( (unsigned char*)tmp, ofs+20, h ); + sprintf(buf, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + h[ 0], h[ 1], h[ 2], h[ 3], h[ 4], h[ 5], h[ 6], h[ 7], h[ 8], h[ 9], + h[10], h[11], h[12], h[13], h[14], h[15], h[16], h[17], h[18], h[19] + ); + printf("Final hash: '%s'\n", buf); + fflush(stdout); // Debug + } + + sendf(Socket, "PASS %s\n", buf); + recv(Socket, buf, 511, 0); break; case 404: // Bad Username fprintf(stderr, "Bad Username '%s'\n", pwd->pw_name);