+
+ if(giDebugLevel >= 2) {
+ printf("Client %i: Disconnected\n", clientInfo.ID);
+ }
+}
+
+/**
+ * \brief Parses a client command and calls the required helper function
+ * \param Client Pointer to client state structure
+ * \param CommandString Command from client (single line of the command)
+ * \return Heap String to return to the client
+ */
+char *Server_ParseClientCommand(tClient *Client, char *CommandString)
+{
+ char *space, *args;
+ int i;
+
+ // Split at first space
+ space = strchr(CommandString, ' ');
+ if(space == NULL) {
+ args = NULL;
+ }
+ else {
+ *space = '\0';
+ args = space + 1;
+ }
+
+ // Find command
+ for( i = 0; i < NUM_COMMANDS; i++ )
+ {
+ if(strcmp(CommandString, gaServer_Commands[i].Name) == 0)
+ return gaServer_Commands[i].Function(Client, args);
+ }
+
+ return strdup("400 Unknown Command\n");
+}
+
+// ---
+// Commands
+// ---
+/**
+ * \brief Set client username
+ *
+ * Usage: USER <username>
+ */
+char *Server_Cmd_USER(tClient *Client, char *Args)
+{
+ char *ret;
+
+ // Debug!
+ if( giDebugLevel )
+ printf("Client %i authenticating as '%s'\n", Client->ID, Args);
+
+ // Save username
+ if(Client->Username)
+ free(Client->Username);
+ Client->Username = strdup(Args);
+
+ #if USE_SALT
+ // Create a salt (that changes if the username is changed)
+ // Yes, I know, I'm a little paranoid, but who isn't?
+ Client->Salt[0] = 0x21 + (rand()&0x3F);
+ Client->Salt[1] = 0x21 + (rand()&0x3F);
+ Client->Salt[2] = 0x21 + (rand()&0x3F);
+ Client->Salt[3] = 0x21 + (rand()&0x3F);
+ Client->Salt[4] = 0x21 + (rand()&0x3F);
+ Client->Salt[5] = 0x21 + (rand()&0x3F);
+ Client->Salt[6] = 0x21 + (rand()&0x3F);
+ Client->Salt[7] = 0x21 + (rand()&0x3F);
+
+ // "100 Salt xxxxXXXX\n"
+ ret = strdup("100 SALT xxxxXXXX\n");
+ sprintf(ret, "100 SALT %s\n", Client->Salt);
+ #else
+ ret = strdup("100 User Set\n");
+ #endif
+ return ret;
+}
+
+/**
+ * \brief Authenticate as a user
+ *
+ * Usage: PASS <hash>
+ */
+char *Server_Cmd_PASS(tClient *Client, char *Args)
+{
+ uint8_t clienthash[HASH_LENGTH] = {0};
+
+ // Read user's hash
+ HexBin(clienthash, Args, HASH_LENGTH);
+
+ if( giDebugLevel ) {
+ int i;
+ printf("Client %i: Password hash ", Client->ID);
+ for(i=0;i<HASH_LENGTH;i++)
+ printf("%02x", clienthash[i]&0xFF);
+ printf("\n");
+ }
+
+ return strdup("401 Auth Failure\n");
+}
+
+/**
+ * \brief Authenticate as a user without a password
+ *
+ * Usage: AUTOAUTH <user>
+ */
+char *Server_Cmd_AUTOAUTH(tClient *Client, char *Args)
+{
+ char *spos = strchr(Args, ' ');
+ if(spos) *spos = '\0'; // Remove characters after the ' '
+
+ // Check if trusted
+ if( !Client->bIsTrusted ) {
+ if(giDebugLevel)
+ printf("Client %i: Untrusted client attempting to AUTOAUTH\n", Client->ID);
+ return strdup("401 Untrusted\n");
+ }
+
+ // Get UID
+ Client->UID = GetUserID( Args );
+ if( Client->UID < 0 ) {
+ if(giDebugLevel)
+ printf("Client %i: Unknown user '%s'\n", Client->ID, Args);
+ return strdup("401 Auth Failure\n");
+ }
+
+ if(giDebugLevel)
+ printf("Client %i: Authenticated as '%s' (%i)\n", Client->ID, Args, Client->UID);
+
+ return strdup("200 Auth OK\n");
+}
+
+// --- INTERNAL HELPERS ---
+// TODO: Move to another file
+void HexBin(uint8_t *Dest, char *Src, int BufSize)
+{
+ int i;
+ for( i = 0; i < BufSize; i ++ )
+ {
+ uint8_t val = 0;
+
+ if('0' <= *Src && *Src <= '9')
+ val |= (*Src-'0') << 4;
+ else if('A' <= *Src && *Src <= 'F')
+ val |= (*Src-'A'+10) << 4;
+ else if('a' <= *Src && *Src <= 'f')
+ val |= (*Src-'a'+10) << 4;
+ else
+ break;
+ Src ++;
+
+ if('0' <= *Src && *Src <= '9')
+ val |= (*Src-'0');
+ else if('A' <= *Src && *Src <= 'F')
+ val |= (*Src-'A'+10);
+ else if('a' <= *Src && *Src <= 'f')
+ val |= (*Src-'a'+10);
+ else
+ break;
+ Src ++;
+
+ Dest[i] = val;
+ }
+ for( ; i < BufSize; i++ )
+ Dest[i] = 0;
+}
+
+/**
+ * \brief Decode a Base64 value
+ */
+int UnBase64(uint8_t *Dest, char *Src, int BufSize)
+{
+ uint32_t val;
+ int i, j;
+ char *start_src = Src;
+
+ for( i = 0; i+2 < BufSize; i += 3 )
+ {
+ val = 0;
+ for( j = 0; j < 4; j++, Src ++ ) {
+ if('A' <= *Src && *Src <= 'Z')
+ val |= (*Src - 'A') << ((3-j)*6);
+ else if('a' <= *Src && *Src <= 'z')
+ val |= (*Src - 'a' + 26) << ((3-j)*6);
+ else if('0' <= *Src && *Src <= '9')
+ val |= (*Src - '0' + 52) << ((3-j)*6);
+ else if(*Src == '+')
+ val |= 62 << ((3-j)*6);
+ else if(*Src == '/')
+ val |= 63 << ((3-j)*6);
+ else if(!*Src)
+ break;
+ else if(*Src != '=')
+ j --; // Ignore invalid characters
+ }
+ Dest[i ] = (val >> 16) & 0xFF;
+ Dest[i+1] = (val >> 8) & 0xFF;
+ Dest[i+2] = val & 0xFF;
+ if(j != 4) break;
+ }
+
+ // Finish things off
+ if(i < BufSize)
+ Dest[i] = (val >> 16) & 0xFF;
+ if(i+1 < BufSize)
+ Dest[i+1] = (val >> 8) & 0xFF;
+
+ return Src - start_src;