+/*
+ \femailaddress():
+ Picks the email address out of an RFC 2822 From: or Sender: header.
+ Added 26 Nov 2005. Handles all common, and some uncommon, cases but
+ doesn't totally bother about nested comments. Needed this for fetching
+ email from a POP server and then constructing the BSD "From " line.
+ Works with or without the "From: " or "Sender: " tag.
+*/
+ if (cx == FN_EMAIL) {
+ char c, * s = bp[0], * s2, * s3, * ap = "";
+ int k, state = 0, quote = 0, infield = 0;
+ int pc = 0; /* For nested comments */
+ if (!s) s = "";
+ if (!*s) goto xemail;
+
+ if (ckindex("From: ",s,0,0,0) == 1) s += 5;
+ if (ckindex("Sender: ",s,0,0,0) == 1) s += 7;
+
+ k = strlen(s); /* Strip junk from end */
+ if (k < 1) goto xemail;
+ k--;
+ while (k >= 0 && s[k] == CR || s[k] == LF)
+ s[k--] = NUL;
+ while (k >= 0 && s[k] == SP || s[k] == HT)
+ s[k--] = NUL;
+ if (k == 0)
+ goto xemail;
+
+#ifndef COMMENT /* Simple method if not 100% foolproof */
+ k = 0;
+ for (s2 = s; *s2; s2++) { /* Find at-sign */
+ if (*s2 == '@') {
+ k++; /* If more than one use rightmost */
+ s3 = s2;
+ }
+ }
+ if (k < 1) /* No at-sign */
+ goto xemail;
+
+ for (ap = s3-1; ap >= s; ap--) { /* Back up to beginning of address */
+ if (isspace(*ap) || *ap == '<') {
+ ap++;
+ break;
+ }
+ if (ap == s)
+ break;
+ }
+ for (s2 = s3+1; *s2; s2++) { /* Find end of address */
+ if (isspace(*s2) || *s2 == '>')
+ break;
+ }
+ *s2-- = NUL;
+ if (*ap == '[' && *s2 == ']') { /* Handle [blah@blah.blah] */
+ ap++;
+ *s2 = NUL;
+ }
+ if (!ckstrcmp(ap,"mailto:",7,0)) /* Handle mailto: URLs */
+ ap += 7;
+
+#else /* Too complicated and error-prone */
+
+ k = 0;
+ for (s2 = s; *s2; s2++) { /* Strip leading whitespace */
+ if (*s2 == SP || *s2 == HT) {
+ k = 1;
+ break;
+ }
+ }
+ if (!k) { /* Simple address */
+ ap = s;
+ goto xemail;
+ }
+ do { /* Not simple, have to extract it */
+ if (quote) {
+ quote = 0;
+ continue;
+ } else if (*s == '\\') {
+ quote = 1;
+ continue;
+ }
+ switch (state) {
+ case 0:
+ if (!infield && *s == '"') { /* Quoted string */
+ infield = 1;
+ c = '"';
+ state = 1;
+ } else if (!infield && *s == '(') { /* Comment in parens */
+ pc++;
+ infield = 1;
+ c = ')';
+ if (*ap) *s = NUL;
+ state = 1;
+ } else if (!infield && *s == '<') { /* Address */
+ infield = 1;
+ c = '>';
+ ap = s+1;
+ state = 2;
+ } else if (infield && (*s == SP || *s == HT)) {
+ infield = 0;
+ } else { /* One or more bare words */
+ infield = 1; /* Could be an address */
+ if (!*ap) ap = s; /* Could be comments */
+ }
+ continue;
+ case 1: /* In Quoted string or Comment */
+ if (infield && *s == c) { /* Look for end */
+ infield = 0;
+ *s++ = NUL;
+ while (*s == SP || *s == HT) s++;
+ if (!*ap)
+ ap = s;
+ state = 0;
+ }
+ continue;
+ case 2: /* In address */
+ if (infield && *s == c) { /* Looking for end */
+ infield = 0;
+ *s = NUL;
+ break;
+ }
+ }
+ } while (*s++);
+
+ xemail:
+ if (*ap) {
+ while (*ap == SP || *ap == HT) ap++;
+ }
+ k = strlen(ap) - 1;
+ while (k >= 0 && (ap[k] == SP || ap[k] == HT))
+ ap[k--] = NUL;
+ if (*ap) {
+ failed = 0;
+ if (*ap == '<') {
+ k = strlen(ap);
+ if (*(ap+k-1) == '>') {
+ ap[k-1] = NUL;
+ ap++;
+ }
+ }
+ } else
+ failed = 1;
+ /* Here we might also want check against "*@*.*" */
+#endif /* COMMENt */
+ xemail:
+ ckstrncpy(fnval,ap,FNVALL);
+ goto fnend;
+ }
+
+/*
+ \fpicture(): Get dimensions of GIF or JPG image.
+ fdc June 2006
+*/
+ if (cx == FN_PICTURE) {
+ FILE *fp = NULL;
+ int c, x, w = 0, h = 0, eof = 0;
+ unsigned int i, j, k;
+ unsigned char buf[1024];
+ char abuf[16], * p, * s;
+ char ** ap = NULL;
+
+ p = fnval; /* Point to result */
+ failed = 1; /* Assume failure */
+ if (argn > 1) {
+ int xi;
+ ckstrncpy(abuf,bp[1],16); /* Get array reference */
+ s = abuf;
+ if (*s == CMDQ) s++;
+ if (fndiags) /* Default is this error message */
+ ckmakmsg(fnval,FNVALL,
+ "<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
+ if (s[0] != '&') /* "Address" of array */
+ goto fnend;
+ if (s[2])
+ if (s[2] != '[' || s[3] != ']')
+ goto fnend;
+ if (s[1] >= 64 && s[1] < 91) /* Convert upper to lower */
+ s[1] += 32;
+ if ((xi = dclarray(s[1],2)) < 0) /* Two elements */
+ goto fnend;
+ ap = a_ptr[xi]; /* Point to array we just declared */
+ }
+ s = bp[0]; /* Filename */
+ failed = 0; /* From here on we don't fail */
+ p[0] = '0'; /* Default return value */
+ p[1] = NUL;
+ if (!ckmatch("*.{jpg,jpeg,gif}$",s,0,1+4)) /* Appropriate name? */
+ goto fnend; /* No, fail */
+ fp = fopen(s, "r"); /* Open it */
+ if (fp == NULL) { /* Can't, fail */
+ p[0] = '-';
+ p[1] = '1';
+ p[2] = NUL; /* Return -1 */
+ goto fnend;
+ }
+ k = strlen(s);
+ if (!ckstrcmp(&s[k-4],".gif",4,0)) { /* GIF file */
+ if (fread(buf,1,10,fp) != 10) {
+ fclose(fp);
+ goto fnend;
+ }
+ /* Check signature */
+ if (ckstrcmp((char *)buf,"GIF87a",6,0) &&
+ ckstrcmp((char *)buf,"GIF89a",6,0)) {
+ fclose(fp);
+ goto fnend;
+ }
+ w = buf[6] + 256 * buf[7];
+ h = buf[8] + 256 * buf[9];
+ goto picend;
+ } else if (!ckstrcmp(&s[k-4],".jpg",4,0) || /* JPEG file */
+ !ckstrcmp(&s[k-5],".jpeg",5,0)) {
+ if (fread(buf,1,2,fp) != 2) {
+ fclose(fp);
+ goto fnend;
+ }
+ if (buf[0] != 0xff || buf[1] != 0xd8) { /* Check signature */
+ fclose(fp);
+ goto fnend;
+ }
+ eof = 0;
+ while (!eof) { /* Loop for each marker */
+ while (!eof) { /* Find next marker */
+ c = getc(fp);
+ if (c == (unsigned int)EOF) {
+ eof++;
+ break;
+ }
+ if (c == 0xff) {
+ buf[0] = c;
+ c = getc(fp);
+ if (c == (unsigned int)EOF) {
+ eof++;
+ break;
+ }
+ buf[1] = c;
+ if (c == 0xd9)
+ eof++;
+ if (c >= 0xc0 && c <= 0xfe)
+ break;
+ }
+ }
+ if (eof) break;
+ x = buf[1];
+ if (x == 0xc0 || x == 0xc1 || x == 0xc2 || x == 0xc3 ||
+ x == 0xc9 || x == 0xca || x == 0xcb) {
+ if (fread(buf,1,7,fp) != 7) {
+ fclose(fp);
+ goto fnend;
+ }
+ h = buf[3] * 256 + buf[4];
+ w = buf[5] * 256 + buf[6];
+ goto picend;
+ } else { /* Not a desired field */
+ if (feof(fp)) {
+ eof++;
+ break;
+ }
+ if (fread(buf,1,2,fp) != 2) { /* Length of this field */
+ fclose(fp);
+ goto fnend;
+ }
+ j = 256 * buf[0] + buf[1] - 2; /* Skip next field */
+ if (CKFSEEK(fp,(CK_OFF_T)j,SEEK_CUR) != 0) {
+ fclose(fp);
+ goto fnend;
+ }
+ }
+ }
+ }
+ picend:
+ fclose(fp);
+ if (ap) {
+ makestr(&(ap[0]),"2");
+ makestr(&(ap[1]),ckitoa(w));
+ makestr(&(ap[2]),ckitoa(h));
+ }
+ if (w > 0 && h > 0) {
+ if (w > h) p[0] = '1';
+ if (h >= w) p[0] = '2';
+ }
+ goto fnend;
+ }
+ if (cx == FN_PID) {
+ int x = -1;
+ if (chknum(bp[0])) { /* Need numeric argument */
+ int pid;
+ pid = atoi(bp[0]); /* Convert to int */
+#ifdef UNIX
+ if (kill(pid,0) < 0) { /* Test it */
+ if (errno ==
+#ifdef ESRCH
+ ESRCH /* No such process */
+#else
+ 3
+#endif /* ESRCH */
+ )
+ x = 0;
+ } else /* Process exists */
+ x = 1;
+#endif /* UNIX */
+ }
+ sprintf(fnval,"%d",x); /* SAFE */
+ goto fnend;
+ }
+
+ if (cx == FN_FUNC) {
+ char * s = bp[0];
+ p = "0";
+ debug(F111,"ffunc",s,argn);
+ if (argn > 0) {
+ int x, y;
+ for (p = s; *p; p++) { /* Chop off trailing parens if any */
+ if (*p == '(') {
+ *p = NUL;
+ break;
+ }
+ }
+ /* Chop off leading "\\f" or "\f" or "f" */
+ p = s;
+ if (*p == CMDQ) /* Allow for \\f... */
+ p++;
+ if (*p == CMDQ && (*(p+1) == 'f' || *(p+1) == 'F')) { /* or \f */
+ p += 2;
+ } else if (*p == 'f' || *p == 'F') { /* or just f */
+ p++;
+ }
+ y = lookup(fnctab,p,nfuncs,&x); /* Look up the result */
+ debug(F111,"ffunc",p,y);
+ p = (y > -1) ? "1" : "0";
+ }
+ goto fnend;
+ }
+ if (cx == FN_RECURSE) {
+ int t, n;
+ char * s;
+ fnval[0] = NUL; /* Default result is empty string */
+ s = bp[0]; /* Check for null argument */
+ if (!s) s = ""; /* or empty argument */
+ if (!*s) goto fnend; /* in which case return empty string */
+ n = FNVALL; /* Not empty, max size for result */
+ s = fnval; /* Location of result */
+ {
+ /* Force VARIABLE-EVALUATION SIMPLE RECURSIVE */
+ /* NOTE: This is vulnerable to SIGINT and whatnot... */
+ int tmp = vareval; /* Save VARIABLE-EVALUATION setting */
+ vareval = 1; /* Force it to RECURSIVE */
+ zzstring(bp[0],&s,&n); /* Expand arg into result space */
+ vareval = tmp; /* Restore VARIABLE-EVALUATION */
+ }
+ goto fnend;
+ }
+
+ if (cx == FN_XLATE) { /* f_cvtcset() */
+#ifdef NOFRILLS
+ ckstrncpy(fnval,bp[0],FNVALL);
+#else
+#ifndef NOUNICODE
+ _PROTOTYP( char * cvtstring, (char *, int, int) );
+ char * string, * cset1, * cset2;
+ int id1, id2;
+#endif /* NOUNICODE */
+ fnval[0] = NUL;
+#ifdef NOUNICODE
+ ckstrncpy(fnval,bp[0],FNVALL);
+#else
+ string = bp[0] ? bp[0] : ""; /* String to convert */
+ if (!*string) goto fnend; /* It's empty */
+
+ cset1 = bp[1] ? bp[1] : "ascii"; /* Current charset of string */
+ cset2 = bp[2] ? bp[2] : "ascii"; /* Charset to convert to */
+
+ id1 = lookup(fcstab,cset1,nfilc,NULL); /* Lookup 'from' set */
+ if (id1 < 0) {
+ failed = 1;
+ ckmakmsg(fnval,FNVALL,"<ERROR:UNKNOWN_CHARSET:\\f",fn,"()>",NULL);
+ goto fnend;
+ }
+ id2 = lookup(fcstab,cset2,nfilc,NULL); /* Lookup 'to' set */
+ if (id2 < 0) {
+ failed = 1;
+ ckmakmsg(fnval,FNVALL,"<ERROR:UNKNOWN_CHARSET:\\f",fn,"()>",NULL);
+ goto fnend;
+ }
+ string = cvtstring(string,id1,id2);
+ ckstrncpy(fnval,string,FNVALL);
+#endif /* NOUNICODE */
+#endif /* NOFRILLS */
+ goto fnend;
+ }
+
+/* Decode strings containing hex escapes */
+
+ if (cx == FN_UNPCT) { /* \fdecodehex() */
+ char *s1, *s2;
+ char *prefix; /* Can be 1 or 2 chars */
+ char buf[3];
+ int n = 0, k;
+
+ p = fnval;
+ *p = NUL;
+ if (argn < 1) goto fnend; /* Empty string */
+
+ s1 = bp[0] ? bp[0] : ""; /* Original string */
+ prefix = bp[1] ? bp[1] : "%%"; /* Hex byte prefix */
+ n = (int)strlen(prefix); /* Length of prefix */
+ if (n < 1 || n > 2) { /* must be 1 or 2 */
+ ckmakmsg(fnval,FNVALL,
+ "<ERROR:INVALID_HEX_PREFIX:\\f",fn,"()>",NULL);
+ goto xunpct;
+ }
+ while (*s1) {
+ if (!ckstrcmp(s1,prefix,n,0)) { /* Case-independent */
+ if (!*(s1+n)) {
+ ckmakmsg(fnval,FNVALL,
+ "<ERROR:INCOMPLETE_SEQUENCE:\\f",fn,"()>",NULL);
+ goto xunpct;
+ }
+ buf[0] = *(s1+n); /* First hex character */
+ buf[1] = *(s1+n+1); /* Second hex character */
+ buf[2] = NUL;
+ if ((k = ckhexbytetoint((char *)buf)) > -1) {
+ *p++ = (char) k; /* Deposit decoded result */
+ s1 += 2+n; /* and advance the source pointer */
+ } else { /* Fail on conversion error */
+ ckmakmsg(fnval,FNVALL,
+ "<ERROR:NON_HEX_CHARS:\\f",fn,"()>",NULL);
+ goto xunpct;
+ }
+ } else { /* Not a hex escape sequence */
+ *p++ = *s1++; /* Just copy the character */
+ }
+ }
+ *p = NUL; /* Terminate the result string */
+ failed = 0; /* Say we didn't fail */
+ p = fnval; /* Set up result pointer */
+ goto fnend; /* and finish */
+
+ xunpct: /* Error exit */
+ p = fnval;
+ failed = 1;
+ goto fnend;
+ }
+
+/* Check a string for encoding family */
+
+ if (cx == FN_STRINGT) { /* \fstringtype() */
+ p = "UNK";
+ switch (scanstring(bp[0])) {
+ case FT_7BIT: p = "7BIT"; break;
+ case FT_8BIT: p = "8BIT"; break;
+ case FT_UTF8: p = "UTF8"; break;
+ case FT_UCS2: p = "UCS2"; break;
+ case FT_TEXT: p = "TEXT"; break;
+ case FT_BIN: p = "BINARY"; break;
+ }
+ ckstrncpy(fnval,p,FNVALL);
+ p = fnval;
+ goto fnend;
+ }
+
+/* String compare s1, s2, [ case ], [ start ] , [ len ] */
+
+ if (cx == FN_STRCMP) {
+ int docase = 0; /* Case matters or not */
+ int start = 0; /* Start of substring */
+ int len = -1; /* Length of substring to compare */
+ int x; char * s1, * s2; /* workers */
+
+ p = "0"; /* Return value */
+ if (argn == 0) { /* Two null strings are equal */
+ ckstrncpy(fnval,p,FNVALL);
+ p = fnval;
+ goto fnend;
+ }
+ if (argn == 1) { /* Non-null string > null string */
+ p = "1";
+ ckstrncpy(fnval,p,FNVALL);
+ p = fnval;
+ goto fnend;
+ }
+ if (argn > 2) {
+ s = *(bp[2]) ? evalx(bp[2]) : "0"; /* 0 = caseless */
+ if (chknum(s)) docase = atoi(s);
+ if (argn > 3) {
+ s = *(bp[3]) ? evalx(bp[3]) : "1"; /* start is 1-based */
+ if (chknum(s)) start = atoi(s);
+ if (argn > 4) {
+ s = *(bp[4]) ? evalx(bp[4]) : "-1"; /* -1 = whole thing */
+ if (chknum(s)) len = atoi(s);
+ }
+ }
+ }
+ if (start > 0) start--; /* start is 0-based internally */
+ s1 = bp[0]; /* Get length of first arg */
+ x = (int)strlen(s1);
+ if (x > start) /* Point to start position of s1 */
+ s1 += start;
+ else
+ s1 = "";
+ s2 = bp[1]; /* Get length of second arg */
+ x = (int)strlen(s2);
+ if (x > start) /* Point to start position of s2 */
+ s2 += start;
+ else
+ s2 = "";
+ x = ckstrcmp(s,s2,len,docase);
+ p = ckitoa(x);
+ ckstrncpy(fnval,p,FNVALL);
+ p = fnval;
+ goto fnend;
+ }
+