5 #include <sys/socket.h>
18 extern server_config config
;
20 extern session_info
*sessionlist
;
21 extern pthread_mutex_t sessionlist_mutex
;
23 #define BUF_SIZE 1 * 1024
26 // return 1 if enter is pressed
27 static int input (char c
, char *str
, int *pos
, int len
, char *out
, int *out_pos
)
38 if (c
== 8 || c
== 127)
40 if (*pos
> 0) *pos
-= 1;
43 if (*out_pos
< len
) *out_pos
+= 1;
45 else if (*pos
< len
- 1)
48 if (*pos
< len
) *pos
+= 1;
51 if (*out_pos
< len
) *out_pos
+= 1;
59 static int recv_input (session_info
*sess
, char *inp
, int len
)
61 char buf
[BUF_SIZE
], out
[BUF_SIZE
];
69 int n
= recv(sess
->socket
, buf
, BUF_SIZE
, 0);
70 if (n
== -1) return -1;
72 n
= translate_telnet_input(buf
, n
, sess
);
76 for (i
= 0; i
< n
; i
++)
78 if (input(buf
[i
], inp
, &pos
, len
, out
, &opos
))
86 if (send(sess
->socket
, out
, opos
, 0) == -1) return -1;
92 static int print_and_wait (session_info
*sess
, const char *msg
)
97 if (send(sess
->socket
, msg
, strlen(msg
) + 1, 0) == -1) return -1;
101 // wait for a keypress
102 int n
= recv(sess
->socket
, buf
, BUF_SIZE
, 0);
103 if (n
== -1) return -1;
105 if (translate_telnet_input(buf
, n
, sess
) > 0) break;
114 static int register_menu (session_info
*sess
)
116 char data
[REG_INPUTS
][BUF_SIZE
];
118 const char *const prompts
[REG_INPUTS
] =
120 "\r\nUser name (3-16 chars, alphabet and digits only, start with a letter): ",
121 "\r\nPassword (4-16 chars, it is INSECURE, so do not use the same as email): ",
122 "\r\nEmail (optional): "
124 int lengths
[REG_INPUTS
][2] = { {MIN_USER_NAME_LEN
, USER_NAME_LEN
}, {MIN_USER_PASS_LEN
, USER_PASS_LEN
}, {0, USER_EMAIL_LEN
} };
130 while (r
< REG_INPUTS
)
134 if (send(sess
->socket
, prompts
[r
], strlen(prompts
[r
]), 0) == -1) return -1;
136 len
= recv_input(sess
, data
[r
], lengths
[r
][1]);
137 if (len
< 0) return -1;
139 if (len
< lengths
[r
][0])
141 int l
= sprintf(buf
, "\r\nminimum %d symbols, try again: ", lengths
[r
][0]);
142 if (send(sess
->socket
, buf
, l
, 0) == -1) return -1;
150 // try to register using the data
151 id
= add_user(&usr
, data
[0], data
[1], data
[2]);
156 sess
->user_id
= usr
.id
;
157 strcpy (sess
->user_name
, usr
.name
);
159 write_log (LOG_DEBUG
, "registered user %s", usr
.name
);
161 l
= sprintf(buf
, "\r\nRegistration succeded\r\n");
163 if (send(sess
->socket
, buf
, l
, 0) == -1) return -1;
170 l
= sprintf(buf
, "\r\nInvalid name\r\n");
174 l
= sprintf(buf
, "\r\nUser with such name already exists\r\n");
178 write_log (LOG_ERROR
, "user registration failed, error %d", id
);
180 l
= sprintf(buf
, "\r\nRegistration failed\r\n");
183 if (send(sess
->socket
, buf
, l
, 0) == -1) return -1;
186 // wait for a keypress
187 int n
= recv(sess
->socket
, buf
, BUF_SIZE
, 0);
188 if (n
== -1) return -1;
195 static int login_menu (session_info
*sess
)
197 const char login_prompt
[] = "\r\nUser name: ";
198 const char pass_prompt
[] = "\r\nPassword: ";
199 char name
[USER_NAME_LEN
], pass
[USER_PASS_LEN
];
204 if (send(sess
->socket
, login_prompt
, sizeof(login_prompt
) - 1, 0) == -1) return -1;
205 if (recv_input(sess
, name
, USER_NAME_LEN
) < 0) return -1;
207 if (send(sess
->socket
, pass_prompt
, sizeof(pass_prompt
) - 1, 0) == -1) return -1;
208 if (recv_input(sess
, pass
, USER_PASS_LEN
) < 0) return -1;
210 // check user name and password
211 if (check_user_login(&usr
, name
, pass
) < 0)
213 if (print_and_wait(sess
, "\r\nLogin failed\r\n") == -1) return -1;
217 // disallow simultaneous connections
218 if (user_is_connected(usr
.id
))
220 if (print_and_wait(sess
, "\r\nYou are already logged in\r\n") == -1) return -1;
224 sess
->user_id
= usr
.id
;
225 strcpy (sess
->user_name
, usr
.name
);
226 write_log (LOG_DEBUG
, "user %s logs in", usr
.name
);
233 static int passwd_menu (session_info
*sess
)
235 const char old_prompt
[] = "\r\nOld Password: ";
236 const char new_prompt
[] = "\r\nNew Password: ";
237 char pass
[USER_PASS_LEN
];
242 // ask for old password first
243 if (send(sess
->socket
, old_prompt
, sizeof(old_prompt
) - 1, 0) == -1) return -1;
244 if (recv_input(sess
, pass
, USER_PASS_LEN
) < 0) return -1;
246 // check old password
247 if (check_user_login(&usr
, sess
->user_name
, pass
) < 0)
249 if (print_and_wait(sess
, "\r\nWrong password\r\n") == -1) return -1;
253 // ask for new password
254 if (send(sess
->socket
, new_prompt
, sizeof(new_prompt
) - 1, 0) == -1) return -1;
255 len
= recv_input(sess
, pass
, USER_PASS_LEN
);
256 if (len
< 0) return -1;
258 if (len
< MIN_USER_PASS_LEN
)
260 if (print_and_wait(sess
, "\r\nNew password is too short\r\n") == -1) return -1;
264 if (change_password(sess
->user_id
, pass
) == 0)
266 if (print_and_wait(sess
, "\r\nPassword has been changed\r\n") == -1) return -1;
274 static int observe (session_info
*sess
)
276 uint8_t buf
[BUF_SIZE
];
281 int n
= recv(sess
->socket
, buf
, BUF_SIZE
, 0);
282 if (n
== -1) return -1;
284 if (buf
[0] == 'q') break;
292 static int observer_menu (session_info
*sess
)
294 char header
[] = TELNET_CLS
"\tGame\t\t\t\tUser\t\t Idle\t\tTerminal\r\n";
297 int n
, l
, gn
, count
= 0;
303 if (send(sess
->socket
, header
, sizeof(header
) - 1, 0) == -1) return -1;
305 pthread_mutex_lock (&sessionlist_mutex
);
307 for (s
= sessionlist
; s
!= NULL
; s
= s
->next
)
311 if (s
->game
[0] == 0 || s
->observer_count
== -1) continue;
313 idle
= now
- s
->last_activity
;
315 l
= sprintf(buf
, "%3d\t%s", count
+ 1, s
->game
);
316 for (; l
< 4 * 8; l
++) buf
[l
] = ' ';
318 l
+= sprintf(&buf
[l
], "\t%s", s
->user_name
);
319 for (; l
< 6 * 8; l
++) buf
[l
] = ' ';
321 l
+= sprintf(&buf
[l
], "\t%3dm %02ds\t%dx%d\r\n", idle
/ 60, idle
% 60, s
->term_wid
, s
->term_hgt
);
322 if (send(sess
->socket
, buf
, l
, 0) == -1) return -1;
325 pthread_mutex_unlock (&sessionlist_mutex
);
326 l
= sprintf(buf
, "\r\n%d games total\r\n\r\n"
327 "Input number to observe or anything else to return to the main menu\r\n"
328 "You can press 'q' anytime to stop observing\r\n", count
);
329 if (send(sess
->socket
, buf
, l
, 0) == -1) return -1;
333 n
= recv_input(sess
, inp
, 20);
334 if (n
== -1) return -1;
337 if (gn
> 0 && gn
<= count
)
339 // try to observe the specified game
340 if (add_observer(gn
- 1, sess
->socket
))
342 int r
= observe(sess
);
343 stop_observing (sess
);
344 if (r
== -1) return -1;
353 // if score == 1, display highscore list and exit rather than playing
354 static int play_menu (session_info
*sess
, int score
)
356 const char msg
[] = TELNET_CLS
"Select a game by entering its id\r\n";
357 game_info
*gamelist
, *g
;
363 gamelist
= load_gamelist();
365 if (send(sess
->socket
, msg
, sizeof(msg
), 0) == -1)
367 free_gamelist (gamelist
);
371 for (g
= gamelist
; g
!= NULL
; g
= g
->next
)
373 if (score
&& !g
->cmd_score
[0]) continue;
375 int l
= sprintf(buf
, "%8s: %s\r\n", g
->id
, g
->name
);
377 if (send(sess
->socket
, buf
, l
, 0) == -1)
379 free_gamelist (gamelist
);
386 n
= recv_input(sess
, inp
, 20);
387 if (n
== -1) return -1;
389 // find and run game with such id
390 for (g
= gamelist
; g
!= NULL
; g
= g
->next
)
392 if (score
&& !g
->cmd_score
[0]) continue;
394 if (strcmp(inp
, g
->id
) == 0)
396 // hack for displaying score
399 sess
->observer_count
= -1;
400 strcpy (g
->cmd
, g
->cmd_score
);
404 free_gamelist (gamelist
);
406 if (print_and_wait(sess
, "\r\nYou have left the game\r\n") == -1) return -1;
408 if (sess
->observer_count
== -1) sess
->observer_count
= 0;
414 free_gamelist (gamelist
);
421 static void run_editor (session_info
*sess
, const game_info
*g
, int i
)
428 // prefix with game directory name
429 if (g
->dir
[0]) dirln
= sprintf(name
, "%s/", g
->dir
);
432 // copy file name to bufer
433 strcat (&name
[dirln
], g
->files
[i
].copy
);
435 // set up a a game struct for editor
436 memset (&ed
, 0, sizeof(game_info
));
437 strcpy (ed
.id
, "editor");
438 strcpy (ed
.name
, "editor");
440 snprintf (ed
.cmd
, GAME_COMMAND_LEN
, "%s %s", config
.editor
, name
);
443 sess
->observer_count
= -1;
445 run_game (sess
, &ed
);
447 // enable observing again
448 sess
->observer_count
= 0;
453 static int edit_menu (session_info
*sess
)
455 const char msg
[] = TELNET_CLS
"Select a file to edit by entering its number\r\n";
456 game_info
*gamelist
, *g
;
457 char buf
[BUF_SIZE
], name
[BUF_SIZE
];
462 gamelist
= load_gamelist();
464 if (send(sess
->socket
, msg
, sizeof(msg
), 0) == -1)
466 free_gamelist (gamelist
);
476 for (i
= 0; i
< GAME_FILES
; i
++)
478 if (!g
->files
[i
].file
[0]) break;
479 if (!g
->files
[i
].allow_edit
) continue;
481 // prefix with game directory name, expanding user name
484 str_replace (name
, BUF_SIZE
, g
->dir
, "$USER$", sess
->user_name
);
486 dirln
= strlen(name
);
489 // copy file name to bufer, expanding user name
490 str_replace (&name
[dirln
], BUF_SIZE
, g
->files
[i
].copy
, "$USER$", sess
->user_name
);
491 // file does not exist
492 if (stat(name
, &st
) != 0) continue;
494 l
= sprintf(buf
, "%3d: [%s] %s\r\n", idx
, g
->name
, name
);
496 if (send(sess
->socket
, buf
, l
, 0) == -1)
498 free_gamelist (gamelist
);
502 // we (ab)use the allow_edit to store index
503 // to find the file when the user enters a number
504 g
->files
[i
].allow_edit
= 0x80000000 | idx
;
514 n
= recv_input(sess
, inp
, 20);
515 if (n
== -1) return -1;
517 n
= 0x80000000 | atoi(inp
);
519 // find and run game with such id
525 for (i
= 0; i
< GAME_FILES
; i
++)
527 if (!g
->files
[i
].file
[0]) break;
528 if (g
->files
[i
].allow_edit
!= n
) continue;
530 run_editor (sess
, g
, i
);
532 free_gamelist (gamelist
);
540 free_gamelist (gamelist
);
547 static int print_main_menu (session_info
*sess
)
550 const char *reg
, *name
, *play
;
553 // XXX this is ugly, clean it up someday
555 if (sess
->user_id
< 0)
557 reg
= "r) Register as a user (needed in order to play)";
558 name
= "player of roguelikes";
559 play
= "l) Log in to play";
563 reg
= "c) Change password";
564 name
= sess
->user_name
;
565 play
= "p) Play\r\n\te) Edit game options files";
568 int l
= snprintf(msg
, BUF_SIZE
, TELNET_CLS
"Welcome to the roguelike server, %s!\r\n"
570 "\to) Observe running games\r\n"
571 "\ts) View high-score lists\r\n"
573 "\tq) Leave\r\n", name
, play
, reg
);
575 if (send(sess
->socket
, msg
, l
, 0) == -1) return -1;
581 int main_menu (session_info
*sess
)
583 if (print_main_menu(sess
) == -1) return -1;
588 int n
= recv(sess
->socket
, buf
, BUF_SIZE
, 0);
589 if (n
== -1) return -1;
591 n
= translate_telnet_input(buf
, n
, sess
);
594 if (buf
[0] == 'p' && sess
->user_id
>= 0)
596 if (play_menu(sess
, 0) == -1) return -1;
597 if (print_main_menu(sess
) == -1) return -1;
602 if (play_menu(sess
, 1) == -1) return -1;
603 if (print_main_menu(sess
) == -1) return -1;
606 if (buf
[0] == 'e' && sess
->user_id
>= 0)
608 if (edit_menu(sess
) == -1) return -1;
609 if (print_main_menu(sess
) == -1) return -1;
612 if (buf
[0] == 'l' && sess
->user_id
< 0)
614 if (login_menu(sess
) == -1) return -1;
615 if (print_main_menu(sess
) == -1) return -1;
618 if (buf
[0] == 'r' && sess
->user_id
< 0)
620 if (register_menu(sess
) == -1) return -1;
621 if (print_main_menu(sess
) == -1) return -1;
624 if (buf
[0] == 'c' && sess
->user_id
>= 0)
626 if (passwd_menu(sess
) == -1) return -1;
627 if (print_main_menu(sess
) == -1) return -1;
632 if (observer_menu(sess
) == -1) return -1;
633 if (print_main_menu(sess
) == -1) return -1;
636 if (buf
[0] == 'q') return 0;