Ejemplo n.º 1
0
static int put(void)
{
    res = ne_concat(i_path, "lockme", NULL);
    res2 = ne_concat(i_path, "notlocked", NULL);
    
    CALL(upload_foo("lockme"));
    CALL(upload_foo("notlocked"));    

    return OK;
}
Ejemplo n.º 2
0
static int put(void)
{
    res = ne_concat(i_path, "lockme", NULL);
    res2 = ne_concat(i_path, "notlocked", NULL);
    //res3 = ne_concat(i_path,"not-existing",NULL);

    CALL(upload_foo("lockme"));
    CALL(upload("notlocked", "./htdocs/bar"));    

    return OK;
}
Ejemplo n.º 3
0
static int copy_init(void)
{
    src = ne_concat(i_path, "copysrc", NULL);
    dest = ne_concat(i_path, "copydest", NULL);
    coll = ne_concat(i_path, "copycoll/", NULL);
    ncoll = ne_concat(i_path, "copycoll", NULL);

    CALL(upload_foo("copysrc"));
    ONNREQ("could not create collection", ne_mkcol(i_session, coll));

    copy_ok = 1;
    return OK;
}
Ejemplo n.º 4
0
static int lock_collection(void)
{
    struct ne_lock dummy;
    char *tmp,*tmp2;
    
    CALL(getlock(ne_lockscope_exclusive, NE_DEPTH_INFINITE));
    
    PRECOND(gotlock);

    memcpy(&dummy, &reslock, sizeof(reslock));
    dummy.token = NULL;
    dummy.uri.path = collZ;
    dummy.owner = ne_strdup("litmus: owner lock");
    dummy.depth = NE_DEPTH_INFINITE;
    dummy.type = ne_locktype_write;
    dummy.scope = ne_lockscope_exclusive;
    
    ONNREQ2("LOCK on second collection for further tests", 
	    ne_lock(i_session, &dummy));
    gotlock = ne_lock_copy(&dummy);
    ne_lockstore_add(store, gotlock);

    /* Testing creation of directories under a collection */ 
    ONV(ne_mkcol(i_session, collX),
        ("MKCOL %s: %s", collX, ne_get_error(i_session)));
    ONV(ne_mkcol(i_session, collY),
        ("MKCOL %s: %s", collY, ne_get_error(i_session)));

    upload_foo("lockcoll/collX/temp");
    tmp = ne_concat(collY,"copy-temp",NULL);	
    ONV(ne_copy(i_session, 0, NE_DEPTH_INFINITE, res3,tmp),
	("collection COPY `%s' to `%s': %s", res2, tmp,
	 ne_get_error(i_session)));
    free(tmp);
    
    tmp2=ne_concat(collZ,"testing",NULL);
    ONV(ne_copy(i_session, 0, NE_DEPTH_INFINITE, res3,tmp2),
	("collection COPY `%s' to `%s': %s", res2, tmp2,
	 ne_get_error(i_session)));

    ne_free(res3);	
    res3 = ne_concat(i_path,"not-existing",NULL);

 
    /* change res to point to a normal resource for subsequent
     * {not_,}owner_modify tests */
    res = ne_concat(coll, "lockme.txt", NULL);
    return upload_foo("lockcoll/lockme.txt");
}
Ejemplo n.º 5
0
static int move(void)
{
    char *src2;

    src = ne_concat(i_path, "move", NULL);
    src2 = ne_concat(i_path, "move2", NULL);
    dest = ne_concat(i_path, "movedest", NULL);
    coll = ne_concat(i_path, "movecoll/", NULL);
    ncoll = ne_concat(i_path, "movecoll", NULL);

    /* Upload it twice. */
    CALL(upload_foo("move"));
    CALL(upload_foo("move2"));
    ONMREQ("MKCOL", coll, ne_mkcol(i_session, coll));

    /* Now move it */
    ONM2REQ("MOVE", src, dest, ne_move(i_session, 0, src, dest));

    if (STATUS(201)) {
	t_warning("MOVE to new resource didn't give 201");
    }

    /* Try a move with Overwrite: F to check that fails. */
    ONM2REQ("MOVE on existing resource with Overwrite: F succeeded",
	    src2, dest, 
	    ne_move(i_session, 0, src2, dest) != NE_ERROR);

    if (STATUS(412)) {
	t_warning("MOVE-on-existing should fail with 412");
    }

    ONM2REQ("MOVE onto existing resource with Overwrite: T",
	    src2, dest,
	    ne_move(i_session, 1, src2, dest));

    ONM2REQ("MOVE overwrites collection", coll, dest,
	    ne_move(i_session, 1, dest, coll));
    
    if (STATUS(204)) {
	t_warning("MOVE to existing collection resource didn't give 204");
    }
		  
    if (ne_delete(i_session, ncoll)) {
	t_warning("Could not clean up `%s'", ncoll);
    }

    return OK;
}
Ejemplo n.º 6
0
static void init_options(void)
{
    char *lockowner, *tmp;
    char *user = getenv("USER"), *hostname = getenv("HOSTNAME");
    
    if (user && hostname) {
	/* set this here so they can override it */
	lockowner = ne_concat("mailto:", user, "@", hostname, NULL);
	set_option(opt_lockowner, lockowner);
    } else {
	set_option(opt_lockowner, NULL);
    }

    set_option(opt_editor, NULL);
    set_option(opt_namespace, ne_strdup(DEFAULT_NAMESPACE));
    set_bool_option(opt_overwrite, 1);
    set_bool_option(opt_quiet, 1);
    set_bool_option(opt_searchall, 1);
    lockdepth = NE_DEPTH_INFINITE;
    lockscope = ne_lockscope_exclusive;
    searchdepth = NE_DEPTH_INFINITE;

    /* This is what Markus Kahn says we should do. */
    if ((tmp = getenv("LC_ALL")) ||
	(tmp = getenv("LC_CTYPE")) ||
	(tmp = getenv("LANG"))) {
	if (strstr(tmp, "UTF-8")) {
	    int val = 1;
	    set_option(opt_utf8, &val);
	}
    }

}
Ejemplo n.º 7
0
static void init_netrc(void)
{
#ifdef ENABLE_NETRC
    char *netrc = ne_concat(getenv("HOME"), "/.netrc", NULL);
    netrc_list = parse_netrc(netrc);
#endif
}
Ejemplo n.º 8
0
static int owner_modify(void)
{
    char *tmp;
    ne_propname pname = { "http://webdav.org/neon/litmus/", "random" };
    ne_proppatch_operation pops[] = { 
	{ NULL, ne_propset, "foobar" },
	{ NULL }
    };
    PRECOND(gotlock);

    ONV(ne_put(i_session, res, i_foo_fd),
	("PUT on locked resource failed: %s", ne_get_error(i_session)));
    
    tmp = ne_concat(i_path, "whocares", NULL);
    ONN("COPY of locked resource", 
	ne_copy(i_session, 1, NE_DEPTH_ZERO, res, tmp) == NE_ERROR);
    
   if (STATUS(201))
	t_warning("COPY failed with %d not 201", GETSTATUS);
    
    ONN("DELETE of locked resource by owner", 
	ne_delete(i_session, tmp) == NE_ERROR);

    if (STATUS(204)) 
	t_warning("DELETE of %s failed with %d not 200", tmp, GETSTATUS);
    free(tmp);
    
    ONN("PROPPATCH of locked resource",
	ne_proppatch(i_session, res, pops) == NE_ERROR);
    
    if (STATUS(207))
	t_warning("PROPPATCH failed with %d", GETSTATUS);

    return OK;
}
Ejemplo n.º 9
0
/* Add GSSAPI authentication credentials to a request */
static char *request_gssapi(auth_session *sess) 
{
    if (sess->gssapi_token) 
        return ne_concat("Negotiate ", sess->gssapi_token, "\r\n", NULL);
    else
        return NULL;
}
Ejemplo n.º 10
0
int ne_mkcol(ne_session *sess, const char *uri) 
{
    ne_request *req;
    char *real_uri;
    int ret;

    if (ne_path_has_trailing_slash(uri)) {
	real_uri = ne_strdup(uri);
    } else {
	real_uri = ne_concat(uri, "/", NULL);
    }

    req = ne_request_create(sess, "MKCOL", real_uri);

#ifdef NE_HAVE_DAV
    ne_lock_using_resource(req, real_uri, 0);
    ne_lock_using_parent(req, real_uri);
#endif
    
    ret = ne_simple_request(sess, req);

    ne_free(real_uri);

    return ret;
}
Ejemplo n.º 11
0
/* check that locks don't follow copies. */
static int copy(void)
{
    char *dest;
    int count = 0;
    
    PRECOND(gotlock);

    dest = ne_concat(res, "-copydest", NULL);

    ne_delete(i_session2, dest);

    ONNREQ2("could not COPY locked resource",
	    ne_copy(i_session2, 1, NE_DEPTH_ZERO, res, dest));
    
    ONNREQ2("LOCK discovery failed",
	    ne_lock_discover(i_session2, dest, count_discover, &count));
    
    ONV(count != 0,
	("found %d locks on copied resource", count));

    ONNREQ2("could not delete copy of locked resource",
	    ne_delete(i_session2, dest));

    free(dest);

    return OK;
}
Ejemplo n.º 12
0
static int init_ssl(void)
{
    char *server_key;
    ne_ssl_certificate *cert;
 
    /* take srcdir as argv[1]. */
    if (test_argc > 1) {
	server_key = ne_concat(test_argv[1], "/server.key", NULL);
    } else {
	server_key = ne_strdup("server.key");
    }

    ONN("sock_init failed", ne_sock_init());
    server_ctx = ne_ssl_context_create(1);
    ONN("SSL_CTX_new failed", server_ctx == NULL);

    ne_ssl_context_keypair(server_ctx, "server.cert", server_key);

    client_ctx = ne_ssl_context_create(0);
    ONN("SSL_CTX_new failed for client", client_ctx == NULL);
    
    cert = ne_ssl_cert_read("ca/cert.pem");
    ONN("could not load ca/cert.pem", cert == NULL);

    ne_ssl_context_trustcert(client_ctx, cert);
    ne_free(server_key);

    return OK;
}
Ejemplo n.º 13
0
/*
 * Create a context to authenticate to specified server, using either
 * ntlm or negotiate.
 */
