mirror of
https://github.com/anope/anope.git
synced 2026-06-12 19:14:47 +02:00
811ce26663
git-svn-id: svn://svn.anope.org/anope/trunk@4 31f1291d-b8d6-0310-a050-a5561fc1590b git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/trunk@2 5417fbe8-f217-4b02-8779-1006273d7864
643 lines
17 KiB
C
643 lines
17 KiB
C
/* Miscellaneous routines.
|
|
*
|
|
* (C) 2003 Anope Team
|
|
* Contact us at info@anope.org
|
|
*
|
|
* Please read COPYING and README for furhter details.
|
|
*
|
|
* Based on the original code of Epona by Lara.
|
|
* Based on the original code of Services by Andy Church.
|
|
*
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
#include "services.h"
|
|
#include "language.h"
|
|
|
|
/* Cheaper than isspace() or isblank() */
|
|
#define issp(c) ((c) == 32)
|
|
|
|
/*************************************************************************/
|
|
|
|
/* toupper/tolower: Like the ANSI functions, but make sure we return an
|
|
* int instead of a (signed) char.
|
|
*/
|
|
|
|
int toupper(char c)
|
|
{
|
|
if (islower(c))
|
|
return (unsigned char) c - ('a' - 'A');
|
|
else
|
|
return (unsigned char) c;
|
|
}
|
|
|
|
int tolower(char c)
|
|
{
|
|
if (isupper(c))
|
|
return (unsigned char) c + ('a' - 'A');
|
|
else
|
|
return (unsigned char) c;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* strscpy: Copy at most len-1 characters from a string to a buffer, and
|
|
* add a null terminator after the last character copied.
|
|
*/
|
|
|
|
char *strscpy(char *d, const char *s, size_t len)
|
|
{
|
|
char *d_orig = d;
|
|
|
|
if (!len)
|
|
return d;
|
|
while (--len && (*d++ = *s++));
|
|
*d = '\0';
|
|
return d_orig;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* stristr: Search case-insensitively for string s2 within string s1,
|
|
* returning the first occurrence of s2 or NULL if s2 was not
|
|
* found.
|
|
*/
|
|
|
|
char *stristr(char *s1, char *s2)
|
|
{
|
|
register char *s = s1, *d = s2;
|
|
|
|
while (*s1) {
|
|
if (tolower(*s1) == tolower(*d)) {
|
|
s1++;
|
|
d++;
|
|
if (*d == 0)
|
|
return s;
|
|
} else {
|
|
s = ++s1;
|
|
d = s2;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* strnrepl: Replace occurrences of `old' with `new' in string `s'. Stop
|
|
* replacing if a replacement would cause the string to exceed
|
|
* `size' bytes (including the null terminator). Return the
|
|
* string.
|
|
*/
|
|
|
|
char *strnrepl(char *s, int32 size, const char *old, const char *new)
|
|
{
|
|
char *ptr = s;
|
|
int32 left = strlen(s);
|
|
int32 avail = size - (left + 1);
|
|
int32 oldlen = strlen(old);
|
|
int32 newlen = strlen(new);
|
|
int32 diff = newlen - oldlen;
|
|
|
|
while (left >= oldlen) {
|
|
if (strncmp(ptr, old, oldlen) != 0) {
|
|
left--;
|
|
ptr++;
|
|
continue;
|
|
}
|
|
if (diff > avail)
|
|
break;
|
|
if (diff != 0)
|
|
memmove(ptr + oldlen + diff, ptr + oldlen, left + 1);
|
|
strncpy(ptr, new, newlen);
|
|
ptr += newlen;
|
|
left -= oldlen;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
/* merge_args: Take an argument count and argument vector and merge them
|
|
* into a single string in which each argument is separated by
|
|
* a space.
|
|
*/
|
|
|
|
char *merge_args(int argc, char **argv)
|
|
{
|
|
int i;
|
|
static char s[4096];
|
|
char *t;
|
|
|
|
t = s;
|
|
for (i = 0; i < argc; i++)
|
|
t += snprintf(t, sizeof(s) - (t - s), "%s%s", *argv++,
|
|
(i < argc - 1) ? " " : "");
|
|
return s;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
/* match_wild: Attempt to match a string to a pattern which might contain
|
|
* '*' or '?' wildcards. Return 1 if the string matches the
|
|
* pattern, 0 if not.
|
|
*/
|
|
|
|
static int do_match_wild(const char *pattern, const char *str, int docase)
|
|
{
|
|
char c;
|
|
const char *s;
|
|
|
|
/* This WILL eventually terminate: either by *pattern == 0, or by a
|
|
* trailing '*'. */
|
|
|
|
for (;;) {
|
|
switch (c = *pattern++) {
|
|
case 0:
|
|
if (!*str)
|
|
return 1;
|
|
return 0;
|
|
case '?':
|
|
if (!*str)
|
|
return 0;
|
|
str++;
|
|
break;
|
|
case '*':
|
|
if (!*pattern)
|
|
return 1; /* trailing '*' matches everything else */
|
|
s = str;
|
|
while (*s) {
|
|
if ((docase ? (*s == *pattern)
|
|
: (tolower(*s) == tolower(*pattern)))
|
|
&& do_match_wild(pattern, s, docase))
|
|
return 1;
|
|
s++;
|
|
}
|
|
break;
|
|
default:
|
|
if (docase ? (*str++ != c) : (tolower(*str++) != tolower(c)))
|
|
return 0;
|
|
break;
|
|
} /* switch */
|
|
}
|
|
}
|
|
|
|
|
|
int match_wild(const char *pattern, const char *str)
|
|
{
|
|
return do_match_wild(pattern, str, 1);
|
|
}
|
|
|
|
int match_wild_nocase(const char *pattern, const char *str)
|
|
{
|
|
return do_match_wild(pattern, str, 0);
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
/* Process a string containing a number/range list in the form
|
|
* "n1[-n2][,n3[-n4]]...", calling a caller-specified routine for each
|
|
* number in the list. If the callback returns -1, stop immediately.
|
|
* Returns the sum of all nonnegative return values from the callback.
|
|
* If `count' is non-NULL, it will be set to the total number of times the
|
|
* callback was called.
|
|
*
|
|
* The callback should be of type range_callback_t, which is defined as:
|
|
* int (*range_callback_t)(User *u, int num, va_list args)
|
|
*/
|
|
|
|
int process_numlist(const char *numstr, int *count_ret,
|
|
range_callback_t callback, User * u, ...)
|
|
{
|
|
int n1, n2, i;
|
|
int res = 0, retval = 0, count = 0;
|
|
va_list args;
|
|
|
|
va_start(args, u);
|
|
|
|
/*
|
|
* This algorithm ignores invalid characters, ignores a dash
|
|
* when it precedes a comma, and ignores everything from the
|
|
* end of a valid number or range to the next comma or null.
|
|
*/
|
|
for (;;) {
|
|
n1 = n2 = strtol(numstr, (char **) &numstr, 10);
|
|
numstr += strcspn(numstr, "0123456789,-");
|
|
if (*numstr == '-') {
|
|
numstr++;
|
|
numstr += strcspn(numstr, "0123456789,");
|
|
if (isdigit(*numstr)) {
|
|
n2 = strtol(numstr, (char **) &numstr, 10);
|
|
numstr += strcspn(numstr, "0123456789,-");
|
|
}
|
|
}
|
|
for (i = n1; i <= n2 && i >= 0; i++) {
|
|
int res = callback(u, i, args);
|
|
count++;
|
|
if (res < 0)
|
|
break;
|
|
retval += res;
|
|
if (count >= 32767) {
|
|
if (count_ret)
|
|
*count_ret = count;
|
|
return retval;
|
|
}
|
|
}
|
|
if (res < -1)
|
|
break;
|
|
numstr += strcspn(numstr, ",");
|
|
if (*numstr)
|
|
numstr++;
|
|
else
|
|
break;
|
|
}
|
|
if (count_ret)
|
|
*count_ret = count;
|
|
return retval;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* dotime: Return the number of seconds corresponding to the given time
|
|
* string. If the given string does not represent a valid time,
|
|
* return -1.
|
|
*
|
|
* A time string is either a plain integer (representing a number
|
|
* of seconds), or an integer followed by one of these characters:
|
|
* "s" (seconds), "m" (minutes), "h" (hours), or "d" (days).
|
|
*/
|
|
|
|
int dotime(const char *s)
|
|
{
|
|
int amount;
|
|
|
|
amount = strtol(s, (char **) &s, 10);
|
|
if (*s) {
|
|
switch (*s) {
|
|
case 's':
|
|
return amount;
|
|
case 'm':
|
|
return amount * 60;
|
|
case 'h':
|
|
return amount * 3600;
|
|
case 'd':
|
|
return amount * 86400;
|
|
default:
|
|
return -1;
|
|
}
|
|
} else {
|
|
return amount;
|
|
}
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Expresses in a string the period of time represented by a given amount
|
|
of seconds (with days/hours/minutes). */
|
|
|
|
char *duration(NickAlias * na, char *buf, int bufsize, time_t seconds)
|
|
{
|
|
int days = 0, hours = 0, minutes = 0;
|
|
int need_comma = 0;
|
|
|
|
char buf2[64], *end;
|
|
char *comma = getstring(na, COMMA_SPACE);
|
|
|
|
/* We first calculate everything */
|
|
days = seconds / 86400;
|
|
seconds -= (days * 86400);
|
|
hours = seconds / 3600;
|
|
seconds -= (hours * 3600);
|
|
minutes = seconds / 60;
|
|
|
|
if (!days && !hours && !minutes) {
|
|
snprintf(buf, bufsize,
|
|
getstring(na,
|
|
(seconds <=
|
|
1 ? DURATION_SECOND : DURATION_SECONDS)),
|
|
seconds);
|
|
} else {
|
|
end = buf;
|
|
if (days) {
|
|
snprintf(buf2, sizeof(buf2),
|
|
getstring(na,
|
|
(days == 1 ? DURATION_DAY : DURATION_DAYS)),
|
|
days);
|
|
end += snprintf(end, bufsize - (end - buf), "%s", buf2);
|
|
need_comma = 1;
|
|
}
|
|
if (hours) {
|
|
snprintf(buf2, sizeof(buf2),
|
|
getstring(na,
|
|
(hours ==
|
|
1 ? DURATION_HOUR : DURATION_HOURS)),
|
|
hours);
|
|
end +=
|
|
snprintf(end, bufsize - (end - buf), "%s%s",
|
|
(need_comma ? comma : ""), buf2);
|
|
need_comma = 1;
|
|
}
|
|
if (minutes) {
|
|
snprintf(buf2, sizeof(buf2),
|
|
getstring(na,
|
|
(minutes ==
|
|
1 ? DURATION_MINUTE : DURATION_MINUTES)),
|
|
minutes);
|
|
end +=
|
|
snprintf(end, bufsize - (end - buf), "%s%s",
|
|
(need_comma ? comma : ""), buf2);
|
|
need_comma = 1;
|
|
}
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Generates a human readable string of type "expires in ..." */
|
|
|
|
char *expire_left(NickAlias * na, char *buf, int len, time_t expires)
|
|
{
|
|
time_t now = time(NULL);
|
|
|
|
if (!expires) {
|
|
strncpy(buf, getstring(na, NO_EXPIRE), len);
|
|
} else if (expires <= now) {
|
|
strncpy(buf, getstring(na, EXPIRES_SOON), len);
|
|
} else {
|
|
time_t diff = expires - now + 59;
|
|
|
|
if (diff >= 86400) {
|
|
int days = diff / 86400;
|
|
snprintf(buf, len,
|
|
getstring(na, (days == 1) ? EXPIRES_1D : EXPIRES_D),
|
|
days);
|
|
} else {
|
|
if (diff <= 3600) {
|
|
int minutes = diff / 60;
|
|
snprintf(buf, len,
|
|
getstring(na,
|
|
(minutes ==
|
|
1) ? EXPIRES_1M : EXPIRES_M), minutes);
|
|
} else {
|
|
int hours = diff / 3600, minutes;
|
|
diff -= (hours * 3600);
|
|
minutes = diff / 60;
|
|
snprintf(buf, len,
|
|
getstring(na,
|
|
((hours == 1
|
|
&& minutes ==
|
|
1) ? EXPIRES_1H1M : ((hours == 1
|
|
&& minutes !=
|
|
1) ? EXPIRES_1HM
|
|
: ((hours != 1
|
|
&& minutes ==
|
|
1) ?
|
|
EXPIRES_H1M :
|
|
EXPIRES_HM)))),
|
|
hours, minutes);
|
|
}
|
|
}
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* Return 1 if a host is valid, 0 if it isnt.
|
|
* host = string to check
|
|
* type = format, 1 = ip4addr, 2 = hostname
|
|
*
|
|
* shortname = ( letter / digit ) *( letter / digit / "-" ) *( letter / digit )
|
|
* hostname = shortname *( "." shortname )
|
|
* ip4addr = 1*3digit "." 1*3digit "." 1*3digit "." 1*3digit
|
|
*
|
|
**/
|
|
|
|
int doValidHost(const char *host, int type)
|
|
{
|
|
int idx = 0;
|
|
int len = 0;
|
|
int sec_len = 0;
|
|
int dots = 1;
|
|
if (type != 1 && type != 2) {
|
|
return 0;
|
|
}
|
|
if (!host) {
|
|
return 0;
|
|
}
|
|
|
|
len = strlen(host);
|
|
|
|
if (len > HOSTMAX) {
|
|
return 0;
|
|
}
|
|
|
|
switch (type) {
|
|
case 1:
|
|
for (idx = 0; idx < len; idx++) {
|
|
if (isdigit(host[idx])) {
|
|
if (sec_len < 3) {
|
|
sec_len++;
|
|
} else {
|
|
return 0;
|
|
}
|
|
} else {
|
|
if (idx == 0) {
|
|
return 0;
|
|
} /* cant start with a non-digit */
|
|
if (host[idx] != '.') {
|
|
return 0;
|
|
} /* only . is a valid non-digit */
|
|
if (sec_len > 3) {
|
|
return 0;
|
|
} /* sections cant be more than 3 digits */
|
|
sec_len = 0;
|
|
dots++;
|
|
}
|
|
}
|
|
if (dots != 4) {
|
|
return 0;
|
|
}
|
|
break;
|
|
case 2:
|
|
dots = 0;
|
|
for (idx = 0; idx < len; idx++) {
|
|
if (!isalnum(host[idx])) {
|
|
if (idx == 0) {
|
|
return 0;
|
|
}
|
|
if ((host[idx] != '.') && (host[idx] != '-')) {
|
|
return 0;
|
|
}
|
|
if (host[idx] == '.') {
|
|
dots++;
|
|
}
|
|
}
|
|
}
|
|
if (host[len - 1] == '.') {
|
|
return 0;
|
|
}
|
|
/**
|
|
* Ultimate3 dosnt like a non-dotted hosts at all, nor does unreal,
|
|
* so just dont allow them.
|
|
**/
|
|
if (dots == 0) {
|
|
return 0;
|
|
}
|
|
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Return 1 if a host is valid, 0 if it isnt.
|
|
* host = string to check
|
|
* type = format, 1 = ip4addr, 2 = hostname, 3 = either
|
|
*
|
|
* shortname = ( letter / digit ) *( letter / digit / "-" ) *( letter / digit )
|
|
* hostname = shortname *( "." shortname )
|
|
* ip4addr = 1*3digit "." 1*3digit "." 1*3digit "." 1*3digit
|
|
*
|
|
**/
|
|
int isValidHost(const char *host, int type)
|
|
{
|
|
int status = 0;
|
|
if (type == 3) {
|
|
if (!(status = doValidHost(host, 1))) {
|
|
status = doValidHost(host, 2);
|
|
}
|
|
} else {
|
|
status = doValidHost(host, type);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
int isvalidchar(const char c)
|
|
{
|
|
if (((c >= 'A') && (c <= 'Z')) ||
|
|
((c >= 'a') && (c <= 'z')) ||
|
|
((c >= '0') && (c <= '9')) || (c == '.') || (c == '-'))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
char *myStrGetToken(const char *str, const char dilim, int token_number)
|
|
{
|
|
int len, idx, counter = 0, start_pos = 0;
|
|
char *substring = NULL;
|
|
if (!str) {
|
|
return NULL;
|
|
}
|
|
len = strlen(str);
|
|
for (idx = 0; idx <= len; idx++) {
|
|
if ((str[idx] == dilim) || (idx == len)) {
|
|
if (counter == token_number) {
|
|
substring = myStrSubString(str, start_pos, idx);
|
|
counter++;
|
|
} else {
|
|
start_pos = idx + 1;
|
|
counter++;
|
|
}
|
|
}
|
|
}
|
|
return substring;
|
|
}
|
|
|
|
char *myStrGetOnlyToken(const char *str, const char dilim,
|
|
int token_number)
|
|
{
|
|
int len, idx, counter = 0, start_pos = 0;
|
|
char *substring = NULL;
|
|
if (!str) {
|
|
return NULL;
|
|
}
|
|
len = strlen(str);
|
|
for (idx = 0; idx <= len; idx++) {
|
|
if (str[idx] == dilim) {
|
|
if (counter == token_number) {
|
|
if (str[idx] == '\r')
|
|
substring = myStrSubString(str, start_pos, idx - 1);
|
|
else
|
|
substring = myStrSubString(str, start_pos, idx);
|
|
counter++;
|
|
} else {
|
|
start_pos = idx + 1;
|
|
counter++;
|
|
}
|
|
}
|
|
}
|
|
return substring;
|
|
}
|
|
|
|
char *myStrGetTokenRemainder(const char *str, const char dilim,
|
|
int token_number)
|
|
{
|
|
int len, idx, counter = 0, start_pos = 0;
|
|
char *substring = NULL;
|
|
if (!str) {
|
|
return NULL;
|
|
}
|
|
len = strlen(str);
|
|
|
|
for (idx = 0; idx <= len; idx++) {
|
|
if ((str[idx] == dilim) || (idx == len)) {
|
|
if (counter == token_number) {
|
|
substring = myStrSubString(str, start_pos, len);
|
|
counter++;
|
|
} else {
|
|
start_pos = idx + 1;
|
|
counter++;
|
|
}
|
|
}
|
|
}
|
|
return substring;
|
|
}
|
|
|
|
char *myStrSubString(const char *src, int start, int end)
|
|
{
|
|
char *substring = NULL;
|
|
int len, idx;
|
|
if (!src) {
|
|
return NULL;
|
|
}
|
|
len = strlen(src);
|
|
if (((start >= 0) && (end <= len)) && (end > start)) {
|
|
substring = (char *) malloc(sizeof(char) * ((end - start) + 1));
|
|
for (idx = 0; idx <= end - start; idx++) {
|
|
substring[idx] = src[start + idx];
|
|
}
|
|
substring[end - start] = '\0';
|
|
}
|
|
return substring;
|
|
}
|
|
|
|
void doCleanBuffer(char *str)
|
|
{
|
|
char *in = str;
|
|
char *out = str;
|
|
char ch;
|
|
|
|
while (issp(ch = *in++));
|
|
if (ch != '\0')
|
|
for (;;) {
|
|
*out++ = ch;
|
|
ch = *in++;
|
|
if (ch == '\0')
|
|
break;
|
|
if (!issp(ch))
|
|
continue;
|
|
while (issp(ch = *in++));
|
|
if (ch == '\0')
|
|
break;
|
|
*out++ = ' ';
|
|
}
|
|
*out = ch; // == '\0'
|
|
}
|