PHP_METHOD(PharFileInfo, __construct)
{
	char *fname, *arch, *entry, *error;
	int fname_len, arch_len, entry_len;
	phar_entry_object *entry_obj;
	phar_entry_info *entry_info;
	phar_archive_data *phar_data;
	zval *zobj = getThis(), arg1;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &fname, &fname_len) == FAILURE) {
		return;
	}

	entry_obj = (phar_entry_object*)zend_object_store_get_object(getThis() TSRMLS_CC);

	if (entry_obj->ent.entry) {
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot call constructor twice");
		return;
	}

	if (fname_len < 7 || memcmp(fname, "phar://", 7) || phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC) == FAILURE) {
		zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC,
			"'%s' is not a valid phar archive URL (must have at least phar://filename.phar)", fname);
		return;
	}

	if (phar_open_from_filename(arch, arch_len, NULL, 0, REPORT_ERRORS, &phar_data, &error TSRMLS_CC) == FAILURE) {
		efree(arch);
		efree(entry);
		if (error) {
			zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC,
				"Cannot open phar file '%s': %s", fname, error);
			efree(error);
		} else {
			zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC,
				"Cannot open phar file '%s'", fname);
		}
		return;
	}

	if ((entry_info = phar_get_entry_info_dir(phar_data, entry, entry_len, 1, &error, 1 TSRMLS_CC)) == NULL) {
		zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC,
			"Cannot access phar file entry '%s' in archive '%s'%s%s", entry, arch, error ? ", " : "", error ? error : "");
		efree(arch);
		efree(entry);
		return;
	}

	efree(arch);
	efree(entry);

	entry_obj->ent.entry = entry_info;

	INIT_PZVAL(&arg1);
	ZVAL_STRINGL(&arg1, fname, fname_len, 0);

	zend_call_method_with_1_params(&zobj, Z_OBJCE_P(zobj),
		&spl_ce_SplFileInfo->constructor, "__construct", NULL, &arg1);
}


static int phar_build(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ */
{
	zval **value;
	zend_bool close_fp = 1;
	struct _phar_t *p_obj = (struct _phar_t*) puser;
	uint str_key_len, base_len = p_obj->l, fname_len;
	phar_entry_data *data;
	php_stream *fp;
	size_t contents_len;
	char *fname, *error = NULL, *base = p_obj->b, *opened, *save = NULL, *temp = NULL;
	char *str_key;
	zend_class_entry *ce = p_obj->c;
	phar_archive_object *phar_obj = p_obj->p;
	char *str = "[stream]";

	iter->funcs->get_current_data(iter, &value TSRMLS_CC);

	if (EG(exception)) {
		return ZEND_HASH_APPLY_STOP;
	}

	if (!value) {
		/* failure in get_current_data */
		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned no value", ce->name);
		return ZEND_HASH_APPLY_STOP;
	}

	switch (Z_TYPE_PP(value)) {
		case IS_STRING:
			break;
		case IS_RESOURCE:
			php_stream_from_zval_no_verify(fp, value);

			if (!fp) {
				zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Iterator %v returned an invalid stream handle", ce->name);
				return ZEND_HASH_APPLY_STOP;
			}

			if (iter->funcs->get_current_key) {
				zval key;
				iter->funcs->get_current_key(iter, &key TSRMLS_CC);

				if (EG(exception)) {
					return ZEND_HASH_APPLY_STOP;
				}

				if (Z_TYPE(key) != IS_STRING) {
					zval_dtor(&key);
					zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name);
					return ZEND_HASH_APPLY_STOP;
				}

				str_key_len = Z_STRLEN(key);
				str_key = estrndup(Z_STRVAL(key), str_key_len);

				save = str_key;
				zval_dtor(&key);
			} else {
				zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name);
				return ZEND_HASH_APPLY_STOP;
			}

			close_fp = 0;
			opened = (char *) estrndup(str, sizeof("[stream]") - 1);
			goto after_open_fp;
		case IS_OBJECT:
			if (instanceof_function(Z_OBJCE_PP(value), spl_ce_SplFileInfo TSRMLS_CC)) {
				char *test = NULL;
				zval dummy;
				spl_filesystem_object *intern = (spl_filesystem_object*)zend_object_store_get_object(*value TSRMLS_CC);

				if (!base_len) {
					zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Iterator %v returns an SplFileInfo object, so base directory must be specified", ce->name);
					return ZEND_HASH_APPLY_STOP;
				}

				switch (intern->type) {
					case SPL_FS_DIR:
						test = spl_filesystem_object_get_path(intern, NULL TSRMLS_CC);
						fname_len = spprintf(&fname, 0, "%s%c%s", test, DEFAULT_SLASH, intern->u.dir.entry.d_name);
						php_stat(fname, fname_len, FS_IS_DIR, &dummy TSRMLS_CC);

						if (Z_BVAL(dummy)) {
							/* ignore directories */
							efree(fname);
							return ZEND_HASH_APPLY_KEEP;
						}

						test = expand_filepath(fname, NULL TSRMLS_CC);
						efree(fname);

						if (test) {
							fname = test;
							fname_len = strlen(fname);
						} else {
							zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Could not resolve file path");
							return ZEND_HASH_APPLY_STOP;
						}

						save = fname;
						goto phar_spl_fileinfo;
					case SPL_FS_INFO:
					case SPL_FS_FILE:
						fname = expand_filepath(intern->file_name, NULL TSRMLS_CC);
						if (!fname) {
							zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Could not resolve file path");
							return ZEND_HASH_APPLY_STOP;
						}

						fname_len = strlen(fname);
						save = fname;
						goto phar_spl_fileinfo;
				}
			}
			/* fall-through */
		default:
			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid value (must return a string)", ce->name);
			return ZEND_HASH_APPLY_STOP;
	}

	fname = Z_STRVAL_PP(value);
	fname_len = Z_STRLEN_PP(value);

phar_spl_fileinfo:
	if (base_len) {
		temp = expand_filepath(base, NULL TSRMLS_CC);
		if (!temp) {
			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Could not resolve file path");
			if (save) {
				efree(save);
			}
			return ZEND_HASH_APPLY_STOP;
		}

		base = temp;
		base_len = strlen(base);

		if (strstr(fname, base)) {
			str_key_len = fname_len - base_len;

			if (str_key_len <= 0) {
				if (save) {
					efree(save);
					efree(temp);
				}
				return ZEND_HASH_APPLY_KEEP;
			}

			str_key = fname + base_len;

			if (*str_key == '/' || *str_key == '\\') {
				str_key++;
				str_key_len--;
			}

		} else {
			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned a path \"%s\" that is not in the base directory \"%s\"", ce->name, fname, base);

			if (save) {
				efree(save);
				efree(temp);
			}

			return ZEND_HASH_APPLY_STOP;
		}
	} else {
		if (iter->funcs->get_current_key) {
			zval key;
			iter->funcs->get_current_key(iter, &key TSRMLS_CC);

			if (EG(exception)) {
				return ZEND_HASH_APPLY_STOP;
			}

			if (Z_TYPE(key) != IS_STRING) {
				zval_dtor(&key);
				zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name);
				return ZEND_HASH_APPLY_STOP;
			}

			str_key_len = Z_STRLEN(key);
			str_key = estrndup(Z_STRVAL(key), str_key_len);

			save = str_key;
			zval_dtor(&key);
		} else {
			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name);
			return ZEND_HASH_APPLY_STOP;
		}
	}
#if PHP_API_VERSION < 20100412
	if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned a path \"%s\" that safe mode prevents opening", ce->name, fname);

		if (save) {
			efree(save);
		}

		if (temp) {
			efree(temp);
		}

		return ZEND_HASH_APPLY_STOP;
	}
#endif

	if (php_check_open_basedir(fname TSRMLS_CC)) {
		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned a path \"%s\" that open_basedir prevents opening", ce->name, fname);

		if (save) {
			efree(save);
		}

		if (temp) {
			efree(temp);
		}

		return ZEND_HASH_APPLY_STOP;
	}

	/* try to open source file, then create internal phar file and copy contents */
	fp = php_stream_open_wrapper(fname, "rb", STREAM_MUST_SEEK|0, &opened);

	if (!fp) {
		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned a file that could not be opened \"%s\"", ce->name, fname);

		if (save) {
			efree(save);
		}

		if (temp) {
			efree(temp);
		}

		return ZEND_HASH_APPLY_STOP;
	}
after_open_fp:
	if (str_key_len >= sizeof(".phar")-1 && !memcmp(str_key, ".phar", sizeof(".phar")-1)) {
		/* silently skip any files that would be added to the magic .phar directory */
		if (save) {
			efree(save);
		}

		if (temp) {
			efree(temp);
		}

		if (opened) {
			efree(opened);
		}

		if (close_fp) {
			php_stream_close(fp);
		}

		return ZEND_HASH_APPLY_KEEP;
	}

	if (!(data = phar_get_or_create_entry_data(phar_obj->arc.archive->fname, phar_obj->arc.archive->fname_len, str_key, str_key_len, "w+b", 0, &error, 1 TSRMLS_CC))) {
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s cannot be created: %s", str_key, error);
		efree(error);

		if (save) {
			efree(save);
		}

		if (opened) {
			efree(opened);
		}

		if (temp) {
			efree(temp);
		}

		if (close_fp) {
			php_stream_close(fp);
		}

		return ZEND_HASH_APPLY_STOP;

	} else {
		if (error) {
			efree(error);
		}
		/* convert to PHAR_UFP */
		if (data->internal_file->fp_type == PHAR_MOD) {
			php_stream_close(data->internal_file->fp);
		}

		data->internal_file->fp = NULL;
		data->internal_file->fp_type = PHAR_UFP;
		data->internal_file->offset_abs = data->internal_file->offset = php_stream_tell(p_obj->fp);
		data->fp = NULL;
		phar_stream_copy_to_stream(fp, p_obj->fp, PHP_STREAM_COPY_ALL, &contents_len);
		data->internal_file->uncompressed_filesize = data->internal_file->compressed_filesize =
			php_stream_tell(p_obj->fp) - data->internal_file->offset;
	}

	if (close_fp) {
		php_stream_close(fp);
	}

	add_assoc_string(p_obj->ret, str_key, opened, 0);

	if (save) {
		efree(save);
	}

	if (temp) {
		efree(temp);
	}

	data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize = contents_len;
	phar_entry_delref(data TSRMLS_CC);

	return ZEND_HASH_APPLY_KEEP;
}


static int phar_set_compression(void *pDest, void *argument TSRMLS_DC) /* {{{ */
{
	phar_entry_info *entry = (phar_entry_info *)pDest;
	php_uint32 compress = *(php_uint32 *)argument;

	if (entry->is_deleted) {
		return ZEND_HASH_APPLY_KEEP;
	}

	entry->old_flags = entry->flags;
	entry->flags &= ~PHAR_ENT_COMPRESSION_MASK;
	entry->flags |= compress;
	entry->is_modified = 1;
	return ZEND_HASH_APPLY_KEEP;
}


PHP_METHOD(Phar, copy)
{
	char *oldfile, *newfile, *error;
	const char *pcr_error;
	int oldfile_len, newfile_len;
	phar_entry_info *oldentry, newentry = {0}, *temp;

	PHAR_ARCHIVE_OBJECT();

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &oldfile, &oldfile_len, &newfile, &newfile_len) == FAILURE) {
		return;
	}

	if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) {
		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
			"Cannot copy \"%s\" to \"%s\", phar is read-only", oldfile, newfile);
		RETURN_FALSE;
	}

	if (oldfile_len >= sizeof(".phar")-1 && !memcmp(oldfile, ".phar", sizeof(".phar")-1)) {
		/* can't copy a meta file */
		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
			"file \"%s\" cannot be copied to file \"%s\", cannot copy Phar meta-file in %s", oldfile, newfile, phar_obj->arc.archive->fname);
		RETURN_FALSE;
	}

	if (newfile_len >= sizeof(".phar")-1 && !memcmp(newfile, ".phar", sizeof(".phar")-1)) {
		/* can't copy a meta file */
		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
			"file \"%s\" cannot be copied to file \"%s\", cannot copy to Phar meta-file in %s", oldfile, newfile, phar_obj->arc.archive->fname);
		RETURN_FALSE;
	}

	if (!zend_hash_exists(&phar_obj->arc.archive->manifest, oldfile, (uint) oldfile_len) || SUCCESS != zend_hash_find(&phar_obj->arc.archive->manifest, oldfile, (uint) oldfile_len, (void**)&oldentry) || oldentry->is_deleted) {
		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
			"file \"%s\" cannot be copied to file \"%s\", file does not exist in %s", oldfile, newfile, phar_obj->arc.archive->fname);
		RETURN_FALSE;
	}

	if (zend_hash_exists(&phar_obj->arc.archive->manifest, newfile, (uint) newfile_len)) {
		if (SUCCESS == zend_hash_find(&phar_obj->arc.archive->manifest, newfile, (uint) newfile_len, (void**)&temp) || !temp->is_deleted) {
			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
				"file \"%s\" cannot be copied to file \"%s\", file must not already exist in phar %s", oldfile, newfile, phar_obj->arc.archive->fname);
			RETURN_FALSE;
		}
	}

	if (phar_path_check(&newfile, &newfile_len, &pcr_error) > pcr_is_ok) {
		zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
				"file \"%s\" contains invalid characters %s, cannot be copied from \"%s\" in phar %s", newfile, pcr_error, oldfile, phar_obj->arc.archive->fname);
		RETURN_FALSE;
	}

	if (phar_obj->arc.archive->is_persistent) {
		if (FAILURE == phar_copy_on_write(&(phar_obj->arc.archive) TSRMLS_CC)) {
			zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar_obj->arc.archive->fname);
			return;
		}
		/* re-populate with copied-on-write entry */
		zend_hash_find(&phar_obj->arc.archive->manifest, oldfile, (uint) oldfile_len, (void**)&oldentry);
	}

	memcpy((void *) &newentry, oldentry, sizeof(phar_entry_info));

	if (newentry.metadata) {
		zval *t;

		t = newentry.metadata;
		ALLOC_ZVAL(newentry.metadata);
		*newentry.metadata = *t;
		zval_copy_ctor(newentry.metadata);
		Z_SET_REFCOUNT_P(newentry.metadata, 1);

		newentry.metadata_str.c = NULL;
		newentry.metadata_str.len = 0;
	}

	newentry.filename = estrndup(newfile, newfile_len);
	newentry.filename_len = newfile_len;
	newentry.fp_refcount = 0;

	if (oldentry->fp_type != PHAR_FP) {
		if (FAILURE == phar_copy_entry_fp(oldentry, &newentry, &error TSRMLS_CC)) {
			efree(newentry.filename);
			php_stream_close(newentry.fp);
			zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
			efree(error);
			return;
		}
	}

	zend_hash_add(&oldentry->phar->manifest, newfile, newfile_len, (void*)&newentry, sizeof(phar_entry_info), NULL);
	phar_obj->arc.archive->is_modified = 1;
	phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC);

	if (error) {
		zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
		efree(error);
	}

	RETURN_TRUE;
}