int ne_sspi_create_context(void **context, char *serverName, int ntlm)
{
    SSPIContext *sspiContext;

    if (initialized <= 0) {
        return -1;
    }

    sspiContext = ne_calloc(sizeof(SSPIContext));
    sspiContext->continueNeeded = 0;

    if (ntlm) {
        sspiContext->mechanism = "NTLM";
        sspiContext->serverName = ne_strdup(serverName);
        sspiContext->maxTokenSize = ntlmMaxTokenSize;
    } else {
        sspiContext->mechanism = "Negotiate";
        sspiContext->serverName = ne_concat("HTTP/", serverName, NULL);
        sspiContext->maxTokenSize = negotiateMaxTokenSize;
    }

    sspiContext->ntlm = ntlm;
    sspiContext->authfinished = 0;
    *context = sspiContext;
    return 0;
}
Ejemplo n.º 14
0
/*
 * Create a context to authenticate to specified server, using either
 * ntlm or negotiate.
 */
int ne_sspi_create_context(void **context, char *serverName, int ntlm)
{
    SSPIContext *sspiContext;
    char *canonicalName;

    if (initialized <= 0) {
        return -1;
    }

    sspiContext = ne_calloc(sizeof(SSPIContext));
    sspiContext->continueNeeded = 0;

    if (ntlm) {
        sspiContext->mechanism = "NTLM";
        sspiContext->serverName = ne_strdup(serverName);
        sspiContext->maxTokenSize = ntlmMaxTokenSize;
    } else {
        sspiContext->mechanism = "Negotiate";
        /* Canonicalize to conform to GSSAPI behavior */
        canonicalName = canonical_hostname(serverName);
        sspiContext->serverName = ne_concat("HTTP/", canonicalName, NULL);
        ne_free(canonicalName);
        NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Created context with SPN '%s'\n",
                 sspiContext->serverName);
        sspiContext->maxTokenSize = negotiateMaxTokenSize;
    }

    sspiContext->ntlm = ntlm;
    sspiContext->authfinished = 0;
    *context = sspiContext;
    return 0;
}
Ejemplo n.º 15
0
/* Examine a Basic auth challenge.
 * Returns 0 if an valid challenge, else non-zero. */
