/***************************************************************************** * * $Id$ * Purpose ...............: MBSE BBS Common Library - RFC address functions * ***************************************************************************** * Copyright (C) 1997-2004 * * Michiel Broek FIDO: 2:280/2802 * Beekmansbos 10 * 1971 BV IJmuiden * the Netherlands * * This file is part of MBSE BBS. * * This BBS 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. * * MBSE BBS 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 MBSE BBS; see the file COPYING. If not, write to the Free * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *****************************************************************************/ #include "../config.h" #include "mbselib.h" extern int addrerror; static char *errname[] = { (char *)"nested <>", (char *)"multiple <>", (char *)"unmatched <>""()", (char *)"badtoken", (char *)"badstructure", }; char *addrerrstr(int err) { int i; static char buf[128]; buf[0] = '\0'; for (i = 0; i < ADDR_ERRMAX; i++) if (err & (1 << i)) { if (buf[0]) strcat(buf,","); strcat(buf, errname[i]); } if (buf[0] == '\0') strcpy(buf,"none"); return buf; } void tidyrfcaddr(parsedaddr addr) { if (addr.target) free(addr.target); if (addr.remainder) free(addr.remainder); if (addr.comment) free(addr.comment); } parsedaddr parserfcaddr(char *s) { parsedaddr result; char *inbrackets = NULL, *outbrackets = NULL, *cleanbuf = NULL, *combuf = NULL; char *t, *r, *c, *p, *q, **x; int quotes, brackets, escaped, anglecomplete; char *firstat, *lastat, *percent, *colon, *comma, *exclam; result.target = NULL; result.remainder = NULL; result.comment = NULL; addrerror = 0; if ((s == NULL) || (*s == '\0')) return result; /* * First check if there is an "angled" portion */ inbrackets = calloc(strlen(s)+1, sizeof(char)); outbrackets = calloc(strlen(s)+1, sizeof(char)); brackets = quotes = escaped = anglecomplete = 0; for (p = s,q = inbrackets, r = outbrackets, x = &r; *p; p++) { if (escaped) escaped = FALSE; else /* process all special chars */ switch (*p) { case '\\': escaped = TRUE; break; case '\"': quotes = !quotes; break; case '<': if (quotes) break; if (brackets) addrerror |= ADDR_NESTED; if (anglecomplete) addrerror |= ADDR_MULTIPLE; brackets++; x = &q; break; case '>': if (quotes) break; if (brackets) brackets--; else addrerror |= ADDR_UNMATCHED; if (!brackets) anglecomplete = 1; break; } *((*x)++) = *p; if (!brackets) x = &r; } *q = '\0'; *r = '\0'; if (brackets || quotes) addrerror |= ADDR_UNMATCHED; if (addrerror) goto leave1; cleanbuf = calloc(strlen(s)+1, sizeof(char)); combuf = calloc(strlen(s)+1, sizeof(char)); if (*inbrackets) { /* there actually is an angled portion */ strcpy(combuf, outbrackets); c = combuf + strlen(combuf); p = inbrackets + 1; *(p+strlen(p)-1) = '\0'; } else { c = combuf; p = outbrackets; } /* OK, now we have result.comment filled with wat was outside angle brackets, c pointing past the end of it, p pointing to what is supposed to be address, with angle brackets already removed */ quotes = brackets = escaped = 0; for (r = cleanbuf, x = &r; *p; p++) { if (escaped) { escaped=0; *((*x)++)=*p; } else /* process all special chars */ if (isspace(*p)) { if ((quotes) || (brackets)) *((*x)++) = *p; } else switch (*p) { case '\\': escaped=1; /* pass backslash itself only inside quotes and comments, or for the special cases \" and \\ otherwise eat it away */ if ((quotes) || (brackets)) *((*x)++) = *p; else if ((*(p+1)=='"') || (*(p+1)=='\\')) *((*x)++) = *p; break; case '\"': quotes = !quotes; *((*x)++) = *p; break; case '(': brackets++; x = &c; break; case ')': if (brackets) brackets--; else addrerror |= ADDR_UNMATCHED; if (!brackets) x = &r; break; default: *((*x)++) = *p; break; } } *r = '\0'; *c = '\0'; if (brackets || quotes) addrerror |= ADDR_UNMATCHED; if (addrerror) goto leave2; /* OK, now we have inangles buffer filled with the 'clean' address, all comments removed, and result.comment is ready filled */ /* seach for special chars that are outside quotes */ firstat = lastat = percent = colon = comma = exclam = NULL; quotes = 0; escaped = 0; for (p = cleanbuf; *p; p++) if (*p == '\\') p++; else if (*p == '\"') quotes = !quotes; else if (!quotes) switch (*p) { case '@': if (!firstat) firstat = p; lastat = p; break; case '%': percent = p; break; case ':': colon = p; break; case ',': comma = p; break; case '!': if (!exclam) exclam = p; break; } if ((firstat == cleanbuf) && colon) { if (comma && (comma < colon)) { *comma = '\0'; r = comma + 1; } else { *colon = '\0'; r = colon + 1; } t = firstat + 1; } else if (lastat) { *lastat = '\0'; r = cleanbuf; t = lastat + 1; } else if (exclam) { *exclam = '\0'; r = exclam + 1; t = cleanbuf; } else if (percent) { *percent = '\0'; r = cleanbuf; t = percent + 1; } else { /* unquote it if necessary */ if ((*cleanbuf == '\"') && (*(p = (cleanbuf+strlen(cleanbuf)-1)) == '\"')) { *p = '\0'; r = cleanbuf + 1; } else r = cleanbuf; t = NULL; } if (t && (*t != '\0')) result.target = xstrcpy(t); if (r && (*r != '\0')) result.remainder = xstrcpy(r); if (*combuf != '\0') result.comment = xstrcpy(combuf); leave1: /* this is also normal exit */ free(cleanbuf); free(combuf); free(inbrackets); free(outbrackets); return result; leave2: /* if error found on second stage, free */ free(cleanbuf); free(combuf); return result; }