PHP_METHOD(PharFileInfo, decompress)
{
	char *error;
	PHAR_ENTRY_OBJECT();

	if (zend_parse_parameters_none() == FAILURE) {
		return;
	}

	if (entry_obj->ent.entry->is_dir) {
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \
			"Phar entry is a directory, cannot set compression"); \
		return;
	}

	if ((entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSION_MASK) == 0) {
		RETURN_TRUE;
	}

	if (PHAR_G(readonly) && !entry_obj->ent.entry->phar->is_data) {
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
			"Phar is readonly, cannot decompress");
		return;
	}

	if (entry_obj->ent.entry->is_deleted) {
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
			"Cannot compress deleted file");
		return;
	}

	if ((entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSED_GZ) != 0 && !PHAR_G(has_zlib)) {
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
			"Cannot decompress Gzip-compressed file, zlib extension is not enabled");
		return;
	}

	if ((entry_obj->ent.entry->flags & PHAR_ENT_COMPRESSED_BZ2) != 0 && !PHAR_G(has_bz2)) {
		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
			"Cannot decompress Bzip2-compressed file, bz2 extension is not enabled");
		return;
	}

	if (entry_obj->ent.entry->is_persistent) {
		phar_archive_data *phar = entry_obj->ent.entry->phar;

		if (FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) {
			zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "phar \"%s\" is persistent, unable to copy on write", phar->fname);
			return;
		}
		/* re-populate after copy-on-write */
		zend_hash_find(&phar->manifest, entry_obj->ent.entry->filename, entry_obj->ent.entry->filename_len, (void **)&entry_obj->ent.entry);
	}
	if (!entry_obj->ent.entry->fp) {
		if (FAILURE == phar_open_archive_fp(entry_obj->ent.entry->phar TSRMLS_CC)) {
			zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot decompress entry \"%s\", phar error: Cannot open phar archive \"%s\" for reading", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname);
			return;
		}
		entry_obj->ent.entry->fp_type = PHAR_FP;
	}

	entry_obj->ent.entry->old_flags = entry_obj->ent.entry->flags;
	entry_obj->ent.entry->flags &= ~PHAR_ENT_COMPRESSION_MASK;
	entry_obj->ent.entry->phar->is_modified = 1;
	entry_obj->ent.entry->is_modified = 1;
	phar_flush(entry_obj->ent.entry->phar, 0, 0, 0, &error TSRMLS_CC);

	if (error) {
		zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s", error);
		efree(error);
	}
	RETURN_TRUE;
}


static void phar_mung_server_vars(char *fname, char *entry, int entry_len, char *basename, int request_uri_len TSRMLS_DC) /* {{{ */
{
	HashTable *_SERVER;
	zval **stuff;
	char *path_info;
	int basename_len = strlen(basename);
	int code;
	zval *temp;

	/* "tweak" $_SERVER variables requested in earlier call to Phar::mungServer() */
	if (!PG(http_globals)[TRACK_VARS_SERVER]) {
		return;
	}

	_SERVER = Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]);

	/* PATH_INFO and PATH_TRANSLATED should always be munged */
	if (SUCCESS == zend_hash_find(_SERVER, "PATH_INFO", sizeof("PATH_INFO"), (void **) &stuff)) {
		path_info = Z_STRVAL_PP(stuff);
		code = Z_STRLEN_PP(stuff);

		if (Z_STRLEN_PP(stuff) > entry_len && !memcmp(Z_STRVAL_PP(stuff), entry, entry_len)) {
			ZVAL_STRINGL(*stuff, Z_STRVAL_PP(stuff) + entry_len, request_uri_len, 1);

			MAKE_STD_ZVAL(temp);
			ZVAL_STRINGL(temp, path_info, code, 0);

			zend_hash_update(_SERVER, "PHAR_PATH_INFO", sizeof("PHAR_PATH_INFO"), &temp, sizeof(zval **), NULL);
		}
	}

	if (SUCCESS == zend_hash_find(_SERVER, "PATH_TRANSLATED", sizeof("PATH_TRANSLATED"), (void **) &stuff)) {
		path_info = Z_STRVAL_PP(stuff);
		code = Z_STRLEN_PP(stuff);
		Z_STRLEN_PP(stuff) = spprintf(&(Z_STRVAL_PP(stuff)), 4096, "phar://%s%s", fname, entry);

		MAKE_STD_ZVAL(temp);
		ZVAL_STRINGL(temp, path_info, code, 0);

		zend_hash_update(_SERVER, "PHAR_PATH_TRANSLATED", sizeof("PHAR_PATH_TRANSLATED"), (void *) &temp, sizeof(zval **), NULL);
	}

	if (!PHAR_GLOBALS->phar_SERVER_mung_list) {
		return;
	}

	if (PHAR_GLOBALS->phar_SERVER_mung_list & PHAR_MUNG_REQUEST_URI) {
		if (SUCCESS == zend_hash_find(_SERVER, "REQUEST_URI", sizeof("REQUEST_URI"), (void **) &stuff)) {
			path_info = Z_STRVAL_PP(stuff);
			code = Z_STRLEN_PP(stuff);

			if (Z_STRLEN_PP(stuff) > basename_len && !memcmp(Z_STRVAL_PP(stuff), basename, basename_len)) {
				ZVAL_STRINGL(*stuff, Z_STRVAL_PP(stuff) + basename_len, Z_STRLEN_PP(stuff) - basename_len, 1);

				MAKE_STD_ZVAL(temp);
				ZVAL_STRINGL(temp, path_info, code, 0);

				zend_hash_update(_SERVER, "PHAR_REQUEST_URI", sizeof("PHAR_REQUEST_URI"), (void *) &temp, sizeof(zval **), NULL);
			}
		}
	}

	if (PHAR_GLOBALS->phar_SERVER_mung_list & PHAR_MUNG_PHP_SELF) {
		if (SUCCESS == zend_hash_find(_SERVER, "PHP_SELF", sizeof("PHP_SELF"), (void **) &stuff)) {
			path_info = Z_STRVAL_PP(stuff);
			code = Z_STRLEN_PP(stuff);

			if (Z_STRLEN_PP(stuff) > basename_len && !memcmp(Z_STRVAL_PP(stuff), basename, basename_len)) {
				ZVAL_STRINGL(*stuff, Z_STRVAL_PP(stuff) + basename_len, Z_STRLEN_PP(stuff) - basename_len, 1);

				MAKE_STD_ZVAL(temp);
				ZVAL_STRINGL(temp, path_info, code, 0);

				zend_hash_update(_SERVER, "PHAR_PHP_SELF", sizeof("PHAR_PHP_SELF"), (void *) &temp, sizeof(zval **), NULL);
			}
		}
	}

	if (PHAR_GLOBALS->phar_SERVER_mung_list & PHAR_MUNG_SCRIPT_NAME) {
		if (SUCCESS == zend_hash_find(_SERVER, "SCRIPT_NAME", sizeof("SCRIPT_NAME"), (void **) &stuff)) {
			path_info = Z_STRVAL_PP(stuff);
			code = Z_STRLEN_PP(stuff);
			ZVAL_STRINGL(*stuff, entry, entry_len, 1);

			MAKE_STD_ZVAL(temp);
			ZVAL_STRINGL(temp, path_info, code, 0);

			zend_hash_update(_SERVER, "PHAR_SCRIPT_NAME", sizeof("PHAR_SCRIPT_NAME"), (void *) &temp, sizeof(zval **), NULL);
		}
	}

	if (PHAR_GLOBALS->phar_SERVER_mung_list & PHAR_MUNG_SCRIPT_FILENAME) {
		if (SUCCESS == zend_hash_find(_SERVER, "SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME"), (void **) &stuff)) {
			path_info = Z_STRVAL_PP(stuff);
			code = Z_STRLEN_PP(stuff);
			Z_STRLEN_PP(stuff) = spprintf(&(Z_STRVAL_PP(stuff)), 4096, "phar://%s%s", fname, entry);

			MAKE_STD_ZVAL(temp);
			ZVAL_STRINGL(temp, path_info, code, 0);

			zend_hash_update(_SERVER, "PHAR_SCRIPT_FILENAME", sizeof("PHAR_SCRIPT_FILENAME"), (void *) &temp, sizeof(zval **), NULL);
		}
	}
}


static int crt_reinit(gnutls_x509_crt_t crt)
{
	int result;

	crt->raw_dn.size = 0;
	crt->raw_issuer_dn.size = 0;

	asn1_delete_structure(&crt->cert);

	result = asn1_create_element(_gnutls_get_pkix(),
				     "PKIX1.Certificate",
				     &crt->cert);
	if (result != ASN1_SUCCESS) {
		result = _gnutls_asn2err(result);
		gnutls_assert();
		return result;
	}

	return 0;
}


gnutls_x509_crt_get_issuer_dn(gnutls_x509_crt_t cert, char *buf,
			      size_t * buf_size)
{
	if (cert == NULL) {
		gnutls_assert();
		return GNUTLS_E_INVALID_REQUEST;
	}

	return _gnutls_x509_parse_dn(cert->cert,
				     "tbsCertificate.issuer.rdnSequence",
				     buf, buf_size);
}


gnutls_x509_crt_get_crl_dist_points(gnutls_x509_crt_t cert,
				    unsigned int seq, void *san,
				    size_t * san_size,
				    unsigned int *reason_flags,
				    unsigned int *critical)
{
	int ret;
	gnutls_datum_t dist_points = { NULL, 0 };
	unsigned type;
	gnutls_x509_crl_dist_points_t cdp = NULL;
	gnutls_datum_t t_san;

	if (cert == NULL) {
		gnutls_assert();
		return GNUTLS_E_INVALID_REQUEST;
	}

	ret = gnutls_x509_crl_dist_points_init(&cdp);
	if (ret < 0)
		return gnutls_assert_val(ret);

	if (reason_flags)
		*reason_flags = 0;

	ret =
	    _gnutls_x509_crt_get_extension(cert, "2.5.29.31", 0,
					   &dist_points, critical);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	if (dist_points.size == 0 || dist_points.data == NULL) {
		gnutls_assert();
		ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
		goto cleanup;
	}

	ret = gnutls_x509_ext_import_crl_dist_points(&dist_points, cdp, 0);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	ret = gnutls_x509_crl_dist_points_get(cdp, seq, &type, &t_san, reason_flags);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	ret = _gnutls_copy_string(&t_san, san, san_size);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	ret = type;

 cleanup:
	_gnutls_free_datum(&dist_points);
	if (cdp != NULL)
		gnutls_x509_crl_dist_points_deinit(cdp);

	return ret;
}


gnutls_x509_crt_get_subject_key_id(gnutls_x509_crt_t cert, void *ret,
				   size_t * ret_size,
				   unsigned int *critical)
{
	int result;
	gnutls_datum_t id = {NULL,0};
	gnutls_datum_t der = {NULL, 0};

	if (cert == NULL) {
		gnutls_assert();
		return GNUTLS_E_INVALID_REQUEST;
	}

	if (ret == NULL)
		*ret_size = 0;

	if ((result =
	     _gnutls_x509_crt_get_extension(cert, "2.5.29.14", 0, &der,
					    critical)) < 0) {
		return result;
	}

	result = gnutls_x509_ext_import_subject_key_id(&der, &id);
	if (result < 0) {
		gnutls_assert();
		goto cleanup;
	}

	result = _gnutls_copy_data(&id, ret, ret_size);
	if (result < 0) {
		gnutls_assert();
		goto cleanup;
	}

	result = 0;

 cleanup:
	gnutls_free(der.data);
	gnutls_free(id.data);
	return result;
}


time_t gnutls_x509_crt_get_activation_time(gnutls_x509_crt_t cert)
{
	if (cert == NULL) {
		gnutls_assert();
		return (time_t) - 1;
	}

	return _gnutls_x509_get_time(cert->cert,
				     "tbsCertificate.validity.notBefore",
				     0);
}


gnutls_x509_crt_check_revocation(gnutls_x509_crt_t cert,
				 const gnutls_x509_crl_t * crl_list,
				 int crl_list_length)
{
	return _gnutls_x509_crt_check_revocation(cert, crl_list,
						 crl_list_length, NULL);
}


gnutls_x509_crt_get_basic_constraints(gnutls_x509_crt_t cert,
				      unsigned int *critical,
				      unsigned int *ca, int *pathlen)
{
	int result;
	gnutls_datum_t basicConstraints;
	unsigned int tmp_ca;

	if (cert == NULL) {
		gnutls_assert();
		return GNUTLS_E_INVALID_REQUEST;
	}

	if ((result =
	     _gnutls_x509_crt_get_extension(cert, "2.5.29.19", 0,
					    &basicConstraints,
					    critical)) < 0) {
		return result;
	}

	if (basicConstraints.size == 0 || basicConstraints.data == NULL) {
		gnutls_assert();
		return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
	}

	result = gnutls_x509_ext_import_basic_constraints(&basicConstraints, &tmp_ca, pathlen);
	if (ca)
		*ca = tmp_ca;

	_gnutls_free_datum(&basicConstraints);

	if (result < 0) {
		gnutls_assert();
		return result;
	}

	return tmp_ca;
}


gnutls_x509_crt_verify_data(gnutls_x509_crt_t crt, unsigned int flags,
			    const gnutls_datum_t * data,
			    const gnutls_datum_t * signature)
{
	int result;

	if (crt == NULL) {
		gnutls_assert();
		return GNUTLS_E_INVALID_REQUEST;
	}

	result =
	    _gnutls_x509_verify_data(GNUTLS_DIG_UNKNOWN, data, signature,
				     crt);
	if (result < 0) {
		gnutls_assert();
		return result;
	}

	return result;
}