static int basic_challenge(auth_session *sess, struct auth_challenge *parms) 
{
    char *tmp, password[NE_ABUFSIZ];

    /* Verify challenge... must have a realm */
    if (parms->realm == NULL) {
	return -1;
    }

    NE_DEBUG(NE_DBG_HTTPAUTH, "Got Basic challenge with realm [%s]\n", 
	     parms->realm);

    clean_session(sess);
    
    sess->realm = ne_strdup(parms->realm);

    if (get_credentials(sess, password)) {
	/* Failed to get credentials */
	return -1;
    }

    sess->scheme = auth_scheme_basic;

    tmp = ne_concat(sess->username, ":", password, NULL);
    sess->basic = ne_base64((unsigned char *)tmp, strlen(tmp));
    ne_free(tmp);

    /* Paranoia. */
    memset(password, 0, sizeof password);

    return 0;
}
Ejemplo n.º 16
0
static int init_largefile(void)
{
    int n;

#ifndef NE_LFS
    if (sizeof(off_t) == 4) {
        t_context("32-bit off_t and no LFS support detected "
                  "=> cannot run tests!");
        return SKIPREST;
    }
#endif    

    for (n = 0; n < BLOCKSIZE; n++)
        block[n] = n % 256;

    /* upload a random file to prep auth if necessary. */
    CALL(upload_foo("random.txt"));

    path = ne_concat(i_path, "large.txt", NULL);

    /* don't log a message for each body block! */
    ne_debug_init(ne_debug_stream, ne_debug_mask & ~(NE_DBG_HTTPBODY|NE_DBG_HTTP));

    return OK;
}
Ejemplo n.º 17
0
static int lock_collection(void)
{
    CALL(getlock(ne_lockscope_exclusive, NE_DEPTH_INFINITE));
    /* change res to point to a normal resource for subsequent
     * {not_,}owner_modify tests */
    res = ne_concat(coll, "lockme.txt", NULL);
    return upload_foo("lockcoll/lockme.txt");
}
Ejemplo n.º 18
0
static char *request_sspi(auth_session *sess) 
{
    const char *mechanism;
    
    if (ne_sspi_get_mechanism(sess->sspi_context, &mechanism)) {
        return NULL;
    }

    return ne_concat(mechanism, " ", sess->sspi_token, "\r\n", NULL);
}
Ejemplo n.º 19
0
static int open_foo(void)
{
    char *foofn = ne_concat(htdocs_root, "/foo", NULL);
    i_foo_fd = open(foofn, O_RDONLY | O_BINARY);
    if (i_foo_fd < 0) {
	t_context("could not open %s: %s", foofn, strerror(errno));
	return FAILHARD;
    }
    return OK;
}
Ejemplo n.º 20
0
int upload_foo(const char *path)
{
    char *uri = ne_concat(i_path, path, NULL);
    int ret;
    /* i_foo_fd is rewound automagically by ne_request.c */
    ret = ne_put(i_session, uri, i_foo_fd);
    free(uri);
    if (ret)
	t_context("PUT of `%s': %s", uri, ne_get_error(i_session));
    return ret;
}
Ejemplo n.º 21
0
static int prep_collection(void)
{
    if (gotlock) {
        ne_lock_destroy(gotlock);
        gotlock = NULL;
    }
    ne_free(res);
    res = coll = ne_concat(i_path, "lockcoll/", NULL);
    ONV(ne_mkcol(i_session, res),
        ("MKCOL %s: %s", res, ne_get_error(i_session)));
    return OK;
}
Ejemplo n.º 22
0
static int copy_shallow(void)
{
    char *csrc, *cdest, *res;

    csrc = ne_concat(i_path, "ccsrc/", NULL);
    cdest = ne_concat(i_path, "ccdest/", NULL);

    /* Set up the ccsrc collection with one member */
    ONMREQ("MKCOL", csrc, ne_mkcol(i_session, csrc));
    CALL(upload_foo("ccsrc/foo"));

    /* Clean up to make some fresh copies. */
    ne_delete(i_session, cdest);

    /* Now copy with Depth 0 */
    ONV(ne_copy(i_session, 0, NE_DEPTH_ZERO, csrc, cdest),
	("collection COPY `%s' to `%s': %s", csrc, cdest,
	 ne_get_error(i_session)));

    /* Remove the source, to be paranoid. */
    if (ne_delete(i_session, csrc)) {
	t_warning("Could not delete csrc");
    }

    /* Now make sure the child resource hasn't been copied along with
     * the collection. */
    res = ne_concat(i_path, "foo", NULL);
    ne_delete(i_session, res);
    ONV(STATUS(404), 
        ("DELETE on `%s' should fail with 404: got %d", res, GETSTATUS));
    ne_free(res);

    if (ne_delete(i_session, cdest)) {
	t_warning("Could not clean up cdest");
    }

    return OK;
}
Ejemplo n.º 23
0
static int prep_collection(void)
{
    if (gotlock) {
        ne_lock_destroy(gotlock);
        gotlock = NULL;
    }
    ne_free(res);
    ne_free(res3);
    res = coll = ne_concat(i_path, "lockcoll/", NULL);
   
    /* Setting directories for further tests */
    collX = ne_concat(coll,"collX/",NULL);
    collY = ne_concat(coll,"collY/",NULL);
    res3 = ne_concat(collX,"temp",NULL);
    collZ = ne_concat(i_path, "lockcoll2/", NULL);
 
    ONV(ne_mkcol(i_session, res),
        ("MKCOL %s: %s", res, ne_get_error(i_session)));
    ONV(ne_mkcol(i_session, collZ),
        ("MKCOL %s: %s", collZ, ne_get_error(i_session)));

    return OK;
}
Ejemplo n.º 24
0
static int notowner_modify(void)
{
    char *tmp;
    ne_propname pname = { "http://webdav.org/neon/litmus/", "random" };
    ne_proppatch_operation pops[] = { 
	{ NULL, ne_propset, "foobar" },
	{ NULL }
    };

    PRECOND(gotlock);

    pops[0].name = &pname;

    ONN("DELETE of locked resource should fail", 
	ne_delete(i_session2, res) != NE_ERROR);

    if (STATUS2(423)) 
	t_warning("DELETE failed with %d not 423", GETSTATUS2);

    tmp = ne_concat(i_path, "whocares", NULL);
    ONN("MOVE of locked resource should fail", 
	ne_move(i_session2, 0, res, tmp) != NE_ERROR);
    free(tmp);
    
    if (STATUS2(423))
	t_warning("MOVE failed with %d not 423", GETSTATUS2);
    
    ONN("COPY onto locked resource should fail",
	ne_copy(i_session2, 1, NE_DEPTH_ZERO, res2, res) != NE_ERROR);

    if (STATUS2(423))
	t_warning("COPY failed with %d not 423", GETSTATUS2);

    ONN("PROPPATCH of locked resource should fail",
	ne_proppatch(i_session2, res, pops) != NE_ERROR);
    
    if (STATUS2(423))
	t_warning("PROPPATCH failed with %d not 423", GETSTATUS2);

    ONN("PUT on locked resource should fail",
	ne_put(i_session2, res, i_foo_fd) != NE_ERROR);

    if (STATUS2(423))
	t_warning("PUT failed with %d not 423", GETSTATUS2);

    return OK;    
}
Ejemplo n.º 25
0
/* Create an GSSAPI name for server HOSTNAME; returns non-zero on
 * error. */
