Server - Added bac to mark zero priced slots as erroring
[tpg/opendispense2.git] / src / server / server.c
index 0a1a718..b7ca068 100644 (file)
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <unistd.h>
+#include <fcntl.h>     // O_*
 #include <string.h>
 #include <limits.h>
 #include <stdarg.h>
-#include <signal.h>
+#include <signal.h>    // Signal handling
+#include <ident.h>     // AUTHIDENT
+#include <time.h>      // time(2)
 
 #define        DEBUG_TRACE_CLIENT      0
+#define HACK_NO_REFUNDS        1
 
 // Statistics
 #define MAX_CONNECTION_QUEUE   5
@@ -31,6 +35,9 @@
 
 #define MSG_STR_TOO_LONG       "499 Command too long (limit "EXPSTR(INPUT_BUFFER_SIZE)")\n"
 
+#define IDENT_TRUSTED_NETWORK 0x825F0D00
+#define IDENT_TRUSTED_NETMASK 0xFFFFFFC0
+
 // === TYPES ===
 typedef struct sClient
 {
@@ -56,6 +63,7 @@ void  Server_ParseClientCommand(tClient *Client, char *CommandString);
 void   Server_Cmd_USER(tClient *Client, char *Args);
 void   Server_Cmd_PASS(tClient *Client, char *Args);
 void   Server_Cmd_AUTOAUTH(tClient *Client, char *Args);
+void   Server_Cmd_AUTHIDENT(tClient *Client, char *Args);
 void   Server_Cmd_SETEUSER(tClient *Client, char *Args);
 void   Server_Cmd_ENUMITEMS(tClient *Client, char *Args);
 void   Server_Cmd_ITEMINFO(tClient *Client, char *Args);
@@ -70,6 +78,7 @@ void  Server_Cmd_USERINFO(tClient *Client, char *Args);
 void   _SendUserInfo(tClient *Client, int UserID);
 void   Server_Cmd_USERADD(tClient *Client, char *Args);
 void   Server_Cmd_USERFLAGS(tClient *Client, char *Args);
+void   Server_Cmd_UPDATEITEM(tClient *Client, char *Args);
 // --- Helpers ---
 void   Debug(tClient *Client, const char *Format, ...);
  int   sendf(int Socket, const char *Format, ...);
@@ -85,6 +94,7 @@ const struct sClientCommand {
        {"USER", Server_Cmd_USER},
        {"PASS", Server_Cmd_PASS},
        {"AUTOAUTH", Server_Cmd_AUTOAUTH},
+       {"AUTHIDENT", Server_Cmd_AUTHIDENT},
        {"SETEUSER", Server_Cmd_SETEUSER},
        {"ENUM_ITEMS", Server_Cmd_ENUMITEMS},
        {"ITEM_INFO", Server_Cmd_ITEMINFO},
@@ -97,7 +107,8 @@ const struct sClientCommand {
        {"ENUM_USERS", Server_Cmd_ENUMUSERS},
        {"USER_INFO", Server_Cmd_USERINFO},
        {"USER_ADD", Server_Cmd_USERADD},
-       {"USER_FLAGS", Server_Cmd_USERFLAGS}
+       {"USER_FLAGS", Server_Cmd_USERFLAGS},
+       {"UPDATE_ITEM", Server_Cmd_UPDATEITEM}
 };
 #define NUM_COMMANDS   ((int)(sizeof(gaServer_Commands)/sizeof(gaServer_Commands[0])))
 
@@ -145,7 +156,7 @@ void Server_Start(void)
                return ;
        }
 
-#if 0
+       // Fork into background
        if( gbServer_RunInBackground )
        {
                int pid = fork();
@@ -156,14 +167,26 @@ void Server_Start(void)
                }
                if( pid != 0 ) {
                        // Parent, quit
+                       printf("Forked child %i\n", pid);
                        exit(0);
                }
-               // In child, sort out stdin/stdout
-               reopen(0, "/dev/null", O_READ);
-               reopen(1, gsServer_LogFile, O_CREAT|O_APPEND);
-               reopen(2, gsServer_ErrorLog, O_CREAT|O_APPEND);
+               // In child
+               // - Sort out stdin/stdout
+               #if 0
+               dup2( open("/dev/null", O_RDONLY, 0644), STDIN_FILENO );
+               dup2( open(gsServer_LogFile, O_CREAT|O_APPEND, 0644), STDOUT_FILENO );
+               dup2( open(gsServer_ErrorLog, O_CREAT|O_APPEND, 0644), STDERR_FILENO );
+               #else
+               freopen("/dev/null", "r", stdin);
+               freopen(gsServer_LogFile, "a", stdout);
+               freopen(gsServer_ErrorLog, "a", stderr);
+               fprintf(stdout, "OpenDispense 2 Server Started at %lld\n", (long long)time(NULL));
+               fprintf(stderr, "OpenDispense 2 Server Started at %lld\n", (long long)time(NULL));
+               #endif
        }
