static int _handle_bind(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) { const char *type; xmpp_stanza_t *iq, *session; /* delete missing bind handler */ xmpp_timed_handler_delete(conn, _handle_missing_bind); /* server has replied to bind request */ type = xmpp_stanza_get_type(stanza); if (type && strcmp(type, "error") == 0) { xmpp_error(conn->ctx, "xmpp", "Binding failed."); xmpp_disconnect(conn); } else if (type && strcmp(type, "result") == 0) { xmpp_stanza_t *binding = xmpp_stanza_get_child_by_name(stanza, "bind"); xmpp_debug(conn->ctx, "xmpp", "Bind successful."); if (binding) { xmpp_stanza_t *jid_stanza = xmpp_stanza_get_child_by_name(binding, "jid"); if (jid_stanza) { conn->bound_jid = xmpp_stanza_get_text(jid_stanza); } } /* establish a session if required */ if (conn->session_required) { /* setup response handlers */ handler_add_id(conn, _handle_session, "_xmpp_session1", NULL); handler_add_timed(conn, _handle_missing_session, SESSION_TIMEOUT, NULL); /* send session request */ iq = xmpp_stanza_new(conn->ctx); if (!iq) { disconnect_mem_error(conn); return 0; } xmpp_stanza_set_name(iq, "iq"); xmpp_stanza_set_type(iq, "set"); xmpp_stanza_set_id(iq, "_xmpp_session1"); session = xmpp_stanza_new(conn->ctx); if (!session) { xmpp_stanza_release(iq); disconnect_mem_error(conn); } xmpp_stanza_set_name(session, "session"); xmpp_stanza_set_ns(session, XMPP_NS_SESSION); xmpp_stanza_add_child(iq, session); xmpp_stanza_release(session); /* send session establishment request */ xmpp_send(conn, iq); xmpp_stanza_release(iq); } else { conn->authenticated = 1; /* call connection handler */ conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata); } } else { xmpp_error(conn->ctx, "xmpp", "Server sent malformed bind reply."); xmpp_disconnect(conn); } return 0; }
/* authenticate the connection * this may get called multiple times. if any auth method fails, * this will get called again until one auth method succeeds or every * method fails */ static void _auth(xmpp_conn_t * const conn) { xmpp_stanza_t *auth, *authdata, *query, *child, *iq; char *str, *authid; char *scram_init; int anonjid; /* if there is no node in conn->jid, we assume anonymous connect */ str = xmpp_jid_node(conn->ctx, conn->jid); if (str == NULL) { anonjid = 1; } else { xmpp_free(conn->ctx, str); anonjid = 0; } if (conn->tls_support) { tls_t *tls = tls_new(conn->ctx, conn->sock, conn->certfail_handler, conn->tls_cert_path); /* If we couldn't init tls, it isn't there, so go on */ if (!tls) { conn->tls_support = 0; _auth(conn); return; } else { tls_free(tls); } auth = _make_starttls(conn); if (!auth) { disconnect_mem_error(conn); return; } handler_add(conn, _handle_proceedtls_default, XMPP_NS_TLS, NULL, NULL, NULL); xmpp_send(conn, auth); xmpp_stanza_release(auth); /* TLS was tried, unset flag */ conn->tls_support = 0; /* _auth() will be called later */ return; } if (conn->tls_mandatory && !xmpp_conn_is_secured(conn)) { xmpp_error(conn->ctx, "xmpp", "TLS is not supported, but set as " "mandatory for this connection"); conn_disconnect(conn); return; } if (anonjid && conn->sasl_support & SASL_MASK_ANONYMOUS) { /* some crap here */ auth = _make_sasl_auth(conn, "ANONYMOUS"); if (!auth) { disconnect_mem_error(conn); return; } handler_add(conn, _handle_sasl_result, XMPP_NS_SASL, NULL, NULL, "ANONYMOUS"); xmpp_send(conn, auth); xmpp_stanza_release(auth); /* SASL ANONYMOUS was tried, unset flag */ conn->sasl_support &= ~SASL_MASK_ANONYMOUS; } else if (anonjid) { xmpp_error(conn->ctx, "auth", "No node in JID, and SASL ANONYMOUS unsupported."); xmpp_disconnect(conn); } else if (conn->sasl_support & SASL_MASK_SCRAMSHA1) { auth = _make_sasl_auth(conn, "SCRAM-SHA-1"); if (!auth) { disconnect_mem_error(conn); return; } /* don't free scram_init on success */ scram_init = _make_scram_sha1_init_msg(conn); if (!scram_init) { xmpp_stanza_release(auth); disconnect_mem_error(conn); return; } str = xmpp_base64_encode(conn->ctx, (unsigned char *)scram_init, strlen(scram_init)); if (!str) { xmpp_free(conn->ctx, scram_init); xmpp_stanza_release(auth); disconnect_mem_error(conn); return; } authdata = xmpp_stanza_new(conn->ctx); if (!authdata) { xmpp_free(conn->ctx, str); xmpp_free(conn->ctx, scram_init); xmpp_stanza_release(auth); disconnect_mem_error(conn); return; } xmpp_stanza_set_text(authdata, str); xmpp_free(conn->ctx, str); xmpp_stanza_add_child(auth, authdata); xmpp_stanza_release(authdata); handler_add(conn, _handle_scram_sha1_challenge, XMPP_NS_SASL, NULL, NULL, (void *)scram_init); xmpp_send(conn, auth); xmpp_stanza_release(auth); /* SASL SCRAM-SHA-1 was tried, unset flag */ conn->sasl_support &= ~SASL_MASK_SCRAMSHA1; } else if (conn->sasl_support & SASL_MASK_DIGESTMD5) { auth = _make_sasl_auth(conn, "DIGEST-MD5"); if (!auth) { disconnect_mem_error(conn); return; } handler_add(conn, _handle_digestmd5_challenge, XMPP_NS_SASL, NULL, NULL, NULL); xmpp_send(conn, auth); xmpp_stanza_release(auth); /* SASL DIGEST-MD5 was tried, unset flag */ conn->sasl_support &= ~SASL_MASK_DIGESTMD5; } else if (conn->sasl_support & SASL_MASK_PLAIN) { auth = _make_sasl_auth(conn, "PLAIN"); if (!auth) { disconnect_mem_error(conn); return; } authdata = xmpp_stanza_new(conn->ctx); if (!authdata) { disconnect_mem_error(conn); return; } authid = _get_authid(conn); if (!authid) { disconnect_mem_error(conn); return; } str = sasl_plain(conn->ctx, authid, conn->pass); if (!str) { disconnect_mem_error(conn); return; } xmpp_stanza_set_text(authdata, str); xmpp_free(conn->ctx, str); xmpp_free(conn->ctx, authid); xmpp_stanza_add_child(auth, authdata); xmpp_stanza_release(authdata); handler_add(conn, _handle_sasl_result, XMPP_NS_SASL, NULL, NULL, "PLAIN"); xmpp_send(conn, auth); xmpp_stanza_release(auth); /* SASL PLAIN was tried */ conn->sasl_support &= ~SASL_MASK_PLAIN; } else if (conn->type == XMPP_CLIENT) { /* legacy client authentication */ iq = xmpp_stanza_new(conn->ctx); if (!iq) { disconnect_mem_error(conn); return; } xmpp_stanza_set_name(iq, "iq"); xmpp_stanza_set_type(iq, "set"); xmpp_stanza_set_id(iq, "_xmpp_auth1"); query = xmpp_stanza_new(conn->ctx); if (!query) { xmpp_stanza_release(iq); disconnect_mem_error(conn); return; } xmpp_stanza_set_name(query, "query"); xmpp_stanza_set_ns(query, XMPP_NS_AUTH); xmpp_stanza_add_child(iq, query); xmpp_stanza_release(query); child = xmpp_stanza_new(conn->ctx); if (!child) { xmpp_stanza_release(iq); disconnect_mem_error(conn); return; } xmpp_stanza_set_name(child, "username"); xmpp_stanza_add_child(query, child); xmpp_stanza_release(child); authdata = xmpp_stanza_new(conn->ctx); if (!authdata) { xmpp_stanza_release(iq); disconnect_mem_error(conn); return; } str = xmpp_jid_node(conn->ctx, conn->jid); xmpp_stanza_set_text(authdata, str); xmpp_free(conn->ctx, str); xmpp_stanza_add_child(child, authdata); xmpp_stanza_release(authdata); child = xmpp_stanza_new(conn->ctx); if (!child) { xmpp_stanza_release(iq); disconnect_mem_error(conn); return; } xmpp_stanza_set_name(child, "password"); xmpp_stanza_add_child(query, child); xmpp_stanza_release(child); authdata = xmpp_stanza_new(conn->ctx); if (!authdata) { xmpp_stanza_release(iq); disconnect_mem_error(conn); return; } xmpp_stanza_set_text(authdata, conn->pass); xmpp_stanza_add_child(child, authdata); xmpp_stanza_release(authdata); child = xmpp_stanza_new(conn->ctx); if (!child) { xmpp_stanza_release(iq); disconnect_mem_error(conn); return; } xmpp_stanza_set_name(child, "resource"); xmpp_stanza_add_child(query, child); xmpp_stanza_release(child); authdata = xmpp_stanza_new(conn->ctx); if (!authdata) { xmpp_stanza_release(iq); disconnect_mem_error(conn); return; } str = xmpp_jid_resource(conn->ctx, conn->jid); if (str) { xmpp_stanza_set_text(authdata, str); xmpp_free(conn->ctx, str); } else { xmpp_stanza_release(authdata); xmpp_stanza_release(iq); xmpp_error(conn->ctx, "auth", "Cannot authenticate without resource"); xmpp_disconnect(conn); return; } xmpp_stanza_add_child(child, authdata); xmpp_stanza_release(authdata); handler_add_id(conn, _handle_legacy, "_xmpp_auth1", NULL); handler_add_timed(conn, _handle_missing_legacy, LEGACY_TIMEOUT, NULL); xmpp_send(conn, iq); xmpp_stanza_release(iq); } }
static int _handle_features_sasl(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) { xmpp_stanza_t *bind, *session, *iq, *res, *text; char *resource; /* remove missing features handler */ xmpp_timed_handler_delete(conn, _handle_missing_features_sasl); /* we are expecting <bind/> and <session/> since this is a XMPP style connection */ bind = xmpp_stanza_get_child_by_name(stanza, "bind"); if (bind && strcmp(xmpp_stanza_get_ns(bind), XMPP_NS_BIND) == 0) { /* resource binding is required */ conn->bind_required = 1; } session = xmpp_stanza_get_child_by_name(stanza, "session"); if (session && strcmp(xmpp_stanza_get_ns(session), XMPP_NS_SESSION) == 0) { /* session establishment required */ conn->session_required = 1; } /* if bind is required, go ahead and start it */ if (conn->bind_required) { /* bind resource */ /* setup response handlers */ handler_add_id(conn, _handle_bind, "_xmpp_bind1", NULL); handler_add_timed(conn, _handle_missing_bind, BIND_TIMEOUT, NULL); /* send bind request */ iq = xmpp_stanza_new(conn->ctx); if (!iq) { disconnect_mem_error(conn); return 0; } xmpp_stanza_set_name(iq, "iq"); xmpp_stanza_set_type(iq, "set"); xmpp_stanza_set_id(iq, "_xmpp_bind1"); bind = xmpp_stanza_copy(bind); if (!bind) { xmpp_stanza_release(iq); disconnect_mem_error(conn); return 0; } /* request a specific resource if we have one */ resource = xmpp_jid_resource(conn->ctx, conn->jid); if ((resource != NULL) && (strlen(resource) == 0)) { /* jabberd2 doesn't handle an empty resource */ xmpp_free(conn->ctx, resource); resource = NULL; } /* if we have a resource to request, do it. otherwise the server will assign us one */ if (resource) { res = xmpp_stanza_new(conn->ctx); if (!res) { xmpp_stanza_release(bind); xmpp_stanza_release(iq); disconnect_mem_error(conn); return 0; } xmpp_stanza_set_name(res, "resource"); text = xmpp_stanza_new(conn->ctx); if (!text) { xmpp_stanza_release(res); xmpp_stanza_release(bind); xmpp_stanza_release(iq); disconnect_mem_error(conn); return 0; } xmpp_stanza_set_text(text, resource); xmpp_stanza_add_child(res, text); xmpp_stanza_release(text); xmpp_stanza_add_child(bind, res); xmpp_stanza_release(res); xmpp_free(conn->ctx, resource); } xmpp_stanza_add_child(iq, bind); xmpp_stanza_release(bind); /* send bind request */ xmpp_send(conn, iq); xmpp_stanza_release(iq); } else { /* can't bind, disconnect */ xmpp_error(conn->ctx, "xmpp", "Stream features does not allow "\ "resource bind."); xmpp_disconnect(conn); } return 0; }
/* stream:error handler */ static int _handle_error(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) { xmpp_stanza_t *child; const char *name; /* free old stream error if it's still there */ if (conn->stream_error) { xmpp_stanza_release(conn->stream_error->stanza); if (conn->stream_error->text) xmpp_free(conn->ctx, conn->stream_error->text); xmpp_free(conn->ctx, conn->stream_error); } /* create stream error structure */ conn->stream_error = (xmpp_stream_error_t *)xmpp_alloc(conn->ctx, sizeof(xmpp_stream_error_t)); conn->stream_error->text = NULL; conn->stream_error->type = XMPP_SE_UNDEFINED_CONDITION; if (conn->stream_error) { child = xmpp_stanza_get_children(stanza); do { const char *ns = NULL; if (child) { ns = xmpp_stanza_get_ns(child); } if (ns && strcmp(ns, XMPP_NS_STREAMS_IETF) == 0) { name = xmpp_stanza_get_name(child); if (strcmp(name, "text") == 0) { if (conn->stream_error->text) xmpp_free(conn->ctx, conn->stream_error->text); conn->stream_error->text = xmpp_stanza_get_text(child); } else if (strcmp(name, "bad-format") == 0) conn->stream_error->type = XMPP_SE_BAD_FORMAT; else if (strcmp(name, "bad-namespace-prefix") == 0) conn->stream_error->type = XMPP_SE_BAD_NS_PREFIX; else if (strcmp(name, "conflict") == 0) conn->stream_error->type = XMPP_SE_CONFLICT; else if (strcmp(name, "connection-timeout") == 0) conn->stream_error->type = XMPP_SE_CONN_TIMEOUT; else if (strcmp(name, "host-gone") == 0) conn->stream_error->type = XMPP_SE_HOST_GONE; else if (strcmp(name, "host-unknown") == 0) conn->stream_error->type = XMPP_SE_HOST_UNKNOWN; else if (strcmp(name, "improper-addressing") == 0) conn->stream_error->type = XMPP_SE_IMPROPER_ADDR; else if (strcmp(name, "internal-server-error") == 0) conn->stream_error->type = XMPP_SE_INTERNAL_SERVER_ERROR; else if (strcmp(name, "invalid-from") == 0) conn->stream_error->type = XMPP_SE_INVALID_FROM; else if (strcmp(name, "invalid-id") == 0) conn->stream_error->type = XMPP_SE_INVALID_ID; else if (strcmp(name, "invalid-namespace") == 0) conn->stream_error->type = XMPP_SE_INVALID_NS; else if (strcmp(name, "invalid-xml") == 0) conn->stream_error->type = XMPP_SE_INVALID_XML; else if (strcmp(name, "not-authorized") == 0) conn->stream_error->type = XMPP_SE_NOT_AUTHORIZED; else if (strcmp(name, "policy-violation") == 0) conn->stream_error->type = XMPP_SE_POLICY_VIOLATION; else if (strcmp(name, "remote-connection-failed") == 0) conn->stream_error->type = XMPP_SE_REMOTE_CONN_FAILED; else if (strcmp(name, "resource-constraint") == 0) conn->stream_error->type = XMPP_SE_RESOURCE_CONSTRAINT; else if (strcmp(name, "restricted-xml") == 0) conn->stream_error->type = XMPP_SE_RESTRICTED_XML; else if (strcmp(name, "see-other-host") == 0) conn->stream_error->type = XMPP_SE_SEE_OTHER_HOST; else if (strcmp(name, "system-shutdown") == 0) conn->stream_error->type = XMPP_SE_SYSTEM_SHUTDOWN; else if (strcmp(name, "undefined-condition") == 0) conn->stream_error->type = XMPP_SE_UNDEFINED_CONDITION; else if (strcmp(name, "unsupported-encoding") == 0) conn->stream_error->type = XMPP_SE_UNSUPPORTED_ENCODING; else if (strcmp(name, "unsupported-stanza-type") == 0) conn->stream_error->type = XMPP_SE_UNSUPPORTED_STANZA_TYPE; else if (strcmp(name, "unsupported-version") == 0) conn->stream_error->type = XMPP_SE_UNSUPPORTED_VERSION; else if (strcmp(name, "xml-not-well-formed") == 0) conn->stream_error->type = XMPP_SE_XML_NOT_WELL_FORMED; } } while ((child = xmpp_stanza_get_next(child))); conn->stream_error->stanza = xmpp_stanza_clone(stanza); } return 1; }
/* handle the challenge phase of SCRAM-SHA-1 auth */ static int _handle_scram_sha1_challenge(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) { char *text; char *response; xmpp_stanza_t *auth, *authdata; const char *name; char *challenge; char *scram_init = (char *)userdata; name = xmpp_stanza_get_name(stanza); xmpp_debug(conn->ctx, "xmpp", "handle SCRAM-SHA-1 (challenge) called for %s", name); if (strcmp(name, "challenge") == 0) { text = xmpp_stanza_get_text(stanza); if (!text) goto err; challenge = xmpp_base64_decode_str(conn->ctx, text, strlen(text)); xmpp_free(conn->ctx, text); if (!challenge) goto err; response = sasl_scram_sha1(conn->ctx, challenge, scram_init, conn->jid, conn->pass); xmpp_free(conn->ctx, challenge); if (!response) goto err; auth = xmpp_stanza_new(conn->ctx); if (!auth) goto err_free_response; xmpp_stanza_set_name(auth, "response"); xmpp_stanza_set_ns(auth, XMPP_NS_SASL); authdata = xmpp_stanza_new(conn->ctx); if (!authdata) goto err_release_auth; xmpp_stanza_set_text(authdata, response); xmpp_free(conn->ctx, response); xmpp_stanza_add_child(auth, authdata); xmpp_stanza_release(authdata); xmpp_send(conn, auth); xmpp_stanza_release(auth); } else { xmpp_free(conn->ctx, scram_init); return _handle_sasl_result(conn, stanza, "SCRAM-SHA-1"); } return 1; err_release_auth: xmpp_stanza_release(auth); err_free_response: xmpp_free(conn->ctx, response); err: xmpp_free(conn->ctx, scram_init); disconnect_mem_error(conn); return 0; }
/** Release a Strophe connection object. * Decrement the reference count by one for a connection, freeing the * connection object if the count reaches 0. * * @param conn a Strophe connection object * * @return TRUE if the connection object was freed and FALSE otherwise * * @ingroup Connections */ int xmpp_conn_release(xmpp_conn_t * const conn) { xmpp_ctx_t *ctx; xmpp_connlist_t *item, *prev; xmpp_handlist_t *hlitem, *thli; hash_iterator_t *iter; const char *key; int released = 0; if (conn->ref > 1) conn->ref--; else { ctx = conn->ctx; /* remove connection from context's connlist */ if (ctx->connlist->conn == conn) { item = ctx->connlist; ctx->connlist = item->next; xmpp_free(ctx, item); } else { prev = NULL; item = ctx->connlist; while (item && item->conn != conn) { prev = item; item = item->next; } if (!item) { xmpp_error(ctx, "xmpp", "Connection not in context's list\n"); } else { prev->next = item->next; xmpp_free(ctx, item); } } /* free handler stuff * note that userdata is the responsibility of the client * and the handler pointers don't need to be freed since they * are pointers to functions */ hlitem = conn->timed_handlers; while (hlitem) { thli = hlitem; hlitem = hlitem->next; xmpp_free(ctx, thli); } /* id handlers * we have to traverse the hash table freeing list elements * then release the hash table */ iter = hash_iter_new(conn->id_handlers); while ((key = hash_iter_next(iter))) { hlitem = (xmpp_handlist_t *)hash_get(conn->id_handlers, key); while (hlitem) { thli = hlitem; hlitem = hlitem->next; xmpp_free(conn->ctx, thli->id); xmpp_free(conn->ctx, thli); } } hash_iter_release(iter); hash_release(conn->id_handlers); hlitem = conn->handlers; while (hlitem) { thli = hlitem; hlitem = hlitem->next; if (thli->ns) xmpp_free(ctx, thli->ns); if (thli->name) xmpp_free(ctx, thli->name); if (thli->type) xmpp_free(ctx, thli->type); xmpp_free(ctx, thli); } if (conn->stream_error) { xmpp_stanza_release(conn->stream_error->stanza); if (conn->stream_error->text) xmpp_free(ctx, conn->stream_error->text); xmpp_free(ctx, conn->stream_error); } parser_free(conn->parser); if (conn->domain) xmpp_free(ctx, conn->domain); if (conn->jid) xmpp_free(ctx, conn->jid); if (conn->bound_jid) xmpp_free(ctx, conn->bound_jid); if (conn->pass) xmpp_free(ctx, conn->pass); if (conn->stream_id) xmpp_free(ctx, conn->stream_id); if (conn->lang) xmpp_free(ctx, conn->lang); xmpp_free(ctx, conn); released = 1; } return released; }
static void _send_bookmarks(void) { xmpp_conn_t *conn = connection_get_conn(); xmpp_ctx_t *ctx = connection_get_ctx(); xmpp_stanza_t *iq = xmpp_stanza_new(ctx); xmpp_stanza_set_name(iq, STANZA_NAME_IQ); char *id = create_unique_id("bookmarks_update"); xmpp_stanza_set_id(iq, id); free(id); xmpp_stanza_set_type(iq, STANZA_TYPE_SET); xmpp_stanza_t *query = xmpp_stanza_new(ctx); xmpp_stanza_set_name(query, STANZA_NAME_QUERY); xmpp_stanza_set_ns(query, "jabber:iq:private"); xmpp_stanza_t *storage = xmpp_stanza_new(ctx); xmpp_stanza_set_name(storage, STANZA_NAME_STORAGE); xmpp_stanza_set_ns(storage, "storage:bookmarks"); GList *curr = bookmark_list; while (curr != NULL) { Bookmark *bookmark = curr->data; xmpp_stanza_t *conference = xmpp_stanza_new(ctx); xmpp_stanza_set_name(conference, STANZA_NAME_CONFERENCE); xmpp_stanza_set_attribute(conference, STANZA_ATTR_JID, bookmark->jid); Jid *jidp = jid_create(bookmark->jid); if (jidp->localpart != NULL) { xmpp_stanza_set_attribute(conference, STANZA_ATTR_NAME, jidp->localpart); } jid_destroy(jidp); if (bookmark->autojoin) { xmpp_stanza_set_attribute(conference, STANZA_ATTR_AUTOJOIN, "true"); } else { xmpp_stanza_set_attribute(conference, STANZA_ATTR_AUTOJOIN, "false"); } if (bookmark->nick != NULL) { xmpp_stanza_t *nick_st = xmpp_stanza_new(ctx); xmpp_stanza_set_name(nick_st, STANZA_NAME_NICK); xmpp_stanza_t *nick_text = xmpp_stanza_new(ctx); xmpp_stanza_set_text(nick_text, bookmark->nick); xmpp_stanza_add_child(nick_st, nick_text); xmpp_stanza_add_child(conference, nick_st); xmpp_stanza_release(nick_text); xmpp_stanza_release(nick_st); } if (bookmark->password != NULL) { xmpp_stanza_t *password_st = xmpp_stanza_new(ctx); xmpp_stanza_set_name(password_st, STANZA_NAME_PASSWORD); xmpp_stanza_t *password_text = xmpp_stanza_new(ctx); xmpp_stanza_set_text(password_text, bookmark->password); xmpp_stanza_add_child(password_st, password_text); xmpp_stanza_add_child(conference, password_st); xmpp_stanza_release(password_text); xmpp_stanza_release(password_st); } xmpp_stanza_add_child(storage, conference); xmpp_stanza_release(conference); curr = curr->next; } xmpp_stanza_add_child(query, storage); xmpp_stanza_add_child(iq, query); xmpp_stanza_release(storage); xmpp_stanza_release(query); xmpp_send(conn, iq); xmpp_stanza_release(iq); }
xmpp_stanza_t* form_create_submission(DataForm *form) { xmpp_ctx_t *ctx = connection_get_ctx(); xmpp_stanza_t *x = xmpp_stanza_new(ctx); xmpp_stanza_set_name(x, STANZA_NAME_X); xmpp_stanza_set_ns(x, STANZA_NS_DATA); xmpp_stanza_set_type(x, "submit"); GSList *curr_field = form->fields; while (curr_field) { FormField *field = curr_field->data; if (field->type_t != FIELD_FIXED) { xmpp_stanza_t *field_stanza = xmpp_stanza_new(ctx); xmpp_stanza_set_name(field_stanza, "field"); xmpp_stanza_set_attribute(field_stanza, "var", field->var); xmpp_stanza_t *value_stanza = NULL; GSList *curr_value = NULL; switch (field->type_t) { case FIELD_HIDDEN: case FIELD_TEXT_SINGLE: case FIELD_TEXT_PRIVATE: case FIELD_BOOLEAN: case FIELD_LIST_SINGLE: case FIELD_JID_SINGLE: value_stanza = xmpp_stanza_new(ctx); xmpp_stanza_set_name(value_stanza, "value"); if (field->values) { if (field->values->data) { xmpp_stanza_t *text_stanza = xmpp_stanza_new(ctx); xmpp_stanza_set_text(text_stanza, field->values->data); xmpp_stanza_add_child(value_stanza, text_stanza); xmpp_stanza_release(text_stanza); } } xmpp_stanza_add_child(field_stanza, value_stanza); xmpp_stanza_release(value_stanza); break; case FIELD_TEXT_MULTI: case FIELD_LIST_MULTI: case FIELD_JID_MULTI: curr_value = field->values; while (curr_value) { char *value = curr_value->data; value_stanza = xmpp_stanza_new(ctx); xmpp_stanza_set_name(value_stanza, "value"); if (value) { xmpp_stanza_t *text_stanza = xmpp_stanza_new(ctx); xmpp_stanza_set_text(text_stanza, value); xmpp_stanza_add_child(value_stanza, text_stanza); xmpp_stanza_release(text_stanza); } xmpp_stanza_add_child(field_stanza, value_stanza); xmpp_stanza_release(value_stanza); curr_value = g_slist_next(curr_value); } break; case FIELD_FIXED: default: break; } xmpp_stanza_add_child(x, field_stanza); xmpp_stanza_release(field_stanza); } curr_field = g_slist_next(curr_field); } return x; }
/** registration at connections * * @param conn a Strophe connection object */ static void _register(xmpp_conn_t * const conn) { char *str; xmpp_stanza_t *iq, *q, *child, *regdata; iq = xmpp_stanza_new(conn->ctx); if (!iq) { disconnect_mem_error(conn); return; } xmpp_stanza_set_name(iq, "iq"); xmpp_stanza_set_type(iq, "set"); xmpp_stanza_set_id(iq, "_xmpp_reg1"); q = xmpp_stanza_new(conn->ctx); if (!q) { xmpp_stanza_release(iq); disconnect_mem_error(conn); return; } xmpp_stanza_set_name(q, "query"); xmpp_stanza_set_ns(q, XMPP_NS_REGISTER); xmpp_stanza_add_child(iq, q); xmpp_stanza_release(q); child = xmpp_stanza_new(conn->ctx); if (!child) { xmpp_stanza_release(iq); disconnect_mem_error(conn); return; } xmpp_stanza_set_name(child, "registered"); xmpp_stanza_add_child(q, child); xmpp_stanza_release(child); child = xmpp_stanza_new(conn->ctx); if (!child) { xmpp_stanza_release(iq); disconnect_mem_error(conn); return; } xmpp_stanza_set_name(child, "username"); xmpp_stanza_add_child(q, child); xmpp_stanza_release(child); regdata = xmpp_stanza_new(conn->ctx); if (!regdata) { xmpp_stanza_release(iq); disconnect_mem_error(conn); return; } str = xmpp_jid_node(conn->ctx, conn->jid); xmpp_stanza_set_text(regdata, str); xmpp_free(conn->ctx, str); xmpp_stanza_add_child(child, regdata); xmpp_stanza_release(regdata); child = xmpp_stanza_new(conn->ctx); if (!child) { xmpp_stanza_release(iq); disconnect_mem_error(conn); return; } xmpp_stanza_set_name(child, "password"); xmpp_stanza_add_child(q, child); xmpp_stanza_release(child); regdata = xmpp_stanza_new(conn->ctx); if (!regdata) { xmpp_stanza_release(iq); disconnect_mem_error(conn); return; } xmpp_stanza_set_text(regdata, conn->pass); xmpp_stanza_add_child(child, regdata); xmpp_stanza_release(regdata); child = xmpp_stanza_new(conn->ctx); if (!child) { xmpp_stanza_release(iq); disconnect_mem_error(conn); return; } xmpp_stanza_set_name(child, "email"); xmpp_stanza_add_child(q, child); xmpp_stanza_release(child); regdata = xmpp_stanza_new(conn->ctx); if (!regdata) { xmpp_stanza_release(iq); disconnect_mem_error(conn); return; } str = xmpp_jid_node(conn->ctx, conn->jid); xmpp_stanza_set_text(regdata, str); xmpp_free(conn->ctx, str); xmpp_stanza_add_child(child, regdata); xmpp_stanza_release(regdata); handler_add_id(conn, _handle_register, "_xmpp_reg1", NULL); handler_add_timed(conn, _handle_missing_register, LAZY_REGISTRATION_TIMEOUT, NULL); xmpp_send(conn, iq); xmpp_stanza_release(iq); }
static int version_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) { xmpp_stanza_t *reply, *query, *name, *version, *text; conflate_handle_t *handle = (conflate_handle_t*) userdata; xmpp_ctx_t *ctx = handle->ctx; char *ns; struct utsname un = {}; printf("Received version request from %s\n", xmpp_stanza_get_attribute(stanza, "from")); reply = xmpp_stanza_new(ctx); assert(reply); xmpp_stanza_set_name(reply, "iq"); xmpp_stanza_set_type(reply, "result"); xmpp_stanza_set_id(reply, xmpp_stanza_get_id(stanza)); xmpp_stanza_set_attribute(reply, "to", xmpp_stanza_get_attribute(stanza, "from")); query = xmpp_stanza_new(ctx); assert(query); xmpp_stanza_set_name(query, "query"); ns = xmpp_stanza_get_ns(xmpp_stanza_get_children(stanza)); if (ns) { xmpp_stanza_set_ns(query, ns); } name = xmpp_stanza_new(ctx); assert(name); xmpp_stanza_set_name(name, "name"); add_and_release(query, name); text = xmpp_stanza_new(ctx); assert(text); xmpp_stanza_set_text(text, handle->conf->software); add_and_release(name, text); version = xmpp_stanza_new(ctx); assert(version); xmpp_stanza_set_name(version, "version"); add_and_release(query, version); text = xmpp_stanza_new(ctx); assert(text); xmpp_stanza_set_text(text, handle->conf->version); add_and_release(version, text); if (uname(&un) == 0) { char os_buf[128]; snprintf(os_buf, sizeof(os_buf), "%s/%s/%s", un.machine, un.sysname, un.release); xmpp_stanza_t *os = xmpp_stanza_new(ctx); assert(os); xmpp_stanza_set_name(os, "os"); add_and_release(query, os); text = xmpp_stanza_new(ctx); assert(text); xmpp_stanza_set_text(text, os_buf); add_and_release(os, text); } add_and_release(reply, query); xmpp_send(conn, reply); xmpp_stanza_release(reply); return 1; }
static int _iq_handle_version_get(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) { xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata; const char *id = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_ID); const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); if (from != NULL) { xmpp_stanza_t *response = xmpp_stanza_new(ctx); xmpp_stanza_set_name(response, STANZA_NAME_IQ); if (id != NULL) { xmpp_stanza_set_id(response, id); } xmpp_stanza_set_attribute(response, STANZA_ATTR_TO, from); xmpp_stanza_set_type(response, STANZA_TYPE_RESULT); xmpp_stanza_t *query = xmpp_stanza_new(ctx); xmpp_stanza_set_name(query, STANZA_NAME_QUERY); xmpp_stanza_set_ns(query, STANZA_NS_VERSION); xmpp_stanza_t *name = xmpp_stanza_new(ctx); xmpp_stanza_set_name(name, "name"); xmpp_stanza_t *name_txt = xmpp_stanza_new(ctx); xmpp_stanza_set_text(name_txt, "Profanity"); xmpp_stanza_add_child(name, name_txt); xmpp_stanza_t *version = xmpp_stanza_new(ctx); xmpp_stanza_set_name(version, "version"); xmpp_stanza_t *version_txt = xmpp_stanza_new(ctx); GString *version_str = g_string_new(PACKAGE_VERSION); if (strcmp(PACKAGE_STATUS, "development") == 0) { #ifdef HAVE_GIT_VERSION g_string_append(version_str, "dev."); g_string_append(version_str, PROF_GIT_BRANCH); g_string_append(version_str, "."); g_string_append(version_str, PROF_GIT_REVISION); #else g_string_append(version_str, "dev"); #endif } xmpp_stanza_set_text(version_txt, version_str->str); xmpp_stanza_add_child(version, version_txt); xmpp_stanza_add_child(query, name); xmpp_stanza_add_child(query, version); xmpp_stanza_add_child(response, query); xmpp_send(conn, response); g_string_free(version_str, TRUE); xmpp_stanza_release(name_txt); xmpp_stanza_release(version_txt); xmpp_stanza_release(name); xmpp_stanza_release(version); xmpp_stanza_release(query); xmpp_stanza_release(response); } return 1; }
static void add_and_release(xmpp_stanza_t* parent, xmpp_stanza_t* child) { xmpp_stanza_add_child(parent, child); xmpp_stanza_release(child); }
xmpp_stanza_t * caps_create_query_response_stanza(xmpp_ctx_t * const ctx) { xmpp_stanza_t *query = xmpp_stanza_new(ctx); xmpp_stanza_set_name(query, STANZA_NAME_QUERY); xmpp_stanza_set_ns(query, XMPP_NS_DISCO_INFO); xmpp_stanza_t *identity = xmpp_stanza_new(ctx); xmpp_stanza_set_name(identity, "identity"); xmpp_stanza_set_attribute(identity, "category", "client"); xmpp_stanza_set_attribute(identity, "type", "console"); GString *name_str = g_string_new("Profanity "); g_string_append(name_str, PACKAGE_VERSION); if (strcmp(PACKAGE_STATUS, "development") == 0) { #ifdef HAVE_GIT_VERSION g_string_append(name_str, "dev."); g_string_append(name_str, PROF_GIT_BRANCH); g_string_append(name_str, "."); g_string_append(name_str, PROF_GIT_REVISION); #else g_string_append(name_str, "dev"); #endif } xmpp_stanza_set_attribute(identity, "name", name_str->str); xmpp_stanza_t *feature_caps = xmpp_stanza_new(ctx); xmpp_stanza_set_name(feature_caps, STANZA_NAME_FEATURE); xmpp_stanza_set_attribute(feature_caps, STANZA_ATTR_VAR, STANZA_NS_CAPS); xmpp_stanza_t *feature_discoinfo = xmpp_stanza_new(ctx); xmpp_stanza_set_name(feature_discoinfo, STANZA_NAME_FEATURE); xmpp_stanza_set_attribute(feature_discoinfo, STANZA_ATTR_VAR, XMPP_NS_DISCO_INFO); xmpp_stanza_t *feature_discoitems = xmpp_stanza_new(ctx); xmpp_stanza_set_name(feature_discoitems, STANZA_NAME_FEATURE); xmpp_stanza_set_attribute(feature_discoitems, STANZA_ATTR_VAR, XMPP_NS_DISCO_ITEMS); xmpp_stanza_t *feature_muc = xmpp_stanza_new(ctx); xmpp_stanza_set_name(feature_muc, STANZA_NAME_FEATURE); xmpp_stanza_set_attribute(feature_muc, STANZA_ATTR_VAR, STANZA_NS_MUC); xmpp_stanza_t *feature_version = xmpp_stanza_new(ctx); xmpp_stanza_set_name(feature_version, STANZA_NAME_FEATURE); xmpp_stanza_set_attribute(feature_version, STANZA_ATTR_VAR, STANZA_NS_VERSION); xmpp_stanza_t *feature_chatstates = xmpp_stanza_new(ctx); xmpp_stanza_set_name(feature_chatstates, STANZA_NAME_FEATURE); xmpp_stanza_set_attribute(feature_chatstates, STANZA_ATTR_VAR, STANZA_NS_CHATSTATES); xmpp_stanza_t *feature_ping = xmpp_stanza_new(ctx); xmpp_stanza_set_name(feature_ping, STANZA_NAME_FEATURE); xmpp_stanza_set_attribute(feature_ping, STANZA_ATTR_VAR, STANZA_NS_PING); xmpp_stanza_add_child(query, identity); xmpp_stanza_add_child(query, feature_caps); xmpp_stanza_add_child(query, feature_chatstates); xmpp_stanza_add_child(query, feature_discoinfo); xmpp_stanza_add_child(query, feature_discoitems); xmpp_stanza_add_child(query, feature_muc); xmpp_stanza_add_child(query, feature_version); xmpp_stanza_add_child(query, feature_ping); xmpp_stanza_release(feature_ping); xmpp_stanza_release(feature_version); xmpp_stanza_release(feature_muc); xmpp_stanza_release(feature_discoitems); xmpp_stanza_release(feature_discoinfo); xmpp_stanza_release(feature_chatstates); xmpp_stanza_release(feature_caps); xmpp_stanza_release(identity); return query; }
void presence_send(const resource_presence_t presence_type, const int idle, char *signed_status) { if (connection_get_status() != JABBER_CONNECTED) { log_warning("Error setting presence, not connected."); return; } char *msg = connection_get_presence_msg(); if (msg) { log_debug("Updating presence: %s, \"%s\"", string_from_resource_presence(presence_type), msg); } else { log_debug("Updating presence: %s", string_from_resource_presence(presence_type)); } const int pri = accounts_get_priority_for_presence_type(session_get_account_name(), presence_type); connection_set_priority(pri); xmpp_ctx_t * const ctx = connection_get_ctx(); xmpp_stanza_t *presence = xmpp_presence_new(ctx); char *id = create_unique_id("presence"); xmpp_stanza_set_id(presence, id); free(id); const char *show = stanza_get_presence_string_from_type(presence_type); stanza_attach_show(ctx, presence, show); stanza_attach_status(ctx, presence, msg); if (signed_status) { xmpp_stanza_t *x = xmpp_stanza_new(ctx); xmpp_stanza_set_name(x, STANZA_NAME_X); xmpp_stanza_set_ns(x, STANZA_NS_SIGNED); xmpp_stanza_t *signed_text = xmpp_stanza_new(ctx); xmpp_stanza_set_text(signed_text, signed_status); xmpp_stanza_add_child(x, signed_text); xmpp_stanza_release(signed_text); xmpp_stanza_add_child(presence, x); xmpp_stanza_release(x); } stanza_attach_priority(ctx, presence, pri); if (idle > 0) { stanza_attach_last_activity(ctx, presence, idle); } stanza_attach_caps(ctx, presence); _send_presence_stanza(presence); _send_room_presence(presence); xmpp_stanza_release(presence); // set last presence for account const char *last = show; if (last == NULL) { last = STANZA_TEXT_ONLINE; } char *account = session_get_account_name(); accounts_set_last_presence(account, last); accounts_set_last_status(account, msg); }
static int wfuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { wlog("wfuse_readdir path = %s\n", path); if (!is_owner_online) { return -ENOENT; } int rc; /* Return code */ rc = pthread_mutex_lock(&mutex); wsyserr(rc != 0, "pthread_mutex_lock"); xmpp_stanza_t *message = xmpp_stanza_new(ctx); /* message with done */ xmpp_stanza_set_name(message, "message"); xmpp_stanza_set_attribute(message, "to", owner_str); xmpp_stanza_t *files = xmpp_stanza_new(ctx); /* message with done */ xmpp_stanza_set_name(files, "files"); xmpp_stanza_set_ns(files, WNS); xmpp_stanza_set_attribute(files, "action", "list"); xmpp_stanza_set_attribute(files, "path", path); xmpp_stanza_add_child(message, files); xmpp_send(conn, message); xmpp_stanza_release(files); xmpp_stanza_release(message); while (signal_list == false) { pthread_cond_wait(&cond, &mutex); } int res = 0; /* Do your job */ if (signal_fail == true) { res = -ENOENT; signal_attr = false; signal_list = false; signal_read = false; signal_fail = false; } else { // wfatal(root == NULL, "root is NULL"); (void) offset; (void) fi; filler(buf, ".", NULL, 0); filler(buf, "..", NULL, 0); elem_t *aux = root; while (aux != NULL) { filler(buf, aux->filename, NULL, 0); aux = aux->next; } /* Free list */ aux = root; elem_t *aux2; while (aux != NULL) { free(aux->filename); aux2 = aux; aux = aux->next; free(aux2); } root = NULL; } /* Job done */ signal_list = false; rc = pthread_mutex_unlock(&mutex); wsyserr(rc != 0, "pthread_mutex_unlock"); return res; }
/** * Handles the service */ xmpp_stanza_t * _HandleServiceRequest(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) { xmpp_stanza_t *reply; xmpp_stanza_t *wd, *nodetext; xmpp_ctx_t *ctx = (xmpp_ctx_t*) userdata; sdvp_method_t methodType = SDVP_METHOD_UNDEFINED; sdvp_from_t* from; char *method; sdvp_reply_params_t* sdvpReplyParams = NULL; void* replyParams; void* paramStructure = NULL; char strbuf[20]; static unsigned int watchdog = 1; //HJP: Hvorfor oprette reply her, når den næsten altid bliver overskrevet? reply = xmpp_stanza_new(ctx); xmpp_stanza_set_name(reply, "iq"); from = _CreateFrom(xmpp_stanza_get_attribute(stanza, "from"), "TODO", "TODO"); syslog(LOG_DEBUG, "Received a RPC Method call from %s\n", from->name); method = _GetRpcMethode(stanza); methodType = _GetDefinedMetodeFromMethod(method); syslog(LOG_DEBUG, "XML-RPC method name %s\n", method); free(method); if (methodType == SDVP_METHOD_UNDEFINED) { syslog(LOG_DEBUG, "Undefined method type\n"); sdvp_InitiateReplyParameters(&sdvpReplyParams, 1); sdvpReplyParams->params[0].strValue = strdup("ERROR: Method is undefined. Methods are CasE SentItive.\n"); sdvpReplyParams->params[0].type = IEC_VISIBLE_STRING; //HJP: Jeg releaser reply her, det ville være smartere kun at oprette den når nødvendigt... xmpp_stanza_release(reply); reply = _CreateReply(ctx, methodType, sdvpReplyParams); xmpp_stanza_set_type(reply, "error"); sdvp_FreeReplyParameters(sdvpReplyParams); } else if (methodType == LIAB_WATCHDOG) { xmpp_stanza_release(reply); reply = xmpp_stanza_new(ctx); xmpp_stanza_set_name(reply, "query"); xmpp_stanza_set_ns(reply, "jabber:iq:rpc"); wd = xmpp_stanza_new(ctx); xmpp_stanza_set_name(wd, "watchdog"); xmpp_stanza_add_child(reply, wd); xmpp_stanza_release(wd); nodetext = xmpp_stanza_new(ctx); sprintf(strbuf, "%u", watchdog++); xmpp_stanza_set_text(nodetext, strbuf); xmpp_stanza_add_child(wd, nodetext); xmpp_stanza_release(nodetext); } else{ if (_CallbackIsRegistered(methodType)) { paramStructure = _CreateParamStructureFromParameters(methodType, stanza); replyParams = _CallTheRightCallback(methodType, paramStructure); _FreeParamStructure(methodType,paramStructure); xmpp_stanza_release(reply); reply = _CreateReply(ctx, methodType, replyParams); sdvp_FreeCallbackReply(methodType, replyParams); sdvp_FreeReplyParameters(sdvpReplyParams); } else { // TODO: Send a error with method not implemented or alike } } _FreeFrom(from); return reply; }
static int wfuse_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { wlog("wfuse_read path = %s\n", path); if (!is_owner_online) { return -ENOENT; } int rc; /* Return code */ rc = pthread_mutex_lock(&mutex); wsyserr(rc != 0, "pthread_mutex_lock"); xmpp_stanza_t *message = xmpp_stanza_new(ctx); /* message with done */ xmpp_stanza_set_name(message, "message"); xmpp_stanza_set_attribute(message, "to", owner_str); xmpp_stanza_t *files = xmpp_stanza_new(ctx); /* message with done */ xmpp_stanza_set_name(files, "files"); xmpp_stanza_set_ns(files, WNS); xmpp_stanza_set_attribute(files, "action", "read"); xmpp_stanza_set_attribute(files, "path", path); xmpp_stanza_add_child(message, files); xmpp_send(conn, message); xmpp_stanza_release(files); xmpp_stanza_release(message); /* Wait until attributes is set */ while (signal_read == false) { pthread_cond_wait(&cond, &mutex); } int res = size; /* Do your job */ if (signal_fail == true) { res = -ENOENT; signal_attr = false; signal_list = false; signal_read = false; signal_fail = false; } else { size_t len; (void) fi; len = strlen(read_data); if (offset < len) { if (offset + size > len) { size = len - offset; } memcpy(buf, read_data + offset, size); } else { size = 0; } } /* Job done */ signal_read = false; rc = pthread_mutex_unlock(&mutex); wsyserr(rc != 0, "pthread_mutex_unlock"); return res; }
/** Release a Strophe connection object. * Decrement the reference count by one for a connection, freeing the * connection object if the count reaches 0. * * @param conn a Strophe connection object * * @return TRUE if the connection object was freed and FALSE otherwise * * @ingroup Connections */ int xmpp_conn_release(xmpp_conn_t * const conn) { xmpp_ctx_t *ctx; list_t *item; xmpp_handler_t *temp; xmpp_handlist_t *hlitem, *thli; hash_iterator_t *iter; const char *key; if (conn->ref > 1) { conn->ref--; return 0; } ctx = conn->ctx; /* remove connection from context's connlist */ item = list_pop_by_data(ctx->connlist, (void *)conn); if (item) xmpp_free(ctx, item); else xmpp_error(ctx, "xmpp", "Connection not in context's list\n"); /* free handler stuff * note that userdata is the responsibility of the client * and the handler pointers don't need to be freed since they * are pointers to functions */ while ((item = list_shift(conn->timed_handlers))) { xmpp_free(ctx, item->data); xmpp_free(ctx, item); } list_destroy(conn->timed_handlers); /* id handlers * we have to traverse the hash table freeing list elements * then release the hash table */ iter = hash_iter_new(conn->id_handlers); while ((key = hash_iter_next(iter))) { hlitem = (xmpp_handlist_t *)hash_get(conn->id_handlers, key); while (hlitem) { thli = hlitem; hlitem = hlitem->next; xmpp_free(ctx, thli->id); xmpp_free(ctx, thli); } } hash_iter_release(iter); hash_release(conn->id_handlers); while ((item = list_shift(conn->handlers))) { temp = (xmpp_handler_t *)item->data; if (temp->ns) xmpp_free(ctx, temp->ns); if (temp->name) xmpp_free(ctx, temp->name); if (temp->type) xmpp_free(ctx, temp->type); xmpp_free(ctx, temp); xmpp_free(ctx, item); } list_destroy(conn->handlers); if (conn->stream_error) { xmpp_stanza_release(conn->stream_error->stanza); if (conn->stream_error->text) xmpp_free(ctx, conn->stream_error->text); xmpp_free(ctx, conn->stream_error); } parser_free(conn->parser); /* free send_queue */ list_destroy(conn->send_queue); if (conn->domain) xmpp_free(ctx, conn->domain); if (conn->jid) xmpp_free(ctx, conn->jid); if (conn->bound_jid) xmpp_free(ctx, conn->bound_jid); if (conn->pass) xmpp_free(ctx, conn->pass); if (conn->stream_id) xmpp_free(ctx, conn->stream_id); if (conn->lang) xmpp_free(ctx, conn->lang); if (conn->tls) tls_free(conn->tls); xmpp_free(ctx, conn); return 1; }