static void get_gss_name(gss_name_t *server, const char *hostname)
{
    unsigned int major, minor;
    gss_buffer_desc token;

    token.value = ne_concat("HTTP@", hostname, NULL);
    token.length = strlen(token.value);

    major = gss_import_name(&minor, &token, GSS_C_NT_HOSTBASED_SERVICE,
                            server);
    ne_free(token.value);
    
    if (GSS_ERROR(major)) {
        NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: gss_import_name failed.\n");
        *server = GSS_C_NO_NAME;
    }
}
Ejemplo n.º 26
0
/* lock on unmapped url should return 201 */
static int unmapped_lock(void)
{
    if (gotlock) {
        ne_lock_destroy(gotlock);
        gotlock = NULL;
    }
    ne_free(res);

    res = ne_concat(i_path, "unmapped_url", NULL);

    ONV(getlock(ne_lockscope_exclusive, NE_DEPTH_ZERO),
        ("LOCK on %s via %s: %s",
         coll, res, ne_get_error(i_session)));

    if (STATUS(201)) 
	t_warning("LOCK on unmapped url returned %d not 201 (RFC4918:S7.3)", GETSTATUS);

    return OK;
}
Ejemplo n.º 27
0
/* This function directly implements the "Merge Paths" algorithm
 * described in RFC 3986 section 5.2.3. */
