Moar server work
[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    UID;
25          int    bIsAuthed;
26 }       tClient;
27
28 // === PROTOTYPES ===
29 void    Server_Start(void);
30 void    Server_HandleClient(int Socket);
31 char    *Server_ParseClientCommand(tClient *Client, char *CommandString);
32 char    *Server_Cmd_USER(tClient *Client, char *Args);
33
34 // === GLOBALS ===
35  int    giServer_Port = 1020;
36  int    giServer_NextClientID = 1;
37 // - Commands
38 struct sClientCommand {
39         char    *Name;
40         char    *(*Function)(tClient *Client, char *Arguments);
41 }       gaServer_Commands[] = {
42         {"USER", Server_Cmd_USER}
43 };
44 #define NUM_COMMANDS    (sizeof(gaServer_Commands)/sizeof(gaServer_Commands[0]))
45
46 // === CODE ===
47 /**
48  * \brief Open listenting socket and serve connections
49  */
50 void Server_Start(void)
51 {
52         // Create Server
53 }
54
55 /**
56  * \brief Reads from a client socket and parses the command strings
57  */
58 void Server_HandleClient(int Socket)
59 {
60         char    inbuf[INPUT_BUFFER_SIZE];
61         char    *buf = inbuf;
62          int    remspace = INPUT_BUFFER_SIZE-1;
63          int    bytes = -1;
64         tClient clientInfo = {0};
65                 
66         // Read from client
67         while( (bytes = recv(Socket, buf, remspace, 0)) > 0 )
68         {
69                 char    *eol, *start;
70                 buf[bytes] = '\0';      // Allow us to use stdlib string functions on it
71                 
72                 // Split by lines
73                 start = inbuf;
74                 while( (eol = strchr(start, '\n')) )
75                 {
76                         char    *ret;
77                         *eol = '\0';
78                         ret = Server_ParseClientCommand(&clientInfo, start);
79                         // `ret` is a string on the heap
80                         send(Socket, ret, strlen(ret), 0);
81                         free(ret);
82                         start = eol + 1;
83                 }
84                 
85                 // Check if there was an incomplete line
86                 if( *start != '\0' ) {
87                          int    tailBytes = bytes - (start-buf);
88                         // Roll back in buffer
89                         memcpy(inbuf, start, tailBytes);
90                         remspace -= tailBytes;
91                         if(remspace == 0) {
92                                 send(Socket, MSG_STR_TOO_LONG, sizeof(MSG_STR_TOO_LONG), 0);
93                                 buf = inbuf;
94                                 remspace = INPUT_BUFFER_SIZE - 1;
95                         }
96                 }
97                 else {
98                         buf = inbuf;
99                         remspace = INPUT_BUFFER_SIZE - 1;
100                 }
101         }
102         
103         // Check for errors
104         if( bytes < 0 ) {
105                 fprintf(stderr, "ERROR: Unable to recieve from client on socket %i\n", Socket);
106                 return ;
107         }
108 }
109
110 /**
111  * \brief Parses a client command and calls the required helper function
112  * \param Client        Pointer to client state structure
113  */
114 char *Server_ParseClientCommand(tClient *Client, char *CommandString)
115 {
116         char    *space, *args;
117          int    i;
118         
119         // Split at first space
120         space = strchr(CommandString, ' ');
121         if(space == NULL) {
122                 args = NULL;
123         }
124         else {
125                 *space = '\0';
126                 args = space + 1;
127         }
128         
129         // Find command
130         for( i = 0; i < NUM_COMMANDS; i++ )
131         {
132                 if(strcmp(CommandString, gaServer_Commands[i].Name) == 0)
133                         return gaServer_Commands[i].Function(Client, args);
134         }
135         
136         return strdup("400      Unknown Command\n");
137 }
138
139 // ---
140 // Commands
141 // ---
142 /**
143  * \brief Set client username
144  */
145 char *Server_Cmd_USER(tClient *Client, char *Args)
146 {
147         char    *ret;
148         
149         // Debug!
150         if( gbDebugLevel )
151                 printf("Client %i authenticating as '%s'\n", Args);
152         
153         // Save username
154         if(Client->Username)
155                 free(Client->Username);
156         Client->Username = strdup(Args);
157         
158         // Create a salt (that changes if the username is changed)
159         if(!Client->Salt)
160                 Client->Salt = malloc(9);
161         Client->Salt[0] = 0x21 + (rand()&0x3F);
162         Client->Salt[1] = 0x21 + (rand()&0x3F);
163         Client->Salt[2] = 0x21 + (rand()&0x3F);
164         Client->Salt[3] = 0x21 + (rand()&0x3F);
165         Client->Salt[4] = 0x21 + (rand()&0x3F);
166         Client->Salt[5] = 0x21 + (rand()&0x3F);
167         Client->Salt[6] = 0x21 + (rand()&0x3F);
168         Client->Salt[7] = 0x21 + (rand()&0x3F);
169         
170         // "100 Salt xxxxXXXX\n"
171         ret = strdup("100 SALT xxxxXXXX\n");
172         sprintf(ret, "100 SALT %s\n", Client->Salt);
173         
174         return ret;
175 }

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