-#endif
+
+       // Start the helper thread
+       StartPeriodicThread();
        
        // Listen
        if( listen(giServer_Socket, MAX_CONNECTION_QUEUE) < 0 ) {
@@ -227,12 +250,14 @@ void Server_Start(void)
                        {
                        case 0x7F000001:        // 127.0.0.1    localhost
                //      case 0x825F0D00:        // 130.95.13.0
+                       case 0x825F0D04:        // 130.95.13.4  merlo
+               //      case 0x825F0D05:        // 130.95.13.5  heathred (MR)
                        case 0x825F0D07:        // 130.95.13.7  motsugo
                        case 0x825F0D11:        // 130.95.13.17 mermaid
                        case 0x825F0D12:        // 130.95.13.18 mussel
                        case 0x825F0D17:        // 130.95.13.23 martello
                        case 0x825F0D2A:        // 130.95.13.42 meersau
-                       case 0x825F0D42:        // 130.95.13.66 heathred
+               //      case 0x825F0D42:        // 130.95.13.66 heathred (Clubroom)
                                bTrusted = 1;
                                break;
                        default:
@@ -346,9 +371,7 @@ void Server_ParseClientCommand(tClient *Client, char *CommandString)
        if( Server_int_ParseArgs(1, CommandString, &command, &args, NULL) )
        {
                if( command == NULL )   return ;
-//             printf("command=%s, args=%s\n", command, args);
                // Is this an error? (just ignore for now)
-               //args = "";
        }
        
        
@@ -477,7 +500,7 @@ void Server_Cmd_AUTOAUTH(tClient *Client, char *Args)
        }
        
        // Get UID
-       Client->UID = Bank_GetAcctByName( username );   
+       Client->UID = Bank_GetAcctByName( username, 0 );        
        if( Client->UID < 0 ) {
                if(giDebugLevel)
                        Debug(Client, "Unknown user '%s'", username);
@@ -502,6 +525,11 @@ void Server_Cmd_AUTOAUTH(tClient *Client, char *Args)
                return ;
        }
 
+       // Save username
+       if(Client->Username)
+               free(Client->Username);
+       Client->Username = strdup(username);
+
        Client->bIsAuthed = 1;
        
        if(giDebugLevel)
@@ -510,6 +538,95 @@ void Server_Cmd_AUTOAUTH(tClient *Client, char *Args)
        sendf(Client->Socket, "200 Auth OK\n");
 }
 
