/* $Id: etc.c,v 1.81 2007/05/23 15:06:05 inu Exp $ */ #include "fm.h" #ifndef __MINGW32_VERSION #include <pwd.h> #endif #include "myctype.h" #include "html.h" #include "local.h" #include "hash.h" #include <fcntl.h> #include <sys/types.h> #include <time.h> #if defined(HAVE_WAITPID) || defined(HAVE_WAIT3) #include <sys/wait.h> #endif #include <signal.h> #ifdef __WATT32__ #define read(a,b,c) read_s(a,b,c) #define close(x) close_s(x) #endif /* __WATT32__ */ struct auth_pass { int bad; int is_proxy; Str host; int port; /* Str file; */ Str realm; Str uname; Str pwd; struct auth_pass *next; }; struct auth_pass *passwords = NULL; int columnSkip(Buffer *buf, int offset) { int i, maxColumn; int column = buf->currentColumn + offset; int nlines = buf->LINES + 1; Line *l; maxColumn = 0; for (i = 0, l = buf->topLine; i < nlines && l != NULL; i++, l = l->next) { if (l->width < 0) l->width = COLPOS(l, l->len); if (l->width - 1 > maxColumn) maxColumn = l->width - 1; } maxColumn -= buf->COLS - 1; if (column < maxColumn) maxColumn = column; if (maxColumn < 0) maxColumn = 0; if (buf->currentColumn == maxColumn) return 0; buf->currentColumn = maxColumn; return 1; } int columnPos(Line *line, int column) { int i; for (i = 1; i < line->len; i++) { if (COLPOS(line, i) > column) break; } #ifdef USE_M17N for (i--; i > 0 && line->propBuf[i] & PC_WCHAR2; i--) ; return i; #else return i - 1; #endif } Line * lineSkip(Buffer *buf, Line *line, int offset, int last) { int i; Line *l; l = currentLineSkip(buf, line, offset, last); if (!nextpage_topline) for (i = buf->LINES - 1 - (buf->lastLine->linenumber - l->linenumber); i > 0 && l->prev != NULL; i--, l = l->prev) ; return l; } Line * currentLineSkip(Buffer *buf, Line *line, int offset, int last) { int i, n; Line *l = line; if (buf->pagerSource && !(buf->bufferprop & BP_CLOSE)) { n = line->linenumber + offset + buf->LINES; if (buf->lastLine->linenumber < n) getNextPage(buf, n - buf->lastLine->linenumber); while ((last || (buf->lastLine->linenumber < n)) && (getNextPage(buf, 1) != NULL)) ; if (last) l = buf->lastLine; } if (offset == 0) return l; if (offset > 0) for (i = 0; i < offset && l->next != NULL; i++, l = l->next) ; else for (i = 0; i < -offset && l->prev != NULL; i++, l = l->prev) ; return l; } #define MAX_CMD_LEN 128 int gethtmlcmd(char **s) { extern Hash_si tagtable; char cmdstr[MAX_CMD_LEN]; char *p = cmdstr; char *save = *s; int cmd; (*s)++; /* first character */ if (IS_ALNUM(**s) || **s == '_' || **s == '/') { *(p++) = TOLOWER(**s); (*s)++; } else return HTML_UNKNOWN; if (p[-1] == '/') SKIP_BLANKS(*s); while ((IS_ALNUM(**s) || **s == '_') && p - cmdstr < MAX_CMD_LEN) { *(p++) = TOLOWER(**s); (*s)++; } if (p - cmdstr == MAX_CMD_LEN) { /* buffer overflow: perhaps caused by bad HTML source */ *s = save + 1; return HTML_UNKNOWN; } *p = '\0'; /* hash search */ cmd = getHash_si(&tagtable, cmdstr, HTML_UNKNOWN); while (**s && **s != '>') (*s)++; if (**s == '>') (*s)++; return cmd; } #ifdef USE_ANSI_COLOR static int parse_ansi_color(char **str, Lineprop *effect, Linecolor *color) { char *p = *str, *q; Lineprop e = *effect; Linecolor c = *color; int i; if (*p != ESC_CODE || *(p + 1) != '[') return 0; p += 2; for (q = p; IS_DIGIT(*q) || *q == ';'; q++) ; if (*q != 'm') return 0; *str = q + 1; while (1) { if (*p == 'm') { e = PE_NORMAL; c = 0; break; } if (IS_DIGIT(*p)) { q = p; for (p++; IS_DIGIT(*p); p++) ; i = atoi(allocStr(q, p - q)); switch (i) { case 0: e = PE_NORMAL; c = 0; break; case 1: case 5: e = PE_BOLD; break; case 4: e = PE_UNDER; break; case 7: e = PE_STAND; break; case 100: /* for EWS4800 kterm */ c = 0; break; case 39: c &= 0xf0; break; case 49: c &= 0x0f; break; default: if (i >= 30 && i <= 37) c = (c & 0xf0) | (i - 30) | 0x08; else if (i >= 40 && i <= 47) c = (c & 0x0f) | ((i - 40) << 4) | 0x80; break; } if (*p == 'm') break; } else { e = PE_NORMAL; c = 0; break; } p++; /* *p == ';' */ } *effect = e; *color = c; return 1; } #endif /* * Check character type */ Str checkType(Str s, Lineprop **oprop, Linecolor **ocolor) { Lineprop mode; Lineprop effect = PE_NORMAL; Lineprop *prop; static Lineprop *prop_buffer = NULL; static int prop_size = 0; char *str = s->ptr, *endp = &s->ptr[s->length], *bs = NULL; #ifdef USE_ANSI_COLOR Lineprop ceffect = PE_NORMAL; Linecolor cmode = 0; int check_color = FALSE; Linecolor *color = NULL; static Linecolor *color_buffer = NULL; static int color_size = 0; char *es = NULL; #endif int do_copy = FALSE; int i; int plen = 0, clen; if (prop_size < s->length) { prop_size = (s->length > LINELEN) ? s->length : LINELEN; prop_buffer = New_Reuse(Lineprop, prop_buffer, prop_size); } prop = prop_buffer; if (ShowEffect) { bs = memchr(str, '\b', s->length); #ifdef USE_ANSI_COLOR if (ocolor) { es = memchr(str, ESC_CODE, s->length); if (es) { if (color_size < s->length) { color_size = (s->length > LINELEN) ? s->length : LINELEN; color_buffer = New_Reuse(Linecolor, color_buffer, color_size); } color = color_buffer; } } #endif if ((bs != NULL) #ifdef USE_ANSI_COLOR || (es != NULL) #endif ) { char *sp = str, *ep; s = Strnew_size(s->length); do_copy = TRUE; ep = bs ? (bs - 2) : endp; #ifdef USE_ANSI_COLOR if (es && ep > es - 2) ep = es - 2; #endif for (; str < ep && IS_ASCII(*str); str++) { *(prop++) = PE_NORMAL | (IS_CNTRL(*str) ? PC_CTRL : PC_ASCII); #ifdef USE_ANSI_COLOR if (color) *(color++) = 0; #endif } Strcat_charp_n(s, sp, (int)(str - sp)); } } if (!do_copy) { for (; str < endp && IS_ASCII(*str); str++) *(prop++) = PE_NORMAL | (IS_CNTRL(*str) ? PC_CTRL : PC_ASCII); } while (str < endp) { if (prop - prop_buffer >= prop_size) break; if (bs != NULL) { #ifdef USE_M17N if (str == bs - 2 && !strncmp(str, "__\b\b", 4)) { str += 4; effect = PE_UNDER; if (str < endp) bs = memchr(str, '\b', endp - str); continue; } else #endif if (str == bs - 1 && *str == '_') { str += 2; effect = PE_UNDER; if (str < endp) bs = memchr(str, '\b', endp - str); continue; } else if (str == bs) { if (*(str + 1) == '_') { if (s->length) { str += 2; #ifdef USE_M17N for (i = 1; i <= plen; i++) *(prop - i) |= PE_UNDER; #else *(prop - 1) |= PE_UNDER; #endif } else { str++; } } #ifdef USE_M17N else if (!strncmp(str + 1, "\b__", 3)) { if (s->length) { str += (plen == 1) ? 3 : 4; for (i = 1; i <= plen; i++) *(prop - i) |= PE_UNDER; } else { str += 2; } } else if (*(str + 1) == '\b') { if (s->length) { clen = get_mclen(str + 2); if (plen == clen && !strncmp(str - plen, str + 2, plen)) { for (i = 1; i <= plen; i++) *(prop - i) |= PE_BOLD; str += 2 + clen; } else { Strshrink(s, plen); prop -= plen; str += 2; } } else { str += 2; } } #endif else { if (s->length) { #ifdef USE_M17N clen = get_mclen(str + 1); if (plen == clen && !strncmp(str - plen, str + 1, plen)) { for (i = 1; i <= plen; i++) *(prop - i) |= PE_BOLD; str += 1 + clen; } else { Strshrink(s, plen); prop -= plen; str++; } #else if (*(str - 1) == *(str + 1)) { *(prop - 1) |= PE_BOLD; str += 2; } else { Strshrink(s, 1); prop--; str++; } #endif } else { str++; } } if (str < endp) bs = memchr(str, '\b', endp - str); continue; } #ifdef USE_ANSI_COLOR else if (str > bs) bs = memchr(str, '\b', endp - str); #endif } #ifdef USE_ANSI_COLOR if (es != NULL) { if (str == es) { int ok = parse_ansi_color(&str, &ceffect, &cmode); if (str < endp) es = memchr(str, ESC_CODE, endp - str); if (ok) { if (cmode) check_color = TRUE; continue; } } else if (str > es) es = memchr(str, ESC_CODE, endp - str); } #endif plen = get_mclen(str); mode = get_mctype(str) | effect; #ifdef USE_ANSI_COLOR if (color) { *(color++) = cmode; mode |= ceffect; } #endif *(prop++) = mode; #ifdef USE_M17N if (plen > 1) { mode = (mode & ~PC_WCHAR1) | PC_WCHAR2; for (i = 1; i < plen; i++) { *(prop++) = mode; #ifdef USE_ANSI_COLOR if (color) *(color++) = cmode; #endif } if (do_copy) Strcat_charp_n(s, (char *)str, plen); str += plen; } else #endif { if (do_copy) Strcat_char(s, (char)*str); str++; } effect = PE_NORMAL; } *oprop = prop_buffer; #ifdef USE_ANSI_COLOR if (ocolor) *ocolor = check_color ? color_buffer : NULL; #endif return s; } static int nextColumn(int n, char *p, Lineprop *pr) { if (*pr & PC_CTRL) { if (*p == '\t') return (n + Tabstop) / Tabstop * Tabstop; else if (*p == '\n') return n + 1; else if (*p != '\r') return n + 2; return n; } #ifdef USE_M17N if (*pr & PC_UNKNOWN) return n + 4; return n + wtf_width((wc_uchar *) p); #else return n + 1; #endif } int calcPosition(char *l, Lineprop *pr, int len, int pos, int bpos, int mode) { static int *realColumn = NULL; static int size = 0; static char *prevl = NULL; int i, j; if (l == NULL || len == 0) return bpos; if (l == prevl && mode == CP_AUTO) { if (pos <= len) return realColumn[pos]; } if (size < len + 1) { size = (len + 1 > LINELEN) ? (len + 1) : LINELEN; realColumn = New_N(int, size); } prevl = l; i = 0; j = bpos; #ifdef USE_M17N if (pr[i] & PC_WCHAR2) { for (; i < len && pr[i] & PC_WCHAR2; i++) realColumn[i] = j; if (i > 0 && pr[i - 1] & PC_KANJI && WcOption.use_wide) j++; } #endif while (1) { realColumn[i] = j; if (i == len) break; j = nextColumn(j, &l[i], &pr[i]); i++; #ifdef USE_M17N for (; i < len && pr[i] & PC_WCHAR2; i++) realColumn[i] = realColumn[i - 1]; #endif } if (pos >= i) return j; return realColumn[pos]; } int columnLen(Line *line, int column) { int i, j; for (i = 0, j = 0; i < line->len;) { j = nextColumn(j, &line->lineBuf[i], &line->propBuf[i]); if (j > column) return i; i++; #ifdef USE_M17N while (i < line->len && line->propBuf[i] & PC_WCHAR2) i++; #endif } return line->len; } char * lastFileName(char *path) { char *p, *q; p = q = path; while (*p != '\0') { if (*p == '/') q = p + 1; p++; } return allocStr(q, -1); } #ifdef USE_INCLUDED_SRAND48 static unsigned long R1 = 0x1234abcd; static unsigned long R2 = 0x330e; #define A1 0x5deec #define A2 0xe66d #define C 0xb void srand48(long seed) { R1 = (unsigned long)seed; R2 = 0x330e; } long lrand48(void) { R1 = (A1 * R1 << 16) + A1 * R2 + A2 * R1 + ((A2 * R2 + C) >> 16); R2 = (A2 * R2 + C) & 0xffff; return (long)(R1 >> 1); } #endif char * mybasename(char *s) { char *p = s; while (*p) p++; while (s <= p && *p != '/') p--; if (*p == '/') p++; else p = s; return allocStr(p, -1); } char * mydirname(char *s) { char *p = s; while (*p) p++; if (s != p) p--; while (s != p && *p == '/') p--; while (s != p && *p != '/') p--; if (*p != '/') return "."; while (s != p && *p == '/') p--; return allocStr(s, strlen(s) - strlen(p) + 1); } #ifndef HAVE_STRERROR char * strerror(int errno) { extern char *sys_errlist[]; return sys_errlist[errno]; } #endif /* not HAVE_STRERROR */ #ifndef HAVE_SYS_ERRLIST char **sys_errlist; prepare_sys_errlist() { int i, n; i = 1; while (strerror(i) != NULL) i++; n = i; sys_errlist = New_N(char *, n); sys_errlist[0] = ""; for (i = 1; i < n; i++) sys_errlist[i] = strerror(i); } #endif /* not HAVE_SYS_ERRLIST */ int next_status(char c, int *status) { switch (*status) { case R_ST_NORMAL: if (c == '<') { *status = R_ST_TAG0; return 0; } else if (c == '&') { *status = R_ST_AMP; return 1; } else return 1; break; case R_ST_TAG0: if (c == '!') { *status = R_ST_CMNT1; return 0; } *status = R_ST_TAG; /* continues to next case */ case R_ST_TAG: if (c == '>') *status = R_ST_NORMAL; else if (c == '=') *status = R_ST_EQL; return 0; case R_ST_EQL: if (c == '"') *status = R_ST_DQUOTE; else if (c == '\'') *status = R_ST_QUOTE; else if (IS_SPACE(c)) *status = R_ST_EQL; else if (c == '>') *status = R_ST_NORMAL; else *status = R_ST_VALUE; return 0; case R_ST_QUOTE: if (c == '\'') *status = R_ST_TAG; return 0; case R_ST_DQUOTE: if (c == '"') *status = R_ST_TAG; return 0; case R_ST_VALUE: if (c == '>') *status = R_ST_NORMAL; else if (IS_SPACE(c)) *status = R_ST_TAG; return 0; case R_ST_AMP: if (c == ';') { *status = R_ST_NORMAL; return 0; } else if (c != '#' && !IS_ALNUM(c) && c != '_') { /* something's wrong! */ *status = R_ST_NORMAL; return 0; } else return 0; case R_ST_CMNT1: switch (c) { case '-': *status = R_ST_CMNT2; break; case '>': *status = R_ST_NORMAL; break; default: *status = R_ST_IRRTAG; } return 0; case R_ST_CMNT2: switch (c) { case '-': *status = R_ST_CMNT; break; case '>': *status = R_ST_NORMAL; break; default: *status = R_ST_IRRTAG; } return 0; case R_ST_CMNT: if (c == '-') *status = R_ST_NCMNT1; return 0; case R_ST_NCMNT1: if (c == '-') *status = R_ST_NCMNT2; else *status = R_ST_CMNT; return 0; case R_ST_NCMNT2: switch (c) { case '>': *status = R_ST_NORMAL; break; case '-': *status = R_ST_NCMNT2; break; default: if (IS_SPACE(c)) *status = R_ST_NCMNT3; else *status = R_ST_CMNT; break; } break; case R_ST_NCMNT3: switch (c) { case '>': *status = R_ST_NORMAL; break; case '-': *status = R_ST_NCMNT1; break; default: if (IS_SPACE(c)) *status = R_ST_NCMNT3; else *status = R_ST_CMNT; break; } return 0; case R_ST_IRRTAG: if (c == '>') *status = R_ST_NORMAL; return 0; } /* notreached */ return 0; } int read_token(Str buf, char **instr, int *status, int pre, int append) { char *p; int prev_status; if (!append) Strclear(buf); if (**instr == '\0') return 0; for (p = *instr; *p; p++) { prev_status = *status; next_status(*p, status); switch (*status) { case R_ST_NORMAL: if (prev_status == R_ST_AMP && *p != ';') { p--; break; } if (prev_status == R_ST_NCMNT2 || prev_status == R_ST_NCMNT3 || prev_status == R_ST_IRRTAG || prev_status == R_ST_CMNT1) { if (prev_status == R_ST_CMNT1 && !append && !pre) Strclear(buf); if (pre) Strcat_char(buf, *p); p++; goto proc_end; } Strcat_char(buf, (!pre && IS_SPACE(*p)) ? ' ' : *p); if (ST_IS_REAL_TAG(prev_status)) { *instr = p + 1; if (buf->length < 2 || buf->ptr[buf->length - 2] != '<' || buf->ptr[buf->length - 1] != '>') return 1; Strshrink(buf, 2); } break; case R_ST_TAG0: case R_ST_TAG: if (prev_status == R_ST_NORMAL && p != *instr) { *instr = p; *status = prev_status; return 1; } if (*status == R_ST_TAG0 && !REALLY_THE_BEGINNING_OF_A_TAG(p)) { /* it seems that this '<' is not a beginning of a tag */ /* * Strcat_charp(buf, "<"); */ Strcat_char(buf, '<'); *status = R_ST_NORMAL; } else Strcat_char(buf, *p); break; case R_ST_EQL: case R_ST_QUOTE: case R_ST_DQUOTE: case R_ST_VALUE: case R_ST_AMP: Strcat_char(buf, *p); break; case R_ST_CMNT: case R_ST_IRRTAG: if (pre) Strcat_char(buf, *p); else if (!append) Strclear(buf); break; case R_ST_CMNT1: case R_ST_CMNT2: case R_ST_NCMNT1: case R_ST_NCMNT2: case R_ST_NCMNT3: /* do nothing */ if (pre) Strcat_char(buf, *p); break; } } proc_end: *instr = p; return 1; } Str correct_irrtag(int status) { char c; Str tmp = Strnew(); while (status != R_ST_NORMAL) { switch (status) { case R_ST_CMNT: /* required "-->" */ case R_ST_NCMNT1: /* required "->" */ c = '-'; break; case R_ST_NCMNT2: case R_ST_NCMNT3: case R_ST_IRRTAG: case R_ST_CMNT1: case R_ST_CMNT2: case R_ST_TAG: case R_ST_TAG0: case R_ST_EQL: /* required ">" */ case R_ST_VALUE: c = '>'; break; case R_ST_QUOTE: c = '\''; break; case R_ST_DQUOTE: c = '"'; break; case R_ST_AMP: c = ';'; break; default: return tmp; } next_status(c, &status); Strcat_char(tmp, c); } return tmp; } /* * RFC2617: 1.2 Access Authentication Framework * * The realm value (case-sensitive), in combination with the canonical root * URL (the absoluteURI for the server whose abs_path is empty; see section * 5.1.2 of RFC2616 ) of the server being accessed, defines the protection * space. These realms allow the protected resources on a server to be * partitioned into a set of protection spaces, each with its own * authentication scheme and/or authorization database. * */ static void add_auth_pass_entry(const struct auth_pass *ent, int netrc, int override) { if ((ent->host || netrc) /* netrc accept default (host == NULL) */ &&(ent->is_proxy || ent->realm || netrc) && ent->uname && ent->pwd) { struct auth_pass *newent = New(struct auth_pass); memcpy(newent, ent, sizeof(struct auth_pass)); if (override) { newent->next = passwords; passwords = newent; } else { if (passwords == NULL) passwords = newent; else if (passwords->next == NULL) passwords->next = newent; else { struct auth_pass *ep = passwords; for (; ep->next; ep = ep->next) ; ep->next = newent; } } } /* ignore invalid entries */ } static struct auth_pass * find_auth_pass_entry(char *host, int port, char *realm, char *uname, int is_proxy) { struct auth_pass *ent; for (ent = passwords; ent != NULL; ent = ent->next) { if (ent->is_proxy == is_proxy && (ent->bad != TRUE) && (!ent->host || !Strcasecmp_charp(ent->host, host)) && (!ent->port || ent->port == port) && (!ent->uname || !uname || !Strcmp_charp(ent->uname, uname)) && (!ent->realm || !realm || !Strcmp_charp(ent->realm, realm)) ) return ent; } return NULL; } int find_auth_user_passwd(ParsedURL *pu, char *realm, Str *uname, Str *pwd, int is_proxy) { struct auth_pass *ent; if (pu->user && pu->pass) { *uname = Strnew_charp(pu->user); *pwd = Strnew_charp(pu->pass); return 1; } ent = find_auth_pass_entry(pu->host, pu->port, realm, pu->user, is_proxy); if (ent) { *uname = ent->uname; *pwd = ent->pwd; return 1; } return 0; } void add_auth_user_passwd(ParsedURL *pu, char *realm, Str uname, Str pwd, int is_proxy) { struct auth_pass ent; memset(&ent, 0, sizeof(ent)); ent.is_proxy = is_proxy; ent.host = Strnew_charp(pu->host); ent.port = pu->port; ent.realm = Strnew_charp(realm); ent.uname = uname; ent.pwd = pwd; add_auth_pass_entry(&ent, 0, 1); } void invalidate_auth_user_passwd(ParsedURL *pu, char *realm, Str uname, Str pwd, int is_proxy) { struct auth_pass *ent; ent = find_auth_pass_entry(pu->host, pu->port, realm, NULL, is_proxy); if (ent) { ent->bad = TRUE; } return; } /* passwd */ /* * machine <host> * host <host> * port <port> * proxy * path <file> ; not used * realm <realm> * login <login> * passwd <passwd> * password <passwd> */ static Str next_token(Str arg) { Str narg = NULL; char *p, *q; if (arg == NULL || arg->length == 0) return NULL; p = arg->ptr; q = p; SKIP_NON_BLANKS(q); if (*q != '\0') { *q++ = '\0'; SKIP_BLANKS(q); if (*q != '\0') narg = Strnew_charp(q); } return narg; } static void parsePasswd(FILE * fp, int netrc) { struct auth_pass ent; Str line = NULL; bzero(&ent, sizeof(struct auth_pass)); while (1) { Str arg = NULL; char *p; if (line == NULL || line->length == 0) line = Strfgets(fp); if (line->length == 0) break; Strchop(line); Strremovefirstspaces(line); p = line->ptr; if (*p == '#' || *p == '\0') { line = NULL; continue; /* comment or empty line */ } arg = next_token(line); if (!strcmp(p, "machine") || !strcmp(p, "host") || (netrc && !strcmp(p, "default"))) { add_auth_pass_entry(&ent, netrc, 0); bzero(&ent, sizeof(struct auth_pass)); if (netrc) ent.port = 21; /* XXX: getservbyname("ftp"); ? */ if (strcmp(p, "default") != 0) { line = next_token(arg); ent.host = arg; } else { line = arg; } } else if (!netrc && !strcmp(p, "port") && arg) { line = next_token(arg); ent.port = atoi(arg->ptr); } else if (!netrc && !strcmp(p, "proxy")) { ent.is_proxy = 1; line = arg; } else if (!netrc && !strcmp(p, "path")) { line = next_token(arg); /* ent.file = arg; */ } else if (!netrc && !strcmp(p, "realm")) { /* XXX: rest of line becomes arg for realm */ line = NULL; ent.realm = arg; } else if (!strcmp(p, "login")) { line = next_token(arg); ent.uname = arg; } else if (!strcmp(p, "password") || !strcmp(p, "passwd")) { line = next_token(arg); ent.pwd = arg; } else if (netrc && !strcmp(p, "machdef")) { while ((line = Strfgets(fp))->length != 0) { if (*line->ptr == '\n') break; } line = NULL; } else if (netrc && !strcmp(p, "account")) { /* ignore */ line = next_token(arg); } else { /* ignore rest of line */ line = NULL; } } add_auth_pass_entry(&ent, netrc, 0); } /* FIXME: gettextize? */ #define FILE_IS_READABLE_MSG "SECURITY NOTE: file %s must not be accessible by others" FILE * openSecretFile(char *fname) { char *efname; struct stat st; if (fname == NULL) return NULL; efname = expandPath(fname); if (stat(efname, &st) < 0) return NULL; /* check permissions, if group or others readable or writable, * refuse it, because it's insecure. * * XXX: disable_secret_security_check will introduce some * security issues, but on some platform such as Windows * it's not possible (or feasible) to disable group|other * readable and writable. * [w3m-dev 03368][w3m-dev 03369][w3m-dev 03370] */ if (disable_secret_security_check) /* do nothing */ ; else if ((st.st_mode & (S_IRWXG | S_IRWXO)) != 0) { if (fmInitialized) { message(Sprintf(FILE_IS_READABLE_MSG, fname)->ptr, 0, 0); refresh(); } else { fputs(Sprintf(FILE_IS_READABLE_MSG, fname)->ptr, stderr); fputc('\n', stderr); } sleep(2); return NULL; } return fopen(efname, "r"); } void loadPasswd(void) { FILE *fp; passwords = NULL; fp = openSecretFile(passwd_file); if (fp != NULL) { parsePasswd(fp, 0); fclose(fp); } /* for FTP */ fp = openSecretFile("~/.netrc"); if (fp != NULL) { parsePasswd(fp, 1); fclose(fp); } return; } /* get last modified time */ char * last_modified(Buffer *buf) { TextListItem *ti; struct stat st; if (buf->document_header) { for (ti = buf->document_header->first; ti; ti = ti->next) { if (strncasecmp(ti->ptr, "Last-modified: ", 15) == 0) { return ti->ptr + 15; } } return "unknown"; } else if (buf->currentURL.scheme == SCM_LOCAL) { if (stat(buf->currentURL.file, &st) < 0) return "unknown"; return ctime(&st.st_mtime); } return "unknown"; } static char roman_num1[] = { 'i', 'x', 'c', 'm', '*', }; static char roman_num5[] = { 'v', 'l', 'd', '*', }; static Str romanNum2(int l, int n) { Str s = Strnew(); switch (n) { case 1: case 2: case 3: for (; n > 0; n--) Strcat_char(s, roman_num1[l]); break; case 4: Strcat_char(s, roman_num1[l]); Strcat_char(s, roman_num5[l]); break; case 5: case 6: case 7: case 8: Strcat_char(s, roman_num5[l]); for (n -= 5; n > 0; n--) Strcat_char(s, roman_num1[l]); break; case 9: Strcat_char(s, roman_num1[l]); Strcat_char(s, roman_num1[l + 1]); break; } return s; } Str romanNumeral(int n) { Str r = Strnew(); if (n <= 0) return r; if (n >= 4000) { Strcat_charp(r, "**"); return r; } Strcat(r, romanNum2(3, n / 1000)); Strcat(r, romanNum2(2, (n % 1000) / 100)); Strcat(r, romanNum2(1, (n % 100) / 10)); Strcat(r, romanNum2(0, n % 10)); return r; } Str romanAlphabet(int n) { Str r = Strnew(); int l; char buf[14]; if (n <= 0) return r; l = 0; while (n) { buf[l++] = 'a' + (n - 1) % 26; n = (n - 1) / 26; } l--; for (; l >= 0; l--) Strcat_char(r, buf[l]); return r; } #ifndef SIGIOT #define SIGIOT SIGABRT #endif /* not SIGIOT */ static void reset_signals(void) { #ifdef SIGHUP mySignal(SIGHUP, SIG_DFL); /* terminate process */ #endif mySignal(SIGINT, SIG_DFL); /* terminate process */ #ifdef SIGQUIT mySignal(SIGQUIT, SIG_DFL); /* terminate process */ #endif mySignal(SIGTERM, SIG_DFL); /* terminate process */ mySignal(SIGILL, SIG_DFL); /* create core image */ mySignal(SIGIOT, SIG_DFL); /* create core image */ mySignal(SIGFPE, SIG_DFL); /* create core image */ #ifdef SIGBUS mySignal(SIGBUS, SIG_DFL); /* create core image */ #endif /* SIGBUS */ #ifdef SIGCHLD mySignal(SIGCHLD, SIG_IGN); #endif #ifdef SIGPIPE mySignal(SIGPIPE, SIG_IGN); #endif } #ifndef FOPEN_MAX #define FOPEN_MAX 1024 /* XXX */ #endif static void close_all_fds_except(int i, int f) { switch (i) { /* fall through */ case 0: dup2(open(DEV_NULL_PATH, O_RDONLY), 0); case 1: dup2(open(DEV_NULL_PATH, O_WRONLY), 1); case 2: dup2(open(DEV_NULL_PATH, O_WRONLY), 2); } /* close all other file descriptors (socket, ...) */ for (i = 3; i < FOPEN_MAX; i++) { if (i != f) close(i); } } void setup_child(int child, int i, int f) { reset_signals(); mySignal(SIGINT, SIG_IGN); #ifndef __MINGW32_VERSION if (!child) SETPGRP(); #endif /* __MINGW32_VERSION */ /* * I don't know why but close_tty() sometimes interrupts loadGeneralFile() in loadImage() * and corrupt image data can be cached in ~/.w3m. */ #if 0 close_tty(); #endif close_all_fds_except(i, f); QuietMessage = TRUE; fmInitialized = FALSE; TrapSignal = FALSE; } #ifndef __MINGW32_VERSION pid_t open_pipe_rw(FILE ** fr, FILE ** fw) { int fdr[2]; int fdw[2]; pid_t pid; if (fr && pipe(fdr) < 0) goto err0; if (fw && pipe(fdw) < 0) goto err1; flush_tty(); pid = fork(); if (pid < 0) goto err2; if (pid == 0) { /* child */ if (fr) { close(fdr[0]); dup2(fdr[1], 1); } if (fw) { close(fdw[1]); dup2(fdw[0], 0); } } else { if (fr) { close(fdr[1]); if (*fr == stdin) dup2(fdr[0], 0); else *fr = fdopen(fdr[0], "r"); } if (fw) { close(fdw[0]); if (*fw == stdout) dup2(fdw[1], 1); else *fw = fdopen(fdw[1], "w"); } } return pid; err2: if (fw) { close(fdw[0]); close(fdw[1]); } err1: if (fr) { close(fdr[0]); close(fdr[1]); } err0: return (pid_t) - 1; } #endif /* __MINGW32_VERSION */ void myExec(char *command) { mySignal(SIGINT, SIG_DFL); execl("/bin/sh", "sh", "-c", command, NULL); exit(127); } void mySystem(char *command, int background) { #ifndef __MINGW32_VERSION if (background) { #ifndef __EMX__ flush_tty(); if (!fork()) { setup_child(FALSE, 0, -1); myExec(command); } #else Str cmd = Strnew_charp("start /f "); Strcat_charp(cmd, command); system(cmd->ptr); #endif } else #endif /* __MINGW32_VERSION */ system(command); } Str myExtCommand(char *cmd, char *arg, int redirect) { Str tmp = NULL; char *p; int set_arg = FALSE; for (p = cmd; *p; p++) { if (*p == '%' && *(p + 1) == 's' && !set_arg) { if (tmp == NULL) tmp = Strnew_charp_n(cmd, (int)(p - cmd)); Strcat_charp(tmp, arg); set_arg = TRUE; p++; } else { if (tmp) Strcat_char(tmp, *p); } } if (!set_arg) { if (redirect) tmp = Strnew_m_charp("(", cmd, ") < ", arg, NULL); else tmp = Strnew_m_charp(cmd, " ", arg, NULL); } return tmp; } Str myEditor(char *cmd, char *file, int line) { Str tmp = NULL; char *p; int set_file = FALSE, set_line = FALSE; for (p = cmd; *p; p++) { if (*p == '%' && *(p + 1) == 's' && !set_file) { if (tmp == NULL) tmp = Strnew_charp_n(cmd, (int)(p - cmd)); Strcat_charp(tmp, file); set_file = TRUE; p++; } else if (*p == '%' && *(p + 1) == 'd' && !set_line && line > 0) { if (tmp == NULL) tmp = Strnew_charp_n(cmd, (int)(p - cmd)); Strcat(tmp, Sprintf("%d", line)); set_line = TRUE; p++; } else { if (tmp) Strcat_char(tmp, *p); } } if (!set_file) { if (tmp == NULL) tmp = Strnew_charp(cmd); if (!set_line && line > 1 && strcasestr(cmd, "vi")) Strcat(tmp, Sprintf(" +%d", line)); Strcat_m_charp(tmp, " ", file, NULL); } return tmp; } #ifdef __MINGW32_VERSION char * expandName(char *name) { return getenv("HOME"); } #else char * expandName(char *name) { char *p; struct passwd *passent, *getpwnam(const char *); Str extpath = NULL; if (name == NULL) return NULL; p = name; if (*p == '/') { if ((*(p + 1) == '~' && IS_ALPHA(*(p + 2))) && personal_document_root) { char *q; p += 2; q = strchr(p, '/'); if (q) { /* /~user/dir... */ passent = getpwnam(allocStr(p, q - p)); p = q; } else { /* /~user */ passent = getpwnam(p); p = ""; } if (!passent) goto rest; extpath = Strnew_m_charp(passent->pw_dir, "/", personal_document_root, NULL); if (*personal_document_root == '\0' && *p == '/') p++; } else goto rest; if (Strcmp_charp(extpath, "/") == 0 && *p == '/') p++; Strcat_charp(extpath, p); return extpath->ptr; } else return expandPath(p); rest: return name; } #endif char * file_to_url(char *file) { Str tmp; #ifdef SUPPORT_DOS_DRIVE_PREFIX char *drive = NULL; #endif #ifdef SUPPORT_NETBIOS_SHARE char *host = NULL; #endif file = expandPath(file); #ifdef SUPPORT_NETBIOS_SHARE if (file[0] == '/' && file[1] == '/') { char *p; file += 2; if (*file) { p = strchr(file, '/'); if (p != NULL && p != file) { host = allocStr(file, (p - file)); file = p; } } } #endif #ifdef SUPPORT_DOS_DRIVE_PREFIX if (IS_ALPHA(file[0]) && file[1] == ':') { drive = allocStr(file, 2); file += 2; } else #endif if (file[0] != '/') { tmp = Strnew_charp(CurrentDir); if (Strlastchar(tmp) != '/') Strcat_char(tmp, '/'); Strcat_charp(tmp, file); file = tmp->ptr; } tmp = Strnew_charp("file://"); #ifdef SUPPORT_NETBIOS_SHARE if (host) Strcat_charp(tmp, host); #endif #ifdef SUPPORT_DOS_DRIVE_PREFIX if (drive) Strcat_charp(tmp, drive); #endif Strcat_charp(tmp, file_quote(cleanupName(file))); return tmp->ptr; } #ifdef USE_M17N char * url_unquote_conv(char *url, wc_ces charset) #else char * url_unquote_conv0(char *url) #endif { #ifdef USE_M17N wc_uint8 old_auto_detect = WcOption.auto_detect; #endif Str tmp; tmp = Str_url_unquote(Strnew_charp(url), FALSE, TRUE); #ifdef USE_M17N if (!charset || charset == WC_CES_US_ASCII) charset = SystemCharset; WcOption.auto_detect = WC_OPT_DETECT_ON; tmp = convertLine(NULL, tmp, RAW_MODE, &charset, charset); WcOption.auto_detect = old_auto_detect; #endif return tmp->ptr; } static char *tmpf_base[MAX_TMPF_TYPE] = { "tmp", "src", "frame", "cache", "cookie", }; static unsigned int tmpf_seq[MAX_TMPF_TYPE]; Str tmpfname(int type, char *ext) { Str tmpf; tmpf = Sprintf("%s/w3m%s%d-%d%s", tmp_dir, tmpf_base[type], CurrentPid, tmpf_seq[type]++, (ext) ? ext : ""); pushText(fileToDelete, tmpf->ptr); return tmpf; } static char *monthtbl[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static int get_day(char **s) { Str tmp = Strnew(); int day; char *ss = *s; if (!**s) return -1; while (**s && IS_DIGIT(**s)) Strcat_char(tmp, *((*s)++)); day = atoi(tmp->ptr); if (day < 1 || day > 31) { *s = ss; return -1; } return day; } static int get_month(char **s) { Str tmp = Strnew(); int mon; char *ss = *s; if (!**s) return -1; while (**s && IS_DIGIT(**s)) Strcat_char(tmp, *((*s)++)); if (tmp->length > 0) { mon = atoi(tmp->ptr); } else { while (**s && IS_ALPHA(**s)) Strcat_char(tmp, *((*s)++)); for (mon = 1; mon <= 12; mon++) { if (strncmp(tmp->ptr, monthtbl[mon - 1], 3) == 0) break; } } if (mon < 1 || mon > 12) { *s = ss; return -1; } return mon; } static int get_year(char **s) { Str tmp = Strnew(); int year; char *ss = *s; if (!**s) return -1; while (**s && IS_DIGIT(**s)) Strcat_char(tmp, *((*s)++)); if (tmp->length != 2 && tmp->length != 4) { *s = ss; return -1; } year = atoi(tmp->ptr); if (tmp->length == 2) { if (year >= 70) year += 1900; else year += 2000; } return year; } static int get_time(char **s, int *hour, int *min, int *sec) { Str tmp = Strnew(); char *ss = *s; if (!**s) return -1; while (**s && IS_DIGIT(**s)) Strcat_char(tmp, *((*s)++)); if (**s != ':') { *s = ss; return -1; } *hour = atoi(tmp->ptr); (*s)++; Strclear(tmp); while (**s && IS_DIGIT(**s)) Strcat_char(tmp, *((*s)++)); if (**s != ':') { *s = ss; return -1; } *min = atoi(tmp->ptr); (*s)++; Strclear(tmp); while (**s && IS_DIGIT(**s)) Strcat_char(tmp, *((*s)++)); *sec = atoi(tmp->ptr); if (*hour < 0 || *hour >= 24 || *min < 0 || *min >= 60 || *sec < 0 || *sec >= 60) { *s = ss; return -1; } return 0; } static int get_zone(char **s, int *z_hour, int *z_min) { Str tmp = Strnew(); int zone; char *ss = *s; if (!**s) return -1; if (**s == '+' || **s == '-') Strcat_char(tmp, *((*s)++)); while (**s && IS_DIGIT(**s)) Strcat_char(tmp, *((*s)++)); if (!(tmp->length == 4 && IS_DIGIT(*ss)) && !(tmp->length == 5 && (*ss == '+' || *ss == '-'))) { *s = ss; return -1; } zone = atoi(tmp->ptr); *z_hour = zone / 100; *z_min = zone - (zone / 100) * 100; return 0; } /* RFC 1123 or RFC 850 or ANSI C asctime() format string -> time_t */ time_t mymktime(char *timestr) { char *s; int day, mon, year, hour, min, sec, z_hour = 0, z_min = 0; if (!(timestr && *timestr)) return -1; s = timestr; #ifdef DEBUG fprintf(stderr, "mktime: %s\n", timestr); #endif /* DEBUG */ while (*s && IS_ALPHA(*s)) s++; while (*s && !IS_ALNUM(*s)) s++; if (IS_DIGIT(*s)) { /* RFC 1123 or RFC 850 format */ if ((day = get_day(&s)) == -1) return -1; while (*s && !IS_ALNUM(*s)) s++; if ((mon = get_month(&s)) == -1) return -1; while (*s && !IS_DIGIT(*s)) s++; if ((year = get_year(&s)) == -1) return -1; while (*s && !IS_DIGIT(*s)) s++; if (!*s) { hour = 0; min = 0; sec = 0; } else { if (get_time(&s, &hour, &min, &sec) == -1) return -1; while (*s && !IS_DIGIT(*s) && *s != '+' && *s != '-') s++; get_zone(&s, &z_hour, &z_min); } } else { /* ANSI C asctime() format. */ while (*s && !IS_ALNUM(*s)) s++; if ((mon = get_month(&s)) == -1) return -1; while (*s && !IS_DIGIT(*s)) s++; if ((day = get_day(&s)) == -1) return -1; while (*s && !IS_DIGIT(*s)) s++; if (get_time(&s, &hour, &min, &sec) == -1) return -1; while (*s && !IS_DIGIT(*s)) s++; if ((year = get_year(&s)) == -1) return -1; } #ifdef DEBUG fprintf(stderr, "year=%d month=%d day=%d hour:min:sec=%d:%d:%d zone=%d:%d\n", year, mon, day, hour, min, sec, z_hour, z_min); #endif /* DEBUG */ mon -= 3; if (mon < 0) { mon += 12; year--; } day += (year - 1968) * 1461 / 4; day += ((((mon * 153) + 2) / 5) - 672); hour -= z_hour; min -= z_min; return (time_t) ((day * 60 * 60 * 24) + (hour * 60 * 60) + (min * 60) + sec); } #ifdef USE_COOKIE #ifdef INET6 #include <sys/socket.h> #endif /* INET6 */ #ifndef __MINGW32_VERSION #include <netdb.h> #else #include <winsock.h> #endif char * FQDN(char *host) { char *p; #ifndef INET6 struct hostent *entry; #else /* INET6 */ int *af; #endif /* INET6 */ if (host == NULL) return NULL; if (strcasecmp(host, "localhost") == 0) return host; for (p = host; *p && *p != '.'; p++) ; if (*p == '.') return host; #ifndef INET6 if (!(entry = gethostbyname(host))) return NULL; return allocStr(entry->h_name, -1); #else /* INET6 */ for (af = ai_family_order_table[DNS_order];; af++) { int error; struct addrinfo hints; struct addrinfo *res, *res0; char *namebuf; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_CANONNAME; hints.ai_family = *af; hints.ai_socktype = SOCK_STREAM; error = getaddrinfo(host, NULL, &hints, &res0); if (error) { if (*af == PF_UNSPEC) { /* all done */ break; } /* try next address family */ continue; } for (res = res0; res != NULL; res = res->ai_next) { if (res->ai_canonname) { /* found */ namebuf = strdup(res->ai_canonname); freeaddrinfo(res0); return namebuf; } } freeaddrinfo(res0); if (*af == PF_UNSPEC) { break; } } /* all failed */ return NULL; #endif /* INET6 */ } #endif /* USE_COOKIE */ void (*mySignal(int signal_number, void (*action) (int))) (int) { #ifdef SA_RESTART struct sigaction new_action, old_action; sigemptyset(&new_action.sa_mask); new_action.sa_handler = action; if (signal_number == SIGALRM) { #ifdef SA_INTERRUPT new_action.sa_flags = SA_INTERRUPT; #else new_action.sa_flags = 0; #endif } else { new_action.sa_flags = SA_RESTART; } sigaction(signal_number, &new_action, &old_action); return (old_action.sa_handler); #else return (signal(signal_number, action)); #endif }