gnutls_x509_crt_list_import(gnutls_x509_crt_t * certs,
			    unsigned int *cert_max,
			    const gnutls_datum_t * data,
			    gnutls_x509_crt_fmt_t format,
			    unsigned int flags)
{
	int size;
	const char *ptr;
	gnutls_datum_t tmp;
	int ret, nocopy = 0;
	unsigned int count = 0, j;

	if (format == GNUTLS_X509_FMT_DER) {
		if (*cert_max < 1) {
			*cert_max = 1;
			return GNUTLS_E_SHORT_MEMORY_BUFFER;
		}

		count = 1;	/* import only the first one */

		ret = gnutls_x509_crt_init(&certs[0]);
		if (ret < 0) {
			gnutls_assert();
			goto error;
		}

		ret = gnutls_x509_crt_import(certs[0], data, format);
		if (ret < 0) {
			gnutls_assert();
			goto error;
		}

		*cert_max = 1;
		return 1;
	}

	/* move to the certificate
	 */
	ptr = memmem(data->data, data->size,
		     PEM_CERT_SEP, sizeof(PEM_CERT_SEP) - 1);
	if (ptr == NULL)
		ptr = memmem(data->data, data->size,
			     PEM_CERT_SEP2, sizeof(PEM_CERT_SEP2) - 1);

	if (ptr == NULL)
		return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND);

	count = 0;

	do {
		if (count >= *cert_max) {
			if (!
			    (flags &
			     GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED))
				break;
			else
				nocopy = 1;
		}

		if (!nocopy) {
			ret = gnutls_x509_crt_init(&certs[count]);
			if (ret < 0) {
				gnutls_assert();
				goto error;
			}

			tmp.data = (void *) ptr;
			tmp.size =
			    data->size - (ptr - (char *) data->data);

			ret =
			    gnutls_x509_crt_import(certs[count], &tmp,
						   GNUTLS_X509_FMT_PEM);
			if (ret < 0) {
				gnutls_assert();
				goto error;
			}
		}

		/* now we move ptr after the pem header
		 */
		ptr++;
		/* find the next certificate (if any)
		 */
		size = data->size - (ptr - (char *) data->data);

		if (size > 0) {
			char *ptr2;

			ptr2 =
			    memmem(ptr, size, PEM_CERT_SEP,
				   sizeof(PEM_CERT_SEP) - 1);
			if (ptr2 == NULL)
				ptr2 = memmem(ptr, size, PEM_CERT_SEP2,
					      sizeof(PEM_CERT_SEP2) - 1);

			ptr = ptr2;
		} else
			ptr = NULL;

		count++;
	}
	while (ptr != NULL);

	*cert_max = count;

	if (flags & GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED) {
		ret = check_if_sorted(certs, *cert_max);
		if (ret < 0) {
			gnutls_assert();
			goto error;
		}
	}

	if (nocopy == 0)
		return count;
	else
		return GNUTLS_E_SHORT_MEMORY_BUFFER;

      error:
	for (j = 0; j < count; j++)
		gnutls_x509_crt_deinit(certs[j]);
	return ret;
}


gnutls_x509_crt_get_extension_data(gnutls_x509_crt_t cert, int indx,
				   void *data, size_t * sizeof_data)
{
	int result, len;
	char name[ASN1_MAX_NAME_SIZE];

	if (!cert) {
		gnutls_assert();
		return GNUTLS_E_INVALID_REQUEST;
	}

	snprintf(name, sizeof(name),
		 "tbsCertificate.extensions.?%u.extnValue", indx + 1);

	len = *sizeof_data;
	result = asn1_read_value(cert->cert, name, data, &len);
	*sizeof_data = len;

	if (result == ASN1_ELEMENT_NOT_FOUND) {
		return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
	} else if (result == ASN1_MEM_ERROR && data == NULL) {
		/* normally we should return GNUTLS_E_SHORT_MEMORY_BUFFER,
		 * but we haven't done that for long time, so use
		 * backwards compatible behavior */
		return 0;
	} else if (result != ASN1_SUCCESS) {
		gnutls_assert();
		return _gnutls_asn2err(result);
	}

	return 0;
}


gnutls_x509_crt_get_signature(gnutls_x509_crt_t cert,
			      char *sig, size_t * sig_size)
{
	gnutls_datum_t dsig = {NULL, 0};
	int ret;

	if (cert == NULL)
		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);

	ret = _gnutls_x509_get_signature(cert->cert, "signature", &dsig);
	if (ret < 0)
		return gnutls_assert_val(ret);

	ret = _gnutls_copy_data(&dsig, (uint8_t*)sig, sig_size);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	ret = 0;
 cleanup:
 	gnutls_free(dsig.data);
 	return ret;
}


gnutls_x509_crt_get_ca_status(gnutls_x509_crt_t cert,
			      unsigned int *critical)
{
	int pathlen;
	unsigned int ca;
	return gnutls_x509_crt_get_basic_constraints(cert, critical, &ca,
						     &pathlen);
}


void gnutls_x509_crt_set_pin_function(gnutls_x509_crt_t crt,
				      gnutls_pin_callback_t fn,
				      void *userdata)
{
	crt->pin.cb = fn;
	crt->pin.data = userdata;
}


_gnutls_x509_crt_check_revocation(gnutls_x509_crt_t cert,
				  const gnutls_x509_crl_t * crl_list,
				  int crl_list_length,
				  gnutls_verify_output_function func)
{
	uint8_t serial[128];
	uint8_t cert_serial[128];
	size_t serial_size, cert_serial_size;
	int ret, j;
	gnutls_x509_crl_iter_t iter = NULL;

	if (cert == NULL) {
		gnutls_assert();
		return GNUTLS_E_INVALID_REQUEST;
	}

	for (j = 0; j < crl_list_length; j++) {	/* do for all the crls */

		/* Step 1. check if issuer's DN match
		 */
		ret = crl_issuer_matches(crl_list[j], cert);
		if (ret == 0) {
			/* issuers do not match so don't even
			 * bother checking.
			 */
			gnutls_assert();
			continue;
		}

		/* Step 2. Read the certificate's serial number
		 */
		cert_serial_size = sizeof(cert_serial);
		ret =
		    gnutls_x509_crt_get_serial(cert, cert_serial,
					       &cert_serial_size);
		if (ret < 0) {
			gnutls_assert();
			return ret;
		}

		/* Step 3. cycle through the CRL serials and compare with
		 *   certificate serial we have.
		 */

		iter = NULL;
		do {
			serial_size = sizeof(serial);
			ret =
			    gnutls_x509_crl_iter_crt_serial(crl_list[j],
							    &iter,
							    serial,
							    &serial_size,
							    NULL);
			if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
				break;
			} else if (ret < 0) {
				gnutls_assert();
				goto fail;
			}

			if (serial_size == cert_serial_size) {
				if (memcmp
				    (serial, cert_serial,
				     serial_size) == 0) {
					/* serials match */
					if (func)
						func(cert, NULL,
						     crl_list[j],
						     GNUTLS_CERT_REVOKED |
						     GNUTLS_CERT_INVALID);
					ret = 1;	/* revoked! */
					goto fail;
				}
			}
		} while(1);

		gnutls_x509_crl_iter_deinit(iter);
		iter = NULL;

		if (func)
			func(cert, NULL, crl_list[j], 0);

	}
	return 0;		/* not revoked. */

 fail:
 	gnutls_x509_crl_iter_deinit(iter);
 	return ret;
}


void get_checksum2(char *buf, int32 len, char *sum)
{
	md_context m;

	if (protocol_version >= 30) {
		uchar seedbuf[4];
		md5_begin(&m);
		if (proper_seed_order) {
			if (checksum_seed) {
				SIVALu(seedbuf, 0, checksum_seed);
				md5_update(&m, seedbuf, 4);
			}
			md5_update(&m, (uchar *)buf, len);
		} else {
			md5_update(&m, (uchar *)buf, len);
			if (checksum_seed) {
				SIVALu(seedbuf, 0, checksum_seed);
				md5_update(&m, seedbuf, 4);
			}
		}
		md5_result(&m, (uchar *)sum);
	} else {
		int32 i;
		static char *buf1;
		static int32 len1;

		mdfour_begin(&m);

		if (len > len1) {
			if (buf1)
				free(buf1);
			buf1 = new_array(char, len+4);
			len1 = len;
			if (!buf1)
				out_of_memory("get_checksum2");
		}

		memcpy(buf1, buf, len);
		if (checksum_seed) {
			SIVAL(buf1,len,checksum_seed);
			len += 4;
		}

		for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK)
			mdfour_update(&m, (uchar *)(buf1+i), CSUM_CHUNK);

		/*
		 * Prior to version 27 an incorrect MD4 checksum was computed
		 * by failing to call mdfour_tail() for block sizes that
		 * are multiples of 64.  This is fixed by calling mdfour_update()
		 * even when there are no more bytes.
		 */
		if (len - i > 0 || protocol_version >= 27)
			mdfour_update(&m, (uchar *)(buf1+i), len-i);

		mdfour_result(&m, (uchar *)sum);
	}
}


idna_strerror (Idna_rc rc)
{
  const char *p;

  bindtextdomain (PACKAGE, LOCALEDIR);

  switch (rc)
    {
    case IDNA_SUCCESS:
      p = _("Success");
      break;

    case IDNA_STRINGPREP_ERROR:
      p = _("String preparation failed");
      break;

    case IDNA_PUNYCODE_ERROR:
      p = _("Punycode failed");
      break;

    case IDNA_CONTAINS_NON_LDH:
      p = _("Non-digit/letter/hyphen in input");
      break;

    case IDNA_CONTAINS_MINUS:
      p = _("Forbidden leading or trailing minus sign (`-')");
      break;

    case IDNA_INVALID_LENGTH:
      p = _("Output would be too large or too small");
      break;

    case IDNA_NO_ACE_PREFIX:
      p = _("Input does not start with ACE prefix (`xn--')");
      break;

    case IDNA_ROUNDTRIP_VERIFY_ERROR:
      p = _("String not idempotent under ToASCII");
      break;

    case IDNA_CONTAINS_ACE_PREFIX:
      p = _("Input already contain ACE prefix (`xn--')");
      break;

    case IDNA_ICONV_ERROR:
      p = _("Could not convert string in locale encoding");
      break;

    case IDNA_MALLOC_ERROR:
      p = _("Cannot allocate memory");
      break;

    case IDNA_DLOPEN_ERROR:
      p = _("System dlopen failed");
      break;

    default:
      p = _("Unknown error");
      break;
    }

  return p;
}


combine (gunichar a, gunichar b, gunichar * result)
{
  gushort index_a, index_b;

  if (combine_hangul (a, b, result))
    return TRUE;

  index_a = COMPOSE_INDEX (a);

  if (index_a >= COMPOSE_FIRST_SINGLE_START && index_a < COMPOSE_SECOND_START)
    {
      if (b == compose_first_single[index_a - COMPOSE_FIRST_SINGLE_START][0])
	{
	  *result =
	    compose_first_single[index_a - COMPOSE_FIRST_SINGLE_START][1];
	  return TRUE;
	}
      else
	return FALSE;
    }

  index_b = COMPOSE_INDEX (b);

  if (index_b >= COMPOSE_SECOND_SINGLE_START)
    {
      if (a ==
	  compose_second_single[index_b - COMPOSE_SECOND_SINGLE_START][0])
	{
	  *result =
	    compose_second_single[index_b - COMPOSE_SECOND_SINGLE_START][1];
	  return TRUE;
	}
      else
	return FALSE;
    }

  if (index_a >= COMPOSE_FIRST_START && index_a < COMPOSE_FIRST_SINGLE_START
      && index_b >= COMPOSE_SECOND_START
      && index_b < COMPOSE_SECOND_SINGLE_START)
    {
      gunichar res =
	compose_array[index_a - COMPOSE_FIRST_START][index_b -
						     COMPOSE_SECOND_START];

      if (res)
	{
	  *result = res;
	  return TRUE;
	}
    }

  return FALSE;
}


idna_to_unicode_8zlz (const char *input, char **output, int flags)
{
  char *utf8;
  int rc;

  rc = idna_to_unicode_8z8z (input, &utf8, flags);
  if (rc != IDNA_SUCCESS)
    return rc;

  *output = stringprep_utf8_to_locale (utf8);
  free (utf8);

  if (!*output)
    return IDNA_ICONV_ERROR;

  return IDNA_SUCCESS;
}


check_secret_key( ELG_secret_key *sk )
{
  int rc;
  gcry_mpi_t y = mpi_alloc( mpi_get_nlimbs(sk->y) );

  gcry_mpi_powm( y, sk->g, sk->x, sk->p );
  rc = !mpi_cmp( y, sk->y );
  mpi_free( y );
  return rc;
}


void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size)
{
    char *protocols = vncws_extract_handshake_entry((const char *)line, size,
            "Sec-WebSocket-Protocol");
    char *version = vncws_extract_handshake_entry((const char *)line, size,
            "Sec-WebSocket-Version");
    char *key = vncws_extract_handshake_entry((const char *)line, size,
            "Sec-WebSocket-Key");

    if (protocols && version && key
            && g_strrstr(protocols, "binary")
            && !strcmp(version, WS_SUPPORTED_VERSION)
            && strlen(key) == WS_CLIENT_KEY_LEN) {
        vncws_send_handshake_response(vs, key);
    } else {
        VNC_DEBUG("Defective Websockets header or unsupported protocol\n");
        vnc_client_error(vs);
    }

    g_free(protocols);
    g_free(version);
    g_free(key);
}


    TEST(BSONValidateFast, Simple2 ) {
        char buf[64];
        for ( int i=1; i<=JSTypeMax; i++ ) {
            BSONObjBuilder b;
            sprintf( buf, "foo%d", i );
            b.appendMinForType( buf, i );
            sprintf( buf, "bar%d", i );
            b.appendMaxForType( buf, i );
            BSONObj x = b.obj();
            ASSERT_OK( validateBSON( x.objdata(), x.objsize() ) );
        }
    }


    TEST( BSONValidateFast, RegEx ) {
        BSONObjBuilder b;
        b.appendRegex( "foo", "i" );
        BSONObj x = b.obj();
        ASSERT_OK( validateBSON( x.objdata(), x.objsize() ) );
    }


            void setIsCodeWithScope(bool isCodeWithScope) {
                if (isCodeWithScope) {
                    _startPosition |= 1 << 31;
                }
                else {
                    _startPosition &= ~(1 << 31);
                }
            }


            Status readUTF8String( StringData* out ) {
                int sz;
                if ( !readNumber<int>( &sz ) )
                    return makeError("invalid bson", _idElem);

                if ( sz <= 0 ) {
                    // must have NULL at the very least
                    return makeError("invalid bson", _idElem);
                }

                if ( out ) {
                    *out = StringData( _buffer + _position, sz );
                }

                if ( !skip( sz - 1 ) )
                    return makeError("invalid bson", _idElem);

                char c;
                if ( !readNumber<char>( &c ) )
                    return makeError("invalid bson", _idElem);

                if ( c != 0 )
                    return makeError("not null terminated string", _idElem);

                return Status::OK();
            }


    TEST(BSONValidate, RandomData) {
        PseudoRandom r(17);

        int numValid = 0;
        int numToRun = 1000;
        long long jsonSize = 0;

        for ( int i=0; i<numToRun; i++ ) {
            int size = 1234;

            char* x = new char[size];
            DataView(x).writeLE(size);

            for ( int i=4; i<size; i++ ) {
                x[i] = r.nextInt32( 255 );
            }

            x[size-1] = 0;

            BSONObj o( x );

            ASSERT_EQUALS( size, o.objsize() );

            if ( o.valid() ) {
                numValid++;
                jsonSize += o.jsonString().size();
                ASSERT_OK( validateBSON( o.objdata(), o.objsize() ) );
            }
            else {
                ASSERT_NOT_OK( validateBSON( o.objdata(), o.objsize() ) );
            }

            delete[] x;
        }

        log() << "RandomData: didn't crash valid/total: " << numValid << "/" << numToRun
              << " (want few valid ones)"
              << " jsonSize: " << jsonSize << endl;
    }


