/* * Return nonzero if 's' matches the grammar for a sequence */ EXPORTED int imparse_issequence(const char* s) { int c; int len = 0; int sawcolon = 0; while ((c = *s)) { if (c == ',') { if (!len) return 0; if (!Uisdigit(s[-1]) && s[-1] != '*') return 0; sawcolon = 0; } else if (c == ':') { if (sawcolon || !len) return 0; if (!Uisdigit(s[-1]) && s[-1] != '*') return 0; sawcolon = 1; } else if (c == '*') { if (len && s[-1] != ',' && s[-1] != ':') return 0; if (Uisdigit(s[1])) return 0; } else if (!Uisdigit(c)) { return 0; } s++; len++; } if (len == 0) return 0; if (!Uisdigit(s[-1]) && s[-1] != '*') return 0; return 1; }
int masterconf_getint(struct entry *e, const char *key, int def) { const char *val = masterconf_getstring(e, key, NULL); if (!val) return def; if (!Uisdigit(*val) && (*val != '-' || !Uisdigit(val[1]))) return def; return atoi(val); }
/* * Return nonzero if 's' matches the grammar for a number */ EXPORTED int imparse_isnumber(const char *s) { if (!*s) return 0; for (; *s; s++) { if (!Uisdigit(*s)) return 0; } return 1; }
/* * Parse an RFC 3339 = ISO 8601 format date-time string. * Returns: number of characters in @s consumed, or -1 on error. */ EXPORTED int time_from_iso8601(const char *s, time_t *tp) { const char *origs = s; struct tm exp; int n, tm_off; /* parse the ISO 8601 date/time */ /* XXX should use strptime ? */ memset(&exp, 0, sizeof(struct tm)); n = sscanf(s, "%4d-%2d-%2dT%2d:%2d:%2d", &exp.tm_year, &exp.tm_mon, &exp.tm_mday, &exp.tm_hour, &exp.tm_min, &exp.tm_sec); if (n != 6) return -1; s += 19; if (*s == '.') { /* skip fractional secs */ while (Uisdigit(*(++s))); } /* handle offset */ switch (*s++) { case 'Z': tm_off = 0; break; case '-': tm_off = -1; break; case '+': tm_off = 1; break; default: return -1; } if (tm_off) { int tm_houroff, tm_minoff; n = sscanf(s, "%2d:%2d", &tm_houroff, &tm_minoff); if (n != 2) return -1; tm_off *= 60 * (60 * tm_houroff + tm_minoff); s += 5; } exp.tm_year -= 1900; /* normalize to years since 1900 */ exp.tm_mon--; /* normalize to months since January */ /* sanity check the date/time (including leap day & second) */ if (exp.tm_year < 70 || exp.tm_mon < 0 || exp.tm_mon > 11 || exp.tm_mday < 1 || exp.tm_mday > monthdays(exp.tm_year, exp.tm_mon) || exp.tm_hour > 23 || exp.tm_min > 59 || exp.tm_sec > 60) { return -1; } /* normalize to GMT */ *tp = mkgmtime(&exp) - tm_off; return s - origs; }
/* ** Parse a number field converting it into a "when did this start?". ** This makes the "keep it" tests fast, but inverts the logic of ** just about everything you expect. Print a message and return FALSE ** on error. */ STATIC BOOL EXPgetnum(int line, char *word, time_t *v, char *name) { char *p; BOOL SawDot; double d; if (caseEQ(word, "never")) { *v = (time_t)0; return TRUE; } /* Check the number. We don't have strtod yet. */ for (p = word; ISWHITE(*p); p++) continue; if (*p == '+' || *p == '-') p++; for (SawDot = FALSE; *p; p++) if (*p == '.') { if (SawDot) break; SawDot = TRUE; } else if (!Uisdigit(*p)) break; if (*p) { (void)fprintf(stderr, "Line %d, bad `%c' character in %s field\n", line, *p, name); return FALSE; } d = atof(word); if (d > MAGIC_TIME) *v = (time_t)0; else *v = Now - (time_t)(d * 86400.); return TRUE; }
int timlex(mystring_t **outstr, unsigned long *outnum, struct protstream *stream) { int ch; char *buff_ptr; char *buff_end; unsigned long tmpnum = 0; unsigned long count=0; int result = TIMSIEVE_OK; buff_ptr = buffer; /* ptr into the buffer */ buff_end = buffer + maxscriptsize - 10; /* ptr to end of buffer */ while (1) { /* get a character this may block on a read if there is nothing in the buffer */ ch=prot_getc(stream); if (ch==EOF) { /* Lost connection */ return EOF; } switch (lexer_state) { case LEXER_STATE_RECOVER: if (ch == '\n') { lexer_state=LEXER_STATE_NORMAL; } if (ch == '\r') lexer_state=LEXER_STATE_RECOVER_CR; break; case LEXER_STATE_RECOVER_CR: if (ch == '\n') lexer_state=LEXER_STATE_NORMAL; break; case LEXER_STATE_CR: if (ch == '\n') { lexer_state=LEXER_STATE_NORMAL; return EOL; } /* otherwise, life is bad */ ERR_PUSHBACK(); case LEXER_STATE_QSTR: if (ch == '\"') { /* End of the string */ if (outstr!=NULL) { *outstr = NULL; result = string_allocate(buff_ptr - buffer, buffer, outstr); if (result != TIMSIEVE_OK) ERR_PUSHBACK(); } /*} */ lexer_state=LEXER_STATE_NORMAL; return STRING; } /* illegal character */ if (ch == '\0' || ch == '\r' || ch == '\n' || 0x7F < ((unsigned char)ch)) { ERR_PUSHBACK(); } /* Otherwise, we're appending a character */ if (buff_end <= buff_ptr) ERR_PUSHBACK(); /* too long! */ if (ch == '\\') { ch=prot_getc(stream); if (result != TIMSIEVE_OK) ERR(); if (ch != '\"' && ch != '\\') ERR_PUSHBACK(); } *buff_ptr++ = ch; break; case LEXER_STATE_LITERAL: if (('0' <= ch) && (ch <= '9')) { unsigned long newcount = count * 10 + (ch - '0'); if (newcount < count) ERR_PUSHBACK(); /* overflow */ /* * XXX This should be fatal if non-synchronizing. */ count = newcount; break; } if (ch != '+') ERR_PUSHBACK(); ch=prot_getc(stream); if (ch != '}') ERR_PUSHBACK(); ch=prot_getc(stream); if (ch < 0) ERR(); if (ch != '\r') ERR_PUSHBACK(); ch=prot_getc(stream); if (ch < 0) ERR(); if (ch != '\n') ERR_PUSHBACK(); if (count > maxscriptsize) { /* too big, eat the input */ for(;count > 0;count--) { if(prot_getc(stream)==EOF) break; } ERR(); } if (outstr!=NULL) { *outstr = NULL; result = string_allocate(count, NULL, outstr); if (result != TIMSIEVE_OK) ERR_PUSHBACK(); } /* there is a literal string on the wire. let's read it */ if (outstr!=NULL) { char *it = string_DATAPTR(*outstr), *end = it + count; while (it < end) { *it=prot_getc(stream); it++; } } else { /* just read the chars and throw them away */ unsigned long lup; for (lup=0;lup<count;lup++) (void)prot_getc(stream); } lexer_state=LEXER_STATE_NORMAL; return STRING; case LEXER_STATE_NUMBER: if (Uisdigit(ch)) { unsigned long newcount = tmpnum * 10 + (ch - '0'); if (newcount < tmpnum) ERR_PUSHBACK(); /* overflow */ tmpnum = newcount; } else { lexer_state=LEXER_STATE_NORMAL; prot_ungetc(ch, stream); if (outnum) *outnum = tmpnum; return NUMBER; } break; case LEXER_STATE_NORMAL: if (Uisalpha(ch)) { lexer_state=LEXER_STATE_ATOM; *buff_ptr++ = tolower(ch); break; } if (Uisdigit(ch)) { lexer_state=LEXER_STATE_NUMBER; tmpnum = ch -'0'; break; } switch (ch) { case '(': return '('; case ')': return ')'; case ' ': return ' '; case '\"': lexer_state=LEXER_STATE_QSTR; break; case '*': return '*'; case '{': count = 0; lexer_state=LEXER_STATE_LITERAL; break; case '\r': lexer_state=LEXER_STATE_CR; break; case '\n': lexer_state=LEXER_STATE_NORMAL; return EOL; break; default: return ch; } break; case LEXER_STATE_ATOM: if (!Uisalpha(ch)) { int token; buffer[ buff_ptr - buffer] = '\0'; /* We've got the atom. */ token = token_lookup((char *) buffer, (int) (buff_ptr - buffer)); if (token!=-1) { lexer_state=LEXER_STATE_NORMAL; prot_ungetc(ch, stream); return token; } else ERR_PUSHBACK(); } if (buff_end <= buff_ptr) ERR_PUSHBACK(); /* atom too long */ *buff_ptr++ = tolower(ch); break; } } /* while (1) */ /* never reached */ }
/* copy our current input to 's' until we hit a true EOL. 'optimistic_literal' is how happy we should be about assuming that a command will go through by converting synchronizing literals of size less than optimistic_literal to nonsync returns 0 on success, <0 on big failure, >0 on full command not sent */ int pipe_command(struct backend *s, int optimistic_literal) { char buf[2048]; char eol[128]; int sl; s->timeout->mark = time(NULL) + IDLE_TIMEOUT; eol[0] = '\0'; /* again, the complication here are literals */ for (;;) { if (!prot_fgets(buf, sizeof(buf), imapd_in)) { /* uh oh */ return -1; } sl = strlen(buf); if (sl == (sizeof(buf) - 1) && buf[sl-1] != '\n') { /* only got part of a line */ strcpy(eol, buf + sl - 64); /* and write this out, except for what we've saved */ prot_write(s->out, buf, sl - 64); continue; } else { int i, nonsynch = 0, islit = 0, litlen = 0; if (sl < 64) { strcat(eol, buf); } else { /* write out what we have, and copy the last 64 characters to eol */ prot_printf(s->out, "%s", eol); prot_write(s->out, buf, sl - 64); strcpy(eol, buf + sl - 64); } /* now determine if eol has a literal in it */ i = strlen(eol); if (i >= 4 && eol[i-1] == '\n' && eol[i-2] == '\r' && eol[i-3] == '}') { /* possible literal */ i -= 4; if (eol[i] == '+') { nonsynch = 1; i--; } while (i > 0 && eol[i] != '{' && Uisdigit(eol[i])) { i--; } if (eol[i] == '{') { islit = 1; litlen = atoi(eol + i + 1); } } if (islit) { if (nonsynch) { prot_write(s->out, eol, strlen(eol)); } else if (!nonsynch && (litlen <= optimistic_literal)) { prot_printf(imapd_out, "+ i am an optimist\r\n"); prot_write(s->out, eol, strlen(eol) - 3); /* need to insert a + to turn it into a nonsynch */ prot_printf(s->out, "+}\r\n"); } else { /* we do a standard synchronizing literal */ prot_write(s->out, eol, strlen(eol)); /* but here the game gets tricky... */ prot_fgets(buf, sizeof(buf), s->in); /* but for now we cheat */ prot_write(imapd_out, buf, strlen(buf)); if (buf[0] != '+' && buf[1] != ' ') { /* char *p = strchr(buf, ' '); */ /* strncpy(s->last_result, p + 1, LAST_RESULT_LEN);*/ /* stop sending command now */ return 1; } } /* gobble literal and sent it onward */ while (litlen > 0) { int j = (litlen > (int) sizeof(buf) ? (int) sizeof(buf) : litlen); j = prot_read(imapd_in, buf, j); if(!j) { /* EOF or other error */ return -1; } prot_write(s->out, buf, j); litlen -= j; } eol[0] = '\0'; /* have to keep going for the send of the command */ continue; } else { /* no literal, so we're done! */ prot_write(s->out, eol, strlen(eol)); return 0; } } } }
/* pipe_response() reads from 's->in' until either the tagged response starting with 'tag' appears, or if 'tag' is NULL, to the end of the current line. If 'include_last' is set, the last/tagged line is included in the output, otherwise the last/tagged line is stored in 's->last_result'. In either case, the result of the tagged command is returned. 's->last_result' assumes that tagged responses don't contain literals. Unfortunately, the IMAP grammar allows them force_notfatal says to not fatal() if we lose connection to backend_current even though it is in 95% of the cases, a good idea... */ static int pipe_response(struct backend *s, const char *tag, int include_last, int force_notfatal) { char buf[2048]; char eol[128]; unsigned sl; int cont = 0, last = !tag, r = PROXY_OK; size_t taglen = 0; s->timeout->mark = time(NULL) + IDLE_TIMEOUT; if (tag) { taglen = strlen(tag); if(taglen >= sizeof(buf) + 1) { fatal("tag too large",EC_TEMPFAIL); } } buf_reset(&s->last_result); /* the only complication here are literals */ do { /* if 'cont' is set, we're looking at the continuation to a very long line. if 'last' is set, we've seen the tag we're looking for, we're just reading the end of the line. */ if (!cont) eol[0] = '\0'; if (!prot_fgets(buf, sizeof(buf), s->in)) { /* uh oh */ if(s == backend_current && !force_notfatal) fatal("Lost connection to selected backend", EC_UNAVAILABLE); proxy_downserver(s); return PROXY_NOCONNECTION; } sl = strlen(buf); if (tag) { /* Check for the tagged line */ if (!cont && buf[taglen] == ' ' && !strncmp(tag, buf, taglen)) { switch (buf[taglen + 1]) { case 'O': case 'o': r = PROXY_OK; break; case 'N': case 'n': r = PROXY_NO; break; case 'B': case 'b': r = PROXY_BAD; break; default: /* huh? no result? */ if(s == backend_current && !force_notfatal) fatal("Lost connection to selected backend", EC_UNAVAILABLE); proxy_downserver(s); r = PROXY_NOCONNECTION; break; } last = 1; } if (last && !include_last) { /* Store the tagged line */ buf_appendcstr(&s->last_result, buf+taglen+1); buf_cstring(&s->last_result); } } if (sl == (sizeof(buf) - 1) && buf[sl-1] != '\n') { /* only got part of a line */ /* we save the last 64 characters in case it has important literal information */ strcpy(eol, buf + sl - 64); /* write out this part, but we have to keep reading until we hit the end of the line */ if (!last || include_last) prot_write(imapd_out, buf, sl); cont = 1; continue; } else { /* we got the end of the line */ int i; int litlen = 0, islit = 0; if (!last || include_last) prot_write(imapd_out, buf, sl); /* now we have to see if this line ends with a literal */ if (sl < 64) { strcat(eol, buf); } else { strcat(eol, buf + sl - 63); } /* eol now contains the last characters from the line; we want to see if we've hit a literal */ i = strlen(eol); if (i >= 4 && eol[i-1] == '\n' && eol[i-2] == '\r' && eol[i-3] == '}') { /* possible literal */ i -= 4; while (i > 0 && eol[i] != '{' && Uisdigit(eol[i])) { i--; } if (eol[i] == '{') { islit = 1; litlen = atoi(eol + i + 1); } } /* copy the literal over */ if (islit) { while (litlen > 0) { int j = (litlen > (int) sizeof(buf) ? (int) sizeof(buf) : litlen); j = prot_read(s->in, buf, j); if(!j) { /* EOF or other error */ return -1; } if (!last || include_last) prot_write(imapd_out, buf, j); litlen -= j; } /* none of our saved information has any relevance now */ eol[0] = '\0'; /* have to keep going for the end of the line */ cont = 1; continue; } } /* ok, let's read another line */ cont = 0; } while (!last || cont); return r; }
/* * Parse an astring from the string starting at the pointer pointed to * by 's'. On success, places a pointer to the parsed word in the * pointer at 'retval', returns the character following the word, and * modifies the pointer at 's' to point after the returned character. * On failure, returns EOF, modifies the pointer at 'retval' to point * at the empty string, and modifies 's' to point around the syntax error. * Modifies the input buffer. */ EXPORTED int imparse_astring(char **s, char **retval) { int c; char *d; int len = 0; int sawdigit = 0; switch (**s) { case '\0': case ' ': case '(': case ')': case '\r': case '\n': /* Invalid starting character */ *retval = ""; return EOF; default: /* * Atom -- parser is liberal in accepting specials other * than whitespace, parens, or double quotes */ return imparse_word(s, retval); case '\"': /* * Quoted-string. Parser is liberal in accepting qspecials * other than double-quote, CR, and LF. */ *retval = d = ++(*s); for (;;) { c = *(*s)++; if (c == '\\') { c = *(*s)++; } else if (c == '\"') { *d = '\0'; return *(*s)++; } else if (c == '\0' || c == '\r' || c == '\n') { *retval = ""; return EOF; } *d++ = c; } case '{': /* Literal */ (*s)++; while (Uisdigit(c = *(*s)++)) { sawdigit = 1; len = len*10 + c - '0'; } if (!sawdigit || c != '}' || *(*s)++ != '\r' || *(*s)++ != '\n') { *retval = ""; return EOF; } *retval = *s; *s += len; c = **s; *(*s)++ = '\0'; /* Note that 0 and '\0' mean the same thing */ return c; } }
/* return malloc'd string containing the address */ static char *parseaddr(char *s) { char *p, *ret; int len; int lmtp_strict_rfc2821 = config_getswitch(IMAPOPT_LMTP_STRICT_RFC2821); p = s; if (*p++ != '<') return 0; /* at-domain-list */ while (*p == '@') { p++; if (*p == '[') { p++; while (Uisdigit(*p) || *p == '.') p++; if (*p++ != ']') return 0; } else { while (Uisalnum(*p) || *p == '.' || *p == '-') p++; } if (*p == ',' && p[1] == '@') p++; else if (*p == ':' && p[1] != '@') p++; else return 0; } /* local-part */ if (*p == '\"') { p++; while (*p && *p != '\"') { if (*p == '\\') { if (!*++p) return 0; } p++; } if (!*p++) return 0; } else { while (*p && *p != '@' && *p != '>') { if (*p == '\\') { if (!*++p) return 0; } else { if (*p & 128 && !lmtp_strict_rfc2821) { /* this prevents us from becoming a backscatter source if our MTA allows 8bit in local-part of adresses. */ *p = 'X'; } if (*p <= ' ' || (*p & 128) || strchr("<>()[]\\,;:\"", *p)) return 0; } p++; } } /* @domain */ if (*p == '@') { p++; if (*p == '[') { p++; while (Uisdigit(*p) || *p == '.') p++; if (*p++ != ']') return 0; } else { while (Uisalnum(*p) || *p == '.' || *p == '-') p++; } } if (*p++ != '>') return 0; if (*p && *p != ' ') return 0; len = p - s; ret = xmalloc(len + 1); memcpy(ret, s, len); ret[len] = '\0'; return ret; }
static int parse_rfc822(const char *s, time_t *tp, int dayonly) { const char *origs = s; struct tm tm; time_t t; char month[4]; int zone_off = 0; if (!s) goto baddate; memset(&tm, 0, sizeof(tm)); s = skip_fws(s); if (!s) goto baddate; if (Uisalpha(*s)) { /* Day name -- skip over it */ s++; if (!Uisalpha(*s)) goto baddate; s++; if (!Uisalpha(*s)) goto baddate; s++; s = skip_fws(s); if (!s || *s++ != ',') goto baddate; s = skip_fws(s); if (!s) goto baddate; } if (!Uisdigit(*s)) goto baddate; tm.tm_mday = *s++ - '0'; if (Uisdigit(*s)) { tm.tm_mday = tm.tm_mday*10 + *s++ - '0'; } /* Parse month name */ s = skip_fws(s); if (!s) goto baddate; month[0] = *s++; if (!Uisalpha(month[0])) goto baddate; month[1] = *s++; if (!Uisalpha(month[1])) goto baddate; month[2] = *s++; if (!Uisalpha(month[2])) goto baddate; month[3] = '\0'; for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++) { if (!strcasecmp(month, monthname[tm.tm_mon])) break; } if (tm.tm_mon == 12) goto baddate; /* Parse year */ s = skip_fws(s); if (!s || !Uisdigit(*s)) goto baddate; tm.tm_year = *s++ - '0'; if (!Uisdigit(*s)) goto baddate; tm.tm_year = tm.tm_year * 10 + *s++ - '0'; if (Uisdigit(*s)) { if (tm.tm_year < 19) goto baddate; tm.tm_year -= 19; tm.tm_year = tm.tm_year * 10 + *s++ - '0'; if (!Uisdigit(*s)) goto baddate; tm.tm_year = tm.tm_year * 10 + *s++ - '0'; } else { if (tm.tm_year < 70) { /* two-digit year, probably after 2000. * This patent was overturned, right? */ tm.tm_year += 100; } } if (Uisdigit(*s)) { /* five-digit date */ goto baddate; } if (tm.tm_mday > monthdays(tm.tm_year, tm.tm_mon)) goto baddate; s = skip_fws(s); if (s && !dayonly) { /* Parse hour */ if (!s || !Uisdigit(*s)) goto badtime; tm.tm_hour = *s++ - '0'; if (!Uisdigit(*s)) goto badtime; tm.tm_hour = tm.tm_hour * 10 + *s++ - '0'; if (!s || *s++ != ':') goto badtime; /* Parse min */ if (!s || !Uisdigit(*s)) goto badtime; tm.tm_min = *s++ - '0'; if (!Uisdigit(*s)) goto badtime; tm.tm_min = tm.tm_min * 10 + *s++ - '0'; if (*s == ':') { /* Parse sec */ if (!++s || !Uisdigit(*s)) goto badtime; tm.tm_sec = *s++ - '0'; if (!Uisdigit(*s)) goto badtime; tm.tm_sec = tm.tm_sec * 10 + *s++ - '0'; } s = skip_fws(s); if (s) { /* Parse timezone offset */ if (*s == '+' || *s == '-') { /* Parse numeric offset */ int east = (*s++ == '-'); if (!s || !Uisdigit(*s)) goto badzone; zone_off = *s++ - '0'; if (!s || !Uisdigit(*s)) goto badzone; zone_off = zone_off * 10 + *s++ - '0'; if (!s || !Uisdigit(*s)) goto badzone; zone_off = zone_off * 6 + *s++ - '0'; if (!s || !Uisdigit(*s)) goto badzone; zone_off = zone_off * 10 + *s++ - '0'; if (east) zone_off = -zone_off; } else if (Uisalpha(*s)) { char zone[4]; zone[0] = *s++; if (!Uisalpha(*s)) { /* Parse military (single-char) zone */ zone[1] = '\0'; lcase(zone); if (zone[0] < 'j') zone_off = (zone[0] - 'a' + 1) * 60; else if (zone[0] == 'j') goto badzone; else if (zone[0] <= 'm') zone_off = (zone[0] - 'a') * 60; else if (zone[0] < 'z') zone_off = ('m' - zone[0]) * 60; else zone_off = 0; } else { zone[1] = *s++; if (!Uisalpha(*s)) { /* Parse UT (universal time) */ zone[2] = '\0'; lcase(zone); if (strcmp(zone, "ut")) goto badzone; zone_off = 0; } else { /* Parse 3-char time zone */ char *p; zone[2] = *s; zone[3] = '\0'; lcase(zone); /* GMT (Greenwich mean time) */ if (!strcmp(zone, "gmt")) zone_off = 0; /* US time zone */ else { p = strchr("aecmpyhb", zone[0]); if (!p || zone[2] != 't') goto badzone; zone_off = (strlen(p) - 12) * 60; if (zone[1] == 'd') zone_off += 60; else if (zone[1] != 's') goto badzone; } } } } else badzone: zone_off = 0; } } else badtime: tm.tm_hour = 12; tm.tm_isdst = -1; if (!dayonly) t = mkgmtime(&tm); else { assert(zone_off == 0); t = mktime(&tm); } if (t >= 0) { *tp = (t - zone_off * 60); return s - origs; } baddate: return -1; }