Commits (3)
  • Rob Swindell's avatar
    Accept all incoming connections in listen_thread() · ccb4d7dd
    Rob Swindell authored
    This sets the stage for "Caller-ID" support and IP/host filtering (before
    sending a "RING" result and requiring an auto-answer or host/BBS software to
    send "ATA").
    ccb4d7dd
  • Rob Swindell's avatar
    Add "Caller ID" support, enabled with AT#CID=1 or AT+VCID=1 · 2283ec95
    Rob Swindell authored
    Also controlled via [modem] CallerID key in svdm.ini fiile.
    Reports the connected IP address between the first and second RING result.
    Required a fix to reset the ringcount to 0 upon new connection.
    
    Simplified the AT command parsing logic a bit.
    2283ec95
  • Rob Swindell's avatar
    Decompose some library files to enable reuse by SVDM (virtual DOS modem) · 92bcd45e
    Rob Swindell authored
    I just wanted to reuse findstr() in vdmodem.c and I fell down this hole :-)
    
    findstr.* is new (findstr() related functions moved from str_util)
    getctrl.* is now finally the real home of get_ctrl_dir(), moved from str_util
    trashcan* functions moved from str_util to scfglib
    other scfg_t dependent functions moved from str_util to scfglib
    
    net_addr() appears to be a function that was never created/used (?)
    
    This will definitely break the *nix build, for now.
    92bcd45e
