1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
3*0Sstevel@tonic-gate  * Copyright (c) 2001 Damien Miller.  All rights reserved.
4*0Sstevel@tonic-gate  *
5*0Sstevel@tonic-gate  * Redistribution and use in source and binary forms, with or without
6*0Sstevel@tonic-gate  * modification, are permitted provided that the following conditions
7*0Sstevel@tonic-gate  * are met:
8*0Sstevel@tonic-gate  * 1. Redistributions of source code must retain the above copyright
9*0Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer.
10*0Sstevel@tonic-gate  * 2. Redistributions in binary form must reproduce the above copyright
11*0Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer in the
12*0Sstevel@tonic-gate  *    documentation and/or other materials provided with the distribution.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15*0Sstevel@tonic-gate  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16*0Sstevel@tonic-gate  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17*0Sstevel@tonic-gate  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18*0Sstevel@tonic-gate  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19*0Sstevel@tonic-gate  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20*0Sstevel@tonic-gate  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21*0Sstevel@tonic-gate  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22*0Sstevel@tonic-gate  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23*0Sstevel@tonic-gate  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24*0Sstevel@tonic-gate  */
25*0Sstevel@tonic-gate 
26*0Sstevel@tonic-gate #include "includes.h"
27*0Sstevel@tonic-gate RCSID("$OpenBSD: sftp-common.c,v 1.7 2002/09/11 22:41:50 djm Exp $");
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
30*0Sstevel@tonic-gate 
31*0Sstevel@tonic-gate #include "buffer.h"
32*0Sstevel@tonic-gate #include "bufaux.h"
33*0Sstevel@tonic-gate #include "log.h"
34*0Sstevel@tonic-gate #include "xmalloc.h"
35*0Sstevel@tonic-gate 
36*0Sstevel@tonic-gate #include "sftp.h"
37*0Sstevel@tonic-gate #include "sftp-common.h"
38*0Sstevel@tonic-gate 
39*0Sstevel@tonic-gate /* Clear contents of attributes structure */
40*0Sstevel@tonic-gate void
41*0Sstevel@tonic-gate attrib_clear(Attrib *a)
42*0Sstevel@tonic-gate {
43*0Sstevel@tonic-gate 	a->flags = 0;
44*0Sstevel@tonic-gate 	a->size = 0;
45*0Sstevel@tonic-gate 	a->uid = 0;
46*0Sstevel@tonic-gate 	a->gid = 0;
47*0Sstevel@tonic-gate 	a->perm = 0;
48*0Sstevel@tonic-gate 	a->atime = 0;
49*0Sstevel@tonic-gate 	a->mtime = 0;
50*0Sstevel@tonic-gate }
51*0Sstevel@tonic-gate 
52*0Sstevel@tonic-gate /* Convert from struct stat to filexfer attribs */
53*0Sstevel@tonic-gate void
54*0Sstevel@tonic-gate stat_to_attrib(struct stat *st, Attrib *a)
55*0Sstevel@tonic-gate {
56*0Sstevel@tonic-gate 	attrib_clear(a);
57*0Sstevel@tonic-gate 	a->flags = 0;
58*0Sstevel@tonic-gate 	a->flags |= SSH2_FILEXFER_ATTR_SIZE;
59*0Sstevel@tonic-gate 	a->size = st->st_size;
60*0Sstevel@tonic-gate 	a->flags |= SSH2_FILEXFER_ATTR_UIDGID;
61*0Sstevel@tonic-gate 	a->uid = st->st_uid;
62*0Sstevel@tonic-gate 	a->gid = st->st_gid;
63*0Sstevel@tonic-gate 	a->flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
64*0Sstevel@tonic-gate 	a->perm = st->st_mode;
65*0Sstevel@tonic-gate 	a->flags |= SSH2_FILEXFER_ATTR_ACMODTIME;
66*0Sstevel@tonic-gate 	a->atime = st->st_atime;
67*0Sstevel@tonic-gate 	a->mtime = st->st_mtime;
68*0Sstevel@tonic-gate }
69*0Sstevel@tonic-gate 
70*0Sstevel@tonic-gate /* Convert from filexfer attribs to struct stat */
71*0Sstevel@tonic-gate void
72*0Sstevel@tonic-gate attrib_to_stat(Attrib *a, struct stat *st)
73*0Sstevel@tonic-gate {
74*0Sstevel@tonic-gate 	memset(st, 0, sizeof(*st));
75*0Sstevel@tonic-gate 
76*0Sstevel@tonic-gate 	if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
77*0Sstevel@tonic-gate 		st->st_size = a->size;
78*0Sstevel@tonic-gate 	if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
79*0Sstevel@tonic-gate 		st->st_uid = a->uid;
80*0Sstevel@tonic-gate 		st->st_gid = a->gid;
81*0Sstevel@tonic-gate 	}
82*0Sstevel@tonic-gate 	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
83*0Sstevel@tonic-gate 		st->st_mode = a->perm;
84*0Sstevel@tonic-gate 	if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
85*0Sstevel@tonic-gate 		st->st_atime = a->atime;
86*0Sstevel@tonic-gate 		st->st_mtime = a->mtime;
87*0Sstevel@tonic-gate 	}
88*0Sstevel@tonic-gate }
89*0Sstevel@tonic-gate 
90*0Sstevel@tonic-gate /* Decode attributes in buffer */
91*0Sstevel@tonic-gate Attrib *
92*0Sstevel@tonic-gate decode_attrib(Buffer *b)
93*0Sstevel@tonic-gate {
94*0Sstevel@tonic-gate 	static Attrib a;
95*0Sstevel@tonic-gate 
96*0Sstevel@tonic-gate 	attrib_clear(&a);
97*0Sstevel@tonic-gate 	a.flags = buffer_get_int(b);
98*0Sstevel@tonic-gate 	if (a.flags & SSH2_FILEXFER_ATTR_SIZE)
99*0Sstevel@tonic-gate 		a.size = buffer_get_int64(b);
100*0Sstevel@tonic-gate 	if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) {
101*0Sstevel@tonic-gate 		a.uid = buffer_get_int(b);
102*0Sstevel@tonic-gate 		a.gid = buffer_get_int(b);
103*0Sstevel@tonic-gate 	}
104*0Sstevel@tonic-gate 	if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
105*0Sstevel@tonic-gate 		a.perm = buffer_get_int(b);
106*0Sstevel@tonic-gate 	if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
107*0Sstevel@tonic-gate 		a.atime = buffer_get_int(b);
108*0Sstevel@tonic-gate 		a.mtime = buffer_get_int(b);
109*0Sstevel@tonic-gate 	}
110*0Sstevel@tonic-gate 	/* vendor-specific extensions */
111*0Sstevel@tonic-gate 	if (a.flags & SSH2_FILEXFER_ATTR_EXTENDED) {
112*0Sstevel@tonic-gate 		char *type, *data;
113*0Sstevel@tonic-gate 		int i, count;
114*0Sstevel@tonic-gate 
115*0Sstevel@tonic-gate 		count = buffer_get_int(b);
116*0Sstevel@tonic-gate 		for (i = 0; i < count; i++) {
117*0Sstevel@tonic-gate 			type = buffer_get_string(b, NULL);
118*0Sstevel@tonic-gate 			data = buffer_get_string(b, NULL);
119*0Sstevel@tonic-gate 			debug3("Got file attribute \"%s\"", type);
120*0Sstevel@tonic-gate 			xfree(type);
121*0Sstevel@tonic-gate 			xfree(data);
122*0Sstevel@tonic-gate 		}
123*0Sstevel@tonic-gate 	}
124*0Sstevel@tonic-gate 	return &a;
125*0Sstevel@tonic-gate }
126*0Sstevel@tonic-gate 
127*0Sstevel@tonic-gate /* Encode attributes to buffer */
128*0Sstevel@tonic-gate void
129*0Sstevel@tonic-gate encode_attrib(Buffer *b, Attrib *a)
130*0Sstevel@tonic-gate {
131*0Sstevel@tonic-gate 	buffer_put_int(b, a->flags);
132*0Sstevel@tonic-gate 	if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
133*0Sstevel@tonic-gate 		buffer_put_int64(b, a->size);
134*0Sstevel@tonic-gate 	if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
135*0Sstevel@tonic-gate 		buffer_put_int(b, a->uid);
136*0Sstevel@tonic-gate 		buffer_put_int(b, a->gid);
137*0Sstevel@tonic-gate 	}
138*0Sstevel@tonic-gate 	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
139*0Sstevel@tonic-gate 		buffer_put_int(b, a->perm);
140*0Sstevel@tonic-gate 	if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
141*0Sstevel@tonic-gate 		buffer_put_int(b, a->atime);
142*0Sstevel@tonic-gate 		buffer_put_int(b, a->mtime);
143*0Sstevel@tonic-gate 	}
144*0Sstevel@tonic-gate }
145*0Sstevel@tonic-gate 
146*0Sstevel@tonic-gate /* Convert from SSH2_FX_ status to text error message */
147*0Sstevel@tonic-gate const char *
148*0Sstevel@tonic-gate fx2txt(int status)
149*0Sstevel@tonic-gate {
150*0Sstevel@tonic-gate 	switch (status) {
151*0Sstevel@tonic-gate 	case SSH2_FX_OK:
152*0Sstevel@tonic-gate 		return("No error");
153*0Sstevel@tonic-gate 	case SSH2_FX_EOF:
154*0Sstevel@tonic-gate 		return("End of file");
155*0Sstevel@tonic-gate 	case SSH2_FX_NO_SUCH_FILE:
156*0Sstevel@tonic-gate 		return("No such file or directory");
157*0Sstevel@tonic-gate 	case SSH2_FX_PERMISSION_DENIED:
158*0Sstevel@tonic-gate 		return("Permission denied");
159*0Sstevel@tonic-gate 	case SSH2_FX_FAILURE:
160*0Sstevel@tonic-gate 		return("Failure");
161*0Sstevel@tonic-gate 	case SSH2_FX_BAD_MESSAGE:
162*0Sstevel@tonic-gate 		return("Bad message");
163*0Sstevel@tonic-gate 	case SSH2_FX_NO_CONNECTION:
164*0Sstevel@tonic-gate 		return("No connection");
165*0Sstevel@tonic-gate 	case SSH2_FX_CONNECTION_LOST:
166*0Sstevel@tonic-gate 		return("Connection lost");
167*0Sstevel@tonic-gate 	case SSH2_FX_OP_UNSUPPORTED:
168*0Sstevel@tonic-gate 		return("Operation unsupported");
169*0Sstevel@tonic-gate 	default:
170*0Sstevel@tonic-gate 		return("Unknown status");
171*0Sstevel@tonic-gate 	}
172*0Sstevel@tonic-gate 	/* NOTREACHED */
173*0Sstevel@tonic-gate }
174*0Sstevel@tonic-gate 
175*0Sstevel@tonic-gate /*
176*0Sstevel@tonic-gate  * drwxr-xr-x    5 markus   markus       1024 Jan 13 18:39 .ssh
177*0Sstevel@tonic-gate  */
178*0Sstevel@tonic-gate char *
179*0Sstevel@tonic-gate ls_file(char *name, struct stat *st, int remote)
180*0Sstevel@tonic-gate {
181*0Sstevel@tonic-gate 	int ulen, glen, sz = 0;
182*0Sstevel@tonic-gate 	struct passwd *pw;
183*0Sstevel@tonic-gate 	struct group *gr;
184*0Sstevel@tonic-gate 	struct tm *ltime = localtime(&st->st_mtime);
185*0Sstevel@tonic-gate 	char *user, *group;
186*0Sstevel@tonic-gate 	char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1];
187*0Sstevel@tonic-gate 
188*0Sstevel@tonic-gate 	strmode(st->st_mode, mode);
189*0Sstevel@tonic-gate 	if (!remote && (pw = getpwuid(st->st_uid)) != NULL) {
190*0Sstevel@tonic-gate 		user = pw->pw_name;
191*0Sstevel@tonic-gate 	} else {
192*0Sstevel@tonic-gate 		snprintf(ubuf, sizeof ubuf, "%u", (u_int)st->st_uid);
193*0Sstevel@tonic-gate 		user = ubuf;
194*0Sstevel@tonic-gate 	}
195*0Sstevel@tonic-gate 	if (!remote && (gr = getgrgid(st->st_gid)) != NULL) {
196*0Sstevel@tonic-gate 		group = gr->gr_name;
197*0Sstevel@tonic-gate 	} else {
198*0Sstevel@tonic-gate 		snprintf(gbuf, sizeof gbuf, "%u", (u_int)st->st_gid);
199*0Sstevel@tonic-gate 		group = gbuf;
200*0Sstevel@tonic-gate 	}
201*0Sstevel@tonic-gate 	if (ltime != NULL) {
202*0Sstevel@tonic-gate 		if (time(NULL) - st->st_mtime < (365*24*60*60)/2)
203*0Sstevel@tonic-gate 			sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime);
204*0Sstevel@tonic-gate 		else
205*0Sstevel@tonic-gate 			sz = strftime(tbuf, sizeof tbuf, "%b %e  %Y", ltime);
206*0Sstevel@tonic-gate 	}
207*0Sstevel@tonic-gate 	if (sz == 0)
208*0Sstevel@tonic-gate 		tbuf[0] = '\0';
209*0Sstevel@tonic-gate 	ulen = MAX(strlen(user), 8);
210*0Sstevel@tonic-gate 	glen = MAX(strlen(group), 8);
211*0Sstevel@tonic-gate 	snprintf(buf, sizeof buf, "%s %3d %-*s %-*s %8llu %s %s", mode,
212*0Sstevel@tonic-gate 	    st->st_nlink, ulen, user, glen, group,
213*0Sstevel@tonic-gate 	    (u_int64_t)st->st_size, tbuf, name);
214*0Sstevel@tonic-gate 	return xstrdup(buf);
215*0Sstevel@tonic-gate }
216