Initial Commit

This commit is contained in:
Andrew Pamment 2016-03-22 11:48:59 +10:00
commit 29f158cb2e
15 changed files with 1249 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.o
*.sq3
*.core

14
Makefile Normal file
View File

@ -0,0 +1,14 @@
CC=cc
CFLAGS=-I/usr/local/include
DEPS = bbs.h
OBJ = inih/ini.o bbs.o main.o users.o main_menu.o
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
magicka: $(OBJ)
$(CC) -o magicka -o $@ $^ $(CFLAGS) -L/usr/local/lib -lsqlite3
.PHONY: clean
clean:
rm -f $(OBJ) magicka

17
ansis/issue.ans Normal file
View File

@ -0,0 +1,17 @@
[?7h
<36><6D><36><31>
<36><6D><36><31><36><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <36><6D><36><31><36><6D> <34><6D><36><31><36><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <36><6D> <31><43><36><31><36><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <36><6D><36><31><37><43><36><31><36><6D> 
<36><6D><36><31><32><43><36><6D> <36><6D><36><6D> <36><6D><36><31><32><43> <31><43><36><31><32><43><36><6D> <20><><EFBFBD><EFBFBD> <36><6D><36><31><32><43><36><6D> <36><6D><36><31><32><43><36><6D> <36><6D><36><31><32><43> 
<36><6D><36><31><32><43><36><31><32><43><36><31><31><43><36><31><32><43><36><31><31><43><36><31><32><43><36><31><31><43><36><31><31><43><36><31><37><43><36><31><32><43><36><31><31><43><36><31><32><43><36><31>
<36><6D><36><31><32><43><36><31><32><43><36><31><31><43><36><31><32><43><36><31><31><43><36><31><32><43><36><31><31><43><36><31><31><43><36><31><37><43><36><31><32><43><36><31><31><43><36><31><32><43><36><31>
<36><6D><36><31><32><43><36><31><32><43><36><31><31><43><36><31><32><43><36><31><31><43><36><31><32><43><36><31><31><43><36><31><31><43><36><31><32><43><36><31><31><43><36><31><32><43><36><31><31><43><36><31><32><43><36><31>
<36><6D><36><31><32><43><36><31><32><43><36><31><31><43><36><31><30><6D><36><6D><36><31><31><43><36><31><32><43><36><31><31><43><36><31><31><43><36><31><32><43><36><31><31><43><36><31><32><43><36><31><31><43><36><31><30><6D><36><6D><36><31>
<36><6D><36><31><32><43><36><31><32><43><36><31><31><43><36><31><32><43><36><31><31><43><36><31><30><6D><36><6D><EFBFBD><EFBFBD><31><43><36><31><31><43><36><31><30><6D><36><6D><36><31><31><43><36><31><30><6D><36><6D><34><36> <36><6D><36><31><32><43><36><31>
<30><34><6D><34><36> <34><6D><34><36> <36><30><6D> <36><30><6D><35> <36><6D><36><31><33><43><36><31><31><30><6D> <36><30><6D>
<36><6D><EFBFBD><EFBFBD><EFBFBD><31><6D><36><31><6D><EFBFBD><36><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <34><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD><36><6D><36><30> <36><6D><31><6D><EFBFBD><36><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><31><6D><EFBFBD><36><6D><EFBFBD> <36><6D><36><31><36><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
Welcome To...<34><6D> <20><><36><6D><30>
Another Fine Magicka BBS
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><31><6D><EFBFBD><EFBFBD><36><31><6D><36><6D><31><36><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><31><36><6D><EFBFBD><31><6D><EFBFBD><EFBFBD><EFBFBD><36><31><36><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><31><36><31><6D><36><31><36><6D><EFBFBD><31><6D><EFBFBD><36><6D><EFBFBD><EFBFBD><EFBFBD><31><6D><EFBFBD><EFBFBD><36><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD>


15
ansis/mainmenu.ans Normal file
View File

@ -0,0 +1,15 @@
[?7h
ワワワワワワワワワワワ ワワワワワワ ワワ ワワワワワワ ワワワワワワワワワワワ ワワワワワワワ ワワワワワワ ワワ ワ ワワ
イ゚ーワ イ゚ーワ イロイ゚ー イロロロイ゚ーワ イロイ゚ーワ イ゚ーワ イロイ゚ワワ ゚゚ イ゚ーワ イロイ゚ロ ロイ
ーイ イ ーイ イ ア゚ーイ゚゚゚ア゚イイ ーイ イ ア゚ーイ イ ーイ イ ア゚アイワー イ゚ーイ イ ア゚ーイワ゚ イ 
゚゚ ゚ ゚゚ ゚ ーロ ゚゚ ゚ ーロ ゚゚ ゚゚ ゚ ーロ ゚゚ ゚ ゚゚ ゚ ーロ ゚゚゚゚゚゚ ゚゚ ゚ ーロ ゚゚゚゚゚゚
トトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトト
M. Message Areasウ L. BBS Listウ
T. File Areasウ C. Chat Systemウ
B. Bulletinsウ U. User Listウ
O. Online Gamesウ 1. Last 10 Callersウ
ウウ
ウウ
ウ G. Goodbye (Log Off)ウ
トトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトト


19
ansis/newuser.ans Normal file
View File

@ -0,0 +1,19 @@
[?7hÜÜÜÜÜÜ ÜÜÜÜÜÜÜ ÜÜ ÜÜ ÛÛ ÜÜ Ü ÜÜ ÜÜÜÜÜÜÜ ÜÜÜÜÜÜÜ ÜÜÜÜÜÜ
²ß°Ü ²Û²ßÜÜ ßß ÛÛ°Û ²Û² ÛÛ²ßÛ Û²²ÛÜÜÜÜÜ ²ßÜÜ ßß ÛÛ°Ü ²Û
°² ² ±ß±²Ü° ²ß²² ßܱßßܲ² °²Üß ² ÜÜ ²ß±²Ü° ²ß²² ² °ß
ßß ß °Û ßßßßßß ßßßßßßÜßßßß ßßßßßß ßßßßßß° ßßßßßß ßß°
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
This is where you would put the information you want to give
people before they sign up.
For Example:
Please use real information, else I will delete your
account.
Do not hack the BBS, or I will send my flying monkeys
after you.
Be nice.


333
bbs.c Normal file
View File

@ -0,0 +1,333 @@
#include <stdio.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <signal.h>
#include "inih/ini.h"
#include "bbs.h"
int mynode;
struct bbs_config conf;
struct user_record *gUser;
int gSocket;
int usertimeout;
void timer_handler(int signum) {
if (signum == SIGALRM) {
if (gUser != NULL) {
gUser->timeleft--;
if (gUser->timeleft <= 0) {
s_putstring(gSocket, "\r\n\r\nSorry, you're out of time today..\r\n");
disconnect(gSocket);
}
}
usertimeout--;
if (usertimeout <= 0) {
s_putstring(gSocket, "\r\n\r\nTimeout waiting for input..\r\n");
disconnect(gSocket);
}
}
}
static int handler(void* user, const char* section, const char* name,
const char* value)
{
struct bbs_config *conf = (struct bbs_config *)user;
if (strcasecmp(section, "main") == 0) {
if (strcasecmp(name, "bbs name") == 0) {
conf->bbs_name = strdup(value);
} else if (strcasecmp(name, "sysop name") == 0) {
conf->sysop_name = strdup(value);
} else if (strcasecmp(name, "nodes") == 0) {
conf->nodes = atoi(value);
} else if (strcasecmp(name, "new user level") == 0) {
conf->newuserlvl = atoi(value);
}
} else if (strcasecmp(section, "paths") == 0){
if (strcasecmp(name, "ansi path") == 0) {
conf->ansi_path = strdup(value);
} else if (strcasecmp(name, "bbs path") == 0) {
conf->bbs_path = strdup(value);
}
}
return 1;
}
void s_putchar(int socket, char c) {
write(socket, &c, 1);
}
void s_putstring(int socket, char *c) {
write(socket, c, strlen(c));
}
void s_displayansi(int socket, char *file) {
FILE *fptr;
char c;
char buffer[256];
sprintf(buffer, "%s/%s.ans", conf.ansi_path, file);
fptr = fopen(buffer, "r");
if (!fptr) {
return;
}
c = fgetc(fptr);
while (!feof(fptr)) {
s_putchar(socket, c);
c = fgetc(fptr);
}
fclose(fptr);
}
char s_getchar(int socket) {
unsigned char c;
int len;
len = read(socket, &c, 1);
if (len == 0) {
disconnect(socket);
}
while (c == 255) {
len = read(socket, &c, 1);
if (len == 0) {
disconnect(socket);
}
len = read(socket, &c, 1);
if (len == 0) {
disconnect(socket);
}
len = read(socket, &c, 1);
if (len == 0) {
disconnect(socket);
}
}
usertimeout = 10;
return (char)c;
}
char s_getc(int socket) {
char c = s_getchar(socket);
s_putchar(socket, c);
return (char)c;
}
void s_readstring(int socket, char *buffer, int max) {
int i;
char c;
for (i=0;i<max;i++) {
c = s_getchar(socket);
if ((c == '\b' || c == 127) && i > 0) {
buffer[i-1] = '\0';
i -= 2;
s_putstring(socket, "\e[D \e[D");
continue;
}
if (c == '\n' || c == '\r') {
c = s_getchar(socket);
return;
}
s_putchar(socket, c);
buffer[i] = c;
buffer[i+1] = '\0';
}
}
void s_readpass(int socket, char *buffer, int max) {
int i;
char c;
for (i=0;i<max;i++) {
c = s_getchar(socket);
if ((c == '\b' || c == 127) && i > 0) {
buffer[i-1] = '\0';
i-=2;
s_putstring(socket, "\e[D \e[D");
continue;
}
if (c == '\n' || c == '\r') {
c = s_getchar(socket);
return;
}
s_putchar(socket, '*');
buffer[i] = c;
buffer[i+1] = '\0';
}
}
void disconnect(int socket) {
char buffer[256];
if (gUser != NULL) {
save_user(gUser);
}
sprintf(buffer, "%s/nodeinuse.%d", conf.bbs_path, mynode);
remove(buffer);
close(socket);
exit(0);
}
void runbbs(int socket, char *config_path) {
char buffer[256];
char password[17];
struct stat s;
FILE *nodefile;
int i;
char iac_echo[] = {255, 251, 1, '\0'};
char iac_sga[] = {255, 251, 3, '\0'};
struct user_record *user;
struct tm thetime;
struct tm oldtime;
time_t now;
struct itimerval itime;
struct sigaction sa;
write(socket, iac_echo, 3);
write(socket, iac_sga, 3);
sprintf(buffer, "Magicka BBS v%d.%d (%s) Loading...\r\n", VERSION_MAJOR, VERSION_MINOR, VERSION_STR);
s_putstring(socket, buffer);
// Load BBS data
if (ini_parse(config_path, handler, &conf) <0) {
printf("Unable to load configuration ini (%s)!\n", config_path);
exit(-1);
}
// find out which node we are
mynode = 0;
for (i=1;i<=conf.nodes;i++) {
sprintf(buffer, "%s/nodeinuse.%d", conf.bbs_path, i);
if (stat(buffer, &s) != 0) {
mynode = i;
nodefile = fopen(buffer, "w");
if (!nodefile) {
printf("Error opening nodefile!\n");
close(socket);
exit(1);
}
fputs("UNKNOWN", nodefile);
fclose(nodefile);
break;
}
}
if (mynode == 0) {
s_putstring(socket, "Sorry, all nodes are in use. Please try later\r\n");
close(socket);
exit(1);
}
gUser = NULL;
gSocket = socket;
usertimeout = 10;
memset (&sa, 0, sizeof (sa));
sa.sa_handler = &timer_handler;
sa.sa_flags = SA_RESTART;
sigaction (SIGALRM, &sa, 0);
itime.it_interval.tv_sec = 60;
itime.it_interval.tv_usec = 0;
itime.it_value.tv_sec = 60;
itime.it_value.tv_usec = 0;
setitimer (ITIMER_REAL, &itime, 0);
s_displayansi(socket, "issue");
s_putstring(socket, "Enter your Login Name or NEW to create an account\r\n");
s_putstring(socket, "Login:> ");
s_readstring(socket, buffer, 25);
if (strcasecmp(buffer, "new") == 0) {
user = new_user(socket);
} else {
s_putstring(socket, "\r\nPassword:> ");
s_readpass(socket, password, 16);
user = check_user_pass(socket, buffer, password);
if (user == NULL) {
s_putstring(socket, "\r\nIncorrect Login.\r\n");
disconnect(socket);
}
for (i=1;i<=conf.nodes;i++) {
sprintf(buffer, "%s/nodeinuse.%d", conf.bbs_path, i);
if (stat(buffer, &s) == 0) {
nodefile = fopen(buffer, "r");
if (!nodefile) {
printf("Error opening nodefile!\n");
disconnect(socket);
}
fgets(buffer, 256, nodefile);
buffer[strlen(buffer) - 1] = '\0';
if (strcasecmp(user->loginname, buffer) == 0) {
fclose(nodefile);
s_putstring(socket, "You are already logged in.\r\n");
disconnect(socket);
}
fclose(nodefile);
}
}
}
sprintf(buffer, "%s/nodeinuse.%d", conf.bbs_path, mynode);
nodefile = fopen(buffer, "w");
if (!nodefile) {
printf("Error opening nodefile!\n");
close(socket);
exit(1);
}
fputs(user->loginname, nodefile);
fclose(nodefile);
// do post-login
// check time left
now = time(NULL);
localtime_r(&now, &thetime);
localtime_r(&user->laston, &oldtime);
if (thetime.tm_mday != oldtime.tm_mday || thetime.tm_mon != oldtime.tm_mon || thetime.tm_year != oldtime.tm_year) {
user->timeleft = user->sec_info->timeperday;
user->laston = now;
save_user(user);
}
gUser = user;
// main menu
main_menu(socket, user);
disconnect(socket);
}

57
bbs.h Normal file
View File

@ -0,0 +1,57 @@
#ifndef __BBS_H__
#define __BBS_H__
#include <time.h>
#define VERSION_MAJOR 0
#define VERSION_MINOR 1
#define VERSION_STR "dev"
struct bbs_config {
char *bbs_name;
char *sysop_name;
char *ansi_path;
char *bbs_path;
int nodes;
int newuserlvl;
};
struct sec_level_t {
int timeperday;
};
struct user_record {
int id;
char *loginname;
char *password;
char *firstname;
char *lastname;
char *email;
char *location;
int sec_level;
struct sec_level_t *sec_info;
time_t laston;
int timeleft;
};
extern void runbbs(int sock, char *config);
extern void s_putchar(int socket, char c);
extern void s_putstring(int socket, char *c);
extern void s_displayansi(int socket, char *file);
extern char s_getchar(int socket);
extern void s_readstring(int socket, char *buffer, int max);
extern char s_getc(int socket);
extern void disconnect(int socket);
extern int save_user(struct user_record *user);
extern int check_user(char *loginname);
extern struct user_record *new_user(int socket);
extern struct user_record *check_user_pass(int socket, char *loginname, char *password);
extern void main_menu(int socket, struct user_record *user);
#endif

9
bbs.ini Normal file
View File

@ -0,0 +1,9 @@
[main]
BBS Name = Enigma BBS
Sysop Name = Andrew Pamment
nodes = 4
New User Level = 10
[paths]
ANSI Path = /home/andrew/MagickaBBS/ansis
BBS Path = /home/andrew/MagickaBBS

27
inih/LICENSE.txt Normal file
View File

@ -0,0 +1,27 @@
The "inih" library is distributed under the New BSD license:
Copyright (c) 2009, Ben Hoyt
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Ben Hoyt nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

194
inih/ini.c Normal file
View File

@ -0,0 +1,194 @@
/* inih -- simple .INI file parser
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
https://github.com/benhoyt/inih
*/
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "ini.h"
#if !INI_USE_STACK
#include <stdlib.h>
#endif
#define MAX_SECTION 50
#define MAX_NAME 50
/* Strip whitespace chars off end of given string, in place. Return s. */
static char* rstrip(char* s)
{
char* p = s + strlen(s);
while (p > s && isspace((unsigned char)(*--p)))
*p = '\0';
return s;
}
/* Return pointer to first non-whitespace char in given string. */
static char* lskip(const char* s)
{
while (*s && isspace((unsigned char)(*s)))
s++;
return (char*)s;
}
/* Return pointer to first char (of chars) or inline comment in given string,
or pointer to null at end of string if neither found. Inline comment must
be prefixed by a whitespace character to register as a comment. */
static char* find_chars_or_comment(const char* s, const char* chars)
{
#if INI_ALLOW_INLINE_COMMENTS
int was_space = 0;
while (*s && (!chars || !strchr(chars, *s)) &&
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
was_space = isspace((unsigned char)(*s));
s++;
}
#else
while (*s && (!chars || !strchr(chars, *s))) {
s++;
}
#endif
return (char*)s;
}
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
static char* strncpy0(char* dest, const char* src, size_t size)
{
strncpy(dest, src, size);
dest[size - 1] = '\0';
return dest;
}
/* See documentation in header file. */
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
void* user)
{
/* Uses a fair bit of stack (use heap instead if you need to) */
#if INI_USE_STACK
char line[INI_MAX_LINE];
#else
char* line;
#endif
char section[MAX_SECTION] = "";
char prev_name[MAX_NAME] = "";
char* start;
char* end;
char* name;
char* value;
int lineno = 0;
int error = 0;
#if !INI_USE_STACK
line = (char*)malloc(INI_MAX_LINE);
if (!line) {
return -2;
}
#endif
/* Scan through stream line by line */
while (reader(line, INI_MAX_LINE, stream) != NULL) {
lineno++;
start = line;
#if INI_ALLOW_BOM
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
(unsigned char)start[1] == 0xBB &&
(unsigned char)start[2] == 0xBF) {
start += 3;
}
#endif
start = lskip(rstrip(start));
if (*start == ';' || *start == '#') {
/* Per Python configparser, allow both ; and # comments at the
start of a line */
}
#if INI_ALLOW_MULTILINE
else if (*prev_name && *start && start > line) {
/* Non-blank line with leading whitespace, treat as continuation
of previous name's value (as per Python configparser). */
if (!handler(user, section, prev_name, start) && !error)
error = lineno;
}
#endif
else if (*start == '[') {
/* A "[section]" line */
end = find_chars_or_comment(start + 1, "]");
if (*end == ']') {
*end = '\0';
strncpy0(section, start + 1, sizeof(section));
*prev_name = '\0';
}
else if (!error) {
/* No ']' found on section line */
error = lineno;
}
}
else if (*start) {
/* Not a comment, must be a name[=:]value pair */
end = find_chars_or_comment(start, "=:");
if (*end == '=' || *end == ':') {
*end = '\0';
name = rstrip(start);
value = lskip(end + 1);
#if INI_ALLOW_INLINE_COMMENTS
end = find_chars_or_comment(value, NULL);
if (*end)
*end = '\0';
#endif
rstrip(value);
/* Valid name[=:]value pair found, call handler */
strncpy0(prev_name, name, sizeof(prev_name));
if (!handler(user, section, name, value) && !error)
error = lineno;
}
else if (!error) {
/* No '=' or ':' found on name[=:]value line */
error = lineno;
}
}
#if INI_STOP_ON_FIRST_ERROR
if (error)
break;
#endif
}
#if !INI_USE_STACK
free(line);
#endif
return error;
}
/* See documentation in header file. */
int ini_parse_file(FILE* file, ini_handler handler, void* user)
{
return ini_parse_stream((ini_reader)fgets, file, handler, user);
}
/* See documentation in header file. */
int ini_parse(const char* filename, ini_handler handler, void* user)
{
FILE* file;
int error;
file = fopen(filename, "r");
if (!file)
return -1;
error = ini_parse_file(file, handler, user);
fclose(file);
return error;
}

93
inih/ini.h Normal file
View File

@ -0,0 +1,93 @@
/* inih -- simple .INI file parser
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
https://github.com/benhoyt/inih
*/
#ifndef __INI_H__
#define __INI_H__
/* Make this header file easier to include in C++ code */
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
/* Typedef for prototype of handler function. */
typedef int (*ini_handler)(void* user, const char* section,
const char* name, const char* value);
/* Typedef for prototype of fgets-style reader function. */
typedef char* (*ini_reader)(char* str, int num, void* stream);
/* Parse given INI-style file. May have [section]s, name=value pairs
(whitespace stripped), and comments starting with ';' (semicolon). Section
is "" if name=value pair parsed before any section heading. name:value
pairs are also supported as a concession to Python's configparser.
For each name=value pair parsed, call handler function with given user
pointer as well as section, name, and value (data only valid for duration
of handler call). Handler should return nonzero on success, zero on error.
Returns 0 on success, line number of first error on parse error (doesn't
stop on first error), -1 on file open error, or -2 on memory allocation
error (only when INI_USE_STACK is zero).
*/
int ini_parse(const char* filename, ini_handler handler, void* user);
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
close the file when it's finished -- the caller must do that. */
int ini_parse_file(FILE* file, ini_handler handler, void* user);
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
filename. Used for implementing custom or string-based I/O. */
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
void* user);
/* Nonzero to allow multi-line value parsing, in the style of Python's
configparser. If allowed, ini_parse() will call the handler with the same
name for each subsequent line parsed. */
#ifndef INI_ALLOW_MULTILINE
#define INI_ALLOW_MULTILINE 1
#endif
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
the file. See http://code.google.com/p/inih/issues/detail?id=21 */
#ifndef INI_ALLOW_BOM
#define INI_ALLOW_BOM 1
#endif
/* Nonzero to allow inline comments (with valid inline comment characters
specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
Python 3.2+ configparser behaviour. */
#ifndef INI_ALLOW_INLINE_COMMENTS
#define INI_ALLOW_INLINE_COMMENTS 1
#endif
#ifndef INI_INLINE_COMMENT_PREFIXES
#define INI_INLINE_COMMENT_PREFIXES ";"
#endif
/* Nonzero to use stack, zero to use heap (malloc/free). */
#ifndef INI_USE_STACK
#define INI_USE_STACK 1
#endif
/* Stop parsing on first error (default is to keep parsing). */
#ifndef INI_STOP_ON_FIRST_ERROR
#define INI_STOP_ON_FIRST_ERROR 0
#endif
/* Maximum line length for any line in INI file. */
#ifndef INI_MAX_LINE
#define INI_MAX_LINE 200
#endif
#ifdef __cplusplus
}
#endif
#endif /* __INI_H__ */