+/**
+ * \brief Authenticate as a user using the IDENT protocol
+ *
+ * Usage: AUTHIDENT
+ */
+void Server_Cmd_AUTHIDENT(tClient *Client, char *Args)
+{
+       char    *username;
+        int    userflags;
+       const int ident_timeout = 5;
+       socklen_t len;
+       struct sockaddr_in client_addr;
+       uint32_t  client_ip;
+
+       if( Args != NULL && strlen(Args) ) {
+               sendf(Client->Socket, "407 AUTHIDENT takes no arguments\n");
+               return ;
+       }
+
+       // Check if trusted (only works with INET sockets at present)
+       len = sizeof(client_addr);
+       if( getpeername(Client->Socket, (struct sockaddr*)&client_addr, &len) == -1 ) {
+               Debug(Client, "500 getpeername() failed\n");
+               perror("Getting AUTHIDENT peer name");
+               sendf(Client->Socket, "500 getpeername() failed\n");
+               return ;
+       }
+
+       client_ip = client_addr.sin_addr.s_addr;
+       if(giDebugLevel >= 2) {
+               Debug(Client, "client_ip = %x, ntohl(client_ip) = %x", client_ip, ntohl(client_ip));
+       }
+       if( ntohl(client_ip) != 0x7F000001 && (ntohl(client_ip) & IDENT_TRUSTED_NETMASK) != IDENT_TRUSTED_NETWORK ) {
+                       if(giDebugLevel)
+                               Debug(Client, "Untrusted client attempting to AUTHIDENT");
+                       sendf(Client->Socket, "401 Untrusted\n");
+                       return ;
+       }
+
+       // Get username via IDENT
+       username = ident_id(Client->Socket, ident_timeout);
+       if( !username ) {
+               sendf(Client->Socket, "403 Authentication failure: IDENT auth timed out\n");
+               return ;
+       }
+
+       // Get UID
+       Client->UID = Bank_GetAcctByName( username, 0 );
+       if( Client->UID < 0 ) {
+               if(giDebugLevel)
+                       Debug(Client, "Unknown user '%s'", username);
+               sendf(Client->Socket, "403 Authentication failure: unknown account\n");
+               free(username);
+               return ;
+       }
+
+       userflags = Bank_GetFlags(Client->UID);
+       // You can't be an internal account
+       if( userflags & USER_FLAG_INTERNAL ) {
+               if(giDebugLevel)
+                       Debug(Client, "IDENT auth as '%s', not allowed", username);
+               Client->UID = -1;
+               sendf(Client->Socket, "403 Authentication failure: that account is internal\n");
+               free(username);
+               return ;
+       }
+
+       // Disabled accounts
+       if( userflags & USER_FLAG_DISABLED ) {
+               Client->UID = -1;
+               sendf(Client->Socket, "403 Authentication failure: account disabled\n");
+               free(username);
+               return ;
+       }
+
+       // Save username
+       if(Client->Username)
+               free(Client->Username);
+       Client->Username = strdup(username);
+
+       Client->bIsAuthed = 1;
+
+       if(giDebugLevel)
+               Debug(Client, "IDENT authenticated as '%s' (%i)", username, Client->UID);
+       free(username);
+
+       sendf(Client->Socket, "200 Auth OK\n");
+}
+
 /**
  * \brief Set effective user
  */
@@ -528,6 +645,12 @@ void Server_Cmd_SETEUSER(tClient *Client, char *Args)
                sendf(Client->Socket, "407 SETEUSER expects an argument\n");
                return ;
        }
+       
+       // Check authentication
+       if( !Client->bIsAuthed ) {
+               sendf(Client->Socket, "401 Not Authenticated\n");
+               return ;
+       }
 
        // Check user permissions
        userFlags = Bank_GetFlags(Client->UID);
@@ -537,7 +660,7 @@ void Server_Cmd_SETEUSER(tClient *Client, char *Args)
        }
        
        // Set id
-       Client->EffectiveUID = Bank_GetAcctByName(username);
+       Client->EffectiveUID = Bank_GetAcctByName(username, 0);
        if( Client->EffectiveUID == -1 ) {
                sendf(Client->Socket, "404 User not found\n");
                return ;
@@ -559,6 +682,13 @@ void Server_Cmd_SETEUSER(tClient *Client, char *Args)
                        return ;
                }
        }
