links

lynx-like text mode web browser
git clone anongit@rnpnr.xyz:links.git
Log | Files | Refs | Feed | README | LICENSE

file.c (11274B)


      1 /* file.c
      2  * (c) 2002 Mikulas Patocka
      3  * This file is a part of the Links program, released under GPL.
      4  */
      5 
      6 #include <limits.h>
      7 #include <sys/stat.h>
      8 
      9 #include "links.h"
     10 
     11 static void
     12 setrwx(unsigned m, unsigned char *p)
     13 {
     14 	if (m & S_IRUSR)
     15 		p[0] = 'r';
     16 	if (m & S_IWUSR)
     17 		p[1] = 'w';
     18 	if (m & S_IXUSR)
     19 		p[2] = 'x';
     20 }
     21 
     22 static void
     23 setst(unsigned m, unsigned char *p)
     24 {
     25 #ifdef S_ISUID
     26 	if (m & S_ISUID) {
     27 		p[2] = 'S';
     28 		if (m & S_IXUSR)
     29 			p[2] = 's';
     30 	}
     31 #endif
     32 #ifdef S_ISGID
     33 	if (m & S_ISGID) {
     34 		p[5] = 'S';
     35 		if (m & S_IXGRP)
     36 			p[5] = 's';
     37 	}
     38 #endif
     39 #ifdef S_ISVTX
     40 	if (m & S_ISVTX) {
     41 		p[8] = 'T';
     42 		if (m & S_IXOTH)
     43 			p[8] = 't';
     44 	}
     45 #endif
     46 }
     47 
     48 static size_t
     49 stat_mode(unsigned char **p, size_t l, struct stat *stp)
     50 {
     51 	unsigned char c = '?';
     52 	unsigned char rwx[10] = "---------";
     53 	if (stp) {
     54 		if (0) {
     55 		}
     56 #ifdef S_ISBLK
     57 		else if (S_ISBLK(stp->st_mode))
     58 			c = 'b';
     59 #endif
     60 #ifdef S_ISCHR
     61 		else if (S_ISCHR(stp->st_mode))
     62 			c = 'c';
     63 #endif
     64 		else if (S_ISDIR(stp->st_mode))
     65 			c = 'd';
     66 		else if (S_ISREG(stp->st_mode))
     67 			c = '-';
     68 #ifdef S_ISFIFO
     69 		else if (S_ISFIFO(stp->st_mode))
     70 			c = 'p';
     71 #endif
     72 #ifdef S_ISLNK
     73 		else if (S_ISLNK(stp->st_mode))
     74 			c = 'l';
     75 #endif
     76 #ifdef S_ISSOCK
     77 		else if (S_ISSOCK(stp->st_mode))
     78 			c = 's';
     79 #endif
     80 #ifdef S_ISNWK
     81 		else if (S_ISNWK(stp->st_mode))
     82 			c = 'n';
     83 #endif
     84 	}
     85 	l = add_chr_to_str(p, l, c);
     86 	if (stp) {
     87 		unsigned mode = stp->st_mode;
     88 		setrwx(mode << 0, &rwx[0]);
     89 		setrwx(mode << 3, &rwx[3]);
     90 		setrwx(mode << 6, &rwx[6]);
     91 		setst(mode, rwx);
     92 	}
     93 	l = add_to_str(p, l, rwx);
     94 	return add_chr_to_str(p, l, ' ');
     95 }
     96 
     97 static size_t
     98 stat_links(unsigned char **p, size_t l, struct stat *stp)
     99 {
    100 	unsigned char lnk[64];
    101 	if (!stp)
    102 		return add_to_str(p, l, cast_uchar "    ");
    103 
    104 	sprintf(cast_char lnk, "%3ld ", (unsigned long)stp->st_nlink);
    105 	return add_to_str(p, l, lnk);
    106 }
    107 
    108 static int last_uid = -1;
    109 static unsigned char last_user[64];
    110 
    111 static int last_gid = -1;
    112 static unsigned char last_group[64];
    113 
    114 static size_t
    115 stat_user(unsigned char **p, size_t l, struct stat *stp, int g)
    116 {
    117 	struct passwd *pwd;
    118 	struct group *grp;
    119 	int id;
    120 	unsigned char *pp;
    121 	size_t i;
    122 	if (!stp)
    123 		return add_to_str(p, l, cast_uchar "         ");
    124 	id = !g ? stp->st_uid : stp->st_gid;
    125 	pp = !g ? last_user : last_group;
    126 	if (!g && id == last_uid && last_uid != -1)
    127 		goto a;
    128 	if (g && id == last_gid && last_gid != -1)
    129 		goto a;
    130 	if (!g) {
    131 		ENULLLOOP(pwd, getpwuid(id));
    132 		if (!pwd || !pwd->pw_name)
    133 			sprintf(cast_char pp, "%d", id);
    134 		else
    135 			sprintf(cast_char pp, "%.8s", pwd->pw_name);
    136 		last_uid = id;
    137 	} else {
    138 		ENULLLOOP(grp, getgrgid(id));
    139 		if (!grp || !grp->gr_name)
    140 			sprintf(cast_char pp, "%d", id);
    141 		else
    142 			sprintf(cast_char pp, "%.8s", grp->gr_name);
    143 		last_gid = id;
    144 	}
    145 a:
    146 	l = add_to_str(p, l, pp);
    147 	for (i = strlen(cast_const_char pp); i < 8; i++)
    148 		l = add_chr_to_str(p, l, ' ');
    149 	return add_chr_to_str(p, l, ' ');
    150 }
    151 
    152 static size_t
    153 stat_size(unsigned char **p, size_t l, struct stat *stp)
    154 {
    155 	unsigned char num[64];
    156 	const int digits = 8;
    157 	size_t i;
    158 	if (!stp)
    159 		num[0] = 0;
    160 	else
    161 		snzprint(num, sizeof num, stp->st_size);
    162 	for (i = strlen(cast_const_char num); i < digits; i++)
    163 		l = add_chr_to_str(p, l, ' ');
    164 	l = add_to_str(p, l, num);
    165 	return add_chr_to_str(p, l, ' ');
    166 }
    167 
    168 static size_t
    169 stat_date(unsigned char **p, size_t l, struct stat *stp)
    170 {
    171 	time_t current_time;
    172 	time_t when;
    173 	struct tm *when_local;
    174 	unsigned char *fmt, *e;
    175 	unsigned char str[13];
    176 	static unsigned char fmt1[] = "%b %e  %Y";
    177 	static unsigned char fmt2[] = "%b %e %H:%M";
    178 	size_t wr;
    179 	EINTRLOOPX(current_time, time(NULL), (time_t)-1);
    180 	if (!stp) {
    181 		wr = 0;
    182 		goto set_empty;
    183 	}
    184 	when = stp->st_mtime;
    185 	when_local = localtime(&when);
    186 	if ((ulonglong)current_time
    187 	        > (ulonglong)when + 6L * 30L * 24L * 60L * 60L
    188 	    || (ulonglong)current_time < (ulonglong)when - 60L * 60L)
    189 		fmt = fmt1;
    190 	else
    191 		fmt = fmt2;
    192 again:
    193 	wr = strftime(cast_char str, 13, cast_const_char fmt, when_local);
    194 	if (wr && strstr(cast_const_char str, " e ")
    195 	    && ((e = cast_uchar strchr(cast_const_char fmt, 'e')))) {
    196 		*e = 'd';
    197 		goto again;
    198 	}
    199 set_empty:
    200 	while (wr < 12)
    201 		str[wr++] = ' ';
    202 	str[12] = 0;
    203 	l = add_to_str(p, l, str);
    204 	return add_chr_to_str(p, l, ' ');
    205 }
    206 
    207 static unsigned char *
    208 get_filename(unsigned char *url)
    209 {
    210 	unsigned char *p, *m;
    211 	for (p = url + 7; *p && *p != POST_CHAR; p++)
    212 		;
    213 	m = NULL;
    214 	add_conv_str(&m, 0, url + 7, (int)(p - url - 7), -2);
    215 	return m;
    216 }
    217 
    218 struct dirs {
    219 	unsigned char *s;
    220 	unsigned char *f;
    221 };
    222 
    223 static int
    224 comp_de(const void *d1_, const void *d2_)
    225 {
    226 	const struct dirs *d1 = (const struct dirs *)d1_;
    227 	const struct dirs *d2 = (const struct dirs *)d2_;
    228 	if (d1->f[0] == '.' && d1->f[1] == '.' && !d1->f[2])
    229 		return -1;
    230 	if (d2->f[0] == '.' && d2->f[1] == '.' && !d2->f[2])
    231 		return 1;
    232 	if (d1->s[0] == 'd' && d2->s[0] != 'd')
    233 		return -1;
    234 	if (d1->s[0] != 'd' && d2->s[0] == 'd')
    235 		return 1;
    236 	return strcmp(cast_const_char d1->f, cast_const_char d2->f);
    237 }
    238 
    239 void
    240 file_func(struct connection *c)
    241 {
    242 	struct cache_entry *e;
    243 	unsigned char *file, *name, *head = NULL;
    244 	int fl, flo;
    245 	DIR *d;
    246 	int h, r;
    247 	struct stat stt;
    248 	int rs;
    249 	if (anonymous) {
    250 		setcstate(c, S_BAD_URL);
    251 		abort_connection(c);
    252 		return;
    253 	}
    254 	if (!(name = get_filename(c->url))) {
    255 		setcstate(c, S_OUT_OF_MEM);
    256 		abort_connection(c);
    257 		return;
    258 	}
    259 	EINTRLOOP(rs, stat(cast_const_char name, &stt));
    260 	if (rs) {
    261 		free(name);
    262 		setcstate(c, get_error_from_errno(errno));
    263 		abort_connection(c);
    264 		return;
    265 	}
    266 	if (!S_ISDIR(stt.st_mode) && !S_ISREG(stt.st_mode)) {
    267 		free(name);
    268 		setcstate(c, S_FILE_TYPE);
    269 		abort_connection(c);
    270 		return;
    271 	}
    272 	h = c_open(name, O_RDONLY | O_NOCTTY);
    273 	if (h == -1) {
    274 		int er = errno;
    275 		d = c_opendir(name);
    276 		if (d)
    277 			goto dir;
    278 		free(name);
    279 		setcstate(c, get_error_from_errno(er));
    280 		abort_connection(c);
    281 		return;
    282 	}
    283 	if (S_ISDIR(stt.st_mode)) {
    284 		struct dirs *dir;
    285 		int dirl;
    286 		int i;
    287 		int er;
    288 		struct dirent *de;
    289 		d = c_opendir(name);
    290 		er = errno;
    291 		EINTRLOOP(rs, close(h));
    292 		if (!d) {
    293 			free(name);
    294 			setcstate(c, get_error_from_errno(er));
    295 			abort_connection(c);
    296 			return;
    297 		}
    298 dir:
    299 		dir = NULL;
    300 		dirl = 0;
    301 		if (name[0]
    302 		    && !dir_sep(name[strlen(cast_const_char name) - 1])) {
    303 			if (!c->cache) {
    304 				if (get_connection_cache_entry(c)) {
    305 					free(name);
    306 					closedir(d);
    307 					setcstate(c, S_OUT_OF_MEM);
    308 					abort_connection(c);
    309 					return;
    310 				}
    311 				c->cache->refcount--;
    312 			}
    313 			e = c->cache;
    314 			free(e->redirect);
    315 			e->redirect = stracpy(c->url);
    316 			add_to_strn(&e->redirect, cast_uchar "/");
    317 			free(name);
    318 			closedir(d);
    319 			goto end;
    320 		}
    321 		last_uid = -1;
    322 		last_gid = -1;
    323 		file = NULL;
    324 		fl = add_to_str(&file, 0, cast_uchar "<html><head><title>");
    325 		flo = fl;
    326 		fl = add_conv_str(&file, fl, name,
    327 		                  (int)strlen(cast_const_char name), -1);
    328 		convert_file_charset(&file, &fl, flo);
    329 		fl = add_to_str(&file, fl,
    330 		                cast_uchar
    331 		                "</title></head><body><h2>Directory ");
    332 		flo = fl;
    333 		fl = add_conv_str(&file, fl, name,
    334 		                  (int)strlen(cast_const_char name), -1);
    335 		convert_file_charset(&file, &fl, flo);
    336 		fl = add_to_str(&file, fl, cast_uchar "</h2>\n<pre>");
    337 		while (1) {
    338 			struct stat stt, *stp;
    339 			unsigned char **p;
    340 			int l;
    341 			unsigned char *n;
    342 			ENULLLOOP(de, (void *)readdir(d));
    343 			if (!de)
    344 				break;
    345 			if (!strcmp(cast_const_char de->d_name, "."))
    346 				continue;
    347 			if (!strcmp(cast_const_char de->d_name, "..")) {
    348 				unsigned char *n = name;
    349 				if (strspn(cast_const_char n,
    350 				           dir_sep('\\') ? "/\\" : "/")
    351 				    == strlen(cast_const_char n))
    352 					continue;
    353 			}
    354 			if ((unsigned)dirl > INT_MAX / sizeof(struct dirs) - 1)
    355 				overalloc();
    356 			dir = xrealloc(dir, (dirl + 1) * sizeof(struct dirs));
    357 			dir[dirl].f = stracpy(cast_uchar de->d_name);
    358 			*(p = &dir[dirl++].s) = NULL;
    359 			l = 0;
    360 			n = stracpy(name);
    361 			add_to_strn(&n, cast_uchar de->d_name);
    362 			EINTRLOOP(rs, lstat(cast_const_char n, &stt));
    363 			if (rs)
    364 				stp = NULL;
    365 			else
    366 				stp = &stt;
    367 			free(n);
    368 			l = stat_mode(p, l, stp);
    369 			l = stat_links(p, l, stp);
    370 			l = stat_user(p, l, stp, 0);
    371 			l = stat_user(p, l, stp, 1);
    372 			l = stat_size(p, l, stp);
    373 			l = stat_date(p, l, stp);
    374 		}
    375 		closedir(d);
    376 		if (dirl)
    377 			qsort(dir, dirl, sizeof(struct dirs),
    378 			      (int (*)(const void *, const void *))comp_de);
    379 		for (i = 0; i < dirl; i++) {
    380 			char *lnk = NULL;
    381 			if (dir[i].s[0] == 'l') {
    382 				char *buf = NULL;
    383 				size_t size = 0;
    384 				int r;
    385 				char *n = (char *)stracpy(name);
    386 				add_to_strn((unsigned char **)&n, dir[i].f);
    387 				do {
    388 					free(buf);
    389 					size += ALLOC_GR;
    390 					if (size > INT_MAX)
    391 						overalloc();
    392 					buf = xmalloc(size);
    393 					EINTRLOOP(r, readlink(n, buf, size));
    394 				} while (r == size);
    395 				if (r == -1)
    396 					goto yyy;
    397 				buf[r] = 0;
    398 				lnk = buf;
    399 				goto xxx;
    400 yyy:
    401 				free(buf);
    402 xxx:
    403 				free(n);
    404 			}
    405 			fl = add_to_str(&file, fl, dir[i].s);
    406 			fl = add_to_str(&file, fl, cast_uchar "<a href=\"./");
    407 			fl = add_conv_str(&file, fl, dir[i].f,
    408 			                  (int)strlen(cast_const_char dir[i].f),
    409 			                  1);
    410 			if (dir[i].s[0] == 'd')
    411 				fl = add_chr_to_str(&file, fl, '/');
    412 			else if (lnk) {
    413 				struct stat st;
    414 				unsigned char *n = stracpy(name);
    415 				add_to_strn(&n, dir[i].f);
    416 				EINTRLOOP(rs, stat(cast_const_char n, &st));
    417 				if (!rs && S_ISDIR(st.st_mode))
    418 					fl = add_chr_to_str(&file, fl, '/');
    419 				free(n);
    420 			}
    421 			fl = add_to_str(&file, fl, cast_uchar "\">");
    422 			flo = fl;
    423 			fl = add_conv_str(&file, fl, dir[i].f,
    424 			                  (int)strlen(cast_const_char dir[i].f),
    425 			                  0);
    426 			convert_file_charset(&file, &fl, flo);
    427 			fl = add_to_str(&file, fl, cast_uchar "</a>");
    428 			if (lnk) {
    429 				fl = add_to_str(&file, fl, cast_uchar " -> ");
    430 				fl = add_to_str(&file, fl, cast_uchar lnk);
    431 				free(lnk);
    432 			}
    433 			fl = add_to_str(&file, fl, cast_uchar "\n");
    434 		}
    435 		free(name);
    436 		for (i = 0; i < dirl; i++) {
    437 			free(dir[i].s);
    438 			free(dir[i].f);
    439 		}
    440 		free(dir);
    441 		fl = add_to_str(&file, fl, cast_uchar "</pre></body></html>\n");
    442 		head = stracpy(cast_uchar "\r\nContent-Type: text/html\r\n");
    443 	} else {
    444 		free(name);
    445 		if (stt.st_size < 0 || stt.st_size > INT_MAX) {
    446 			EINTRLOOP(rs, close(h));
    447 			setcstate(c, S_LARGE_FILE);
    448 			abort_connection(c);
    449 			return;
    450 		}
    451 		/* + !stt.st_size is there because of bug in Linux. Read returns
    452 		   -EACCES when reading 0 bytes to invalid address */
    453 		file = xmalloc((size_t)stt.st_size + !stt.st_size);
    454 		if (!file) {
    455 			EINTRLOOP(rs, close(h));
    456 			setcstate(c, S_OUT_OF_MEM);
    457 			abort_connection(c);
    458 			return;
    459 		}
    460 		if ((r = hard_read(h, file, (int)stt.st_size)) != stt.st_size) {
    461 			free(file);
    462 			EINTRLOOP(rs, close(h));
    463 			setcstate(c, r == -1 ? get_error_from_errno(errno)
    464 			                     : S_FILE_ERROR);
    465 			abort_connection(c);
    466 			return;
    467 		}
    468 		fl = r;
    469 		EINTRLOOP(rs, close(h));
    470 		head = stracpy(cast_uchar "");
    471 	}
    472 	if (!c->cache) {
    473 		if (get_connection_cache_entry(c)) {
    474 			free(file);
    475 			free(head);
    476 			setcstate(c, S_OUT_OF_MEM);
    477 			abort_connection(c);
    478 			return;
    479 		}
    480 		c->cache->refcount--;
    481 	}
    482 	e = c->cache;
    483 	free(e->head);
    484 	e->head = head;
    485 	if ((r = add_fragment(e, 0, file, fl)) < 0) {
    486 		free(file);
    487 		setcstate(c, r);
    488 		abort_connection(c);
    489 		return;
    490 	}
    491 	truncate_entry(e, fl, 1);
    492 	free(file);
    493 end:
    494 	c->cache->incomplete = 0;
    495 	setcstate(c, S__OK);
    496 	abort_connection(c);
    497 }