Initial Commit
This commit is contained in:
commit
29f158cb2e
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
*.o
|
||||||
|
*.sq3
|
||||||
|
*.core
|
14
Makefile
Normal file
14
Makefile
Normal 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
17
ansis/issue.ans
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[?7h[255D[40m
|
||||||
|
|
||||||
|
|
||||||
|
[0;1m[57C[34;46m<36><6D>[0;34;46m<36>[1m<31>[40m
|
||||||
|
[46m<36><6D>[0;34;46m<36>[1m<31>[0;36m<36><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> [1;34;46m<36><6D>[0;34;46m<36>[1m<31>[0;36m<36><6D>[46m [1C[1;34m<34><6D>[0;34;46m<36>[1m<31>[0;36m<36><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD> [1;34;46m<36><6D> [1C<31><43>[0;34;46m<36>[1m<31>[0;36m<36><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD> [1;34;46m<36><6D>[0;34;46m<36>[1m<31>[7C<37><43>[0;34;46m<36>[1m<31>[0;36m<36><6D>[46m [40m
|
||||||
|
[1;34;46m<36><6D>[0;34;46m<36>[1m<31>[2C<32><43>[0;36m<36><6D> [1;34;46m<36><6D>[0;36m<36><6D> [1;34;46m<36><6D>[0;34;46m<36>[1m<31>[2C<32><43> [1C<31><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;36m<36><6D> <20><><EFBFBD><EFBFBD> [1;34;46m<36><6D>[0;34;46m<36>[1m<31>[2C<32><43>[0;36m<36><6D> [1;34;46m<36><6D>[0;34;46m<36>[1m<31>[2C<32><43>[0;36m<36><6D> [1;34;46m<36><6D>[0;34;46m<36>[1m<31>[2C<32><43> [40m
|
||||||
|
[46m<36><6D>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[7C<37><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[40m
|
||||||
|
[46m<36><6D>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[7C<37><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[40m
|
||||||
|
[46m<36><6D>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[40m
|
||||||
|
[46m<36><6D>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[40m<30><6D>[46m<36><6D>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[40m<30><6D>[46m<36><6D>[0;34;46m<36>[1m<31>[40m
|
||||||
|
[46m<36><6D>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[40m<30><6D>[46m<36><6D><EFBFBD><EFBFBD>[1C<31><43>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[40m<30><6D>[46m<36><6D>[0;34;46m<36>[1m<31>[1C<31><43>[0;34;46m<36>[1m<31>[40m<30><6D>[46m<36><6D>[0;34m<34>[36m<36> [1;34;46m<36><6D>[0;34;46m<36>[1m<31>[2C<32><43>[0;34;46m<36>[1m<31>[40m
|
||||||
|
[30m<30>[5C[34m<34><6D>[0;34m<34>[36m<36> [1;34m<34><6D>[0;34m<34>[36m<36> [1;34;46m<36>[40m<30><6D> [46m<36>[40m<30><6D>[5C<35> [46m<36><6D>[0;34;46m<36>[1m<31>[23C<33><43>[0;34;46m<36>[1m<31>[1C<31>[40m<30><6D> [46m<36>[40m<30><6D>
|
||||||
|
[0;36m<36><6D><EFBFBD><EFBFBD><EFBFBD>[1m<31><6D>[0;36m<36>[1m<31><6D><EFBFBD>[0;36m<36><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> [1;34m<34><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD>[46m<36><6D>[0;34;46m<36>[1;40m<30> [0;36m<36><6D>[1m<31><6D><EFBFBD>[0;36m<36><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>[1m<31><6D><EFBFBD>[0;36m<36><6D><EFBFBD> [1;34;46m<36><6D>[0;34;46m<36>[1m<31>[1C[0;36m<36><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
W[1melcome [0;36mT[1mo...[15C[34m<34><6D> <20><>[46m<36><6D>[40m<30>
|
||||||
|
[54C[0;36mA[1mnother [0;36mF[1mine [0;36mM[1magicka [0;36mBBS
|
||||||
|
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>[1m<31><6D><EFBFBD><EFBFBD>[0;36m<36>[1m<31><6D>[0;36m<36><6D>[1m<31>[0;36m<36><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>[1m<31>[0;36m<36><6D><EFBFBD>[1m<31><6D><EFBFBD><EFBFBD><EFBFBD>[0;36m<36>[1m<31>[0;36m<36><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>[1m<31>[0;36m<36>[1m<31><6D>[0;36m<36>[1m<31>[0;36m<36><6D><EFBFBD>[1m<31><6D><EFBFBD>[0;36m<36><6D><EFBFBD><EFBFBD><EFBFBD>[1m<31><6D><EFBFBD><EFBFBD>[0;36m<36><6D><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
[0m[255D
|
15
ansis/mainmenu.ans
Normal file
15
ansis/mainmenu.ans
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
[?7h[255D[40m
|
||||||
|
[0;1m[5C[32mワワワワワワワワワワワ ワワワワワワ ワワ ワワワワワワ ワワワワワワワワワワワ ワワワワワワワ ワワワワワワ ワワ [30mワ [32mワワ
|
||||||
|
[5C[42mイ゚[30;40mーワ [32;42mイ゚[30;40mーワ [32;42mイロ[1Cイ゚[30;40mー [32;42mイロ[1Cロロ[1Cイ゚[30;40mーワ [32;42mイロ[2Cイ゚[30;40mーワ [32;42mイ゚[30;40mーワ [32;42mイロ[1Cイ゚[0;32mワワ [1m゚゚ [42mイ゚[30;40mーワ [32;42mイロ[1Cイ゚[1C[30;40mロ [32;42mロイ[40m
|
||||||
|
[5C[42mー[0;32mイ [1;30mイ [32;42mー[0;32mイ [1;30mイ [32;42mア゚[1Cー[0;32mイ[1m゚゚゚[42mア゚[1Cイ[0;32mイ [1;42mー[0;32mイ [1;30mイ [32;42mア゚[2Cー[0;32mイ [1;30mイ [32;42mー[0;32mイ [1;30mイ [32;42mア゚[1Cア[0;32mイワ[1;30mー [32;42mイ゚[1Cー[0;32mイ [1;30mイ [32;42mア゚[1Cー[0;32mイワ[1;30m゚ [32;42mイ [40m
|
||||||
|
[5C[0;32m゚゚ [1;30m゚ [0;32m゚゚ [1;30m゚ [32;42mー[0;32mロ ゚゚ [1;30m゚ [32;42mー[0;32mロ ゚゚ ゚゚ [1;30m゚ [32;42mー[0;32mロ ゚゚ [1;30m゚ [0;32m゚゚ [1;30m゚ [32;42mー[0;32mロ ゚゚゚゚゚゚ ゚゚ [1;30m゚ [32;42mー[0;32mロ ゚゚゚゚゚゚
|
||||||
|
[1;30mトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトト
|
||||||
|
ウ [0;36mM. [1;37mMessage Areas[16C[30mウ [0;36mL. [1;37mBBS List[22C[30mウ
|
||||||
|
ウ [0;36mT. [1;37mFile Areas[19C[30mウ [0;36mC. [1;37mChat System[19C[30mウ
|
||||||
|
ウ [0;36mB. [1;37mBulletins[20C[30mウ [0;36mU. [1;37mUser List[21C[30mウ
|
||||||
|
ウ [0;36mO. [1;37mOnline Games[17C[30mウ [0;36m1. [1;37mLast 10 Callers[15C[30mウ
|
||||||
|
ウ[34Cウ[35Cウ
|
||||||
|
ウ[34Cウ[35Cウ
|
||||||
|
ウ[34Cウ [0;36mG. [1;31mGoodbye [0;31m([1mLog Off[0;31m)[13C[1;30mウ
|
||||||
|
トトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトト
|
||||||
|
[0m[255D
|
19
ansis/newuser.ans
Normal file
19
ansis/newuser.ans
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
[?7h[255D[9C[0;1;32mÜÜÜÜÜÜ ÜÜÜÜÜÜÜ ÜÜ ÜÜ ÛÛ [31mÜÜ [30mÜ [31mÜÜ ÜÜÜÜÜÜÜ ÜÜÜÜÜÜÜ ÜÜÜÜÜÜ
|
||||||
|
[9C[32;42m²ß[30;40m°Ü [32;42m²Û[1C²ß[0;32mÜÜ [1mßß [42mÛÛ[30;40m°Û [32;42m²Û[1C[30;40m² [32;42mÛÛ[2C[31;41m²ß[1C[30;40mÛ [31;41mÛ²[1C²[40mÛÜÜÜÜÜ [41m²ß[0;31mÜÜ [1mßß [41mÛÛ[30;40m°Ü [31;41m²Û[40m
|
||||||
|
[9C[32;42m°[0;32m² [1;30m² [32;42m±ß[1C±[0;32m²Ü[1;30m° [32;42m²ß[1C²[0;32m² [1;30mß[0;32mÜ[1;42m±ß[1C[30;40mß[0;32mÜ[1;42m²[0;32m² [1;31;41m°[0;31m²Ü[1;30mß [31;41m² [1C[40mÜ[0;31mÜ [1;41m²ß[1C±[0;31m²Ü[1;30m° [31;41m²ß[1C²[0;31m² [1;30m² [31;41m°ß[40m
|
||||||
|
[9C[0;32mßß [1;30mß [32;42m°[0;32mÛ ßßßßßß ßßßßßß[1;30mÜ[0;32mßßßß [31mßßßßßß ßßßßßß[1;30m° [0;31mßßßßßß ßß[1;30m°
|
||||||
|
[5CÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||||||
|
|
||||||
|
[9C[0mThis is where you would put the information you want to give
|
||||||
|
[9Cpeople before they sign up.
|
||||||
|
|
||||||
|
[9CFor Example:
|
||||||
|
|
||||||
|
[14CPlease use real information, else I will delete your
|
||||||
|
[9Caccount.
|
||||||
|
|
||||||
|
[14CDo not hack the BBS, or I will send my flying monkeys
|
||||||
|
[9Cafter you.
|
||||||
|
|
||||||
|
[14CBe nice.
|
||||||
|
[0m[255D
|
333
bbs.c
Normal file
333
bbs.c
Normal 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
57
bbs.h
Normal 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
9
bbs.ini
Normal 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
27
inih/LICENSE.txt
Normal 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
194
inih/ini.c
Normal 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
93
inih/ini.h
Normal 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
57
main.c
Normal 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
35
main_menu.c
Normal 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
374
users.c
Normal file
374
users.c
Normal 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;
|
||||||
|
}
|
Reference in New Issue
Block a user