_zip_readstr(unsigned char **buf, int len, int nulp, struct zip_error *error)
{
    char *r, *o;

    r = (char *)malloc(nulp ? len+1 : len);
    if (!r) {
	_zip_error_set(error, ZIP_ER_MEMORY, 0);
	return NULL;
    }

    memcpy(r, *buf, len);
    *buf += len;

    if (nulp) {
	/* replace any in-string NUL characters with spaces */
	r[len] = 0;
	for (o=r; o<r+len; o++)
	    if (*o == '\0')
		*o = ' ';
    }

    return r;
}


_zip_dirent_read(struct zip_dirent *zde, FILE *fp,
		 unsigned char **bufp, zip_uint32_t *leftp, int local,
		 struct zip_error *error)
{
    unsigned char buf[CDENTRYSIZE];
    unsigned char *cur;
    unsigned short dostime, dosdate;
    zip_uint32_t size;

    if (local)
	size = LENTRYSIZE;
    else
	size = CDENTRYSIZE;

    if (leftp && (*leftp < size)) {
	_zip_error_set(error, ZIP_ER_NOZIP, 0);
	return -1;
    }

    if (bufp) {
	/* use data from buffer */
	cur = *bufp;
    }
    else {
	/* read entry from disk */
	if ((fread(buf, 1, size, fp)<size)) {
	    _zip_error_set(error, ZIP_ER_READ, errno);
	    return -1;
	}
	cur = buf;
    }

    if (memcmp(cur, (local ? LOCAL_MAGIC : CENTRAL_MAGIC), 4) != 0) {
	_zip_error_set(error, ZIP_ER_NOZIP, 0);
	return -1;
    }
    cur += 4;

    /* convert buffercontents to zip_dirent */

    if (!local)
	zde->version_madeby = _zip_read2(&cur);
    else
	zde->version_madeby = 0;
    zde->version_needed = _zip_read2(&cur);
    zde->bitflags = _zip_read2(&cur);
    zde->comp_method = _zip_read2(&cur);

    /* convert to time_t */
    dostime = _zip_read2(&cur);
    dosdate = _zip_read2(&cur);
    zde->last_mod = _zip_d2u_time(dostime, dosdate);

    zde->crc = _zip_read4(&cur);
    zde->comp_size = _zip_read4(&cur);
    zde->uncomp_size = _zip_read4(&cur);

    zde->filename_len = _zip_read2(&cur);
    zde->extrafield_len = _zip_read2(&cur);

    if (local) {
	zde->comment_len = 0;
	zde->disk_number = 0;
	zde->int_attrib = 0;
	zde->ext_attrib = 0;
	zde->offset = 0;
    } else {
	zde->comment_len = _zip_read2(&cur);
	zde->disk_number = _zip_read2(&cur);
	zde->int_attrib = _zip_read2(&cur);
	zde->ext_attrib = _zip_read4(&cur);
	zde->offset = _zip_read4(&cur);
    }

    zde->filename = NULL;
    zde->extrafield = NULL;
    zde->comment = NULL;

    size += zde->filename_len+zde->extrafield_len+zde->comment_len;

    if (leftp && (*leftp < size)) {
	_zip_error_set(error, ZIP_ER_NOZIP, 0);
	return -1;
    }

    if (bufp) {
	if (zde->filename_len) {
	    zde->filename = _zip_readstr(&cur, zde->filename_len, 1, error);
	    if (!zde->filename)
		    return -1;
	}

	if (zde->extrafield_len) {
	    zde->extrafield = _zip_readstr(&cur, zde->extrafield_len, 0,
					   error);
	    if (!zde->extrafield)
		return -1;
	}

	if (zde->comment_len) {
	    zde->comment = _zip_readstr(&cur, zde->comment_len, 0, error);
	    if (!zde->comment)
		return -1;
	}
    }
    else {
	if (zde->filename_len) {
	    zde->filename = _zip_readfpstr(fp, zde->filename_len, 1, error);
	    if (!zde->filename)
		    return -1;
	}

	if (zde->extrafield_len) {
	    zde->extrafield = _zip_readfpstr(fp, zde->extrafield_len, 0,
					     error);
	    if (!zde->extrafield)
		return -1;
	}

	if (zde->comment_len) {
	    zde->comment = _zip_readfpstr(fp, zde->comment_len, 0, error);
	    if (!zde->comment)
		return -1;
	}
    }

    if (bufp)
      *bufp = cur;
    if (leftp)
	*leftp -= size;

    return 0;
}


_zip_cdir_free(struct zip_cdir *cd)
{
    int i;

    if (!cd)
	return;

    for (i=0; i<cd->nentry; i++)
	_zip_dirent_finalize(cd->entry+i);
    free(cd->comment);
    free(cd->entry);
    free(cd);
}


_zip_cdir_new(int nentry, struct zip_error *error)
{
    struct zip_cdir *cd;

    if ((cd=(struct zip_cdir *)malloc(sizeof(*cd))) == NULL) {
	_zip_error_set(error, ZIP_ER_MEMORY, 0);
	return NULL;
    }

    if ( nentry > ((size_t)-1)/sizeof(*(cd->entry)) || (cd->entry=(struct zip_dirent *)malloc(sizeof(*(cd->entry))*(size_t)nentry))
	== NULL) {
	_zip_error_set(error, ZIP_ER_MEMORY, 0);
	free(cd);
	return NULL;
    }

    /* entries must be initialized by caller */

    cd->nentry = nentry;
    cd->size = cd->offset = 0;
    cd->comment = NULL;
    cd->comment_len = 0;

    return cd;
}


static void model_to_zval_any(zval *ret, xmlNodePtr node TSRMLS_DC)
{
	zval* any = NULL;
	char* name = NULL;

	while (node != NULL) {
		if (get_zval_property(ret, (char*)node->name TSRMLS_CC) == NULL) {
			zval* val = master_to_zval(get_conversion(XSD_ANYXML), node TSRMLS_CC);

			if (any && Z_TYPE_P(any) != IS_ARRAY) {
				/* Convert into array */
				zval *arr;

				MAKE_STD_ZVAL(arr);
				array_init(arr);
				if (name) {
					add_assoc_zval(arr, name, any);
				} else {
					add_next_index_zval(arr, any);
				}
				any = arr;
			}

			if (Z_TYPE_P(val) == IS_STRING && *Z_STRVAL_P(val) == '<') {
				name = NULL;
				while (node->next != NULL) {
					zval* val2 = master_to_zval(get_conversion(XSD_ANYXML), node->next TSRMLS_CC);
					if (Z_TYPE_P(val2) != IS_STRING ||  *Z_STRVAL_P(val) != '<') {
						break;
					}
					add_string_to_string(val, val, val2);
					zval_ptr_dtor(&val2);
					node = node->next;
				}
			} else {
				name = (char*)node->name;
			}

			if (any == NULL) {
				if (name) {
					/* Convert into array */
					zval *arr;

					MAKE_STD_ZVAL(arr);
					array_init(arr);
					add_assoc_zval(arr, name, val);
					any = arr;
					name = NULL;
				} else {
					any = val;
				}
			} else {
				/* Add array element */
				if (name) {
					zval **el;
					if (zend_hash_find(Z_ARRVAL_P(any), name, strlen(name)+1, (void**)&el) == SUCCESS) {
						if (Z_TYPE_PP(el) != IS_ARRAY) {
							/* Convert into array */
							zval *arr;

							MAKE_STD_ZVAL(arr);
							array_init(arr);
							add_next_index_zval(arr, *el);
							*el = arr;
						}
						add_next_index_zval(*el, val);
					} else {
						add_assoc_zval(any, name, val);
					}
				} else {
					add_next_index_zval(any, val);
				}
				name = NULL;
			}
		}
		node = node->next;
	}
	if (any) {
		set_zval_property(ret, name ? name : "any", any TSRMLS_CC);
	}
}


static zval *to_zval_bool(encodeTypePtr type, xmlNodePtr data TSRMLS_DC)
{
	zval *ret;
	MAKE_STD_ZVAL(ret);
	FIND_XML_NULL(data, ret);

	if (data && data->children) {
		if (data->children->type == XML_TEXT_NODE && data->children->next == NULL) {
			whiteSpace_collapse(data->children->content);
			if (stricmp((char*)data->children->content, "true") == 0 ||
				stricmp((char*)data->children->content, "t") == 0 ||
				strcmp((char*)data->children->content, "1") == 0) {
				ZVAL_BOOL(ret, 1);
			} else if (stricmp((char*)data->children->content, "false") == 0 ||
				stricmp((char*)data->children->content, "f") == 0 ||
				strcmp((char*)data->children->content, "0") == 0) {
				ZVAL_BOOL(ret, 0);
			} else {
				ZVAL_STRING(ret, (char*)data->children->content, 1);
				convert_to_boolean(ret);
			}
		} else {
			soap_error0(E_ERROR, "Encoding: Violation of encoding rules");
		}
	} else {
		ZVAL_NULL(ret);
	}
	return ret;
}


static xmlNodePtr to_xml_gyearmonth(encodeTypePtr type, zval *data, int style, xmlNodePtr parent TSRMLS_DC)
{
	return to_xml_datetime_ex(type, data, "%Y-%m", style, parent TSRMLS_CC);
}


void delete_encoder_persistent(void *encode)
{
	encodePtr t = *((encodePtr*)encode);
	if (t->details.ns) {
		free(t->details.ns);
	}
	if (t->details.type_str) {
		free(t->details.type_str);
	}
	/* we should never have mapping in persistent encoder */
	assert(t->details.map == NULL);
	free(t);
}


