1 /*
2  * socket2buffer.c
3  * (C)1998-2011 by Marc Huber <Marc.Huber@web.de>
4  * All rights reserved.
5  *
6  * $Id: socket2buffer.c,v 1.14 2015/03/14 06:11:27 marc Exp marc $
7  *
8  */
9 
10 #include "headers.h"
11 
12 static const char rcsid[] __attribute__ ((used)) = "$Id: socket2buffer.c,v 1.14 2015/03/14 06:11:27 marc Exp marc $";
13 
buffer2file(struct context * ctx,int cur)14 void buffer2file(struct context *ctx, int cur __attribute__ ((unused)))
15 {
16     ssize_t l = 0, len = 0;
17 
18     DebugIn(DEBUG_BUFFER);
19 
20     if (ctx->dbuf) {
21 	do {
22 	    struct iovec v[10];
23 	    int count = 10;
24 
25 	    buffer_setv(ctx->dbuf, v, &count, 0);
26 	    l = writev(ctx->ffn, v, count);
27 	    if (l > 0) {
28 		off_t l2 = (off_t) l;
29 		len += l;
30 		ctx->dbuf = buffer_release(ctx->dbuf, &l2);
31 	    }
32 	}
33 	while (l > 0 && ctx->dbuf);
34 
35 	if (l <= 0 && errno == EDQUOT) {
36 	    reply(ctx, MSG_451_Transfer_incomplete_quota);
37 	    logmsg("%s: quota limit reached", ctx->user);
38 
39 	    ftp_log(ctx, LOG_TRANSFER, "i");
40 	    cleanup_file(ctx, ctx->ffn);
41 	    cleanup_data(ctx, ctx->dfn);
42 	    Debug((DEBUG_PROC, "- %s: quota exceeded\n", __func__));
43 	    return;
44 	}
45 	if (l < 0) {
46 	    if (errno != EAGAIN) {
47 		reply(ctx, MSG_451_Transfer_incomplete);
48 
49 		ftp_log(ctx, LOG_TRANSFER, "i");
50 		cleanup_file(ctx, ctx->ffn);
51 		cleanup_data(ctx, ctx->dfn);
52 	    }
53 	    Debug((DEBUG_BUFFER, "- %s FAILURE transfer incomplete\n", __func__));
54 	    return;
55 	}
56 	ctx->bytecount += len;
57     }
58     if (ctx->dfn < 0) {		/* socket is closed */
59 	if (ctx->cfn < 0) {	/* control connection not available */
60 	    ftp_log(ctx, LOG_TRANSFER, "I");
61 	    cleanup_file(ctx, ctx->ffn);
62 	} else {
63 	    if (!cleanup_file(ctx, ctx->ffn)) {
64 		if (ctx->mode != 'z' || ctx->bytecount == 0)
65 		    reply(ctx, MSG_226_Transfer_complete);
66 		else
67 		    replyf(ctx, MSG_226_Transfer_completeZ, (int) (100 * ctx->filesize / ctx->bytecount), ctx->bytecount - ctx->filesize);
68 		ftp_log(ctx, LOG_TRANSFER, "I");
69 	    } else {
70 		ftp_log(ctx, LOG_TRANSFER, "i");
71 		if (errno == EDQUOT) {
72 		    reply(ctx, MSG_451_Transfer_incomplete_quota);
73 		    logmsg("%s: quota limit reached", ctx->user);
74 		} else
75 		    reply(ctx, MSG_451_Transfer_incomplete);
76 	    }
77 	    ctx->bytecount = 0;
78 	}
79     }
80     DebugOut(DEBUG_BUFFER);
81 }
82 
socket2buffer(struct context * ctx,int cur)83 void socket2buffer(struct context *ctx, int cur __attribute__ ((unused)))
84 {
85     int thats_all = 0;
86     ssize_t l;
87     struct buffer *b = NULL;
88 
89     DebugIn(DEBUG_NET);
90 
91     io_sched_renew_proc(ctx->io, ctx, (void *) cleanup);
92 
93     if (ctx->dbuf == NULL)
94 	ctx->dbuf = buffer_get();
95 
96 #ifdef WITH_ZLIB
97     if (ctx->mode == 'z')
98 	b = buffer_get();
99     else
100 #endif
101 	b = ctx->dbuf;
102 
103 #ifdef WITH_SSL
104     if (ctx->ssl_d)
105 	l = io_SSL_read(ctx->ssl_d, b->buf + b->length, b->size - b->length, ctx->io, ctx->dfn, (void *) socket2buffer);
106     else
107 #endif
108 	l = read(ctx->dfn, b->buf + b->length, b->size - b->length);
109 
110     if (l > 0)
111 	ctx->traffic_total += l, ctx->traffic_files += l;
112 
113     thats_all = (l <= 0);
114 
115     if (!(l == -1 && errno == EAGAIN)) {
116 #ifdef WITH_ZLIB
117 	if (ctx->mode == 'z') {
118 	    struct buffer *out = ctx->dbuf;
119 	    b->length = l;
120 	    b->offset = 0;
121 	    l = 0;
122 
123 	    if (!ctx->zstream) {
124 		ctx->zstream = Xcalloc(1, sizeof(z_stream));
125 		ctx->zstream->next_in = (u_char *) b->buf + b->offset;
126 		ctx->zstream->avail_in = b->length - b->offset;
127 		if (Z_OK != inflateInit(ctx->zstream)) {
128 		    Xfree(&ctx->zstream);
129 
130 		    reply(ctx, MSG_451_Internal_error);
131 		    logmsg("%s: inflateInit failed", ctx->user);
132 		    ftp_log(ctx, LOG_TRANSFER, "i");
133 		    cleanup_file(ctx, ctx->ffn);
134 		    cleanup_data(ctx, ctx->dfn);
135 		    Debug((DEBUG_PROC, "- %s: inflateEnd\n", __func__));
136 		    return;
137 		}
138 		ctx->filesize = 0;
139 	    }
140 	    do {
141 		int res;
142 		off_t decomp;
143 
144 		if (out->length == out->size) {
145 		    out->next = buffer_get();
146 		    out = out->next;
147 		}
148 		ctx->zstream->next_in = (u_char *) b->buf + b->offset;
149 		ctx->zstream->avail_in = b->length - b->offset;
150 		ctx->zstream->next_out = (u_char *) out->buf + out->length;
151 		ctx->zstream->avail_out = out->size - out->length;
152 
153 		res = inflate(ctx->zstream, Z_NO_FLUSH);
154 		decomp = (char *) ctx->zstream->next_in - b->buf - b->offset;
155 		ctx->filesize += decomp;
156 		Debug((DEBUG_PROC, "inflate returns %d\n", res));
157 		switch (res) {
158 		case Z_OK:
159 		    Debug((DEBUG_PROC, "Z_OK\n"));
160 		    l += decomp;
161 		    b = buffer_release(b, &decomp);
162 		    out->length = out->size - ctx->zstream->avail_out;
163 		    break;
164 		case Z_STREAM_END:
165 		    Debug((DEBUG_PROC, "Z_STREAM_END\n"));
166 		    b = buffer_free(b);
167 		    ctx->dbuf->length = ctx->dbuf->size - ctx->zstream->avail_out;
168 		    if (Z_OK != inflateEnd(ctx->zstream)) {
169 			logmsg("%s: inflateEnd", ctx->user);
170 		      bye:
171 			buffer_free(b);
172 			Xfree(&ctx->zstream);
173 			reply(ctx, MSG_451_Internal_error);
174 			ftp_log(ctx, LOG_TRANSFER, "i");
175 			cleanup_file(ctx, ctx->ffn);
176 			cleanup_data(ctx, ctx->dfn);
177 			Debug((DEBUG_PROC, "- %s: inflateEnd\n", __func__));
178 			return;
179 		    }
180 		    Xfree(&ctx->zstream);
181 		    thats_all = 1;
182 		    break;
183 		default:
184 		    inflateEnd(ctx->zstream);
185 		    logmsg("%s: inflate returned %d", ctx->user, res);
186 		    goto bye;
187 		}
188 	    }
189 	    while (b);
190 	} else
191 #endif				/* WITH_ZLIB */
192 	{
193 	    if (l > 0)
194 		ctx->dbuf->length += l;
195 	    l = ctx->dbuf->length;
196 	}
197 
198 	while (ctx->dbuf && (thats_all || (ctx->dbuf->length >= (ctx->dbuf->size >> 2) * 3))) {
199 	    if (l > 0 && ctx->use_ascii) {
200 		char lastchar = ctx->lastchar;
201 		char *t = (char *) ctx->dbuf->buf + ctx->dbuf->offset;
202 		char *u = t;
203 		char *ul = u + ctx->dbuf->length;
204 
205 		for (; u < ul; u++)
206 		    if (*u == '\r') {
207 			lastchar = '\r';
208 			*t++ = '\n';
209 		    } else {
210 			if (*u != '\n' || lastchar != '\r')
211 			    *t++ = *u;
212 			lastchar = *u;
213 		    }
214 
215 		ctx->lastchar = lastchar;
216 		ctx->dbuf->length = (size_t) (t - ctx->dbuf->buf);
217 	    }
218 	    if (thats_all)
219 		cleanup_data(ctx, ctx->dfn);
220 	    else
221 		buffer2file(ctx, ctx->ffn);
222 
223 	    thats_all = 0;
224 	}
225     }
226     DebugOut(DEBUG_NET);
227 }
228