......@@ -153,6 +153,7 @@
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Include="getctrl.c" />
<ClCompile Include="nopen.c">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
......
/* Synchronet find string routines */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright Rob Swindell - http://www.synchro.net/copyright.html *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#include "genwrap.h"
#include "findstr.h"
/****************************************************************************/
/* Pattern matching string search of 'insearchof' in 'pattern'. */
/* pattern matching is case-insensitive */
/* patterns beginning with ';' are comments (never match) */
/* patterns beginning with '!' are reverse-matched (returns FALSE if match) */
/* patterns ending in '~' will match string anywhere (sub-string search) */
/* patterns ending in '^' will match left string fragment only */
/* patterns including '*' must match both left and right string fragments */
/* all other patterns are exact-match checking */
/****************************************************************************/
BOOL findstr_in_string(const char* search, const char* pattern)
{
char buf[256];
char* p;
char* last;
const char* splat;
size_t len;
BOOL found = FALSE;
if(pattern == NULL || search == NULL)
return FALSE;
SAFECOPY(buf, pattern);
p = buf;
if(*p == ';') /* comment */
return FALSE;
if(*p == '!') { /* reverse-match */
found = TRUE;
p++;
}
truncsp(p);
len = strlen(p);
if(len > 0) {
last = p + len - 1;
if(*last == '~') {
*last = '\0';
if(strcasestr(search, p) != NULL)
found = !found;
}
else if(*last == '^') {
if(strnicmp(p, search, len - 1) == 0)
found = !found;
}
else if((splat = strchr(p, '*')) != NULL) {
int left = splat - p;
int right = len - (left + 1);
int slen = strlen(search);
if(slen < left + right)
return found;
if(strnicmp(search, p, left) == 0
&& strnicmp(p + left + 1, search + (slen - right), right) == 0)
found = !found;
}
else if(stricmp(p, search) == 0)
found = !found;
}
return found;
}
static uint32_t encode_ipv4_address(unsigned int byte[])
{
if(byte[0] > 0xff || byte[1] > 0xff || byte[2] > 0xff || byte[3] > 0xff)
return 0;
return (byte[0]<<24) | (byte[1]<<16) | (byte[2]<<8) | byte[3];
}
static uint32_t parse_ipv4_address(const char* str)
{
unsigned int byte[4];
if(sscanf(str, "%u.%u.%u.%u", &byte[0], &byte[1], &byte[2], &byte[3]) != 4)
return 0;
return encode_ipv4_address(byte);
}
static uint32_t parse_cidr(const char* p, unsigned* subnet)
{
unsigned int byte[4];
if(*p == '!')
p++;
*subnet = 0;
if(sscanf(p, "%u.%u.%u.%u/%u", &byte[0], &byte[1], &byte[2], &byte[3], subnet) != 5 || *subnet > 32)
return 0;
return encode_ipv4_address(byte);
}
static BOOL is_cidr_match(const char *p, uint32_t ip_addr, uint32_t cidr, unsigned subnet)
{
BOOL match = FALSE;
if(*p == '!')
match = TRUE;
if(((ip_addr ^ cidr) >> (32-subnet)) == 0)
match = !match;
return match;
}
/****************************************************************************/
/* Pattern matching string search of 'insearchof' in 'list'. */
/****************************************************************************/
BOOL findstr_in_list(const char* insearchof, str_list_t list)
{
size_t index;
BOOL found=FALSE;
char* p;
uint32_t ip_addr, cidr;
unsigned subnet;
if(list==NULL || insearchof==NULL)
return FALSE;
ip_addr = parse_ipv4_address(insearchof);
for(index=0; list[index]!=NULL; index++) {
p=list[index];
// SKIP_WHITESPACE(p);
if(ip_addr != 0 && (cidr = parse_cidr(p, &subnet)) != 0)
found = is_cidr_match(p, ip_addr, cidr, subnet);
else
found = findstr_in_string(insearchof,p);
if(found != (*p=='!'))
break;
}
return found;
}
/****************************************************************************/
/* Pattern matching string search of 'insearchof' in 'fname'. */
/****************************************************************************/
BOOL findstr(const char* insearchof, const char* fname)
{
char str[256];
BOOL found=FALSE;
FILE* fp;
uint32_t ip_addr, cidr;
unsigned subnet;
if(insearchof==NULL || fname==NULL || *fname == '\0')
return FALSE;
if((fp=fopen(fname,"r"))==NULL)
return FALSE;
ip_addr = parse_ipv4_address(insearchof);
while(!feof(fp) && !ferror(fp) && !found) {
if(!fgets(str,sizeof(str),fp))
break;
char* p = str;
SKIP_WHITESPACE(p);
c_unescape_str(p);
if(ip_addr !=0 && (cidr = parse_cidr(p, &subnet)) != 0)
found = is_cidr_match(p, ip_addr, cidr, subnet);
else
found = findstr_in_string(insearchof, p);
}
fclose(fp);
return found;
}
static char* process_findstr_item(size_t index, char *str, void* cbdata)
{
SKIP_WHITESPACE(str);
return c_unescape_str(str);
}
/****************************************************************************/
str_list_t findstr_list(const char* fname)
{
FILE* fp;
str_list_t list;
if((fp=fopen(fname,"r"))==NULL)
return NULL;
list=strListReadFile(fp, NULL, /* Max line length: */255);
strListModifyEach(list, process_findstr_item, /* cbdata: */NULL);
fclose(fp);
return list;
}
/* Synchronet find string functions */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright Rob Swindell - http://www.synchro.net/copyright.html *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#ifndef _FINDSTR_H_
#define _FINDSTR_H_
#include "str_list.h"
#include "dllexport.h"
#ifdef __cplusplus
extern "C" {
#endif
DLLEXPORT BOOL findstr(const char *insearch, const char *fname);
DLLEXPORT BOOL findstr_in_string(const char* insearchof, const char* pattern);
DLLEXPORT BOOL findstr_in_list(const char* insearchof, str_list_t list);
DLLEXPORT str_list_t findstr_list(const char* fname);
#ifdef __cplusplus
}
#endif
#endif /* Don't add anything after this line */
#include <gen_defs.h>
#include <dirwrap.h>
/* Synchronet get "control" directory function */
char *get_ctrl_dir(char *path, size_t pathsz)
{
#ifdef PREFIX
char ini_file[MAX_PATH];
#endif
char *p;
p=getenv("SBBSCTRL");
if(p!=NULL) {
strncpy(path, p, pathsz);
if(pathsz > 0)
path[pathsz-1]=0;
return path;
}
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright Rob Swindell - http://www.synchro.net/copyright.html *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#ifdef PREFIX
strncpy(path, PREFIX"/etc", pathsz);
if(pathsz > 0)
path[pathsz-1]=0;
iniFileName(ini_file, sizeof(ini_file)-1, PREFIX"/etc", "sbbs.ini");
if(fexistcase(ini_file)) {
FILE* fini;
char* str;
#include <stdio.h>
#include "getctrl.h"
#include "sbbsdefs.h"
fini=iniOpenFile(ini_file, FALSE);
if(fini==NULL)
return NULL;
str = iniReadExistingString(fini, "Global", "CtrlDirectory", NULL, ini_file);
iniCloseFile(fini);
return str;
const char* get_ctrl_dir(BOOL warn)
{
char* p = getenv("SBBSCTRL");
if(p == NULL || *p == '\0') {
if(warn)
fprintf(stderr, "!SBBSCTRL environment variable not set, using default value: " SBBSCTRL_DEFAULT "\n\n");
p = SBBSCTRL_DEFAULT;
}
#endif
return NULL;
return p;
}
#ifndef GETCTRL_H
#define GETCTRL_H
/* Synchronet get "control" directory function */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright Rob Swindell - http://www.synchro.net/copyright.html *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#ifndef GETCTRL_H_
#define GETCTRL_H_
#include "gen_defs.h"
#if defined(__cplusplus)
extern "C"
extern "C"
#endif
char *get_ctrl_dir(char *path, size_t pathsz)
const char* get_ctrl_dir(BOOL warn);
#endif
......@@ -157,6 +157,7 @@
</ProjectReference>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="getctrl.c" />
<ClCompile Include="jsdebug.c" />
<ClCompile Include="js_conio.c">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
......
......@@ -101,6 +101,8 @@
<ItemGroup>
<ClCompile Include="..\encode\utf8.c" />
<ClCompile Include="ars.c" />
<ClCompile Include="findstr.c" />
<ClCompile Include="getctrl.c" />
<ClCompile Include="load_cfg.c" />
<ClCompile Include="nopen.c" />
<ClCompile Include="readtext.c" />
......
......@@ -148,6 +148,7 @@
</Bscmake>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="getctrl.c" />
<ClCompile Include="ntsvcs.c">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
......
......@@ -289,6 +289,7 @@ extern int thread_suid_broken; /* NPTL is no longer broken */
#include "nopen.h"
#include "text.h"
#include "str_util.h"
#include "findstr.h"
#include "date_str.h"
#include "load_cfg.h"
#include "getstats.h"
......
......@@ -328,6 +328,7 @@
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Include="findstr.c" />
<ClCompile Include="getkey.cpp">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
......
......@@ -147,6 +147,7 @@
</Bscmake>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="getctrl.c" />
<ClCompile Include="sbbs_ini.c">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
......
......@@ -46,6 +46,7 @@
#include "link_list.h"
#include "str_list.h"
#include "str_util.h"
#include "findstr.h"
#include "datewrap.h"
#include "nopen.h"
#include "crc32.h"
......
......@@ -72,6 +72,14 @@ DLLEXPORT BOOL is_valid_libnum(scfg_t*, int);
DLLEXPORT BOOL is_valid_subnum(scfg_t*, int);
DLLEXPORT BOOL is_valid_grpnum(scfg_t*, int);
DLLEXPORT BOOL trashcan(scfg_t* cfg, const char *insearch, const char *name);
DLLEXPORT char * trashcan_fname(scfg_t* cfg, const char *name, char* fname, size_t);
DLLEXPORT str_list_t trashcan_list(scfg_t* cfg, const char* name);
DLLEXPORT char * sub_newsgroup_name(scfg_t*, sub_t*, char*, size_t);
DLLEXPORT char * sub_area_tag(scfg_t*, sub_t*, char*, size_t);
DLLEXPORT char * dir_area_tag(scfg_t*, dir_t*, char*, size_t);
faddr_t* nearest_sysfaddr(scfg_t*, faddr_t*);
#ifdef __cplusplus
......
......@@ -23,6 +23,7 @@
#include "load_cfg.h"
#include "nopen.h"
#include "ars_defs.h"
#include "findstr.h"
BOOL allocerr(FILE* fp, char* error, size_t maxerrlen, long offset, const char *fname, size_t size)
{
......@@ -874,3 +875,96 @@ faddr_t* nearest_sysfaddr(scfg_t* cfg, faddr_t* addr)
return &cfg->faddr[i];
return &cfg->faddr[0];
}
/****************************************************************************/
/* Searches the file <name>.can in the TEXT directory for matches */
/* Returns TRUE if found in list, FALSE if not. */
/****************************************************************************/
BOOL trashcan(scfg_t* cfg, const char* insearchof, const char* name)
{
char fname[MAX_PATH+1];
return(findstr(insearchof,trashcan_fname(cfg,name,fname,sizeof(fname))));
}
/****************************************************************************/
char* trashcan_fname(scfg_t* cfg, const char* name, char* fname, size_t maxlen)
{
safe_snprintf(fname,maxlen,"%s%s.can",cfg->text_dir,name);
return fname;
}
/****************************************************************************/
str_list_t trashcan_list(scfg_t* cfg, const char* name)
{
char fname[MAX_PATH+1];
return findstr_list(trashcan_fname(cfg, name, fname, sizeof(fname)));
}
char* sub_newsgroup_name(scfg_t* cfg, sub_t* sub, char* str, size_t size)
{
memset(str, 0, size);
if(sub->newsgroup[0])
strncpy(str, sub->newsgroup, size - 1);
else {
snprintf(str, size - 1, "%s.%s", cfg->grp[sub->grp]->sname, sub->sname);
/*
* From RFC5536:
* newsgroup-name = component *( "." component )
* component = 1*component-char
* component-char = ALPHA / DIGIT / "+" / "-" / "_"
*/
if (str[0] == '.')
str[0] = '_';
size_t c;
for(c = 0; str[c] != 0; c++) {
/* Legal characters */
if ((str[c] >= 'A' && str[c] <= 'Z')
|| (str[c] >= 'a' && str[c] <= 'z')
|| (str[c] >= '0' && str[c] <= '9')
|| str[c] == '+'
|| str[c] == '-'
|| str[c] == '_'
|| str[c] == '.')
continue;
str[c] = '_';
}
c--;
if (str[c] == '.')
str[c] = '_';
}
return str;
}
char* sub_area_tag(scfg_t* cfg, sub_t* sub, char* str, size_t size)
{
char* p;
memset(str, 0, size);
if(sub->area_tag[0])
strncpy(str, sub->area_tag, size - 1);
else if(sub->newsgroup[0])
strncpy(str, sub->newsgroup, size - 1);
else {
strncpy(str, sub->sname, size - 1);
REPLACE_CHARS(str, ' ', '_', p);
}
strupr(str);
return str;
}
char* dir_area_tag(scfg_t* cfg, dir_t* dir, char* str, size_t size)
{
char* p;
memset(str, 0, size);
if(dir->area_tag[0])
strncpy(str, dir->area_tag, size - 1);
else {
strncpy(str, dir->sname, size - 1);
REPLACE_CHARS(str, ' ', '_', p);
}
strupr(str);
return str;
}
......@@ -19,6 +19,8 @@
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#include "genwrap.h"
#include "dirwrap.h"
#include "str_util.h"
#include "utf8.h"
#include "unicode.h"
......@@ -257,223 +259,6 @@ char* strip_char(const char* str, char* dest, char ch)
return retval;
}
/****************************************************************************/
/* Pattern matching string search of 'insearchof' in 'pattern'. */
/* pattern matching is case-insensitive */
/* patterns beginning with ';' are comments (never match) */
/* patterns beginning with '!' are reverse-matched (returns FALSE if match) */
/* patterns ending in '~' will match string anywhere (sub-string search) */
/* patterns ending in '^' will match left string fragment only */
/* patterns including '*' must match both left and right string fragments */
/* all other patterns are exact-match checking */
/****************************************************************************/
BOOL findstr_in_string(const char* search, const char* pattern)
{
char buf[256];
char* p;
char* last;
const char* splat;
size_t len;
BOOL found = FALSE;
if(pattern == NULL || search == NULL)
return FALSE;
SAFECOPY(buf, pattern);
p = buf;
if(*p == ';') /* comment */
return FALSE;
if(*p == '!') { /* reverse-match */
found = TRUE;
p++;
}
truncsp(p);
len = strlen(p);
if(len > 0) {
last = p + len - 1;
if(*last == '~') {
*last = '\0';
if(strcasestr(search, p) != NULL)
found = !found;
}
else if(*last == '^') {
if(strnicmp(p, search, len - 1) == 0)
found = !found;
}
else if((splat = strchr(p, '*')) != NULL) {
int left = splat - p;
int right = len - (left + 1);
int slen = strlen(search);
if(slen < left + right)
return found;
if(strnicmp(search, p, left) == 0
&& strnicmp(p + left + 1, search + (slen - right), right) == 0)
found = !found;
}
else if(stricmp(p, search) == 0)
found = !found;
}
return found;
}
static uint32_t encode_ipv4_address(unsigned int byte[])
{
if(byte[0] > 0xff || byte[1] > 0xff || byte[2] > 0xff || byte[3] > 0xff)
return 0;
return (byte[0]<<24) | (byte[1]<<16) | (byte[2]<<8) | byte[3];
}
static uint32_t parse_ipv4_address(const char* str)
{
unsigned int byte[4];
if(sscanf(str, "%u.%u.%u.%u", &byte[0], &byte[1], &byte[2], &byte[3]) != 4)
return 0;
return encode_ipv4_address(byte);
}
static uint32_t parse_cidr(const char* p, unsigned* subnet)
{
unsigned int byte[4];
if(*p == '!')
p++;
*subnet = 0;
if(sscanf(p, "%u.%u.%u.%u/%u", &byte[0], &byte[1], &byte[2], &byte[3], subnet) != 5 || *subnet > 32)
return 0;
return encode_ipv4_address(byte);
}
static BOOL is_cidr_match(const char *p, uint32_t ip_addr, uint32_t cidr, unsigned subnet)
{
BOOL match = FALSE;
if(*p == '!')
match = TRUE;
if(((ip_addr ^ cidr) >> (32-subnet)) == 0)
match = !match;
return match;
}
/****************************************************************************/
/* Pattern matching string search of 'insearchof' in 'list'. */
/****************************************************************************/
BOOL findstr_in_list(const char* insearchof, str_list_t list)
{
size_t index;
BOOL found=FALSE;
char* p;
uint32_t ip_addr, cidr;
unsigned subnet;
if(list==NULL || insearchof==NULL)
return FALSE;
ip_addr = parse_ipv4_address(insearchof);
for(index=0; list[index]!=NULL; index++) {
p=list[index];
// SKIP_WHITESPACE(p);
if(ip_addr != 0 && (cidr = parse_cidr(p, &subnet)) != 0)
found = is_cidr_match(p, ip_addr, cidr, subnet);
else
found = findstr_in_string(insearchof,p);
if(found != (*p=='!'))
break;
}
return found;
}
/****************************************************************************/
/* Pattern matching string search of 'insearchof' in 'fname'. */
/****************************************************************************/
BOOL findstr(const char* insearchof, const char* fname)
{
char str[256];
BOOL found=FALSE;
FILE* fp;
uint32_t ip_addr, cidr;
unsigned subnet;
if(insearchof==NULL || fname==NULL)
return FALSE;
if((fp=fopen(fname,"r"))==NULL)
return FALSE;
ip_addr = parse_ipv4_address(insearchof);
while(!feof(fp) && !ferror(fp) && !found) {
if(!fgets(str,sizeof(str),fp))
break;
char* p = str;
SKIP_WHITESPACE(p);
c_unescape_str(p);
if(ip_addr !=0 && (cidr = parse_cidr(p, &subnet)) != 0)
found = is_cidr_match(p, ip_addr, cidr, subnet);
else
found = findstr_in_string(insearchof, p);
}
fclose(fp);
return found;
}
/****************************************************************************/
/* Searches the file <name>.can in the TEXT directory for matches */
/* Returns TRUE if found in list, FALSE if not. */
/****************************************************************************/
BOOL trashcan(scfg_t* cfg, const char* insearchof, const char* name)
{
char fname[MAX_PATH+1];
return(findstr(insearchof,trashcan_fname(cfg,name,fname,sizeof(fname))));
}
/****************************************************************************/
char* trashcan_fname(scfg_t* cfg, const char* name, char* fname, size_t maxlen)
{
safe_snprintf(fname,maxlen,"%s%s.can",cfg->text_dir,name);
return fname;
}
static char* process_findstr_item(size_t index, char *str, void* cbdata)
{
SKIP_WHITESPACE(str);
return c_unescape_str(str);
}
/****************************************************************************/
str_list_t findstr_list(const char* fname)
{
FILE* fp;
str_list_t list;
if((fp=fopen(fname,"r"))==NULL)
return NULL;
list=strListReadFile(fp, NULL, /* Max line length: */255);
strListModifyEach(list, process_findstr_item, /* cbdata: */NULL);
fclose(fp);
return list;
}
/****************************************************************************/
str_list_t trashcan_list(scfg_t* cfg, const char* name)
{
char fname[MAX_PATH+1];
return findstr_list(trashcan_fname(cfg, name, fname, sizeof(fname)));
}
/****************************************************************************/
/* Returns in 'string' a character representation of the number in l with */
/* thousands separators (e.g. commas). */
......@@ -910,81 +695,3 @@ char* utf8_to_cp437_inplace(char* str)
,/* decode error char: */CP437_INVERTED_EXCLAMATION_MARK);
}
char* sub_newsgroup_name(scfg_t* cfg, sub_t* sub, char* str, size_t size)
{
memset(str, 0, size);
if(sub->newsgroup[0])
strncpy(str, sub->newsgroup, size - 1);
else {
snprintf(str, size - 1, "%s.%s", cfg->grp[sub->grp]->sname, sub->sname);
/*
* From RFC5536:
* newsgroup-name = component *( "." component )
* component = 1*component-char
* component-char = ALPHA / DIGIT / "+" / "-" / "_"
*/
if (str[0] == '.')
str[0] = '_';
size_t c;
for(c = 0; str[c] != 0; c++) {
/* Legal characters */
if ((str[c] >= 'A' && str[c] <= 'Z')
|| (str[c] >= 'a' && str[c] <= 'z')
|| (str[c] >= '0' && str[c] <= '9')
|| str[c] == '+'
|| str[c] == '-'
|| str[c] == '_'
|| str[c] == '.')
continue;
str[c] = '_';
}
c--;
if (str[c] == '.')
str[c] = '_';
}
return str;
}
char* sub_area_tag(scfg_t* cfg, sub_t* sub, char* str, size_t size)
{
char* p;
memset(str, 0, size);
if(sub->area_tag[0])
strncpy(str, sub->area_tag, size - 1);
else if(sub->newsgroup[0])
strncpy(str, sub->newsgroup, size - 1);
else {
strncpy(str, sub->sname, size - 1);
REPLACE_CHARS(str, ' ', '_', p);
}
strupr(str);
return str;
}
char* dir_area_tag(scfg_t* cfg, dir_t* dir, char* str, size_t size)
{
char* p;
memset(str, 0, size);
if(dir->area_tag[0])
strncpy(str, dir->area_tag, size - 1);
else {
strncpy(str, dir->sname, size - 1);
REPLACE_CHARS(str, ' ', '_', p);
}
strupr(str);
return str;
}
char* get_ctrl_dir(BOOL warn)
{
char* p = getenv("SBBSCTRL");
if(p == NULL || *p == '\0') {
if(warn)
fprintf(stderr, "!SBBSCTRL environment variable not set, using default value: " SBBSCTRL_DEFAULT "\n\n");
p = SBBSCTRL_DEFAULT;
}
return p;
}
......@@ -22,7 +22,7 @@
#ifndef _STR_UTIL_H_
#define _STR_UTIL_H_
#include "scfgdefs.h" // scfg_t
#include "gen_defs.h"
#include "dllexport.h"
#ifdef __cplusplus
......@@ -45,13 +45,6 @@ DLLEXPORT char * replace_named_values(const char* src ,char* buf, size_t bufl
named_int_t* int_list, BOOL case_sensitive);
DLLEXPORT char * condense_whitespace(char* str);
DLLEXPORT char exascii_to_ascii_char(uchar ch);
DLLEXPORT BOOL findstr(const char *insearch, const char *fname);
DLLEXPORT BOOL findstr_in_string(const char* insearchof, const char* pattern);
DLLEXPORT BOOL findstr_in_list(const char* insearchof, str_list_t list);
DLLEXPORT str_list_t findstr_list(const char* fname);
DLLEXPORT BOOL trashcan(scfg_t* cfg, const char *insearch, const char *name);
DLLEXPORT char * trashcan_fname(scfg_t* cfg, const char *name, char* fname, size_t);
DLLEXPORT str_list_t trashcan_list(scfg_t* cfg, const char* name);
DLLEXPORT char * convert_ansi(const char* src, char* dest, size_t, int width, BOOL ice_color);
DLLEXPORT char * strip_ansi(char* str);
DLLEXPORT char * strip_exascii(const char *str, char* dest);
......@@ -59,7 +52,6 @@ DLLEXPORT char * strip_cp437_graphics(const char *str, char* dest);
DLLEXPORT char * strip_space(const char *str, char* dest);
DLLEXPORT char * strip_ctrl(const char *str, char* dest);
DLLEXPORT char * strip_char(const char* str, char* dest, char);
DLLEXPORT char * net_addr(net_t* net);
DLLEXPORT BOOL valid_ctrl_a_attr(char a);
DLLEXPORT BOOL valid_ctrl_a_code(char a);
DLLEXPORT size_t strip_invalid_attr(char *str);
......@@ -70,10 +62,6 @@ DLLEXPORT uint32_t str_to_bits(uint32_t currval, const char *str);
DLLEXPORT BOOL str_has_ctrl(const char*);
DLLEXPORT BOOL str_is_ascii(const char*);
DLLEXPORT char * utf8_to_cp437_inplace(char* str);
DLLEXPORT char * sub_newsgroup_name(scfg_t*, sub_t*, char*, size_t);
DLLEXPORT char * sub_area_tag(scfg_t*, sub_t*, char*, size_t);
DLLEXPORT char * dir_area_tag(scfg_t*, dir_t*, char*, size_t);
DLLEXPORT char * get_ctrl_dir(BOOL warn);
#ifdef __cplusplus
}
......
......@@ -156,6 +156,7 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\encode\utf8.c" />
<ClCompile Include="getctrl.c" />
<ClCompile Include="str_util.c" />
<ClCompile Include="textgen.c">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
......
......@@ -20,6 +20,7 @@
****************************************************************************/
#include "str_util.h"
#include "findstr.h"
#include "cmdshell.h"
#include "userdat.h"
#include "filedat.h"
......
......@@ -42,6 +42,7 @@
#define VERSION "0.1"
bool external_socket;
union xp_sockaddr addr;
SOCKET sock = INVALID_SOCKET;
SOCKET listening_sock = INVALID_SOCKET;
HANDLE hangup_event = INVALID_HANDLE_VALUE; // e.g. program drops DTR
......@@ -121,7 +122,6 @@ void usage(const char* progname)
exit(EXIT_SUCCESS);
}
const char* supported_cmds = "ADEHIMOQSVXZ&";
const char* string_cmds = "D";
#define MAX_SAVES 20
struct modem {
......@@ -140,6 +140,7 @@ struct modem {
bool numeric_mode;
bool offhook;
bool online; // false means "command mode"
bool caller_id;
bool ringing;
ulong ringcount;
ulong auto_answer;
......@@ -213,17 +214,27 @@ const char* response_str[] = {
"CONNECT 9600"
};
char* response(struct modem* modem, enum modem_response code)
char* verbal_response(struct modem* modem, const char* response)
{
static char str[128];
safe_snprintf(str, sizeof(str), "%c%c%s%c%c", modem->cr, modem->lf, response, modem->cr, modem->lf);
return str;
}
char* numeric_response(struct modem* modem, int code)
{
static char str[128];
safe_snprintf(str, sizeof(str), "%u%c", code, modem->cr);
return str;
}
char* response(struct modem* modem, enum modem_response code)
{
if(modem->quiet)
return "";
if(modem->numeric_mode)
safe_snprintf(str, sizeof(str), "%u%c", code, modem->cr);
else
safe_snprintf(str, sizeof(str), "%c%c%s%c%c", modem->cr, modem->lf, response_str[code], modem->cr, modem->lf);
return str;
return numeric_response(modem, code);
return verbal_response(modem, response_str[code]);
}
char* ok(struct modem* modem)
......@@ -287,6 +298,7 @@ const char* iniKeyESC = "ESC";
const char* iniKeyExtResults = "ExtResults";
const char* iniKeyDialWait = "DialWait";
const char* iniKeyGuardTime = "GuardTime";
const char* iniKeyCallerID = "CallerID";
void init(struct modem* modem)
{
......@@ -296,6 +308,7 @@ void init(struct modem* modem)
modem->echo_off = !iniGetBool(ini, section, iniKeyEcho, TRUE);
modem->quiet = iniGetBool(ini, section, iniKeyQuiet, FALSE);
modem->numeric_mode = iniGetBool(ini, section, iniKeyNumeric, FALSE);
modem->caller_id = iniGetBool(ini, section, iniKeyCallerID, FALSE);
modem->cr = (char)iniGetInteger(ini, section, iniKeyCR, '\r');
modem->lf = (char)iniGetInteger(ini, section, iniKeyLF, '\n');
modem->bs = (char)iniGetInteger(ini, section, iniKeyBS, '\b');
......@@ -314,6 +327,7 @@ bool write_cfg(struct modem* modem)
iniSetBool(&ini, section, iniKeyEcho, !modem->echo_off, style);
iniSetBool(&ini, section, iniKeyQuiet, modem->quiet, style);
iniSetBool(&ini, section, iniKeyNumeric, modem->numeric_mode, style);
iniSetBool(&ini, section, iniKeyCallerID, modem->caller_id, style);
iniSetInteger(&ini, section, iniKeyCR, modem->cr, style);
iniSetInteger(&ini, section, iniKeyLF, modem->lf, style);
iniSetInteger(&ini, section, iniKeyBS, modem->bs, style);
......@@ -679,28 +693,16 @@ char* answer(struct modem* modem)
if(listening_sock == INVALID_SOCKET)
return response(modem, NO_DIAL_TONE);
fd_set fds = {0};
FD_SET(listening_sock, &fds);
struct timeval tv = { 0, 0 };
if(select(/* ignored: */0, &fds, NULL, NULL, &tv) != 1)
if(sock == INVALID_SOCKET)
return response(modem, NO_CARRIER);
union xp_sockaddr addr;
socklen_t addrlen = sizeof(addr);
sock = accept(listening_sock, (SOCKADDR*)&addr, &addrlen);
if(sock == INVALID_SOCKET) {
dprintf("!accept returned %d (errno=%ld)", sock, WSAGetLastError());
return response(modem, NO_CARRIER);
}
setsockopts(sock);
char tmp[256];
dprintf("Connection accepted from TCP port %hu at %s", inet_addrport(&addr), inet_addrtop(&addr, tmp, sizeof(tmp)));
if(cfg.client_file[0]) {
FILE* fp = fopen(cfg.client_file, "wt");
if(fp == NULL)
dprintf("!Error %d creating '%s'", errno, cfg.client_file);
else {
char tmp[256];
fprintf(fp, "sock=%d\n", sock);
fprintf(fp, "addr=%s\n", inet_addrtop(&addr, tmp, sizeof(tmp)));
fprintf(fp, "port=%u\n", inet_addrport(&addr));
......@@ -729,175 +731,192 @@ char* atmodem_exec(struct modem* modem)
for(char* p = modem->buf; *p != '\0';) {
char ch = toupper(*p);
p++;
if(strchr(supported_cmds, ch) == NULL)
if(ch == '&') {
ch = toupper(*p);
ulong val = strtoul(p + 1, &p, 10);
switch(ch) {
case 'W':
resp = write_cfg(modem) ? ok(modem) : error(modem);
break;
case 'Z':
if(val >= MAX_SAVES)
return error(modem);
if(*p == '=') {
p++;
if(stricmp(p, "L") == 0)
p = modem->last;
SAFECOPY(modem->save[val], p);
return write_save(modem, val) ? ok(modem) : error(modem);
}
if(*p == '?' || stricmp(p, "L?") == 0) {
if(stricmp(p, "L?") == 0)
p = modem->last;
else
p = modem->save[val];
safe_snprintf(respbuf, sizeof(respbuf), "%c%s%c%c%s"
,modem->lf, p, modem->cr, modem->lf, ok(modem));
return respbuf;
}
}
continue;
}
// Caller ID control
if(ch == '#' || ch == '+') {
if(ch == '+' && toupper(*p) == 'V')
p++;
if(stricmp(p, "CID?") == 0) {
safe_snprintf(respbuf, sizeof(respbuf), "%c%u%c%c%s"
,modem->lf, modem->caller_id, modem->cr, modem->lf, ok(modem));
return respbuf;
}
if(stricmp(p, "CID=?") == 0) {
safe_snprintf(respbuf, sizeof(respbuf), "%c0,1%c%c%s"
,modem->lf, modem->cr, modem->lf, ok(modem));
return respbuf;
}
if(strnicmp(p, "CID=", 4) == 0) {
modem->caller_id = strtoul(p + 4, &p, 10);
continue;
}
return error(modem);
if(strchr(string_cmds, ch) == NULL) {
if(ch == '&') {
ch = toupper(*p);
ulong val = strtoul(p + 1, &p, 10); // unused
switch(ch) {
case 'W':
resp = write_cfg(modem) ? ok(modem) : error(modem);
}
// Numeric argument commands
ulong val = 0;
if(strchr(string_cmds, ch) == NULL)
val = strtoul(p, &p, 10);
switch(ch) {
case 'A':
return answer(modem);
case 'D':
if(sock != INVALID_SOCKET) {
dprintf("Can't dial: Already connected");
return error(modem);
}
if(*p == 'T' /* tone */|| *p == 'P' /* pulse */)
p++;
return dial(modem, p);
case 'E':
modem->echo_off = !val;
break;
case 'H':
modem->offhook = val;
modem->ringing = false;
if(!modem->offhook) {
if(sock != INVALID_SOCKET) {
disconnect(modem);
}
}
break;
case 'I':
switch(val) {
case 0:
safe_snprintf(respbuf, sizeof(respbuf)
,"\r\n" TITLE " v" VERSION " Copyright %s Rob Swindell\r\n%s/%s\r\n"
,&__DATE__[7]
,GIT_BRANCH
,GIT_HASH
);
break;
case 'Z':
if(val >= MAX_SAVES)
return error(modem);
if(*p == '=') {
p++;
if(stricmp(p, "L") == 0)
p = modem->last;
SAFECOPY(modem->save[val], p);
return write_save(modem, val) ? ok(modem) : error(modem);
}
if(*p == '?' || stricmp(p, "L?") == 0) {
if(stricmp(p, "L?") == 0)
p = modem->last;
else
p = modem->save[val];
safe_snprintf(respbuf, sizeof(respbuf), "%c%s%c%c%s"
,modem->lf, p, modem->cr, modem->lf, ok(modem));
return respbuf;
}
case 1:
safe_snprintf(respbuf, sizeof(respbuf), "\r\n%s\r\n", ini_fname);
break;
default:
return error(modem);
}
continue;
}
// Numeric argument commands
ulong val = strtoul(p, &p, 10);
switch(ch) {
case 'A':
return answer(modem);
case 'E':
modem->echo_off = !val;
break;
case 'H':
modem->offhook = val;
modem->ringing = false;
if(!modem->offhook) {
if(sock != INVALID_SOCKET) {
disconnect(modem);
}
return respbuf;
case 'O':
if(sock == INVALID_SOCKET)
return error(modem);
modem->online = true;
return connect_result(modem);
break;
case 'V':
modem->numeric_mode = !val;
resp = ok(modem); // Use the new verbal/numeric mode in response (ala USRobotics)
break;
case 'Q':
modem->quiet = val;
resp = ok(modem); // Use the new quiet/verbose mode in response (ala USRobotics)
break;
case 'S':
if(*p == '=') {
ulong sreg = val;
ulong val = strtoul(p + 1, &p, 10);
dprintf("S%lu = %lu", sreg, val);
switch(sreg) {
case 0:
if(val && listening_sock == INVALID_SOCKET) {
dprintf("Can't enable auto-answer when not in listening mode");
return error(modem);
}
modem->auto_answer = val;
break;
case 1:
modem->ringcount = val;
break;
case 2:
modem->esc = (char)val;
break;
case 3:
modem->cr = (char)val;
break;
case 4:
modem->lf = (char)val;
break;
case 5:
modem->bs = (char)val;
break;
case 7:
modem->dial_wait = val;
break;
case 12:
modem->guard_time = val;
break;
}
break;
case 'I':
} else if(*p == '?') {
switch(val) {
case 0:
safe_snprintf(respbuf, sizeof(respbuf)
,"\r\n" TITLE " v" VERSION " Copyright %s Rob Swindell\r\n%s/%s\r\n"
,&__DATE__[7]
,GIT_BRANCH
,GIT_HASH
);
val = modem->auto_answer;
break;
case 1:
safe_snprintf(respbuf, sizeof(respbuf), "\r\n%s\r\n", ini_fname);
val = modem->ringcount;
break;
case 2:
val = modem->esc;
break;
case 3:
val = modem->cr;
break;
case 4:
val = modem->lf;
break;
case 5:
val = modem->bs;
break;
case 7:
val = modem->dial_wait;
break;
case 12:
val = modem->guard_time;
break;
default:
return error(modem);
val = 0;
break;
}
safe_snprintf(respbuf, sizeof(respbuf), "%c%03lu%c%c%s"
,modem->lf, val, modem->cr, modem->lf, ok(modem));
return respbuf;
case 'O':
if(sock == INVALID_SOCKET)
return error(modem);
modem->online = true;
return connect_result(modem);
break;
case 'V':
modem->numeric_mode = !val;
resp = ok(modem); // Use the new verbal/numeric mode in response (ala USRobotics)
break;
case 'Q':
modem->quiet = val;
resp = ok(modem); // Use the new quiet/verbose mode in response (ala USRobotics)
break;
case 'S':
if(*p == '=') {
ulong sreg = val;
ulong val = strtoul(p + 1, &p, 10);
dprintf("S%lu = %lu", sreg, val);
switch(sreg) {
case 0:
if(val && listening_sock == INVALID_SOCKET) {
dprintf("Can't enable auto-answer when not in listening mode");
return error(modem);
}
modem->auto_answer = val;
break;
case 1:
modem->ringcount = val;
break;
case 2:
modem->esc = (char)val;
break;
case 3:
modem->cr = (char)val;
break;
case 4:
modem->lf = (char)val;
break;
case 5:
modem->bs = (char)val;
break;
case 7:
modem->dial_wait = val;
break;
case 12:
modem->guard_time = val;
break;
}
} else if(*p == '?') {
switch(val) {
case 0:
val = modem->auto_answer;
break;
case 1:
val = modem->ringcount;
break;
case 2:
val = modem->esc;
break;
case 3:
val = modem->cr;
break;
case 4:
val = modem->lf;
break;
case 5:
val = modem->bs;
break;
case 7:
val = modem->dial_wait;
break;
case 12:
val = modem->guard_time;
break;
default:
val = 0;
break;
}
safe_snprintf(respbuf, sizeof(respbuf), "%c%03lu%c%c%s"
,modem->lf, val, modem->cr, modem->lf, ok(modem));
return respbuf;
} else
return error(modem);
break;
case 'X':
modem->ext_results = val;
break;
case 'Z':
init(modem);
break;
}
} else { // string argument commands
switch(ch) {
case 'D':
if(sock != INVALID_SOCKET) {
dprintf("Can't dial: Already connected");
return error(modem);
}
if(*p == 'T' /* tone */|| *p == 'P' /* pulse */)
p++;
return dial(modem, p);
}
} else
return error(modem);
break;
case 'X':
modem->ext_results = val;
break;
case 'Z':
init(modem);
break;
default:
return error(modem);
}
}
return resp;
......@@ -983,19 +1002,26 @@ void listen_thread(void* arg)
FD_SET(listening_sock, &fds);
struct timeval tv = { 1, 0 };
if(select(/* ignored: */0, &fds, NULL, NULL, &tv) == 1) {
union xp_sockaddr newaddr;
socklen_t addrlen = sizeof(newaddr);
SOCKET newsock = accept(listening_sock, (SOCKADDR*)&newaddr, &addrlen);
if(newsock == INVALID_SOCKET)
continue;
char tmp[256];
dprintf("Connection accepted from TCP port %hu at %s"
,inet_addrport(&newaddr), inet_addrtop(&newaddr, tmp, sizeof(tmp)));
if(sock != INVALID_SOCKET) { // In-use
SOCKADDR_IN addr;
socklen_t addrlen = sizeof(addr);
SOCKET newsock = accept(listening_sock, (SOCKADDR*)&addr, &addrlen);
if(newsock != INVALID_SOCKET) {
send(newsock, cfg.busy_notice, strlen(cfg.busy_notice), /* flags: */0);
shutdown(newsock, SD_SEND);
closesocket(newsock);
}
send(newsock, cfg.busy_notice, strlen(cfg.busy_notice), /* flags: */0);
shutdown(newsock, SD_SEND);
closesocket(newsock);
continue;
}
if(!modem->offhook && !modem->online)
sock = newsock;
addr = newaddr;
if(!modem->offhook && !modem->online) {
modem->ringing = true;
modem->ringcount = 0;
}
}
}
}
......@@ -1297,8 +1323,8 @@ int main(int argc, char** argv)
dprintf("recv returned %d", rd);
if(rd <= 0) {
int error = WSAGetLastError();
if