/* ** Modular Logfile Analyzer ** Copyright 2000 Jan Kneschke ** ** Homepage: http://www.modlogan.org ** 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, and provided that the above copyright and permission notice is included with all distributed copies of this or derived software. 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA ** ** $Id: misc.c,v 1.41 2004/03/18 02:31:50 ostborn Exp $ */ #include #include #include #include #include #include #include #include "misc.h" #include "mlocale.h" /** * convert URL-encoded string into a plain c-string * * takes care of non printable characters and replaces them by '_'. * The orignal string is desctroyed !! * * @param url URL-encoded string * @return the decoded string */ char *urlescape(char *url) { unsigned char *l = url; unsigned char *o = url; while (*l) { switch(*l) { case '+': *o = ' '; break; case '%': if (l[1] != '%') { if (isxdigit(l[1]) && isxdigit(l[2])) { unsigned char c = (hex2int(l[1]) << 4) + hex2int(l[2]); if (c < ' ' || c == 127 ) c = '_'; *o = c; l += 2; break; } } else l++; default: *o = *l; } o++; l++; } *o = '\0'; return url; } /** * Checks if the specified string is a HTML color tripple * * @param col a HTML-color tripple (e.g.: "#FFEE76") * @return 0 for failure, 1 for success */ int is_htmltripple(const char *col) { if (!col || *col != '#' || strlen(col) != 7) return 0; while(*(++col)) { if (!isxdigit(*col)) return 0; } return 1; } /** * Converts a HTML-tripple to a internal RGB-struct * * @param html3 a HTML-color tripple (e.g.: "#FFEE76") * @param rgb pointer to a internal rgb structure * @return 0 for failure, 1 for success */ int html3torgb3(const char *html3, rgb_tripple * rgb) { if (!is_htmltripple(html3)) return 0; rgb->r = (hex2int(html3[1]) << 4) + hex2int(html3[2]); rgb->g = (hex2int(html3[3]) << 4) + hex2int(html3[4]); rgb->b = (hex2int(html3[5]) << 4) + hex2int(html3[6]); return 1; } /** * the m// operator from perl * * @param pattern the compiled pcre pattern * @param str the string to match * @return 0 for failure, 1 for success */ int strmatch(pcre *pattern, pcre_extra *study, const char *str, int str_len) { #define N 20 + 1 int ovector[3 * N], n; if (!pattern || !str) { fprintf(stderr, "%s.%d: should never be displayed: %p, %p\n", __FILE__, __LINE__, pattern, str); return 0; } if ((n = pcre_exec(pattern, study, str, str_len, 0, 0, ovector, 3 * N)) < 0) { if (n != PCRE_ERROR_NOMATCH) { fprintf(stderr, "%s.%d: execution error while matching: %d\n", __FILE__, __LINE__, n); } return 0; } #undef N return 1; } char *substitute(mconfig *ext_conf, pcre *match, pcre_extra *study, char *subst, const char *str, int str_len) { char *name = NULL; /* name if the state -> directory-name */ #define N 20 int ovector[3 * N], n; /* the dawn string */ if ((n = pcre_exec(match, study, str, str_len, 0, 0, ovector, 3 * N)) < 0) { if (n != PCRE_ERROR_NOMATCH) { fprintf(stderr, "%s.%d: execution error while matching: %d\n", __FILE__, __LINE__, n); return NULL; } } /* the string has matched somehow */ if (n >= 0) { char *s; /* */ int sl = 0; /* calculated string length */ int _err = 0; /* error flag */ s = subst; /* check if we can safely create the state-name */ while (*s && _err == 0) { if (*s == '$') { if (*(s+1) == '_') s++; if (*(s+1) == '^') s++; if (isdigit(*(s+1))) { int d = *(s+1) - '0'; if (d >= n || d < 0) { _err = 1; fprintf(stderr, "%s.%d: match-index out of range: %s - %d\n", __FILE__, __LINE__, subst, d); } else { sl += ovector[d*2+1] - ovector[d*2]; s += 2; } } else { fprintf(stderr, "%s.%d: $ isn't followed by a digit: %s\n", __FILE__, __LINE__, subst); _err = 1; } } else { sl++; s++; } } #if 0 fprintf(stderr, "--> sl: %d, str: %s, %d, %s, %d\n", sl, str, strlen(str), subst, n); #endif if (_err == 0) { char *dst; /* the string is ok. copy the string accordingly to the def */ s = subst; name = malloc(sl+1); *name = '\0'; dst = name; while (*s) { if (*s == '$') { int doupper = 0; int dolower = 0; int d; if (*(s+1) == '_') { dolower = 1; s++; } if (*(s+1) == '^') { doupper = 1; s++; } d = *(s+1) - '0'; if ( d <= n ) { int d_len = ovector[d*2+1] - ovector[d*2]; const char * d_start = str + ovector[d*2]; #if 0 fprintf(stderr, "%d - %s\n", d_len, d_start); #endif if (!dolower && !doupper) { strncpy(dst, d_start, d_len); } else { int strcasecpy; for(strcasecpy = 0; strcasecpy < d_len; strcasecpy++) { *(dst + strcasecpy) = dolower ? tolower(*(d_start + strcasecpy)) : toupper(*(d_start + strcasecpy)); } } dst += d_len; s += 2; } else { /* just a paranoid check */ fprintf(stderr, "%s.%d: ALARM !!! REPORT ME: (should have already been detected)\n", __FILE__, __LINE__); fprintf(stderr, "%s.%d: requested index '$%d' is put of range (0-%d). check your splitby definition", __FILE__, __LINE__, d, n); fprintf(stderr, "%s.%d: using $%d as of the directoryname instead.", __FILE__, __LINE__, d); *dst++ = *s++; } } else { *dst++ = *s++; } *dst = '\0'; if (dst - name > sl) { fprintf(stderr, "%s.%d: ALARM !!! REPORT ME: dst - name > sl [ %d > %d ]. possible SEGV: %s, %s\n", __FILE__, __LINE__, dst - name, sl, name, str); } } /* just a paranoid check */ if (strlen(name) > sl) { fprintf(stderr, "%s.%d: ALARM !!! REPORT ME: strlen(name) > sl [ %d > %d ]. possible SEGV: %s, %s\n", __FILE__, __LINE__, strlen(name), sl, name, str); } } } return name; } static unsigned char hexchars[] = "0123456789ABCDEF"; /*** URL-Encode a string - allocates memory for new string, don't forget to free() ! ***/ char *url_encode(const char *s) { /* x and y denote the position in source respective destination string */ register int x, y; unsigned char *str; if (!s) return NULL; /* calculate the actual length of the resulting string */ for (x = 0, y = 0; s[x] != '\0'; x++, y++) { y++; /* anything except regular chars is hex-encoded */ if ((s[x] < '-') || (s[x] < 'A' && s[x] > '9') || (s[x] > 'Z' && s[x] < 'a' && s[x] != '_') || (s[x] > 'z')) { y += 2; } } /* allocate memory for encoded string */ str = (unsigned char *) malloc(y + 1); /* loop until end of string */ for (x = 0, y = 0; s[x] != '\0'; x++, y++) { str[y] = (unsigned char) s[x]; /* spaces are replaced with + */ if (str[y] == ' ') { str[y] = '+'; /* anything except regular chars is hex-encoded */ } else if ((str[y] < '-') || (str[y] < 'A' && str[y] > '9') || (str[y] > 'Z' && str[y] < 'a' && str[y] != '_') || (str[y] > 'z')) { str[y++] = '%'; str[y++] = hexchars[(unsigned char) s[x] >> 4]; str[y] = hexchars[(unsigned char) s[x] & 15]; } } str[y] = '\0'; return ((char *) str); } /*** URL-DEcode a string - allocates memory for new string, don't forget to free() ! ***/ char* url_decode(const char *s) { unsigned char *str; /* work pointers */ const unsigned char *data; unsigned char *dest; int dup_me = 1; int new_len = 0; if (!s) return NULL; /* dup the string if it is not encoded */ for (data = s; *data; data++) { if (*data == '+') { dup_me = 0; } else if (*data == '%' && isxdigit((int) *(data + 1)) && isxdigit((int) *(data + 2))) { new_len -= 2; dup_me = 0; } new_len++; } if (dup_me) return strdup(s); /* allocate memory for decoded string */ str = (unsigned char *) malloc(new_len + 1); data = s; dest = str; /* until the end of the string */ while (*data != '\0') { if (*data == '+') *dest = ' '; else if (*data == '%' && isxdigit((int) *(data + 1)) && isxdigit((int) *(data + 2))) { *dest = (hex2int(data[1]) << 4) + hex2int(data[2]); data += 2; } else *dest = *data; data++; dest++; } /* add a string terminator */ *dest = '\0'; return str; } /*** URL-DEcode a string - doesn't allocate memory for new string, don't free() ! ***/ void url_decode_on_self(char *s) { /* work pointers */ const unsigned char *data = s; unsigned char *dest = s; if (!s) return; /* until the end of the string */ while (*data != '\0') { if (*data == '+') *dest = ' '; else if (*data == '%' && isxdigit((int) *(data + 1)) && isxdigit((int) *(data + 2))) { *dest = (hex2int(data[1]) << 4) + hex2int(data[2]); data += 2; } else *dest = *data; data++; dest++; } /* add a string terminator */ *dest = '\0'; } /* Returns 1 if parameter is an IPv4 address */ int is_ip(const char *ip) { int n[4]; int i = 0; int x = 1; int l; char *p; if (!ip || *ip < '0' || *ip > '9' || (l = strlen(ip)) > 15 || l < 7) return 0; n[0] = n[1] = n[2] = n[3] = 0; p = (char *)&ip[l]; do { p--; if (*p == '.') { i++; if (i>3) return 0; x = 1; } else if (*p >= '0' || *p <= '9') { n[i] += (*p - '0') * x; if (n[i] > 255) return 0; x *= 10; } else { return 0; } } while (p != ip); if (i < 3) return 0; return 1; } /* Convert a number of seconds to string, format depends on type, see below */ char *seconds_to_string(double s, int type) { static char buf[255]; unsigned int mins, hours, days, secs; days = (s/60/60/24); hours = (unsigned int)(s/60/60)%24; mins = (unsigned int)(s/60)%60; secs = (unsigned int)(s)%60; switch (type) { case 1: if (days) { /* 2 days 10:12:05 */ snprintf(buf, 255, "%u %s %02u:%02u:%02u", days, days==1?_("day"):_("days"), hours, mins, secs); } else { /* 10:12:05 */ snprintf(buf, 255, "%02u:%02u:%02u", hours, mins, secs); } break; default: if (days) /* 123:12:14:05 */ snprintf(buf, 255, "%03u:%02u:%02u:%02u", days, hours, mins, secs); else /* 12:14:05 */ snprintf(buf, 255, "%02u:%02u:%02u", hours, mins, secs); break; } return (char *)buf; } /* Convert a number of bytes to a human readable string. */ char *bytes_to_string(double bytes) { static char buf[255]; char c = ' '; if (bytes > 1024) {bytes /= 1024; c = 'k';} if (bytes > 1024) {bytes /= 1024; c = 'M';} if (bytes > 1024) {bytes /= 1024; c = 'G';} if (bytes > 1024) {bytes /= 1024; c = 'T';} snprintf(buf, 255, "%.2f %cb", bytes, c); return buf; } #ifdef ENABLE_INDENT_HTML /* Output depth spaces to file f. Used for html indentation. */ void indent(FILE *f, int depth) { static int firsttime = 1; static char buf[1024]; if (depth > 1023) depth = 1023; else if (depth < 1) return; if (firsttime) { memset(buf, ' ', 1024); firsttime = 0; } buf[depth] = '\0'; fprintf(f, buf); buf[depth] = ' '; } #endif char *html_encode(const char *s) { char *p; char *q = NULL; int q_len = 0; if (!s) return NULL; q_len = strlen(s) * 2 + 1; q = malloc(q_len); p = q; *p = '\0'; while (*s) { switch(*s) { case '<': *p = '&'; *(++p) = 'l'; *(++p) = 't'; *(++p) = ';'; break; case '>': *p = '&'; *(++p) = 'g'; *(++p) = 't'; *(++p) = ';'; break; case 'ä': case 'ö': case 'ü': case 'Ä': case 'Ö': case 'Ü': *p = '&'; switch(*s) { case 'ä': *(++p) = 'a'; break; case 'ö': *(++p) = 'o'; break; case 'ü': *(++p) = 'u'; break; case 'Ä': *(++p) = 'A'; break; case 'Ö': *(++p) = 'O'; break; case 'Ü': *(++p) = 'U'; break; } *(++p) = 'u'; *(++p) = 'm'; *(++p) = 'l'; *(++p) = ';'; break; default: *p = *s; break; } *(++p) = '\0'; s++; if (strlen(q) > (q_len - 4)) { q_len += 128; q = realloc(q, q_len); p = q + strlen(q); } } return q; } /* tla -> Three Letter Abbreviation */ char *get_month_string(int m, int tla) { int nm,nmn; static char monthname[255]; struct tm *tm; time_t t = time(NULL); tm = localtime(&t); /* dkl */ nm = (m>12) ? m / 12 : 0; nmn = (nm>0) ? m - (nm * 12) : m; m=nmn; /* end dkl */ tm->tm_mon = m > 0 ? m - 1 : 11; strftime(monthname, sizeof(monthname)-1, tla ? "%b" : "%B", tm); return monthname; } /* Insert the content of file named filename in opened file f, info is a * comment field for errors, file is calling c file (__FILE__) and line * __LINE__ * On error, it returns -1 * On success, it returns 0 */ int _include_file(FILE *out, const char *filename, const char *info, const char *file, int line) { char newerrmsg[1024] = ""; static char olderrmsg[sizeof(newerrmsg)] = ""; if (filename) { FILE *inf = fopen(filename, "r"); if (!inf) { snprintf(newerrmsg, sizeof(newerrmsg), "%s.%d: Can't open %s : %s: %s", file, line, info, filename, strerror(errno)); goto error; } else { char buf[1024]; while (fgets(buf, sizeof(buf)-1, inf)) { if (fputs(buf, out) == EOF) { snprintf(newerrmsg, sizeof(newerrmsg), "%s.%d: Can't include %s : %s: %s", file, line, info, filename, strerror(errno)); fclose(inf); goto error; } } fclose(inf); return 0; } } error: if (*newerrmsg && strcmp(newerrmsg, olderrmsg)) { fprintf(stderr, "%s\n", newerrmsg); memcpy(olderrmsg, newerrmsg, sizeof(newerrmsg)); } return -1; } #ifndef HAVE_STRDUP /* Returns a copy of str in an allocated string. */ char *m_strdup(char *str) { char *ret = NULL; if (str) { int slen = strlen(str) + 1; ret = (char *)malloc(slen); if (!ret) return NULL; memcpy(ret, str, slen); } return ret; } #endif #ifndef HAVE_STRNDUP #warning USE STRNDUP /* Returns a copy of n chars from str in an allocated string. * Ending '\0' is added. */ char *m_strndup(const char *str, unsigned int len) { char *ret = NULL; if (str) { int strl = strlen(str); int slen = ((len < strl) ? len : strl); ret = (char *)malloc(slen + 1); if (!ret) return NULL; memcpy(ret, str, slen); ret[slen] = '\0'; } return ret; } #endif #ifndef HAVE_MEMRCHR #warning USE MEMRCHR void *m_memrchr(const void *block, char c, unsigned int size) { register char *q; char *p; for (p = (unsigned char *) block, q = p + size - 1; q >= p; q--) if (*q == c) return (void *) q; return NULL; } #endif #ifndef HAVE_TIMEGM /* Emulates timegm() */ /* Portability note from GLIBC documentation: * mktime is essentially universally available. * timegm is rather rare. For the most portable conversion from a * UTC broken-down time to a simple time, set the TZ environment * variable to UTC, call mktime, then set TZ back. */ time_t mkutctime(struct tm *brokentime) { char *tz = getenv("TZ"); time_t time; #if defined(HAVE_SETENV) && defined(HAVE_UNSETENV) setenv("TZ", "UTC", 1); time = mktime(brokentime); if (tz) setenv("TZ", tz, 1); else unsetenv("TZ"); #else /* only putenv() is available */ putenv("TZ=UTC"); time = mktime(brokentime); if (tz) { char tmp[64]; /* We don't want any buffer overflow here so... */ if (strlen(tz) + 3 + 1 <= sizeof(tmp)) { strcpy(tmp, "TZ="); strcat(tmp, tz); putenv(tmp); } } else putenv("TZ"); #endif tzset(); return time; } #endif int dir_check_perms(const char *dirname) { struct stat st; if (stat(dirname, &st) != 0) { fprintf(stderr, "ERROR: can't check if the directory is ok (%s): %s\n", dirname, strerror(errno)); return -1; } else { if (S_ISDIR(st.st_mode) && ((((st.st_mode & S_IRWXU) == S_IRWXU) && st.st_uid == getuid()) || (((st.st_mode & S_IRWXG) == S_IRWXG) && st.st_gid == getgid()) || (((st.st_mode & S_IRWXO) == S_IRWXO)) || getuid() == 0 || geteuid() == 0)) { /* every thing seems to be ok. */ } else { fprintf(stderr, "ERROR: the directory doesn't have the read and write permissions: %s\n", dirname); return -1; } } return 0; }