isdn4k-utils/isdnlog/tools/dest.c

491 lines
12 KiB
C

/*
* Destination handling
*
* Copyright 1999 by Leopold Toetsch <lt@toetsch.at>
*
* 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, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Changes:
*
* 1.00 08.10.1999 lt Initial Version
* 1.01 26.07.2000 lt Added cdb support
*/
/* #define DEBUG */
#define _DEST_C_
#ifdef STANDALONE
# include <stdlib.h>
# include <stddef.h>
# include <stdio.h>
# ifdef __GLIBC__
# define __USE_GNU /* for declaration of basename() */
# endif
# include <string.h>
# include <ctype.h>
# include <stdarg.h>
# include <time.h>
# include <unistd.h>
# include <errno.h>
# if !defined(__GLIBC__) && !defined(basename)
extern const char *basename(const char *name);
# endif
#else
# include "isdnlog.h"
# include "tools.h"
#endif
#include "dest.h"
#include "zone/config.h"
#include "zone/common.h"
#ifndef LENGTH
#define LENGTH 256
#endif
#ifndef UNKNOWN
#define UNKNOWN -1
#endif
#ifdef DESTTEST
char *Strncpy(char *dest, const char *src, int len);
char *Strncat(char *dest, const char *src, int len);
#endif
#ifndef min
#define min(x,y) (x)<(y)?(x):(y)
#endif
static char version[] = "1.01";
static _DB db; /* our dest.db */
static int init_ok=0;
typedef struct {
char number[TN_MAX_NUM_LEN];
TELNUM num;
int lru;
} num_cache_t;
#define CACHE_SIZE 10
static num_cache_t num_cache[CACHE_SIZE];
static void add_cache(char *number, TELNUM *num) {
int i, mlru,m;
mlru=9999;
m=0;
for (i=0; i<CACHE_SIZE; i++) {
if(!*num_cache[i].number) {
Strncpy(num_cache[i].number, number, TN_MAX_NUM_LEN);
memcpy(&num_cache[i].num, num, sizeof(TELNUM));
break;
}
if(num_cache[i].lru<mlru) {
m=i;
mlru=num_cache[i].lru;
}
}
if(i==CACHE_SIZE) {
Strncpy(num_cache[m].number, number, TN_MAX_NUM_LEN);
memcpy(&num_cache[m].num, num, sizeof(TELNUM));
num_cache[m].lru=1;
}
}
static int get_cache(char *number, TELNUM *num) {
int i;
for (i=0; i<CACHE_SIZE; i++) {
if (!num_cache[i].number)
break;
num_cache[i].lru--;
if(strcmp(num_cache[i].number, number) == 0) {
num_cache[i].lru++;
memcpy((char*)num+offsetof(TELNUM,scountry),
(char*)&num_cache[i].num+offsetof(TELNUM,scountry),
sizeof(TELNUM)-offsetof(TELNUM,scountry));
return true;
}
}
return false;
}
static void warning(char *fmt,...)
{
va_list ap;
char msg[BUFSIZ];
va_start(ap, fmt);
vsnprintf(msg, BUFSIZ, fmt, ap);
va_end(ap);
#ifdef STANDALONE
fprintf(stderr, "WARNING: %s\n", msg);
#else
print_msg(PRT_NORMAL, "WARNING: %s\n", msg);
#endif
}
void exitDest(void)
{
CLOSE(db);
init_ok=0;
}
int initDest(char *path, char **msg)
{
static char message[LENGTH];
char vinfo[] = "vErSiO";
datum key, value;
if (msg)
*(*msg = message) = '\0';
if (init_ok == 1)
return 0;
else if(init_ok == -1)
return -1;
if (!path || !*path) {
if (msg)
snprintf(message, LENGTH,
"Dest V%s: Error: no destination database specified!", version);
return init_ok = -1;
}
if ((db = OPEN(path, READ)) == 0) {
if (msg)
snprintf(message, LENGTH,
"Dest V%s: Error: open '%s': '%s'",
version, path, GET_ERR);
return -1;
}
/* read info */
key.dptr = vinfo;
key.dsize = 7;
value = FETCH(db, key);
if (value.dptr == 0) {
if (msg)
snprintf(message, LENGTH,
"Dest V%s: Error: File '%s': no Vinfo",
version, path);
exitDest();
return init_ok = -1;
}
if (msg)
snprintf(message, LENGTH,
"Dest V%s: File '%s' opened fine - %s", version, path, value.dptr);
if (*dbv == 'G')
free(value.dptr);
init_ok = 1;
return 0;
}
static void append(char *dest, char *what)
{
if (!what || !*what)
return;
if (strcmp(dest, what)) {
if(*dest)
Strncat(dest, "/", TN_MAX_SAREA_LEN);
Strncat(dest, what, TN_MAX_SAREA_LEN);
}
}
static bool isKey(const char *p)
{
bool key;
if ( (key = !isdigit(*p)) )
for (; key && *p; p++)
if(!isupper(*p) && *p != '_' && !isdigit(*p)) { /* e.g. _DEMD1 */
key = false;
break;
}
return key;
}
int getDest(char *onumber, TELNUM * num)
{
bool first = true;
size_t len;
datum key, value, nvalue;
static char unknown[] = "Unknown";
char *p, *q, *r = 0, *city = 0, *s, *name;
int arealen, countrylen, prefixlen;
char *number = strdup(onumber);
char dummy[100]; /* V2.7.2.3 kills stack */
char tld[TN_MAX_TLD_LEN];
char cc_ndc[TN_MAX_COUNTRY_LEN+TN_MAX_AREA_LEN];
char dummy2[100]; /* V2.7.2.3 kills stack */
#ifdef DEBUG
printf("getD. %s\n", number);
#endif
*dummy = *dummy2 = '\0'; /* for keeping gcc happy */
*tld='\0';
*cc_ndc='\0';
if (get_cache(number, num)) {
#ifdef DEBUG
printf("getD (cache). %s %s\n", number, formatNumber("%f",num));
#endif
free(number);
return 0;
}
len = strlen(number);
if (len==2 && isalpha(*number) && isupper(*number))
Strncpy(tld,number,TN_MAX_TLD_LEN);
if (isdigit(*number)) {
warning("getDest called with local number '%s'", number);
add_cache(number, num);
return UNKNOWN;
}
countrylen = arealen = prefixlen = 0;
num->ncountry = 0;
num->narea = 0;
*num->area = '\0';
*num->sarea = '\0';
*num->scountry = '\0';
*num->country = '\0';
*num->msn = '\0';
*num->keys = '\0';
if (len > 1 && !isdigit(number[1]) && !isKey(number))
city = strdup(number);
again:
key.dptr = number;
key.dsize = len;
value = FETCH(db, key);
again2:
if (value.dptr != 0) {
/* we have a value:
* it could be
* :RKEY ... pointer to a KEY
* :city ... pointer to a city
* name;code ... top level entry i.e country
* name;codes[;:KEY] ... region
* [#len];code;:KEY ... city
*/
while (value.dptr && *value.dptr == ':') {
/* check for city, i.e. lowercase chars */
if (!isKey(value.dptr + 1)) {
city = strdup(value.dptr + 1);
#ifdef DEBUG
printf("C. %s\n", city);
#endif
}
else {
append(num->keys, value.dptr + 1);
Strncpy(tld,value.dptr+1,TN_MAX_TLD_LEN);
}
key.dptr = value.dptr + 1;
key.dsize = value.dsize - 2; /* w/o : and \x0 */
nvalue = FETCH(db, key);
if (*dbv == 'G')
free(value.dptr);
value = nvalue;
}
/* do we have something valid */
if (value.dptr == 0 && first) {
strcpy(num->scountry, unknown);
strcpy(num->sarea, unknown);
Strncpy(num->msn, number, TN_MAX_MSN_LEN);
free(number);
if (city)
free(city);
return 0;
}
/* now we must have a name or city */
first = false;
s = value.dptr;
p = strsep(&s, ";");
/* name or #len or empty */
name = 0;
#ifdef DEBUG
printf("1. %s\n", p);
#endif
if (p && *p) {
if (*p == '#')
prefixlen = atoi(p + 1);
else
name = strdup(p);
}
p = strsep(&s, ";");
/* codes or empty */
#ifdef DEBUG
printf("2. %s\n", p);
#endif
if (p && *p) {
q = strtok(p, ","); /* we could have multiple codes */
if (arealen == 0) { /* first, longest, best matching area code */
char *n;
/* for number as input get area code from this number, which is already
* shortened if necessary (full number in onumber), for name as input
* use the first number from the dest.DB lookup for this name */
n = (*number == '+') ? number : q;
arealen = strlen(n);
Strncpy(cc_ndc, n, TN_MAX_COUNTRY_LEN+TN_MAX_AREA_LEN);
}
if (strstr(cc_ndc, q)) /* only if new number has same prefix */
countrylen = strlen(q); /* last one must be country */
else
r = strtok(NULL, ";"); /* save remaining number codes if any */
}
p = strsep(&s, ";");
/* :KEY or empty */
#ifdef DEBUG
printf("3. %s\n", p);
#endif
/* we should be at toplevel i.e country */
if (!p) {
append(num->scountry, name);
if (!countrylen) /* countrylen missing */
while (r) { /* test remaining number codes */
q = strsep(&r, ",");
if (strstr(cc_ndc, q)) {
countrylen = strlen(q);
break;
}
}
if (countrylen && (arealen || prefixlen)) {
append(num->sarea, city);
/* if country or area are too long, the exceeding digits will be
* stored in area or msn. */
countrylen = min(countrylen, TN_MAX_COUNTRY_LEN-1);
Strncpy(num->country, cc_ndc, countrylen+1);
num->ncountry = atoi(num->country+1);
strcpy(num->tld,tld);
p = cc_ndc + countrylen;
arealen -= countrylen;
if(prefixlen)
arealen=prefixlen;
arealen = min(arealen, TN_MAX_AREA_LEN-1);
Strncpy(num->area, p, 1 + arealen);
num->narea = atoi(num->area);
if (*onumber == '+' && strlen(onumber) > arealen + countrylen)
Strncpy(num->msn, onumber + arealen + countrylen, TN_MAX_MSN_LEN);
add_cache(onumber, num);
}
}
else if (p && *p == ':') {
/* do we have a code */
append(num->sarea, name);
append(num->keys, p + 1);
Strncpy(tld,p+1,TN_MAX_TLD_LEN);
key.dptr = p + 1;
key.dsize = strlen(p + 1);
nvalue = FETCH(db, key);
if (*dbv == 'G')
free(value.dptr);
value = nvalue;
goto again2;
}
if (*dbv == 'G')
free(value.dptr);
free(number);
if (city)
free(city);
return 0;
} /* if value */
else if (first && len && *number == '+') { /* try shorter nums */
number[--len] = '\0';
goto again; /* I like it */
}
if (number)
free(number);
if (city)
free(city);
return UNKNOWN;
}
#ifdef STANDALONE
char *Strncpy(char *dest, const char *src, int len)
{
int l = strlen(src);
if (l > len - 1)
l = len - 1;
strncpy(dest, src, l);
dest[l] = '\0';
return dest;
}
char *Strncat(char *dest, const char *src, int len)
{
int destlen = strlen(dest);
return Strncpy(dest + destlen, src, len - destlen);
}
#endif
#ifdef DESTTEST
int main(int argc, char *argv[])
{
char *msg;
TELNUM num;
int i = 1, res, verbose = 0;
if (initDest("./dest" RDBEXT, &msg)) {
fprintf(stderr, "%s\n", msg);
exit(EXIT_FAILURE);
}
fprintf(stderr, "%s\n", msg);
if (argc == 1) {
fprintf(stderr, "Usage:\n\t%s [-v] number|name ...\n", basename(argv[0]));
exit(EXIT_FAILURE);
}
if (!strcmp(argv[i],"-v")) { /* handle verbose option in a pragmatic way */
verbose++;
i++;
argc--;
}
memset(&num, 0, sizeof(num));
while (--argc) {
res = getDest(argv[i++], &num);
printf("%s %s(%d)=%s %s(%s) %s - %s\n",
res == 0 ? "Ok." : "Err", num.country, num.ncountry, num.scountry,
num.sarea, num.area,
num.msn, num.keys);
if (verbose) { /* show TELNUM structure in detail */
printf("\nThe struct TELNUM for it:\n");
printf(" .vbn (%2d of %2d): %s\n",
strlen(num.vbn), TN_MAX_VBN_LEN-1, num.vbn);
printf(" .provider (%2d of %2d): %s\n",
strlen(num.provider), TN_MAX_PROVIDER_LEN-1, num.provider);
printf(" .nprovider : %i\n", num.nprovider);
printf(" .scountry (%2d of %2d): %s\n",
strlen(num.scountry), TN_MAX_SCOUNTRY_LEN-1, num.scountry);
printf(" .country (%2d of %2d): %s\n",
strlen(num.country), TN_MAX_COUNTRY_LEN-1, num.country);
printf(" .keys (%2d of %2d): %s\n",
strlen(num.keys), TN_MAX_SCOUNTRY_LEN-1, num.keys);
printf(" .tld (%2d of %2d): %s\n",
strlen(num.tld), TN_MAX_TLD_LEN-1, num.tld);
printf(" .ncountry : %i\n", num.ncountry);
printf(" .area (%2d of %2d): %s\n",
strlen(num.area), TN_MAX_AREA_LEN-1, num.area);
printf(" .narea : %i\n", num.narea);
printf(" .sarea (%2d of %2d): %s\n",
strlen(num.sarea), TN_MAX_SAREA_LEN-1, num.sarea);
printf(" .msn (%2d of %2d): %s\n\n",
strlen(num.msn), TN_MAX_MSN_LEN-1, num.msn);
}
}
exitDest();
return (EXIT_SUCCESS);
}
#endif