57
main.c Normal file
View File

@ -0,0 +1,57 @@
#include <stdio.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include "bbs.h"
int main(int argc, char **argv) {
int socket_desc, client_sock, c, *new_sock;
int pid;
struct sockaddr_in server, client;
if (argc < 2) {
printf("Usage ./magicka bbs.ini\n");
exit(1);
}
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1) {
printf("Couldn't create socket..\n");
return 1;
}
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(2300);
if (bind(socket_desc, (struct sockaddr *)&server, sizeof(server)) < 0) {
perror("Bind Failed, Error\n");
return 1;
}
listen(socket_desc, 3);
c = sizeof(struct sockaddr_in);
while ((client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t *)&c))) {
pid = fork();
if (pid < 0) {
perror("Error on fork\n");
return 1;
}
if (pid == 0) {
close(socket_desc);
runbbs(client_sock, argv[1]);
exit(0);
} else {
close(client_sock);
}
}
}

35
main_menu.c Normal file
View File

@ -0,0 +1,35 @@
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "bbs.h"
extern struct bbs_config conf;
void main_menu(int socket, struct user_record *user) {
int doquit = 0;
char c;
char prompt[128];
while (!doquit) {
s_displayansi(socket, "mainmenu");
sprintf(prompt, "TL: %dm :> ", user->timeleft);
s_putstring(socket, prompt);
c = s_getc(socket);
switch(tolower(c)) {
case 'g':
{
s_putstring(socket, "\r\nAre you sure you want to log off? (Y/N)");
c = s_getc(socket);
if (tolower(c) == 'y') {
doquit = 1;
}
}
break;
}
}
}