static zval *to_zval_object_ex(encodeTypePtr type, xmlNodePtr data, zend_class_entry *pce TSRMLS_DC)
{
	zval *ret;
	xmlNodePtr trav;
	sdlPtr sdl;
	sdlTypePtr sdlType = type->sdl_type;
	zend_class_entry *ce = ZEND_STANDARD_CLASS_DEF_PTR;
	zval *redo_any = NULL;

	if (pce) {
		ce = pce;
	} else if (SOAP_GLOBAL(class_map) && type->type_str) {
		zval             **classname;
		zend_class_entry  *tmp;

		if (zend_hash_find(SOAP_GLOBAL(class_map), type->type_str, strlen(type->type_str)+1, (void**)&classname) == SUCCESS &&
		    Z_TYPE_PP(classname) == IS_STRING &&
		    (tmp = zend_fetch_class(Z_STRVAL_PP(classname), Z_STRLEN_PP(classname), ZEND_FETCH_CLASS_AUTO TSRMLS_CC)) != NULL) {
			ce = tmp;
		}
	}
	sdl = SOAP_GLOBAL(sdl);
	if (sdlType) {
		if (sdlType->kind == XSD_TYPEKIND_RESTRICTION &&
		    sdlType->encode && type != &sdlType->encode->details) {
			encodePtr enc;

			enc = sdlType->encode;
			while (enc && enc->details.sdl_type &&
			       enc->details.sdl_type->kind != XSD_TYPEKIND_SIMPLE &&
			       enc->details.sdl_type->kind != XSD_TYPEKIND_LIST &&
			       enc->details.sdl_type->kind != XSD_TYPEKIND_UNION) {
				enc = enc->details.sdl_type->encode;
			}
			if (enc) {
				zval *base;

				ALLOC_INIT_ZVAL(ret);
				if (soap_check_xml_ref(&ret, data TSRMLS_CC)) {
					return ret;
				}

				object_init_ex(ret, ce);
				base = master_to_zval_int(enc, data TSRMLS_CC);
				set_zval_property(ret, "_", base TSRMLS_CC);
			} else {
				ALLOC_INIT_ZVAL(ret);
				FIND_XML_NULL(data, ret);
				if (soap_check_xml_ref(&ret, data TSRMLS_CC)) {
					return ret;
				}
				object_init_ex(ret, ce);
			}
		} else if (sdlType->kind == XSD_TYPEKIND_EXTENSION &&
		           sdlType->encode &&
		           type != &sdlType->encode->details) {
			if (sdlType->encode->details.sdl_type &&
			    sdlType->encode->details.sdl_type->kind != XSD_TYPEKIND_SIMPLE &&
			    sdlType->encode->details.sdl_type->kind != XSD_TYPEKIND_LIST &&
			    sdlType->encode->details.sdl_type->kind != XSD_TYPEKIND_UNION) {

				CHECK_XML_NULL(data);
				if ((ret = soap_find_xml_ref(data TSRMLS_CC)) != NULL) {
					return ret;
				}

			    if (ce != ZEND_STANDARD_CLASS_DEF_PTR &&
			        sdlType->encode->to_zval == sdl_guess_convert_zval &&
			        sdlType->encode->details.sdl_type != NULL &&
			        (sdlType->encode->details.sdl_type->kind == XSD_TYPEKIND_COMPLEX ||
			         sdlType->encode->details.sdl_type->kind == XSD_TYPEKIND_RESTRICTION ||
			         sdlType->encode->details.sdl_type->kind == XSD_TYPEKIND_EXTENSION) &&
			        (sdlType->encode->details.sdl_type->encode == NULL ||
			         (sdlType->encode->details.sdl_type->encode->details.type != IS_ARRAY &&
			          sdlType->encode->details.sdl_type->encode->details.type != SOAP_ENC_ARRAY))) {
					ret = to_zval_object_ex(&sdlType->encode->details, data, ce TSRMLS_CC);
			    } else {
					ret = master_to_zval_int(sdlType->encode, data TSRMLS_CC);
				}
				if (soap_check_xml_ref(&ret, data TSRMLS_CC)) {
					return ret;
				}
				redo_any = get_zval_property(ret, "any" TSRMLS_CC);
				if (Z_TYPE_P(ret) == IS_OBJECT && ce != ZEND_STANDARD_CLASS_DEF_PTR) {
					zend_object *zobj = zend_objects_get_address(ret TSRMLS_CC);
					zobj->ce = ce;
				}
			} else {
				zval *base;

				ALLOC_INIT_ZVAL(ret);
				if (soap_check_xml_ref(&ret, data TSRMLS_CC)) {
					return ret;
				}

				object_init_ex(ret, ce);
				base = master_to_zval_int(sdlType->encode, data TSRMLS_CC);
				set_zval_property(ret, "_", base TSRMLS_CC);
			}
		} else {
			ALLOC_INIT_ZVAL(ret);
			FIND_XML_NULL(data, ret);
			if (soap_check_xml_ref(&ret, data TSRMLS_CC)) {
				return ret;
			}
			object_init_ex(ret, ce);
		}
		if (sdlType->model) {
			if (redo_any) {
				Z_ADDREF_P(redo_any);
				unset_zval_property(ret, "any" TSRMLS_CC);
			}
			model_to_zval_object(ret, sdlType->model, data, sdl TSRMLS_CC);
			if (redo_any) {
				zval *tmp = get_zval_property(ret, "any" TSRMLS_CC);

				if (tmp == NULL) {
					model_to_zval_any(ret, data->children TSRMLS_CC);
				} else if (Z_REFCOUNT_P(tmp) == 0) {
					zval_dtor(tmp);
					efree(tmp);
				}
				zval_ptr_dtor(&redo_any);
			}
		}
		if (sdlType->attributes) {
			sdlAttributePtr *attr;
			HashPosition pos;

			zend_hash_internal_pointer_reset_ex(sdlType->attributes, &pos);
			while (zend_hash_get_current_data_ex(sdlType->attributes, (void**)&attr, &pos) == SUCCESS) {
				if ((*attr)->name) {
					xmlAttrPtr val = get_attribute(data->properties, (*attr)->name);
					char *str_val = NULL;

					if (val && val->children && val->children->content) {
						str_val = (char*)val->children->content;
						if ((*attr)->fixed && strcmp((*attr)->fixed, str_val) != 0) {
							soap_error3(E_ERROR, "Encoding: Attribute '%s' has fixed value '%s' (value '%s' is not allowed)", (*attr)->name, (*attr)->fixed, str_val);
						}
					} else if ((*attr)->fixed) {
						str_val = (*attr)->fixed;
					} else if ((*attr)->def) {
						str_val = (*attr)->def;
					}
					if (str_val) {
						xmlNodePtr dummy, text;
						zval *data;

						dummy = xmlNewNode(NULL, BAD_CAST("BOGUS"));
						text = xmlNewText(BAD_CAST(str_val));
						xmlAddChild(dummy, text);
						data = master_to_zval((*attr)->encode, dummy TSRMLS_CC);
						xmlFreeNode(dummy);
						set_zval_property(ret, (*attr)->name, data TSRMLS_CC);
					}
				}
				zend_hash_move_forward_ex(sdlType->attributes, &pos);
			}
		}
	} else {

		ALLOC_INIT_ZVAL(ret);
		FIND_XML_NULL(data, ret);
		if (soap_check_xml_ref(&ret, data TSRMLS_CC)) {
			return ret;
		}

		object_init_ex(ret, ce);
		trav = data->children;

		while (trav != NULL) {
			if (trav->type == XML_ELEMENT_NODE) {
				zval  *tmpVal;
				zval *prop;

				tmpVal = master_to_zval(NULL, trav TSRMLS_CC);

				prop = get_zval_property(ret, (char*)trav->name TSRMLS_CC);
				if (!prop) {
					if (!trav->next || !get_node(trav->next, (char*)trav->name)) {
						set_zval_property(ret, (char*)trav->name, tmpVal TSRMLS_CC);
					} else {
						zval *arr;

						MAKE_STD_ZVAL(arr);
						array_init(arr);
						add_next_index_zval(arr, tmpVal);
						set_zval_property(ret, (char*)trav->name, arr TSRMLS_CC);
					}
				} else {
					/* Property already exist - make array */
					if (Z_TYPE_P(prop) != IS_ARRAY) {
						/* Convert into array */
						zval *arr;

						MAKE_STD_ZVAL(arr);
						array_init(arr);
						Z_ADDREF_P(prop);
						add_next_index_zval(arr, prop);
						set_zval_property(ret, (char*)trav->name, arr TSRMLS_CC);
						prop = arr;
					}
					/* Add array element */
					add_next_index_zval(prop, tmpVal);
				}
			}
			trav = trav->next;
		}
	}
	return ret;
}


static int get_http_body(php_stream *stream, int close, char *headers,  char **response, int *out_size TSRMLS_DC)
{
	char *header, *http_buf = NULL;
	int header_close = close, header_chunked = 0, header_length = 0, http_buf_size = 0;

	if (!close) {
		header = get_http_header_value(headers, "Connection: ");
		if (header) {
			if(!strncasecmp(header, "close", sizeof("close")-1)) header_close = 1;
			efree(header);
		}
	}
	header = get_http_header_value(headers, "Transfer-Encoding: ");
	if (header) {
		if(!strncasecmp(header, "chunked", sizeof("chunked")-1)) header_chunked = 1;
		efree(header);
	}
	header = get_http_header_value(headers, "Content-Length: ");
	if (header) {
		header_length = atoi(header);
		efree(header);
		if (!header_length && !header_chunked) {
			/* Empty response */
			http_buf = emalloc(1);
			http_buf[0] = '\0';
			(*response) = http_buf;
			(*out_size) = 0;
			return TRUE;
		}
	}

	if (header_chunked) {
		char ch, done, headerbuf[8192];

		done = FALSE;

		while (!done) {
			int buf_size = 0;

			php_stream_gets(stream, headerbuf, sizeof(headerbuf));
			if (sscanf(headerbuf, "%x", &buf_size) > 0 ) {
				if (buf_size > 0) {
					int len_size = 0;

					if (http_buf_size + buf_size + 1 < 0) {
						efree(http_buf);
						return FALSE;
					}
					http_buf = erealloc(http_buf, http_buf_size + buf_size + 1);

					while (len_size < buf_size) {
						int len_read = php_stream_read(stream, http_buf + http_buf_size, buf_size - len_size);
						if (len_read <= 0) {
							/* Error or EOF */
							done = TRUE;
						  break;
						}
						len_size += len_read;
	 					http_buf_size += len_read;
 					}

					/* Eat up '\r' '\n' */
					ch = php_stream_getc(stream);
					if (ch == '\r') {
						ch = php_stream_getc(stream);
					}
					if (ch != '\n') {
						/* Somthing wrong in chunked encoding */
						if (http_buf) {
							efree(http_buf);
						}
						return FALSE;
					}
 				}
			} else {
				/* Somthing wrong in chunked encoding */
				if (http_buf) {
					efree(http_buf);
				}
				return FALSE;
			}
			if (buf_size == 0) {
				done = TRUE;
			}
		}

		/* Ignore trailer headers */
		while (1) {
			if (!php_stream_gets(stream, headerbuf, sizeof(headerbuf))) {
				break;
			}

			if ((headerbuf[0] == '\r' && headerbuf[1] == '\n') ||
			    (headerbuf[0] == '\n')) {
				/* empty line marks end of headers */
				break;
			}
		}

		if (http_buf == NULL) {
			http_buf = emalloc(1);
		}

	} else if (header_length) {
		if (header_length < 0 || header_length >= INT_MAX) {
			return FALSE;
		}
		http_buf = safe_emalloc(1, header_length, 1);
		while (http_buf_size < header_length) {
			int len_read = php_stream_read(stream, http_buf + http_buf_size, header_length - http_buf_size);
			if (len_read <= 0) {
				break;
			}
			http_buf_size += len_read;
		}
	} else if (header_close) {
		do {
			int len_read;
			http_buf = erealloc(http_buf, http_buf_size + 4096 + 1);
			len_read = php_stream_read(stream, http_buf + http_buf_size, 4096);
			if (len_read > 0) {
				http_buf_size += len_read;
			}
		} while(!php_stream_eof(stream));
	} else {
		return FALSE;
	}

	http_buf[http_buf_size] = '\0';
	(*response) = http_buf;
	(*out_size) = http_buf_size;
	return TRUE;
}


static xmlNodePtr master_to_xml_int(encodePtr encode, zval *data, int style, xmlNodePtr parent, int check_class_map TSRMLS_DC)
{
	xmlNodePtr node = NULL;
	int add_type = 0;

	/* Special handling of class SoapVar */
	if (data &&
	    Z_TYPE_P(data) == IS_OBJECT &&
	    Z_OBJCE_P(data) == soap_var_class_entry) {
		zval **ztype, **zdata, **zns, **zstype, **zname, **znamens;
		encodePtr enc = NULL;
		HashTable *ht = Z_OBJPROP_P(data);

		if (zend_hash_find(ht, "enc_type", sizeof("enc_type"), (void **)&ztype) == FAILURE ||
		    Z_TYPE_PP(ztype) != IS_LONG) {
			soap_error0(E_ERROR, "Encoding: SoapVar has no 'enc_type' property");
		}

		if (zend_hash_find(ht, "enc_stype", sizeof("enc_stype"), (void **)&zstype) == SUCCESS &&
		    Z_TYPE_PP(zstype) == IS_STRING) {
			if (zend_hash_find(ht, "enc_ns", sizeof("enc_ns"), (void **)&zns) == SUCCESS &&
			    Z_TYPE_PP(zns) == IS_STRING) {
				enc = get_encoder(SOAP_GLOBAL(sdl), Z_STRVAL_PP(zns), Z_STRVAL_PP(zstype));
			} else {
				zns = NULL;
				enc = get_encoder_ex(SOAP_GLOBAL(sdl), Z_STRVAL_PP(zstype), Z_STRLEN_PP(zstype));
			}
			if (enc == NULL && SOAP_GLOBAL(typemap)) {
				encodePtr *new_enc;
				smart_str nscat = {0};

				if (zns != NULL) {
					smart_str_appendl(&nscat, Z_STRVAL_PP(zns), Z_STRLEN_PP(zns));
					smart_str_appendc(&nscat, ':');
				}
				smart_str_appendl(&nscat, Z_STRVAL_PP(zstype), Z_STRLEN_PP(zstype));
				smart_str_0(&nscat);
				if (zend_hash_find(SOAP_GLOBAL(typemap), nscat.c, nscat.len + 1, (void**)&new_enc) == SUCCESS) {
					enc = *new_enc;
				}
				smart_str_free(&nscat);
			}
		}
		if (enc == NULL) {
			enc = get_conversion(Z_LVAL_P(*ztype));
		}
		if (enc == NULL) {
			enc = encode;
		}

		if (zend_hash_find(ht, "enc_value", sizeof("enc_value"), (void **)&zdata) == FAILURE) {
			node = master_to_xml(enc, NULL, style, parent TSRMLS_CC);
		} else {
			node = master_to_xml(enc, *zdata, style, parent TSRMLS_CC);
		}

		if (style == SOAP_ENCODED || (SOAP_GLOBAL(sdl) && encode != enc)) {
			if (zend_hash_find(ht, "enc_stype", sizeof("enc_stype"), (void **)&zstype) == SUCCESS &&
			    Z_TYPE_PP(zstype) == IS_STRING) {
				if (zend_hash_find(ht, "enc_ns", sizeof("enc_ns"), (void **)&zns) == SUCCESS &&
				    Z_TYPE_PP(zns) == IS_STRING) {
					set_ns_and_type_ex(node, Z_STRVAL_PP(zns), Z_STRVAL_PP(zstype));
				} else {
					set_ns_and_type_ex(node, NULL, Z_STRVAL_PP(zstype));
				}
			}
		}

		if (zend_hash_find(ht, "enc_name", sizeof("enc_name"), (void **)&zname) == SUCCESS &&
		    Z_TYPE_PP(zname) == IS_STRING) {
			xmlNodeSetName(node, BAD_CAST(Z_STRVAL_PP(zname)));
		}
		if (zend_hash_find(ht, "enc_namens", sizeof("enc_namens"), (void **)&znamens) == SUCCESS &&
		    Z_TYPE_PP(znamens) == IS_STRING) {
			xmlNsPtr nsp = encode_add_ns(node, Z_STRVAL_PP(znamens));
			xmlSetNs(node, nsp);
		}
	} else {
		if (check_class_map && SOAP_GLOBAL(class_map) && data &&
		    Z_TYPE_P(data) == IS_OBJECT &&
		    !Z_OBJPROP_P(data)->nApplyCount) {
			zend_class_entry *ce = Z_OBJCE_P(data);
			HashPosition pos;
			zval **tmp;
			char *type_name = NULL;
			uint type_len;
			ulong idx;

			for (zend_hash_internal_pointer_reset_ex(SOAP_GLOBAL(class_map), &pos);
			     zend_hash_get_current_data_ex(SOAP_GLOBAL(class_map), (void **) &tmp, &pos) == SUCCESS;
			     zend_hash_move_forward_ex(SOAP_GLOBAL(class_map), &pos)) {
				if (Z_TYPE_PP(tmp) == IS_STRING &&
				    ce->name_length == Z_STRLEN_PP(tmp) &&
				    zend_binary_strncasecmp(ce->name, ce->name_length, Z_STRVAL_PP(tmp), ce->name_length, ce->name_length) == 0 &&
				    zend_hash_get_current_key_ex(SOAP_GLOBAL(class_map), &type_name, &type_len, &idx, 0, &pos) == HASH_KEY_IS_STRING) {

					/* TODO: namespace isn't stored */
					encodePtr enc = NULL;
					if (SOAP_GLOBAL(sdl)) {
						enc = get_encoder(SOAP_GLOBAL(sdl), SOAP_GLOBAL(sdl)->target_ns, type_name);
						if (!enc) {
							enc = find_encoder_by_type_name(SOAP_GLOBAL(sdl), type_name);
						}
					}
					if (enc) {
						if (encode != enc && style == SOAP_LITERAL) {
							add_type = 1;
						}
						encode = enc;
					}
					break;
				}
			}
		}

		if (encode == NULL) {
			encode = get_conversion(UNKNOWN_TYPE);
		}
		if (SOAP_GLOBAL(typemap) && encode->details.type_str) {
			smart_str nscat = {0};
			encodePtr *new_enc;

			if (encode->details.ns) {
				smart_str_appends(&nscat, encode->details.ns);
				smart_str_appendc(&nscat, ':');
			}
			smart_str_appends(&nscat, encode->details.type_str);
			smart_str_0(&nscat);
			if (zend_hash_find(SOAP_GLOBAL(typemap), nscat.c, nscat.len + 1, (void**)&new_enc) == SUCCESS) {
				encode = *new_enc;
			}
			smart_str_free(&nscat);
		}
		if (encode->to_xml) {
			node = encode->to_xml(&encode->details, data, style, parent TSRMLS_CC);
			if (add_type) {
				set_ns_and_type(node, &encode->details);
			}
		}
	}
	return node;
}