+
+       // Disabled accounts
+       if( userFlags & USER_FLAG_DISABLED ) {
+               Client->UID = -1;
+               sendf(Client->Socket, "403 Account disabled\n");
+               return ;
+       }
        
        sendf(Client->Socket, "200 User set\n");
 }
@@ -583,6 +713,12 @@ void Server_int_SendItem(tClient *Client, tItem *Item)
                }
        }
        
+       if( Item->Price == 0 )
+               status = "error";
+       // KNOWN HACK: Naming a slot 'dead' disables it
+       if( strcmp(Item->Name, "dead") == 0 )
+               status = "sold";        // Another status?
+       
        sendf(Client->Socket,
                "202 Item %s:%i %s %i %s\n",
                Item->Handler->Name, Item->ID, status, Item->Price, Item->Name
@@ -713,7 +849,7 @@ void Server_Cmd_DISPENSE(tClient *Client, char *Args)
        case 1: sendf(Client->Socket, "501 Unable to dispense\n");      return ;
        case 2: sendf(Client->Socket, "402 Poor You\n");        return ;
        default:
-               sendf(Client->Socket, "500 Dispense Error\n");
+               sendf(Client->Socket, "500 Dispense Error (%i)\n", ret);
                return ;
        }
 }
@@ -742,7 +878,7 @@ void Server_Cmd_REFUND(tClient *Client, char *Args)
                return ;
        }
 
-       uid = Bank_GetAcctByName(username);
+       uid = Bank_GetAcctByName(username, 0);
        if( uid == -1 ) {
                sendf(Client->Socket, "404 Unknown user\n");
                return ;
@@ -777,6 +913,7 @@ void Server_Cmd_GIVE(tClient *Client, char *Args)
                sendf(Client->Socket, "407 GIVE takes only 3 arguments\n");
                return ;
        }
+       
        // Check for authed
        if( !Client->bIsAuthed ) {
                sendf(Client->Socket, "401 Not Authenticated\n");
@@ -784,7 +921,7 @@ void Server_Cmd_GIVE(tClient *Client, char *Args)
        }
 
        // Get recipient
-       uid = Bank_GetAcctByName(recipient);
+       uid = Bank_GetAcctByName(recipient, 0);
        if( uid == -1 ) {
                sendf(Client->Socket, "404 Invalid target user\n");
                return ;
@@ -894,8 +1031,26 @@ void Server_Cmd_ADD(tClient *Client, char *Args)
                return ;
        }
 
+       #if !ROOT_CAN_ADD
+       if( strcmp( Client->Username, "root" ) == 0 ) {
+               // Allow adding for new users
+               if( strcmp(reason, "treasurer: new user") != 0 ) {
+                       sendf(Client->Socket, "403 Root may not add\n");
+                       return ;
+               }
+       }
+       #endif
+
+       #if HACK_NO_REFUNDS
+       if( strstr(reason, "refund") != NULL || strstr(reason, "misdispense") != NULL )
+       {
+               sendf(Client->Socket, "499 Don't use `dispense acct` for refunds, use `dispense refund` (and `dispense -G` to get item IDs)\n");
+               return ;
+       }
+       #endif
+
        // Get recipient
-       uid = Bank_GetAcctByName(user);
+       uid = Bank_GetAcctByName(user, 0);
        if( uid == -1 ) {
                sendf(Client->Socket, "404 Invalid user\n");
                return ;
@@ -956,7 +1111,7 @@ void Server_Cmd_SET(tClient *Client, char *Args)
        }
 
        // Get recipient
