1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2007 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.0 of the PHP license,       |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_0.txt.                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Antony Dovgal <tony2001@phpclub.net>                        |
16   |          Mikael Johansson <mikael AT synd DOT info>                  |
17   +----------------------------------------------------------------------+
18 */
19 
20 /* $Id$ */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include "memcache_pool.h"
27 #include "ext/standard/php_smart_string.h"
28 
29 typedef struct mmc_ascii_request {
30 	mmc_request_t	base;							/* enable cast to mmc_request_t */
31 	struct {										/* stores value info while the body is being read */
32 		char			key[MMC_MAX_KEY_LEN + 1];
33 		unsigned int	flags;
34 		unsigned long	length;
35 		unsigned long	cas;						/* CAS counter */
36 	} value;
37 } mmc_ascii_request_t;
38 
39 static int mmc_server_read_value(mmc_t *, mmc_request_t *);
40 
mmc_stream_get_line(mmc_stream_t * io,char ** line)41 static int mmc_stream_get_line(mmc_stream_t *io, char **line) /*
42 	attempts to read a line from server, returns the line size or 0 if no complete line was available {{{ */
43 {
44 	size_t returned_len = 0;
45 	io->readline(io, io->input.value + io->input.idx, MMC_BUFFER_SIZE - io->input.idx, &returned_len);
46 	io->input.idx += returned_len;
47 
48 	if (io->input.idx && io->input.value[io->input.idx - 1] == '\n') {
49 		int result = io->input.idx;
50 		*line = io->input.value;
51 		io->input.idx = 0;
52 		return result;
53 	}
54 
55 	return 0;
56 }
57 /* }}} */
58 
mmc_request_check_response(char * line,int line_len)59 static int mmc_request_check_response(char *line, int line_len) /*
60 	checks for response status and error codes {{{ */
61 {
62 	int response;
63 
64 	// remove newline and thus prevent passing it to userland
65 	if (line_len >= 2 && line[line_len - 2] == '\r' && line[line_len - 1] == '\n') {
66 		line[line_len - 2] = '\0';
67 	}
68 
69 	if (mmc_str_left(line, "OK", line_len, sizeof("OK")-1) ||
70 		mmc_str_left(line, "STORED", line_len, sizeof("STORED")-1) ||
71 		mmc_str_left(line, "DELETED", line_len, sizeof("DELETED")-1))
72 	{
73 		response = MMC_OK;
74 	}
75 	else if (mmc_str_left(line, "NOT_FOUND", line_len, sizeof("NOT_FOUND")-1)) {
76 		response = MMC_RESPONSE_NOT_FOUND;
77 	}
78 	else if (
79 		mmc_str_left(line, "NOT_STORED", line_len, sizeof("NOT_STORED")-1) ||
80 		mmc_str_left(line, "EXISTS", line_len, sizeof("EXISTS")-1))
81 	{
82 		response = MMC_RESPONSE_EXISTS;
83 	}
84 	else if (mmc_str_left(line, "SERVER_ERROR out of memory", line_len, sizeof("SERVER_ERROR out of memory")-1)) {
85 		response = MMC_RESPONSE_OUT_OF_MEMORY;
86 	}
87 	else if (mmc_str_left(line, "SERVER_ERROR object too large", line_len, sizeof("SERVER_ERROR object too large")-1)) {
88 		response = MMC_RESPONSE_TOO_LARGE;
89 	}
90 	else if (
91 		mmc_str_left(line, "ERROR", line_len, sizeof("ERROR")-1) ||
92 		mmc_str_left(line, "SERVER_ERROR", line_len, sizeof("SERVER_ERROR")-1))
93 	{
94 		response = MMC_RESPONSE_ERROR;
95 	}
96 	else if (mmc_str_left(line, "CLIENT_ERROR", line_len, sizeof("CLIENT_ERROR")-1)) {
97 		response = MMC_RESPONSE_CLIENT_ERROR;
98 	}
99 	else {
100 		response = MMC_RESPONSE_UNKNOWN;
101 	}
102 
103 	return response;
104 }
105 
mmc_request_parse_response(mmc_t * mmc,mmc_request_t * request)106 static int mmc_request_parse_response(mmc_t *mmc, mmc_request_t *request) /*
107 	reads a generic response header and delegates it to response_handler {{{ */
108 {
109 	char *line;
110 	int line_len = mmc_stream_get_line(request->io, &line);
111 
112 	if (line_len > 0) {
113 		int response = mmc_request_check_response(line, line_len);
114 		return request->response_handler(mmc, request, response, line, line_len - (sizeof("\r\n")-1), request->response_handler_param);
115 	}
116 
117 	return MMC_REQUEST_MORE;
118 }
119 /* }}}*/
120 
mmc_request_parse_mutate(mmc_t * mmc,mmc_request_t * request)121 static int mmc_request_parse_mutate(mmc_t *mmc, mmc_request_t *request) /*
122 	reads and parses the <long-value> response header {{{ */
123 {
124 	char *line;
125 	int line_len;
126 
127 	line_len = mmc_stream_get_line(request->io, &line);
128 	if (line_len > 0) {
129 		long lval;
130 		zval value;
131 
132 		int response = mmc_request_check_response(line, line_len);
133 		if (response != MMC_RESPONSE_UNKNOWN) {
134 			return request->response_handler(mmc, request, response, line, line_len - (sizeof("\r\n")-1), request->response_handler_param);
135 		}
136 
137 		if (sscanf(line, "%ld", &lval) < 1) {
138 			return mmc_server_failure(mmc, request->io, "Malformed VALUE header", 0);
139 		}
140 
141 		ZVAL_LONG(&value, lval);
142 		return request->value_handler(request->key, request->key_len, &value, 0, 0, request->value_handler_param);
143 	}
144 
145 	return MMC_REQUEST_MORE;
146 }
147 /* }}}*/
148 
mmc_request_parse_value(mmc_t * mmc,mmc_request_t * request)149 static int mmc_request_parse_value(mmc_t *mmc, mmc_request_t *request) /*
150 	reads and parses the VALUE <key> <flags> <size> <cas> response header and then reads the value body {{{ */
151 {
152 	char *line;
153 	int line_len;
154 	mmc_ascii_request_t *req = (mmc_ascii_request_t *)request;
155 
156 	line_len = mmc_stream_get_line(request->io, &line);
157 	if (line_len > 0) {
158 		if (mmc_str_left(line, "END", line_len, sizeof("END")-1)) {
159 			return MMC_REQUEST_DONE;
160 		}
161 
162 		if (sscanf(line, MMC_VALUE_HEADER, req->value.key, &(req->value.flags), &(req->value.length), &(req->value.cas)) < 3) {
163 			return mmc_server_failure(mmc, request->io, "Malformed VALUE header", 0);
164 		}
165 
166 		/* memory for data + \r\n */
167 		mmc_buffer_alloc(&(request->readbuf), req->value.length + 2);
168 
169 		/* allow read_value handler to read the value body */
170 		request->parse = mmc_server_read_value;
171 
172 		/* read more, php streams buffer input which must be read if available */
173 		return MMC_REQUEST_AGAIN;
174 	}
175 
176 	return MMC_REQUEST_MORE;
177 }
178 /* }}}*/
179 
mmc_server_read_value(mmc_t * mmc,mmc_request_t * request)180 static int mmc_server_read_value(mmc_t *mmc, mmc_request_t *request) /*
181 	read the value body into the buffer {{{ */
182 {
183 	mmc_ascii_request_t *req = (mmc_ascii_request_t *)request;
184 	request->readbuf.idx +=
185 		request->io->read(request->io, request->readbuf.value.c + request->readbuf.idx, req->value.length + 2 - request->readbuf.idx);
186 
187 	/* done reading? */
188 	if (request->readbuf.idx >= req->value.length + 2) {
189 		int result;
190 
191 		/* allow parse_value to read next VALUE or END line */
192 		request->parse = mmc_request_parse_value;
193 		mmc_buffer_reset(&(request->readbuf));
194 
195 		result = mmc_unpack_value(
196 			mmc, request, &(request->readbuf), req->value.key, strlen(req->value.key),
197 			req->value.flags, req->value.cas, req->value.length);
198 
199 		/* request more data (END line) */
200 		if (result == MMC_REQUEST_DONE) {
201 			return MMC_REQUEST_AGAIN;
202 		}
203 
204 		return result;
205 	}
206 
207 	return MMC_REQUEST_MORE;
208 }
209 /* }}}*/
210 
mmc_ascii_create_request()211 static mmc_request_t *mmc_ascii_create_request() /* {{{ */
212 {
213 	mmc_ascii_request_t *request = emalloc(sizeof(mmc_ascii_request_t));
214 	ZEND_SECURE_ZERO(request, sizeof(*request));
215 	return (mmc_request_t *)request;
216 }
217 /* }}} */
218 
mmc_ascii_clone_request(mmc_request_t * clone,mmc_request_t * request)219 static void mmc_ascii_clone_request(mmc_request_t *clone, mmc_request_t *request) /* {{{ */
220 {}
221 /* }}} */
222 
mmc_ascii_reset_request(mmc_request_t * request)223 static void mmc_ascii_reset_request(mmc_request_t *request) /* {{{ */
224 {
225 	mmc_ascii_request_t *req = (mmc_ascii_request_t *)request;
226 	req->value.cas = 0;
227 	mmc_request_reset(request);
228 }
229 /* }}} */
230 
mmc_ascii_begin_get(mmc_request_t * request,int op)231 static void mmc_ascii_begin_get(mmc_request_t *request, int op) /* {{{ */
232 {
233 	request->parse = mmc_request_parse_value;
234 
235 	if (op == MMC_OP_GETS) {
236 		smart_string_appendl(&(request->sendbuf.value), "gets", sizeof("gets")-1);
237 	}
238 	else {
239 		smart_string_appendl(&(request->sendbuf.value), "get", sizeof("get")-1);
240 	}
241 }
242 /* }}} */
243 
mmc_ascii_append_get(mmc_request_t * request,zval * zkey,const char * key,unsigned int key_len)244 static void mmc_ascii_append_get(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len) /* {{{ */
245 {
246 	smart_string_appendl(&(request->sendbuf.value), " ", 1);
247 	smart_string_appendl(&(request->sendbuf.value), key, key_len);
248 }
249 /* }}} */
250 
mmc_ascii_end_get(mmc_request_t * request)251 static void mmc_ascii_end_get(mmc_request_t *request) /* {{{ */
252 {
253 	smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1);
254 }
255 /* }}} */
256 
mmc_ascii_get(mmc_request_t * request,int op,zval * zkey,const char * key,unsigned int key_len)257 static void mmc_ascii_get(mmc_request_t *request, int op, zval *zkey, const char *key, unsigned int key_len) /* {{{ */
258 {
259 	mmc_ascii_begin_get(request, op);
260 	mmc_ascii_append_get(request, zkey, key, key_len);
261 	mmc_ascii_end_get(request);
262 }
263 /* }}} */
264 
mmc_ascii_store(mmc_pool_t * pool,mmc_request_t * request,int op,const char * key,unsigned int key_len,unsigned int flags,unsigned int exptime,unsigned long cas,zval * value)265 static int mmc_ascii_store(
266 	mmc_pool_t *pool, mmc_request_t *request, int op, const char *key, unsigned int key_len,
267 	unsigned int flags, unsigned int exptime, unsigned long cas, zval *value) /* {{{ */
268 {
269 	int status;
270 	mmc_buffer_t buffer;
271 	request->parse = mmc_request_parse_response;
272 
273 	ZEND_SECURE_ZERO(&buffer, sizeof(buffer));
274 	status = mmc_pack_value(pool, &buffer, value, &flags);
275 
276 	if (status != MMC_OK) {
277 		return status;
278 	}
279 
280 	switch (op) {
281 		case MMC_OP_SET:
282 			smart_string_appendl(&(request->sendbuf.value), "set", sizeof("set")-1);
283 			break;
284 		case MMC_OP_ADD:
285 			smart_string_appendl(&(request->sendbuf.value), "add", sizeof("add")-1);
286 			break;
287 		case MMC_OP_REPLACE:
288 			smart_string_appendl(&(request->sendbuf.value), "replace", sizeof("replace")-1);
289 			break;
290 		case MMC_OP_CAS:
291 			smart_string_appendl(&(request->sendbuf.value), "cas", sizeof("cas")-1);
292 			break;
293 		case MMC_OP_APPEND:
294 			smart_string_appendl(&(request->sendbuf.value), "append", sizeof("append")-1);
295 			break;
296 		case MMC_OP_PREPEND:
297 			smart_string_appendl(&(request->sendbuf.value), "prepend", sizeof("prepend")-1);
298 			break;
299 		default:
300 			return MMC_REQUEST_FAILURE;
301 	}
302 
303 	smart_string_appendl(&(request->sendbuf.value), " ", 1);
304 	smart_string_appendl(&(request->sendbuf.value), key, key_len);
305 	smart_string_appendl(&(request->sendbuf.value), " ", 1);
306 	smart_string_append_unsigned(&(request->sendbuf.value), flags);
307 	smart_string_appendl(&(request->sendbuf.value), " ", 1);
308 	smart_string_append_unsigned(&(request->sendbuf.value), exptime);
309 	smart_string_appendl(&(request->sendbuf.value), " ", 1);
310 	smart_string_append_unsigned(&(request->sendbuf.value), buffer.value.len);
311 
312 	if (op == MMC_OP_CAS) {
313 		smart_string_appendl(&(request->sendbuf.value), " ", 1);
314 		smart_string_append_unsigned(&(request->sendbuf.value), cas);
315 	}
316 
317 	smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1);
318 	smart_string_appendl(&(request->sendbuf.value), buffer.value.c, buffer.value.len);
319 	smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1);
320 
321 	mmc_buffer_free(&buffer);
322 	return MMC_OK;
323 }
324 /* }}} */
325 
mmc_ascii_delete(mmc_request_t * request,const char * key,unsigned int key_len,unsigned int exptime)326 static void mmc_ascii_delete(mmc_request_t *request, const char *key, unsigned int key_len, unsigned int exptime) /* {{{ */
327 {
328 	request->parse = mmc_request_parse_response;
329 
330 	smart_string_appendl(&(request->sendbuf.value), "delete", sizeof("delete")-1);
331 	smart_string_appendl(&(request->sendbuf.value), " ", 1);
332 	smart_string_appendl(&(request->sendbuf.value), key, key_len);
333 
334 	if (exptime > 0) {
335 		smart_string_appendl(&(request->sendbuf.value), " ", 1);
336 		smart_string_append_unsigned(&(request->sendbuf.value), exptime);
337 	}
338 
339 	smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1);
340 }
341 /* }}} */
342 
mmc_ascii_mutate(mmc_request_t * request,zval * zkey,const char * key,unsigned int key_len,long value,long defval,int defval_used,unsigned int exptime)343 static void mmc_ascii_mutate(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len, long value, long defval, int defval_used, unsigned int exptime) /* {{{ */
344 {
345 	request->parse = mmc_request_parse_mutate;
346 
347 	if (value >= 0) {
348 		smart_string_appendl(&(request->sendbuf.value), "incr", sizeof("incr")-1);
349 	}
350 	else {
351 		smart_string_appendl(&(request->sendbuf.value), "decr", sizeof("decr")-1);
352 	}
353 
354 	smart_string_appendl(&(request->sendbuf.value), " ", 1);
355 	smart_string_appendl(&(request->sendbuf.value), key, key_len);
356 	smart_string_appendl(&(request->sendbuf.value), " ", 1);
357 	smart_string_append_unsigned(&(request->sendbuf.value), value >= 0 ? value : -value);
358 	smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1);
359 }
360 /* }}} */
361 
mmc_ascii_flush(mmc_request_t * request,unsigned int exptime)362 static void mmc_ascii_flush(mmc_request_t *request, unsigned int exptime) /* {{{ */
363 {
364 	request->parse = mmc_request_parse_response;
365 	smart_string_appendl(&(request->sendbuf.value), "flush_all", sizeof("flush_all")-1);
366 
367 	if (exptime > 0) {
368 		smart_string_appendl(&(request->sendbuf.value), " ", 1);
369 		smart_string_append_unsigned(&(request->sendbuf.value), exptime);
370 	}
371 
372 	smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1);
373 }
374 /* }}} */
375 
mmc_ascii_version(mmc_request_t * request)376 static void mmc_ascii_version(mmc_request_t *request) /* {{{ */
377 {
378 	request->parse = mmc_request_parse_response;
379 	smart_string_appendl(&(request->sendbuf.value), "version\r\n", sizeof("version\r\n")-1);
380 }
381 /* }}} */
382 
mmc_ascii_stats(mmc_request_t * request,const char * type,long slabid,long limit)383 static void mmc_ascii_stats(mmc_request_t *request, const char *type, long slabid, long limit) /* {{{ */
384 {
385 	char *cmd;
386 	unsigned int cmd_len;
387 	request->parse = mmc_request_parse_response;
388 
389 	if (slabid) {
390 		cmd_len = spprintf(&cmd, 0, "stats %s %ld %ld\r\n", type, slabid, limit);
391 	}
392 	else if (type) {
393 		cmd_len = spprintf(&cmd, 0, "stats %s\r\n", type);
394 	}
395 	else {
396 		cmd_len = spprintf(&cmd, 0, "stats\r\n");
397 	}
398 
399 	smart_string_appendl(&(request->sendbuf.value), cmd, cmd_len);
400 	efree(cmd);
401 }
402 /* }}} */
403 
mmc_set_sasl_auth_data(mmc_pool_t * pool,mmc_request_t * request,const char * user,const char * password)404 static void mmc_set_sasl_auth_data(mmc_pool_t *pool, mmc_request_t *request, const char *user,  const char *password) /* {{{ */
405 {
406 	/* stats not supported */
407 }
408 /* }}} */
409 
410 mmc_protocol_t mmc_ascii_protocol = {
411 	mmc_ascii_create_request,
412 	mmc_ascii_clone_request,
413 	mmc_ascii_reset_request,
414 	mmc_request_free,
415 	mmc_ascii_get,
416 	mmc_ascii_begin_get,
417 	mmc_ascii_append_get,
418 	mmc_ascii_end_get,
419 	mmc_ascii_store,
420 	mmc_ascii_delete,
421 	mmc_ascii_mutate,
422 	mmc_ascii_flush,
423 	mmc_ascii_version,
424 	mmc_ascii_stats,
425 	mmc_set_sasl_auth_data
426 };
427 
428 /*
429  * Local variables:
430  * tab-width: 4
431  * c-basic-offset: 4
432  * End:
433  * vim600: noet sw=4 ts=4 fdm=marker
434  * vim<600: noet sw=4 ts=4
435  */
436