Compile fixes
[tpg/opendispense2.git] / server / src / server.c
1 /*
2  * OpenDispense 2 
3  * UCC (University [of WA] Computer Club) Electronic Accounting System
4  *
5  * server.c - Client Server Code
6  *
7  * This file is licenced under the 3-clause BSD Licence. See the file
8  * COPYING for full details.
9  */
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include "common.h"
13 #include <sys/socket.h>
14 #include <string.h>
15
16 #define MAX_CONNECTION_QUEUE    5
17 #define INPUT_BUFFER_SIZE       128
18
19 #define MSG_STR_TOO_LONG        "499 Command too long (limit "EXPSTR(INPUT_BUFFER_SIZE)")\n"
20
21 // === TYPES ===
22 typedef struct sClient
23 {
24          int    ID;     // Client ID
25         
26         char    *Username;
27         char    Salt[9];
28         
29          int    UID;
30          int    bIsAuthed;
31 }       tClient;
32
33 // === PROTOTYPES ===
34 void    Server_Start(void);
35 void    Server_HandleClient(int Socket);
36 char    *Server_ParseClientCommand(tClient *Client, char *CommandString);
37 char    *Server_Cmd_USER(tClient *Client, char *Args);
38
39 // === GLOBALS ===
40  int    giServer_Port = 1020;
41  int    giServer_NextClientID = 1;
42 // - Commands
43 struct sClientCommand {
44         char    *Name;
45         char    *(*Function)(tClient *Client, char *Arguments);
46 }       gaServer_Commands[] = {
47         {"USER", Server_Cmd_USER}
48 };
49 #define NUM_COMMANDS    (sizeof(gaServer_Commands)/sizeof(gaServer_Commands[0]))
50
51 // === CODE ===
52 /**
53  * \brief Open listenting socket and serve connections
54  */
55 void Server_Start(void)
56 {
57         // Create Server
58 }
59
60 /**
61  * \brief Reads from a client socket and parses the command strings
62  */
63 void Server_HandleClient(int Socket)
64 {
65         char    inbuf[INPUT_BUFFER_SIZE];
66         char    *buf = inbuf;
67          int    remspace = INPUT_BUFFER_SIZE-1;
68          int    bytes = -1;
69         tClient clientInfo = {0};
70         
71         // Initialise Client info
72         clientInfo.ID = giServer_NextClientID ++;
73                 
74         // Read from client
75         /*
76          * Notes:
77          * - The `buf` and `remspace` variables allow a line to span several
78          *   calls to recv(), if a line is not completed in one recv() call
79          *   it is saved to the beginning of `inbuf` and `buf` is updated to
80          *   the end of it.
81          */
82         while( (bytes = recv(Socket, buf, remspace, 0)) > 0 )
83         {
84                 char    *eol, *start;
85                 buf[bytes] = '\0';      // Allow us to use stdlib string functions on it
86                 
87                 // Split by lines
88                 start = inbuf;
89                 while( (eol = strchr(start, '\n')) )
90                 {
91                         char    *ret;
92                         *eol = '\0';
93                         ret = Server_ParseClientCommand(&clientInfo, start);
94                         // `ret` is a string on the heap
95                         send(Socket, ret, strlen(ret), 0);
96                         free(ret);
97                         start = eol + 1;
98                 }
99                 
100                 // Check if there was an incomplete line
101                 if( *start != '\0' ) {
102                          int    tailBytes = bytes - (start-buf);
103                         // Roll back in buffer
104                         memcpy(inbuf, start, tailBytes);
105                         remspace -= tailBytes;
106                         if(remspace == 0) {
107                                 send(Socket, MSG_STR_TOO_LONG, sizeof(MSG_STR_TOO_LONG), 0);
108                                 buf = inbuf;
109                                 remspace = INPUT_BUFFER_SIZE - 1;
110                         }
111                 }
112                 else {
113                         buf = inbuf;
114                         remspace = INPUT_BUFFER_SIZE - 1;
115                 }
116         }
117         
118         // Check for errors
119         if( bytes < 0 ) {
120                 fprintf(stderr, "ERROR: Unable to recieve from client on socket %i\n", Socket);
121                 return ;
122         }
123 }
124
125 /**
126  * \brief Parses a client command and calls the required helper function
127  * \param Client        Pointer to client state structure
128  */
129 char *Server_ParseClientCommand(tClient *Client, char *CommandString)
130 {
131         char    *space, *args;
132          int    i;
133         
134         // Split at first space
135         space = strchr(CommandString, ' ');
136         if(space == NULL) {
137                 args = NULL;
138         }
139         else {
140                 *space = '\0';
141                 args = space + 1;
142         }
143         
144         // Find command
145         for( i = 0; i < NUM_COMMANDS; i++ )
146         {
147                 if(strcmp(CommandString, gaServer_Commands[i].Name) == 0)
148                         return gaServer_Commands[i].Function(Client, args);
149         }
150         
151         return strdup("400      Unknown Command\n");
152 }
153
154 // ---
155 // Commands
156 // ---
157 /**
158  * \brief Set client username
159  */
160 char *Server_Cmd_USER(tClient *Client, char *Args)
161 {
162         char    *ret;
163         
164         // Debug!
165         if( giDebugLevel )
166                 printf("Client %i authenticating as '%s'\n", Client->ID, Args);
167         
168         // Save username
169         if(Client->Username)
170                 free(Client->Username);
171         Client->Username = strdup(Args);
172         
173         // Create a salt (that changes if the username is changed)
174         Client->Salt[0] = 0x21 + (rand()&0x3F);
175         Client->Salt[1] = 0x21 + (rand()&0x3F);
176         Client->Salt[2] = 0x21 + (rand()&0x3F);
177         Client->Salt[3] = 0x21 + (rand()&0x3F);
178         Client->Salt[4] = 0x21 + (rand()&0x3F);
179         Client->Salt[5] = 0x21 + (rand()&0x3F);
180         Client->Salt[6] = 0x21 + (rand()&0x3F);
181         Client->Salt[7] = 0x21 + (rand()&0x3F);
182         
183         // "100 Salt xxxxXXXX\n"
184         ret = strdup("100 SALT xxxxXXXX\n");
185         sprintf(ret, "100 SALT %s\n", Client->Salt);
186         
187         return ret;
188 }

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