static char *merge_paths(const ne_uri *base, const char *path)
{
    const char *p;

    if (base->host && base->path[0] == '\0') {
        return ne_concat("/", path, NULL);
    }
    
    p = strrchr(base->path, '/');
    if (p == NULL) {
        return ne_strdup(path);
    } else {
        size_t len = p - base->path + 1;
        char *ret = ne_malloc(strlen(path) + len + 1);

        memcpy(ret, base->path, len);
        memcpy(ret + len, path, strlen(path) + 1);
        return ret;
    }
}
Ejemplo n.º 28
0
static int copy_nodestcoll(void)
{
    char *nodest = ne_concat(i_path, "nonesuch/foo", NULL);
    int ret;

    PRECOND(copy_ok);

    ret = ne_copy(i_session, 0, NE_DEPTH_ZERO, src, nodest);
    
    ONV(ret == NE_OK, 
        ("COPY into non-existant collection '%snonesuch' succeeded", i_path));

    if (STATUS(409)) {
        t_warning("COPY to non-existant collection '%snonesuch' gave '%s' not 409"
                  " (RFC2518:S8.8.5)",
                  i_path, ne_get_error(i_session));
    }

    ne_free(nodest);
    return OK;
}
Ejemplo n.º 29
0
int begin(void)
{
    const char *scheme = use_secure?"https":"http";
    char *space;
    static const char blanks[] = "          ";
    static const char stars[] = "**********************************";
    

    i_session = ne_session_create(scheme, i_hostname, i_port);
    CALL(init_session(i_session));
    ne_hook_pre_send(i_session, i_pre_send, "X-Prestan");


    space = ne_concat(i_path, "davtest/", NULL);
    ne_delete(i_session, space);
    if (ne_mkcol(i_session, space)) {
	t_context("Could not create new collection `%s' for tests: %s\n"
	  "Server must allow `MKCOL %s' for tests to proceed", 
	  space, ne_get_error(i_session), space);
	return FAILHARD;
    }
    free(i_path);
    i_path = space;    
    
    warmup();

    printf("\nStart Testing %s:\n\n",pget_option.URL) ;
    printf("\n%s%s\n", blanks, stars);
    printf("\n%s* Number of Requests\t\t%d\n", blanks, pget_option.requests);
    printf("\n%s* Number of Dead Properties\t%d\n", 
    			blanks, pget_option.numprops);
    printf("\n%s* Depth of Collection\t\t%d\n", blanks, pget_option.depth);
    printf("\n%s* Width of Collection\t\t%d\n", blanks, pget_option.width);
    printf("\n%s* Type of Methods\t\t%s\n", blanks, pget_option.methods);
    printf("\n%s%s\n", blanks, stars);
    printf("\n\n");
    
    return OK;
}
Ejemplo n.º 30
0
static void parse_args(int argc, char **argv)
{
    static const struct option opts[] = {
	{ "version", no_argument, NULL, 'V' },
	{ "help", no_argument, NULL, 'h' },
	{ "proxy", required_argument, NULL, 'p' },
	{ "tolerant", no_argument, NULL, 't' },
	{ "rcfile", required_argument, NULL, 'r' },
	{ 0, 0, 0, 0 }
    };
    int optc;
    while ((optc = getopt_long(argc, argv, "ehtp:r:V", opts, NULL)) != -1) {
	switch (optc) {
	case 'h': usage(); exit(-1);
	case 'V': execute_about(); exit(-1);
	case 'p': set_proxy(optarg); break;
	case 't': tolerant = 1; break;
	case 'r': rcfile = strdup(optarg); break;
	case '?': 
	default:
	    printf(_("Try `%s --help' for more information.\n"), progname);
	    exit(-1);
	}
    }
    if (optind == (argc-1)) {
	open_connection(argv[optind]);
#ifdef HAVE_ADD_HISTORY
	{ 
	    char *run_cmd;
	    run_cmd = ne_concat("open ", argv[optind], NULL);
	    add_history(run_cmd);
	    free(run_cmd);
	}
#endif
    } else if (optind < argc) {
	usage();
	exit(-1);
    }
}