static void macio_ide_realizefn(DeviceState *dev, Error **errp)
{
    MACIOIDEState *s = MACIO_IDE(dev);

    ide_init2(&s->bus, s->irq);

    /* Register DMA callbacks */
    s->dma.ops = &dbdma_ops;
    s->bus.dma = &s->dma;
}


void ide_sector_read(IDEState *s)
{
    int64_t sector_num;
    int n;

    s->status = READY_STAT | SEEK_STAT;
    s->error = 0; /* not needed by IDE spec, but needed by Windows */
    sector_num = ide_get_sector(s);
    n = s->nsector;

    if (n == 0) {
        ide_transfer_stop(s);
        return;
    }

    s->status |= BUSY_STAT;

    if (n > s->req_nb_sectors) {
        n = s->req_nb_sectors;
    }

#if defined(DEBUG_IDE)
    printf("sector=%" PRId64 "\n", sector_num);
#endif

    if (!ide_sect_range_ok(s, sector_num, n)) {
        ide_rw_error(s);
        return;
    }

    s->iov.iov_base = s->io_buffer;
    s->iov.iov_len  = n * BDRV_SECTOR_SIZE;
    qemu_iovec_init_external(&s->qiov, &s->iov, 1);

    block_acct_start(blk_get_stats(s->blk), &s->acct,
                     n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
    s->pio_aiocb = blk_aio_readv(s->blk, sector_num, &s->qiov, n,
                                 ide_sector_read_cb, s);
}


void macio_ide_init_drives(MACIOIDEState *s, DriveInfo **hd_table)
{
    int i;

    for (i = 0; i < 2; i++) {
        if (hd_table[i]) {
            ide_create_drive(&s->bus, i, hd_table[i]);
        }
    }
}


static void ide_trim_bh_cb(void *opaque)
{
    TrimAIOCB *iocb = opaque;

    iocb->common.cb(iocb->common.opaque, iocb->ret);

    qemu_bh_delete(iocb->bh);
    iocb->bh = NULL;
    qemu_aio_unref(iocb);
}


void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val)
{
#ifdef DEBUG_IDE
    printf("%s: 0x%08x\n", __func__, val);
#endif

    /* Ignore writes to SSBM if it keeps the old value */
    if ((val & BM_CMD_START) != (bm->cmd & BM_CMD_START)) {
        if (!(val & BM_CMD_START)) {
            /*
             * We can't cancel Scatter Gather DMA in the middle of the
             * operation or a partial (not full) DMA transfer would reach
             * the storage so we wait for completion instead (we beahve
             * like if the DMA was completed by the time the guest trying
             * to cancel dma with bmdma_cmd_writeb with BM_CMD_START not
             * set).
             *
             * In the future we'll be able to safely cancel the I/O if the
             * whole DMA operation will be submitted to disk with a single
             * aio operation with preadv/pwritev.
             */
            if (bm->bus->dma->aiocb) {
                blk_drain_all();
                assert(bm->bus->dma->aiocb == NULL);
            }
            bm->status &= ~BM_STATUS_DMAING;
        } else {
            bm->cur_addr = bm->addr;
            if (!(bm->status & BM_STATUS_DMAING)) {
                bm->status |= BM_STATUS_DMAING;
                /* start dma transfer if possible */
                if (bm->dma_cb)
                    bm->dma_cb(bmdma_active_if(bm), 0);
            }
        }
    }

    bm->cmd = val & 0x09;
}


static void ahci_start_dma(IDEDMA *dma, IDEState *s,
                           BlockCompletionFunc *dma_cb)
{
#ifdef DEBUG_AHCI
    AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
#endif
    DPRINTF(ad->port_no, "\n");
    s->io_buffer_offset = 0;
    dma_cb(s, 0);
}


void ide_start_dma(IDEState *s, BlockCompletionFunc *cb)
{
    if (s->bus->dma->ops->start_dma) {
        s->bus->dma->ops->start_dma(s->bus->dma, s, cb);
    }
}


static int ide_pci_post_load(void *opaque, int version_id)
{
    PCIIDEState *d = opaque;
    int i;

    for(i = 0; i < 2; i++) {
        /* current versions always store 0/1, but older version
           stored bigger values. We only need last bit */
        d->bmdma[i].unit &= 1;
        ide_bmdma_post_load(&d->bmdma[i], -1);
    }

    return 0;
}


static void pmac_ide_transfer_cb(void *opaque, int ret)
{
    DBDMA_io *io = opaque;
    MACIOIDEState *m = io->opaque;
    IDEState *s = idebus_active_if(&m->bus);
    int n = 0;
    int64_t sector_num;
    int unaligned;

    if (ret < 0) {
        MACIO_DPRINTF("DMA error\n");
        m->aiocb = NULL;
        qemu_sglist_destroy(&s->sg);
        ide_dma_error(s);
        io->remainder_len = 0;
        goto done;
    }

    if (--io->requests) {
        /* More requests still in flight */
        return;
    }

    if (!m->dma_active) {
        MACIO_DPRINTF("waiting for data (%#x - %#x - %x)\n",
                      s->nsector, io->len, s->status);
        /* data not ready yet, wait for the channel to get restarted */
        io->processing = false;
        return;
    }

    sector_num = ide_get_sector(s);
    MACIO_DPRINTF("io_buffer_size = %#x\n", s->io_buffer_size);
    if (s->io_buffer_size > 0) {
        m->aiocb = NULL;
        qemu_sglist_destroy(&s->sg);
        n = (s->io_buffer_size + 0x1ff) >> 9;
        sector_num += n;
        ide_set_sector(s, sector_num);
        s->nsector -= n;
    }

    if (io->finish_remain_read) {
        /* Finish a stale read from the last iteration */
        io->finish_remain_read = false;
        cpu_physical_memory_write(io->finish_addr, io->remainder,
                                  io->finish_len);
    }

    MACIO_DPRINTF("remainder: %d io->len: %d nsector: %d "
                  "sector_num: %" PRId64 "\n",
                  io->remainder_len, io->len, s->nsector, sector_num);
    if (io->remainder_len && io->len) {
        /* guest wants the rest of its previous transfer */
        int remainder_len = MIN(io->remainder_len, io->len);
        uint8_t *p = &io->remainder[0x200 - remainder_len];

        MACIO_DPRINTF("copying remainder %d bytes at %#" HWADDR_PRIx "\n",
                      remainder_len, io->addr);

        switch (s->dma_cmd) {
        case IDE_DMA_READ:
            cpu_physical_memory_write(io->addr, p, remainder_len);
            break;
        case IDE_DMA_WRITE:
            cpu_physical_memory_read(io->addr, p, remainder_len);
            break;
        case IDE_DMA_TRIM:
            break;
        }
        io->addr += remainder_len;
        io->len -= remainder_len;
        io->remainder_len -= remainder_len;

        if (s->dma_cmd == IDE_DMA_WRITE && !io->remainder_len) {
            io->requests++;
            qemu_iovec_reset(&io->iov);
            qemu_iovec_add(&io->iov, io->remainder, 0x200);

            m->aiocb = blk_aio_writev(s->blk, sector_num - 1, &io->iov, 1,
                                      pmac_ide_transfer_cb, io);
        }
    }

    if (s->nsector == 0 && !io->remainder_len) {
        MACIO_DPRINTF("end of transfer\n");
        s->status = READY_STAT | SEEK_STAT;
        ide_set_irq(s->bus);
        m->dma_active = false;
    }

    if (io->len == 0) {
        MACIO_DPRINTF("end of DMA\n");
        goto done;
    }

    /* launch next transfer */

    s->io_buffer_index = 0;
    s->io_buffer_size = MIN(io->len, s->nsector * 512);

    /* handle unaligned accesses first, get them over with and only do the
       remaining bulk transfer using our async DMA helpers */
    unaligned = io->len & 0x1ff;
    if (unaligned) {
        int nsector = io->len >> 9;

        MACIO_DPRINTF("precopying unaligned %d bytes to %#" HWADDR_PRIx "\n",
                      unaligned, io->addr + io->len - unaligned);

        switch (s->dma_cmd) {
        case IDE_DMA_READ:
            io->requests++;
            io->finish_addr = io->addr + io->len - unaligned;
            io->finish_len = unaligned;
            io->finish_remain_read = true;
            qemu_iovec_reset(&io->iov);
            qemu_iovec_add(&io->iov, io->remainder, 0x200);

            m->aiocb = blk_aio_readv(s->blk, sector_num + nsector, &io->iov, 1,
                                     pmac_ide_transfer_cb, io);
            break;
        case IDE_DMA_WRITE:
            /* cache the contents in our io struct */
            cpu_physical_memory_read(io->addr + io->len - unaligned,
                                     io->remainder + io->remainder_len,
                                     unaligned);
            break;
        case IDE_DMA_TRIM:
            break;
        }
    }

    MACIO_DPRINTF("io->len = %#x\n", io->len);

    qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1,
                     &address_space_memory);
    qemu_sglist_add(&s->sg, io->addr, io->len);
    io->addr += io->len + unaligned;
    io->remainder_len = (0x200 - unaligned) & 0x1ff;
    MACIO_DPRINTF("set remainder to: %d\n", io->remainder_len);

    /* Only subsector reads happening */
    if (!io->len) {
        if (!io->requests) {
            io->requests++;
            pmac_ide_transfer_cb(opaque, ret);
        }
        return;
    }

    io->len = 0;

    MACIO_DPRINTF("sector_num=%" PRId64 " n=%d, nsector=%d, cmd_cmd=%d\n",
                  sector_num, n, s->nsector, s->dma_cmd);

    switch (s->dma_cmd) {
    case IDE_DMA_READ:
        m->aiocb = dma_blk_read(s->blk, &s->sg, sector_num,
                                pmac_ide_transfer_cb, io);
        break;
    case IDE_DMA_WRITE:
        m->aiocb = dma_blk_write(s->blk, &s->sg, sector_num,
                                 pmac_ide_transfer_cb, io);
        break;
    case IDE_DMA_TRIM:
        m->aiocb = dma_blk_io(s->blk, &s->sg, sector_num,
                              ide_issue_trim, pmac_ide_transfer_cb, io,
                              DMA_DIRECTION_TO_DEVICE);
        break;
    }

    io->requests++;
    return;

done:
    if (s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) {
        block_acct_done(blk_get_stats(s->blk), &s->acct);
    }
    io->dma_end(io);
}


static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist,
                                int32_t offset)
{
    AHCICmdHdr *cmd = ad->cur_cmd;
    uint32_t opts = le32_to_cpu(cmd->opts);
    uint64_t prdt_addr = le64_to_cpu(cmd->tbl_addr) + 0x80;
    int sglist_alloc_hint = opts >> AHCI_CMD_HDR_PRDT_LEN;
    dma_addr_t prdt_len = (sglist_alloc_hint * sizeof(AHCI_SG));
    dma_addr_t real_prdt_len = prdt_len;
    uint8_t *prdt;
    int i;
    int r = 0;
    uint64_t sum = 0;
    int off_idx = -1;
    int64_t off_pos = -1;
    int tbl_entry_size;
    IDEBus *bus = &ad->port;
    BusState *qbus = BUS(bus);

    /*
     * Note: AHCI PRDT can describe up to 256GiB. SATA/ATA only support
     * transactions of up to 32MiB as of ATA8-ACS3 rev 1b, assuming a
     * 512 byte sector size. We limit the PRDT in this implementation to
     * a reasonably large 2GiB, which can accommodate the maximum transfer
     * request for sector sizes up to 32K.
     */

    if (!sglist_alloc_hint) {
        DPRINTF(ad->port_no, "no sg list given by guest: 0x%08x\n", opts);
        return -1;
    }

    /* map PRDT */
    if (!(prdt = dma_memory_map(ad->hba->as, prdt_addr, &prdt_len,
                                DMA_DIRECTION_TO_DEVICE))){
        DPRINTF(ad->port_no, "map failed\n");
        return -1;
    }

    if (prdt_len < real_prdt_len) {
        DPRINTF(ad->port_no, "mapped less than expected\n");
        r = -1;
        goto out;
    }

    /* Get entries in the PRDT, init a qemu sglist accordingly */
    if (sglist_alloc_hint > 0) {
        AHCI_SG *tbl = (AHCI_SG *)prdt;
        sum = 0;
        for (i = 0; i < sglist_alloc_hint; i++) {
            /* flags_size is zero-based */
            tbl_entry_size = prdt_tbl_entry_size(&tbl[i]);
            if (offset <= (sum + tbl_entry_size)) {
                off_idx = i;
                off_pos = offset - sum;
                break;
            }
            sum += tbl_entry_size;
        }
        if ((off_idx == -1) || (off_pos < 0) || (off_pos > tbl_entry_size)) {
            DPRINTF(ad->port_no, "%s: Incorrect offset! "
                            "off_idx: %d, off_pos: %"PRId64"\n",
                            __func__, off_idx, off_pos);
            r = -1;
            goto out;
        }

        qemu_sglist_init(sglist, qbus->parent, (sglist_alloc_hint - off_idx),
                         ad->hba->as);
        qemu_sglist_add(sglist, le64_to_cpu(tbl[off_idx].addr + off_pos),
                        prdt_tbl_entry_size(&tbl[off_idx]) - off_pos);

        for (i = off_idx + 1; i < sglist_alloc_hint; i++) {
            /* flags_size is zero-based */
            qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr),
                            prdt_tbl_entry_size(&tbl[i]));
            if (sglist->size > INT32_MAX) {
                error_report("AHCI Physical Region Descriptor Table describes "
                             "more than 2 GiB.\n");
                qemu_sglist_destroy(sglist);
                r = -1;
                goto out;
            }
        }
    }

out:
    dma_memory_unmap(ad->hba->as, prdt, prdt_len,
                     DMA_DIRECTION_TO_DEVICE, prdt_len);
    return r;
}


static void pmac_ide_writeb (void *opaque,
                             hwaddr addr, uint32_t val)
{
    MACIOIDEState *d = opaque;

    addr = (addr & 0xFFF) >> 4;
    switch (addr) {
    case 1 ... 7:
        ide_ioport_write(&d->bus, addr, val);
        break;
    case 8:
    case 22:
        ide_cmd_write(&d->bus, 0, val);
        break;
    default:
        break;
    }
}