-       uid = Bank_GetAcctByName(user);
+       uid = Bank_GetAcctByName(user, 0);
        if( uid == -1 ) {
                sendf(Client->Socket, "404 Invalid user\n");
                return ;
@@ -1169,7 +1324,7 @@ void Server_Cmd_USERINFO(tClient *Client, char *Args)
        if( giDebugLevel )      Debug(Client, "User Info '%s'", user);
        
        // Get recipient
-       uid = Bank_GetAcctByName(user);
+       uid = Bank_GetAcctByName(user, 0);
        
        if( giDebugLevel >= 2 ) Debug(Client, "uid = %i", uid);
        if( uid == -1 ) {
@@ -1224,6 +1379,12 @@ void Server_Cmd_USERADD(tClient *Client, char *Args)
                return ;
        }
        
+       // Check authentication
+       if( !Client->bIsAuthed ) {
+               sendf(Client->Socket, "401 Not Authenticated\n");
+               return ;
+       }
+       
        // Check permissions
        if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN) ) {
                sendf(Client->Socket, "403 Not a coke admin\n");
@@ -1247,13 +1408,22 @@ void Server_Cmd_USERADD(tClient *Client, char *Args)
 
 void Server_Cmd_USERFLAGS(tClient *Client, char *Args)
 {
-       char    *username, *flags;
+       char    *username, *flags, *reason=NULL;
         int    mask=0, value=0;
         int    uid;
        
        // Parse arguments
-       if( Server_int_ParseArgs(0, Args, &username, &flags, NULL) ) {
-               sendf(Client->Socket, "407 USER_FLAGS takes 2 arguments\n");
+       if( Server_int_ParseArgs(1, Args, &username, &flags, &reason, NULL) ) {
+               if( !flags ) {
+                       sendf(Client->Socket, "407 USER_FLAGS takes at least 2 arguments\n");
+                       return ;
+               }
+               reason = "";
+       }
+       
+       // Check authentication
+       if( !Client->bIsAuthed ) {
+               sendf(Client->Socket, "401 Not Authenticated\n");
                return ;
        }
        
@@ -1264,7 +1434,7 @@ void Server_Cmd_USERFLAGS(tClient *Client, char *Args)
        }
        
        // Get UID
-       uid = Bank_GetAcctByName(username);
+       uid = Bank_GetAcctByName(username, 0);
        if( uid == -1 ) {
                sendf(Client->Socket, "404 User '%s' not found\n", username);
                return ;
@@ -1280,11 +1450,60 @@ void Server_Cmd_USERFLAGS(tClient *Client, char *Args)
        
        // Apply flags
        Bank_SetFlags(uid, mask, value);
+
+       // Log the change
+       Log_Info("Updated '%s' with flag set '%s' by '%s' - Reason: %s",
+               username, flags, Client->Username, reason);
        
        // Return OK
        sendf(Client->Socket, "200 User Updated\n");
 }
 
+void Server_Cmd_UPDATEITEM(tClient *Client, char *Args)
+{
+       char    *itemname, *price_str, *description;
+        int    price;
+       tItem   *item;
+       
+       if( Server_int_ParseArgs(1, Args, &itemname, &price_str, &description, NULL) ) {
+               sendf(Client->Socket, "407 UPDATE_ITEM takes 3 arguments\n");
+               return ;
+       }
+       
+       if( !Client->bIsAuthed ) {
+               sendf(Client->Socket, "401 Not Authenticated\n");
+               return ;
+       }
+
+       // Check user permissions
+       if( !(Bank_GetFlags(Client->UID) & (USER_FLAG_COKE|USER_FLAG_ADMIN))  ) {
+               sendf(Client->Socket, "403 Not in coke\n");
+               return ;
+       }
+       
+       item = _GetItemFromString(itemname);
+       if( !item ) {
+               // TODO: Create item?
+               sendf(Client->Socket, "406 Bad Item ID\n");
+               return ;
+       }
+       
+       price = atoi(price_str);
+       if( price <= 0 && price_str[0] != '0' ) {
+               sendf(Client->Socket, "407 Invalid price set\n");
+       }
+       
+       switch( DispenseUpdateItem( Client->UID, item, description, price ) )
+       {
+       case 0:
+               // Return OK
+               sendf(Client->Socket, "200 Item updated\n");
+               break;
+       default:
+               break;
+       }
+}
+
 // --- INTERNAL HELPERS ---
 void Debug(tClient *Client, const char *Format, ...)
 {

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