2
s10.ini Normal file
View File

@ -0,0 +1,2 @@
[main]
time per day = 240

374
users.c Normal file
View File

@ -0,0 +1,374 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <ctype.h>
#include "bbs.h"
#include "inih/ini.h"
extern struct bbs_config conf;
static int secLevel(void* user, const char* section, const char* name,
const char* value)
{
struct sec_level_t *conf = (struct sec_level_t *)user;
if (strcasecmp(section, "main") == 0) {
if (strcasecmp(name, "time per day") == 0) {
conf->timeperday = atoi(value);
}
}
return 1;
}
int save_user(struct user_record *user) {
char buffer[256];
sqlite3 *db;
sqlite3_stmt *res;
int rc;
char *update_sql = "UPDATE users SET password=?, firstname=?,"
"lastname=?, email=?, location=?, sec_level=?, last_on=?, time_left=? where loginname LIKE ?";
char *err_msg = 0;
sprintf(buffer, "%s/users.sq3", conf.bbs_path);
rc = sqlite3_open(buffer, &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
rc = sqlite3_prepare_v2(db, update_sql, -1, &res, 0);
if (rc == SQLITE_OK) {
sqlite3_bind_text(res, 1, user->password, -1, 0);
sqlite3_bind_text(res, 2, user->firstname, -1, 0);
sqlite3_bind_text(res, 3, user->lastname, -1, 0);
sqlite3_bind_text(res, 4, user->email, -1, 0);
sqlite3_bind_text(res, 5, user->location, -1, 0);
sqlite3_bind_int(res, 6, user->sec_level);
sqlite3_bind_int(res, 7, user->laston);
sqlite3_bind_int(res, 8, user->timeleft);
sqlite3_bind_text(res, 9, user->loginname, -1, 0);
} else {
fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(db));
}
rc = sqlite3_step(res);
if (rc != SQLITE_DONE) {
printf("execution failed: %s", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
sqlite3_close(db);
return 1;
}
int inst_user(struct user_record *user) {
char buffer[256];
sqlite3 *db;
sqlite3_stmt *res;
int rc;
char *create_sql = "CREATE TABLE IF NOT EXISTS users ("
"Id INTEGER PRIMARY KEY,"
"loginname TEXT,"
"password TEXT,"
"firstname TEXT,"
"lastname TEXT,"
"email TEXT,"
"location TEXT,"
"sec_level INTEGER,"
"last_on INTEGER,"
"time_left INTEGER);";
char *insert_sql = "INSERT INTO users (loginname, password, firstname,"
"lastname, email, location, sec_level, last_on, time_left) VALUES(?,?, ?, ?, ?, ?, ?, ?, ?)";
char *err_msg = 0;
sprintf(buffer, "%s/users.sq3", conf.bbs_path);
rc = sqlite3_open(buffer, &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
rc = sqlite3_exec(db, create_sql, 0, 0, &err_msg);
if (rc != SQLITE_OK ) {
fprintf(stderr, "SQL error: %s\n", err_msg);
sqlite3_free(err_msg);
sqlite3_close(db);
return 1;
}
rc = sqlite3_prepare_v2(db, insert_sql, -1, &res, 0);
if (rc == SQLITE_OK) {
sqlite3_bind_text(res, 1, user->loginname, -1, 0);
sqlite3_bind_text(res, 2, user->password, -1, 0);
sqlite3_bind_text(res, 3, user->firstname, -1, 0);
sqlite3_bind_text(res, 4, user->lastname, -1, 0);
sqlite3_bind_text(res, 5, user->email, -1, 0);
sqlite3_bind_text(res, 6, user->location, -1, 0);
sqlite3_bind_int(res, 7, user->sec_level);
sqlite3_bind_int(res, 8, user->laston);
sqlite3_bind_int(res, 9, user->timeleft);
} else {
fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(db));
}
rc = sqlite3_step(res);
if (rc != SQLITE_DONE) {
printf("execution failed: %s", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
sqlite3_close(db);
return 1;
}
struct user_record *check_user_pass(int socket, char *loginname, char *password) {
struct user_record *user;
char buffer[256];
sqlite3 *db;
sqlite3_stmt *res;
int rc;
char *sql = "SELECT * FROM users WHERE loginname LIKE ?";
sprintf(buffer, "%s/users.sq3", conf.bbs_path);
rc = sqlite3_open(buffer, &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);
if (rc == SQLITE_OK) {
sqlite3_bind_text(res, 1, loginname, -1, 0);
} else {
fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(db));
sqlite3_finalize(res);
sqlite3_close(db);
return NULL;
}
int step = sqlite3_step(res);
if (step == SQLITE_ROW) {
user = (struct user_record *)malloc(sizeof(struct user_record));
user->id = sqlite3_column_int(res, 0);
user->loginname = strdup((char *)sqlite3_column_text(res, 1));
user->password = strdup((char *)sqlite3_column_text(res, 2));
user->firstname = strdup((char *)sqlite3_column_text(res, 3));
user->lastname = strdup((char *)sqlite3_column_text(res, 4));
user->email = strdup((char *)sqlite3_column_text(res, 5));
user->location = strdup((char *)sqlite3_column_text(res, 6));
user->sec_level = sqlite3_column_int(res, 7);
user->laston = (time_t)sqlite3_column_int(res, 8);
user->timeleft = sqlite3_column_int(res, 9);
if (strcmp(password, user->password) != 0) {
free(user);
sqlite3_finalize(res);
sqlite3_close(db);
return NULL;
}
} else {
sqlite3_finalize(res);
sqlite3_close(db);
return NULL;
}
sqlite3_finalize(res);
sqlite3_close(db);
user->sec_info = (struct sec_level_t *)malloc(sizeof(struct sec_level_t));
sprintf(buffer, "%s/s%d.ini", conf.bbs_path, user->sec_level);
if (ini_parse(buffer, secLevel, user->sec_info) <0) {
printf("Unable to load sec Level ini (%s)!\n", buffer);
exit(-1);
}
return user;
}
int check_user(char *loginname) {
char buffer[256];
sqlite3 *db;
sqlite3_stmt *res;
int rc;
char *sql = "SELECT * FROM users WHERE loginname = ?";
sprintf(buffer, "%s/users.sq3", conf.bbs_path);
rc = sqlite3_open(buffer, &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);
if (rc == SQLITE_OK) {
sqlite3_bind_text(res, 1, loginname, -1, 0);
} else {
fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(db));
}
int step = sqlite3_step(res);
if (step == SQLITE_ROW) {
sqlite3_finalize(res);
sqlite3_close(db);
return 0;
}
sqlite3_finalize(res);
sqlite3_close(db);
return 1;
}
struct user_record *new_user(int socket) {
char buffer[256];
struct user_record *user;
int done = 0;
char c;
int nameok = 0;
int passok = 0;
user = (struct user_record *)malloc(sizeof(struct user_record));
s_displayansi(socket, "newuser");
do {
passok = 0;
nameok = 0;
do {
s_putstring(socket, "\r\nWhat is your login name: ");
s_readstring(socket, buffer, 16);
s_putstring(socket, "\r\n");
if (strlen(buffer) < 3) {
s_putstring(socket, "Sorry, that name is too short.\r\n");
continue;
}
if (strchr(buffer, '%') != NULL) {
s_putstring(socket, "Sorry, invalid character.\r\n");
continue;
}
if (strcasecmp(buffer, "unknown") == 0) {
s_putstring(socket, "Sorry, that name is reserved.\r\n");
continue;
}
user->loginname = strdup(buffer);
nameok = check_user(user->loginname);
if (!nameok) {
s_putstring(socket, "Sorry, that name is in use.\r\n");
free(user->loginname);
memset(buffer, 0, 256);
}
} while (!nameok);
s_putstring(socket, "What is your first name: ");
memset(buffer, 0, 256);
s_readstring(socket, buffer, 32);
s_putstring(socket, "\r\n");
user->firstname = strdup(buffer);
s_putstring(socket, "What is your last name: ");
memset(buffer, 0, 256);
s_readstring(socket, buffer, 32);
s_putstring(socket, "\r\n");
user->lastname = strdup(buffer);
s_putstring(socket, "What is your e-mail address: ");
memset(buffer, 0, 256);
s_readstring(socket, buffer, 64);
s_putstring(socket, "\r\n");
user->email = strdup(buffer);
s_putstring(socket, "Where are you located: ");
memset(buffer, 0, 256);
s_readstring(socket, buffer, 32);
s_putstring(socket, "\r\n");
user->location = strdup(buffer);
do {
s_putstring(socket, "What password would you like (at least 8 characters): ");
memset(buffer, 0, 256);
s_readstring(socket, buffer, 16);
s_putstring(socket, "\r\n");
if (strlen(buffer) >= 8) {
passok = 1;
} else {
s_putstring(socket, "Password too short!\r\n");
}
} while (!passok);
user->password = strdup(buffer);
s_putstring(socket, "You Entered:\r\n");
s_putstring(socket, "-------------------------------------\r\n");
s_putstring(socket, "Login Name: ");
s_putstring(socket, user->loginname);
s_putstring(socket, "\r\nFirst Name: ");
s_putstring(socket, user->firstname);
s_putstring(socket, "\r\nLast Name: ");
s_putstring(socket, user->lastname);
s_putstring(socket, "\r\nE-mail: ");
s_putstring(socket, user->email);
s_putstring(socket, "\r\nLocation: ");
s_putstring(socket, user->location);
s_putstring(socket, "\r\nPassword: ");
s_putstring(socket, user->password);
s_putstring(socket, "\r\n-------------------------------------\r\n");
s_putstring(socket, "Is this Correct? (Y/N)");
c = s_getchar(socket);
while (tolower(c) != 'y' && tolower(c) != 'n') {
c = s_getchar(socket);
}
if (tolower(c) == 'y') {
done = 1;
}
} while (!done);
user->sec_level = conf.newuserlvl;
user->sec_info = (struct sec_level_t *)malloc(sizeof(struct sec_level_t));
sprintf(buffer, "%s/s%d.ini", conf.bbs_path, user->sec_level);
if (ini_parse(buffer, secLevel, user->sec_info) <0) {
printf("Unable to load sec Level ini (%s)!\n", buffer);
exit(-1);
}
user->laston = time(NULL);
user->timeleft = user->sec_info->timeperday;
inst_user(user);
return user;
}