static void ide_identify(IDEState *s)
{
    uint16_t *p;
    unsigned int oldsize;
    IDEDevice *dev = s->unit ? s->bus->slave : s->bus->master;

    p = (uint16_t *)s->identify_data;
    if (s->identify_set) {
        goto fill_buffer;
    }
    memset(p, 0, sizeof(s->identify_data));

    put_le16(p + 0, 0x0040);
    put_le16(p + 1, s->cylinders);
    put_le16(p + 3, s->heads);
    put_le16(p + 4, 512 * s->sectors); /* XXX: retired, remove ? */
    put_le16(p + 5, 512); /* XXX: retired, remove ? */
    put_le16(p + 6, s->sectors);
    padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */
    put_le16(p + 20, 3); /* XXX: retired, remove ? */
    put_le16(p + 21, 512); /* cache size in sectors */
    put_le16(p + 22, 4); /* ecc bytes */
    padstr((char *)(p + 23), s->version, 8); /* firmware version */
    padstr((char *)(p + 27), s->drive_model_str, 40); /* model */
#if MAX_MULT_SECTORS > 1
    put_le16(p + 47, 0x8000 | MAX_MULT_SECTORS);
#endif
    put_le16(p + 48, 1); /* dword I/O */
    put_le16(p + 49, (1 << 11) | (1 << 9) | (1 << 8)); /* DMA and LBA supported */
    put_le16(p + 51, 0x200); /* PIO transfer cycle */
    put_le16(p + 52, 0x200); /* DMA transfer cycle */
    put_le16(p + 53, 1 | (1 << 1) | (1 << 2)); /* words 54-58,64-70,88 are valid */
    put_le16(p + 54, s->cylinders);
    put_le16(p + 55, s->heads);
    put_le16(p + 56, s->sectors);
    oldsize = s->cylinders * s->heads * s->sectors;
    put_le16(p + 57, oldsize);
    put_le16(p + 58, oldsize >> 16);
    if (s->mult_sectors)
        put_le16(p + 59, 0x100 | s->mult_sectors);
    /* *(p + 60) := nb_sectors       -- see ide_identify_size */
    /* *(p + 61) := nb_sectors >> 16 -- see ide_identify_size */
    put_le16(p + 62, 0x07); /* single word dma0-2 supported */
    put_le16(p + 63, 0x07); /* mdma0-2 supported */
    put_le16(p + 64, 0x03); /* pio3-4 supported */
    put_le16(p + 65, 120);
    put_le16(p + 66, 120);
    put_le16(p + 67, 120);
    put_le16(p + 68, 120);
    if (dev && dev->conf.discard_granularity) {
        put_le16(p + 69, (1 << 14)); /* determinate TRIM behavior */
    }

    if (s->ncq_queues) {
        put_le16(p + 75, s->ncq_queues - 1);
        /* NCQ supported */
        put_le16(p + 76, (1 << 8));
    }

    put_le16(p + 80, 0xf0); /* ata3 -> ata6 supported */
    put_le16(p + 81, 0x16); /* conforms to ata5 */
    /* 14=NOP supported, 5=WCACHE supported, 0=SMART supported */
    put_le16(p + 82, (1 << 14) | (1 << 5) | 1);
    /* 13=flush_cache_ext,12=flush_cache,10=lba48 */
    put_le16(p + 83, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
    /* 14=set to 1, 8=has WWN, 1=SMART self test, 0=SMART error logging */
    if (s->wwn) {
        put_le16(p + 84, (1 << 14) | (1 << 8) | 0);
    } else {
        put_le16(p + 84, (1 << 14) | 0);
    }
    /* 14 = NOP supported, 5=WCACHE enabled, 0=SMART feature set enabled */
    if (blk_enable_write_cache(s->blk)) {
        put_le16(p + 85, (1 << 14) | (1 << 5) | 1);
    } else {
        put_le16(p + 85, (1 << 14) | 1);
    }
    /* 13=flush_cache_ext,12=flush_cache,10=lba48 */
    put_le16(p + 86, (1 << 13) | (1 <<12) | (1 << 10));
    /* 14=set to 1, 8=has WWN, 1=SMART self test, 0=SMART error logging */
    if (s->wwn) {
        put_le16(p + 87, (1 << 14) | (1 << 8) | 0);
    } else {
        put_le16(p + 87, (1 << 14) | 0);
    }
    put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */
    put_le16(p + 93, 1 | (1 << 14) | 0x2000);
    /* *(p + 100) := nb_sectors       -- see ide_identify_size */
    /* *(p + 101) := nb_sectors >> 16 -- see ide_identify_size */
    /* *(p + 102) := nb_sectors >> 32 -- see ide_identify_size */
    /* *(p + 103) := nb_sectors >> 48 -- see ide_identify_size */

    if (dev && dev->conf.physical_block_size)
        put_le16(p + 106, 0x6000 | get_physical_block_exp(&dev->conf));
    if (s->wwn) {
        /* LE 16-bit words 111-108 contain 64-bit World Wide Name */
        put_le16(p + 108, s->wwn >> 48);
        put_le16(p + 109, s->wwn >> 32);
        put_le16(p + 110, s->wwn >> 16);
        put_le16(p + 111, s->wwn);
    }
    if (dev && dev->conf.discard_granularity) {
        put_le16(p + 169, 1); /* TRIM support */
    }

    ide_identify_size(s);
    s->identify_set = 1;

fill_buffer:
    memcpy(s->io_buffer, p, sizeof(s->identify_data));
}


void pci_ide_create_devs(PCIDevice *dev, DriveInfo **hd_table)
{
    PCIIDEState *d = PCI_IDE(dev);
    static const int bus[4]  = { 0, 0, 1, 1 };
    static const int unit[4] = { 0, 1, 0, 1 };
    int i;

    for (i = 0; i < 4; i++) {
        if (hd_table[i] == NULL)
            continue;
        ide_create_drive(d->bus+bus[i], unit[i], hd_table[i]);
    }
}


void ide_dma_error(IDEState *s)
{
    dma_buf_commit(s, 0);
    ide_abort_command(s);
    ide_set_inactive(s, false);
    ide_set_irq(s->bus);
}


static void map_page(AddressSpace *as, uint8_t **ptr, uint64_t addr,
                     uint32_t wanted)
{
    hwaddr len = wanted;

    if (*ptr) {
        dma_memory_unmap(as, *ptr, len, DMA_DIRECTION_FROM_DEVICE, len);
    }

    *ptr = dma_memory_map(as, addr, &len, DMA_DIRECTION_FROM_DEVICE);
    if (len < wanted) {
        dma_memory_unmap(as, *ptr, len, DMA_DIRECTION_FROM_DEVICE, len);
        *ptr = NULL;
    }
}


static void ide_sector_start_dma(IDEState *s, enum ide_dma_cmd dma_cmd)
{
    s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT;
    s->io_buffer_index = 0;
    s->io_buffer_size = 0;
    s->dma_cmd = dma_cmd;

    switch (dma_cmd) {
    case IDE_DMA_READ:
        block_acct_start(blk_get_stats(s->blk), &s->acct,
                         s->nsector * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
        break;
    case IDE_DMA_WRITE:
        block_acct_start(blk_get_stats(s->blk), &s->acct,
                         s->nsector * BDRV_SECTOR_SIZE, BLOCK_ACCT_WRITE);
        break;
    default:
        break;
    }

    ide_start_dma(s, ide_dma_cb);
}


void test_getpwnam_r_wrapper(void **state)
{
    int ret;
    struct passwd pwd;
    char *buf;
    size_t buf_len;

    ret = get_buffer(&buf_len, &buf);
    assert_int_equal(ret, 0);

    ret = getpwnam_r_wrapper(MAX_BUF, "non_exisiting_user", &pwd, &buf,
                             &buf_len);
    assert_int_equal(ret, ENOENT);

    ret = getpwnam_r_wrapper(MAX_BUF, "user", &pwd, &buf, &buf_len);
    assert_int_equal(ret, 0);
    assert_string_equal(pwd.pw_name, "user");
    assert_string_equal(pwd.pw_passwd, "x");
    assert_int_equal(pwd.pw_uid, 12345);
    assert_int_equal(pwd.pw_gid, 23456);
    assert_string_equal(pwd.pw_gecos, "gecos");
    assert_string_equal(pwd.pw_dir, "/home/user");
    assert_string_equal(pwd.pw_shell, "/bin/shell");
    free(buf);

    ret = get_buffer(&buf_len, &buf);
    assert_int_equal(ret, 0);

    ret = getpwnam_r_wrapper(MAX_BUF, "user_big", &pwd, &buf, &buf_len);
    assert_int_equal(ret, 0);
    assert_string_equal(pwd.pw_name, "user_big");
    assert_string_equal(pwd.pw_passwd, "x");
    assert_int_equal(pwd.pw_uid, 12346);
    assert_int_equal(pwd.pw_gid, 23457);
    assert_int_equal(strlen(pwd.pw_gecos), 4000 * strlen("gecos"));
    assert_string_equal(pwd.pw_dir, "/home/user_big");
    assert_string_equal(pwd.pw_shell, "/bin/shell");
    free(buf);

    ret = get_buffer(&buf_len, &buf);
    assert_int_equal(ret, 0);

    ret = getpwnam_r_wrapper(1024, "user_big", &pwd, &buf, &buf_len);
    assert_int_equal(ret, ERANGE);
    free(buf);
}


static int handle_sid_request(struct ipa_extdom_ctx *ctx,
                              enum request_types request_type, const char *sid,
                              struct berval **berval)
{
    int ret;
    struct passwd pwd;
    struct group grp;
    char *domain_name = NULL;
    char *fq_name = NULL;
    char *object_name = NULL;
    char *sep;
    size_t buf_len;
    char *buf = NULL;
    enum sss_id_type id_type;
    struct sss_nss_kv *kv_list = NULL;

    ret = sss_nss_getnamebysid(sid, &fq_name, &id_type);
    if (ret != 0) {
        if (ret == ENOENT) {
            ret = LDAP_NO_SUCH_OBJECT;
        } else {
            ret = LDAP_OPERATIONS_ERROR;
        }
        goto done;
    }

    sep = strchr(fq_name, SSSD_DOMAIN_SEPARATOR);
    if (sep == NULL) {
        ret = LDAP_OPERATIONS_ERROR;
        goto done;
    }

    object_name = strndup(fq_name, (sep - fq_name));
    domain_name = strdup(sep + 1);
    if (object_name == NULL || domain_name == NULL) {
        ret = LDAP_OPERATIONS_ERROR;
        goto done;
    }

    if (request_type == REQ_SIMPLE) {
        ret = pack_ber_name(domain_name, object_name, berval);
        goto done;
    }

    ret = get_buffer(&buf_len, &buf);
    if (ret != LDAP_SUCCESS) {
        goto done;
    }

    switch(id_type) {
    case SSS_ID_TYPE_UID:
    case SSS_ID_TYPE_BOTH:
        ret = getpwnam_r_wrapper(ctx->max_nss_buf_size, fq_name, &pwd, &buf,
                                 &buf_len);
        if (ret != 0) {
            if (ret == ENOMEM || ret == ERANGE) {
                ret = LDAP_OPERATIONS_ERROR;
            } else {
                ret = LDAP_NO_SUCH_OBJECT;
            }
            goto done;
        }

        if (request_type == REQ_FULL_WITH_GROUPS) {
            ret = sss_nss_getorigbyname(pwd.pw_name, &kv_list, &id_type);
            if (ret != 0 || !(id_type == SSS_ID_TYPE_UID
                                || id_type == SSS_ID_TYPE_BOTH)) {
                if (ret == ENOENT) {
                    ret = LDAP_NO_SUCH_OBJECT;
                } else {
                    ret = LDAP_OPERATIONS_ERROR;
                }
                goto done;
            }
        }

        ret = pack_ber_user(ctx,
                            (request_type == REQ_FULL ? RESP_USER
                                                      : RESP_USER_GROUPLIST),
                            domain_name, pwd.pw_name, pwd.pw_uid,
                            pwd.pw_gid, pwd.pw_gecos, pwd.pw_dir,
                            pwd.pw_shell, kv_list, berval);
        break;
    case SSS_ID_TYPE_GID:
        ret = getgrnam_r_wrapper(ctx->max_nss_buf_size, fq_name, &grp, &buf,
                                 &buf_len);
        if (ret != 0) {
            if (ret == ENOMEM || ret == ERANGE) {
                ret = LDAP_OPERATIONS_ERROR;
            } else {
                ret = LDAP_NO_SUCH_OBJECT;
            }
            goto done;
        }

        if (request_type == REQ_FULL_WITH_GROUPS) {
            ret = sss_nss_getorigbyname(grp.gr_name, &kv_list, &id_type);
            if (ret != 0 || !(id_type == SSS_ID_TYPE_GID
                                || id_type == SSS_ID_TYPE_BOTH)) {
                if (ret == ENOENT) {
                    ret = LDAP_NO_SUCH_OBJECT;
                } else {
                    ret = LDAP_OPERATIONS_ERROR;
                }
                goto done;
            }
        }

        ret = pack_ber_group((request_type == REQ_FULL ? RESP_GROUP
                                                       : RESP_GROUP_MEMBERS),
                             domain_name, grp.gr_name, grp.gr_gid,
                             grp.gr_mem, kv_list, berval);
        break;
    default:
        ret = LDAP_OPERATIONS_ERROR;
        goto done;
    }

done:
    sss_nss_free_kv(kv_list);
    free(fq_name);
    free(object_name);
    free(domain_name);
    free(buf);

    return ret;
}


_asn1_delete_list_and_nodes (void)
{
  list_type *listElement;

  while (firstElement)
    {
      listElement = firstElement;
      firstElement = firstElement->next;
      _asn1_remove_node (listElement->node, 0);
      free (listElement);
    }
}


_asn1_set_value_m (asn1_node node, void *value, unsigned int len)
{
  if (node == NULL)
    return node;

  if (node->value)
    {
      if (node->value != node->small_value)
	free (node->value);
      node->value = NULL;
      node->value_len = 0;
    }

  if (!len)
    return node;

  node->value = value;
  node->value_len = len;

  return node;
}


_asn1_get_last_right (asn1_node node)
{
  asn1_node p;

  if (node == NULL)
    return NULL;
  p = node;
  while (p->right)
    p = p->right;
  return p;
}


static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops)
{
	while (elements-- > 0) {
		zval key, *data, d, *old_data;
		zend_ulong idx;

		ZVAL_UNDEF(&key);

		if (!php_var_unserialize_ex(&key, p, max, NULL, classes)) {
			zval_dtor(&key);
			return 0;
		}

		data = NULL;
		ZVAL_UNDEF(&d);

		if (!objprops) {
			if (Z_TYPE(key) == IS_LONG) {
				idx = Z_LVAL(key);
numeric_key:
				if (UNEXPECTED((old_data = zend_hash_index_find(ht, idx)) != NULL)) {
					//??? update hash
					var_push_dtor(var_hash, old_data);
					data = zend_hash_index_update(ht, idx, &d);
				} else {
					data = zend_hash_index_add_new(ht, idx, &d);
				}
			} else if (Z_TYPE(key) == IS_STRING) {
				if (UNEXPECTED(ZEND_HANDLE_NUMERIC(Z_STR(key), idx))) {
					goto numeric_key;
				}
				if (UNEXPECTED((old_data = zend_hash_find(ht, Z_STR(key))) != NULL)) {
					//??? update hash
					var_push_dtor(var_hash, old_data);
					data = zend_hash_update(ht, Z_STR(key), &d);
				} else {
					data = zend_hash_add_new(ht, Z_STR(key), &d);
				}
			} else {
				zval_dtor(&key);
				return 0;
			}
		} else {
			if (EXPECTED(Z_TYPE(key) == IS_STRING)) {
string_key:
				if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) {
					if (Z_TYPE_P(old_data) == IS_INDIRECT) {
						old_data = Z_INDIRECT_P(old_data);
					}
					var_push_dtor(var_hash, old_data);
					data = zend_hash_update_ind(ht, Z_STR(key), &d);
				} else {
					data = zend_hash_add_new(ht, Z_STR(key), &d);
				}
			} else if (Z_TYPE(key) == IS_LONG) {
				/* object properties should include no integers */
				convert_to_string(&key);
				goto string_key;
			} else {
				zval_dtor(&key);
				return 0;
			}
		}

		zval_dtor(&key);

		if (!php_var_unserialize_ex(data, p, max, var_hash, classes)) {
			return 0;
		}

		var_push_dtor(var_hash, data);

		if (elements && *(*p-1) != ';' && *(*p-1) != '}') {
			(*p)--;
			return 0;
		}
	}

	return 1;
}


static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
{
	zend_long datalen;

	datalen = parse_iv2((*p) + 2, p);

	(*p) += 2;

	if (datalen < 0 || (max - (*p)) <= datalen) {
		zend_error(E_WARNING, "Insufficient data for unserializing - %pd required, %pd present", datalen, (zend_long)(max - (*p)));
		return 0;
	}

	if (ce->unserialize == NULL) {
		zend_error(E_WARNING, "Class %s has no unserializer", ce->name->val);
		object_init_ex(rval, ce);
	} else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash) != SUCCESS) {
		return 0;
	}

	(*p) += datalen;

	return finish_nested_data(UNSERIALIZE_PASSTHRU);
}


parse_footnote_def(struct buf *ob, struct sd_markdown *rndr, unsigned int num, uint8_t *data, size_t size)
{
	struct buf *work = 0;
	work = rndr_newbuf(rndr, BUFFER_SPAN);

	parse_block(work, rndr, data, size);

	if (rndr->cb.footnote_def)
	rndr->cb.footnote_def(ob, work, num, rndr->opaque);
	rndr_popbuf(rndr, BUFFER_SPAN);
}


free_footnote_ref(struct footnote_ref *ref)
{
	bufrelease(ref->contents);
	free(ref);
}


parse_table_row(
	struct buf *ob,
	struct sd_markdown *rndr,
	uint8_t *data,
	size_t size,
	size_t columns,
	int *col_data,
	int header_flag)
{
	size_t i = 0, col;
	struct buf *row_work = 0;

	if (!rndr->cb.table_cell || !rndr->cb.table_row)
		return;

	row_work = rndr_newbuf(rndr, BUFFER_SPAN);

	if (i < size && data[i] == '|')
		i++;

	for (col = 0; col < columns && i < size; ++col) {
		size_t cell_start, cell_end;
		struct buf *cell_work;

		cell_work = rndr_newbuf(rndr, BUFFER_SPAN);

		while (i < size && _isspace(data[i]))
			i++;

		cell_start = i;

		while (i < size && data[i] != '|')
			i++;

		cell_end = i - 1;

		while (cell_end > cell_start && _isspace(data[cell_end]))
			cell_end--;

		parse_inline(cell_work, rndr, data + cell_start, 1 + cell_end - cell_start);
		rndr->cb.table_cell(row_work, cell_work, col_data[col] | header_flag, rndr->opaque);

		rndr_popbuf(rndr, BUFFER_SPAN);
		i++;
	}

	for (; col < columns; ++col) {
		struct buf empty_cell = { 0, 0, 0, 0 };
		rndr->cb.table_cell(row_work, &empty_cell, col_data[col] | header_flag, rndr->opaque);
	}

	rndr->cb.table_row(ob, row_work, rndr->opaque);

	rndr_popbuf(rndr, BUFFER_SPAN);
}


rndr_newbuf(struct sd_markdown *rndr, int type)
{
	static const size_t buf_size[2] = {256, 64};
	struct buf *work = NULL;
	struct stack *pool = &rndr->work_bufs[type];

	if (pool->size < pool->asize &&
		pool->item[pool->size] != NULL) {
		work = pool->item[pool->size++];
		work->size = 0;
	} else {
		work = bufnew(buf_size[type]);
		redcarpet_stack_push(pool, work);
	}

	return work;
}


std::string get_exe_dir()
{
#ifndef _WIN32
	char buf[1024];
	size_t path_size = readlink("/proc/self/exe", buf, 1024);
	if(path_size == static_cast<size_t>(-1))
		return std::string();
	buf[path_size] = 0;
	return std::string(dirname(buf));
#else
	return get_cwd();
#endif
}


const std::string &get_user_config_dir()
{
	if (user_config_dir.empty())
	{
#if defined(_X11) && !defined(PREFERENCES_DIR)
		char const *xdg_config = getenv("XDG_CONFIG_HOME");
		if (!xdg_config || xdg_config[0] == '\0') {
			xdg_config = getenv("HOME");
			if (!xdg_config) {
				user_config_dir = get_user_data_dir();
				return user_config_dir;
			}
			user_config_dir = xdg_config;
			user_config_dir += "/.config";
		} else user_config_dir = xdg_config;
		user_config_dir += "/wesnoth";
		create_directory_if_missing_recursive(user_config_dir);
#else
		user_config_dir = get_user_data_dir();
#endif
	}
	return user_config_dir;
}


std::string get_addon_campaigns_dir()
{
	const std::string dir_path = get_user_data_dir() + "/data/add-ons";
	return get_dir(dir_path);
}


std::string get_save_index_file()
{
	return get_user_data_dir() + "/save_index.gz";
}


std::string normalize_path(const std::string &p1)
{
	if (p1.empty()) return p1;

	std::string p2;
#ifdef _WIN32
	if (p1.size() >= 2 && p1[1] == ':')
		// Windows relative paths with explicit drive name are not handled.
		p2 = p1;
	else
#endif
	if (!is_path_sep(p1[0]))
		p2 = get_cwd() + "/" + p1;
	else
		p2 = p1;

#ifdef _WIN32
	std::string drive;
	if (p2.size() >= 2 && p2[1] == ':') {
		drive = p2.substr(0, 2);
		p2.erase(0, 2);
	}
#endif

	std::vector<std::string> components(1);
	for (int i = 0, i_end = p2.size(); i <= i_end; ++i)
	{
		std::string &last = components[components.size() - 1];
		char c = p2.c_str()[i];
		if (is_path_sep(c) || c == 0)
		{
			if (last == ".")
				last.clear();
			else if (last == "..")
			{
				if (components.size() >= 2) {
					components.pop_back();
					components[components.size() - 1].clear();
				} else
					last.clear();
			}
			else if (!last.empty())
				components.push_back(std::string());
		}
		else
			last += c;
	}

	std::ostringstream p4;
	components.pop_back();

#ifdef _WIN32
	p4 << drive;
#endif

	BOOST_FOREACH(const std::string &s, components)
	{
		p4 << '/' << s;
	}

	DBG_FS << "Normalizing '" << p2 << "' to '" << p4.str() << "'\n";

	return p4.str();
}


static void get_file_tree_checksum_internal(const std::string& path, file_tree_checksum& res)
{

	std::vector<std::string> dirs;
	get_files_in_dir(path,NULL,&dirs, ENTIRE_FILE_PATH, SKIP_MEDIA_DIR, DONT_REORDER, &res);
	loadscreen::increment_progress();

	for(std::vector<std::string>::const_iterator j = dirs.begin(); j != dirs.end(); ++j) {
		get_file_tree_checksum_internal(*j,res);
	}
}


file_tree_checksum::file_tree_checksum()
	: nfiles(0), sum_size(0), modified(0)
{}


xmlBufDetach(xmlBufPtr buf) {
    xmlChar *ret;

    if (buf == NULL)
        return(NULL);
    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE)
        return(NULL);
    if (buf->buffer != NULL)
        return(NULL);
    if (buf->error)
        return(NULL);

    ret = buf->content;
    buf->content = NULL;
    buf->size = 0;
    buf->use = 0;
    buf->compat_use = 0;
    buf->compat_size = 0;

    return ret;
}


xmlTextReaderReadOuterXml(xmlTextReaderPtr reader ATTRIBUTE_UNUSED)
{
    xmlChar *resbuf;
    xmlNodePtr node;
    xmlBufferPtr buff;
    xmlDocPtr doc;

    node = reader->node;
    doc = reader->doc;
    if (xmlTextReaderExpand(reader) == NULL) {
        return NULL;
    }
	if (node->type == XML_DTD_NODE) {
		node = (xmlNodePtr) xmlCopyDtd((xmlDtdPtr) node);
	} else {
		node = xmlDocCopyNode(node, doc, 1);
	}
    buff = xmlBufferCreate();
    if (xmlNodeDump(buff, doc, node, 0, 0) == -1) {
        xmlFreeNode(node);
        xmlBufferFree(buff);
        return NULL;
    }

    resbuf = buff->content;
    buff->content = NULL;

    xmlFreeNode(node);
    xmlBufferFree(buff);
    return resbuf;
}


xmlBase64Decode(const unsigned char *in, unsigned long *inlen,
                unsigned char *to, unsigned long *tolen)
{
    unsigned long incur;        /* current index in in[] */

    unsigned long inblk;        /* last block index in in[] */

    unsigned long outcur;       /* current index in out[] */

    unsigned long inmax;        /* size of in[] */

    unsigned long outmax;       /* size of out[] */

    unsigned char cur;          /* the current value read from in[] */

    unsigned char intmp[4], outtmp[4];  /* temporary buffers for the convert */

    int nbintmp;                /* number of byte in intmp[] */

    int is_ignore;              /* cur should be ignored */

    int is_end = 0;             /* the end of the base64 was found */

    int retval = 1;

    int i;

    if ((in == NULL) || (inlen == NULL) || (to == NULL) || (tolen == NULL))
        return (-1);

    incur = 0;
    inblk = 0;
    outcur = 0;
    inmax = *inlen;
    outmax = *tolen;
    nbintmp = 0;

    while (1) {
        if (incur >= inmax)
            break;
        cur = in[incur++];
        is_ignore = 0;
        if ((cur >= 'A') && (cur <= 'Z'))
            cur = cur - 'A';
        else if ((cur >= 'a') && (cur <= 'z'))
            cur = cur - 'a' + 26;
        else if ((cur >= '0') && (cur <= '9'))
            cur = cur - '0' + 52;
        else if (cur == '+')
            cur = 62;
        else if (cur == '/')
            cur = 63;
        else if (cur == '.')
            cur = 0;
        else if (cur == '=')    /*no op , end of the base64 stream */
            is_end = 1;
        else {
            is_ignore = 1;
            if (nbintmp == 0)
                inblk = incur;
        }

        if (!is_ignore) {
            int nbouttmp = 3;

            int is_break = 0;

            if (is_end) {
                if (nbintmp == 0)
                    break;
                if ((nbintmp == 1) || (nbintmp == 2))
                    nbouttmp = 1;
                else
                    nbouttmp = 2;
                nbintmp = 3;
                is_break = 1;
            }
            intmp[nbintmp++] = cur;
            /*
             * if intmp is full, push the 4byte sequence as a 3 byte
             * sequence out
             */
            if (nbintmp == 4) {
                nbintmp = 0;
                outtmp[0] = (intmp[0] << 2) | ((intmp[1] & 0x30) >> 4);
                outtmp[1] =
                    ((intmp[1] & 0x0F) << 4) | ((intmp[2] & 0x3C) >> 2);
                outtmp[2] = ((intmp[2] & 0x03) << 6) | (intmp[3] & 0x3F);
                if (outcur + 3 >= outmax) {
                    retval = 2;
                    break;
                }

                for (i = 0; i < nbouttmp; i++)
                    to[outcur++] = outtmp[i];
                inblk = incur;
            }

            if (is_break) {
                retval = 0;
                break;
            }
        }
    }

    *tolen = outcur;
    *inlen = inblk;
    return (retval);
}


xmlBufInflate(xmlBufPtr buf, size_t len) {
    if (buf == NULL) return(-1);
    xmlBufGrowInternal(buf, len + buf->size);
    if (buf->error)
        return(-1);
    return(0);
}


xmlTextReaderStructuredError(void *ctxt, xmlErrorPtr error)
{
    xmlParserCtxtPtr ctx = (xmlParserCtxtPtr) ctxt;

    xmlTextReaderPtr reader = (xmlTextReaderPtr) ctx->_private;

    if (error && reader->sErrorFunc) {
        reader->sErrorFunc(reader->errorFuncArg, (xmlErrorPtr) error);
    }
}


xmlTextReaderConstBaseUri(xmlTextReaderPtr reader) {
    xmlChar *tmp;
    const xmlChar *ret;

    if ((reader == NULL) || (reader->node == NULL))
	return(NULL);
    tmp = xmlNodeGetBase(NULL, reader->node);
    if (tmp == NULL)
        return(NULL);
    ret = CONSTSTR(tmp);
    xmlFree(tmp);
    return(ret);
}


xmlTextReaderError(void *ctxt, const char *msg, ...)
{
    va_list ap;

    va_start(ap, msg);
    xmlTextReaderGenericError(ctxt,
                              XML_PARSER_SEVERITY_ERROR,
                              xmlTextReaderBuildMessage(msg, ap));
    va_end(ap);

}