/* * `on` method on a mqtt client object registers an event handler * for a given event type. Recognized event types: * * - `connect`: invoked on sucessfull connection (mqtt connack message) * - `error`: invoked on connection error * - `close`: invoked when the connection gets closed * - `message`: invoked when a new message is received. The callback * receives (topic, message) arguments, both strings */ enum v7_err MQTT_on(struct v7 *v7, v7_val_t *res) { enum v7_err rcode = V7_OK; v7_val_t evv = v7_arg(v7, 0); v7_val_t cb = v7_arg(v7, 1); const char *ev, *key = NULL; ev = v7_get_cstring(v7, &evv); if (strcmp(ev, "connect") == 0) { key = SJ_MQTT_CONNECT_CB; } else if (strcmp(ev, "message") == 0) { key = SJ_MQTT_MESSAGE_CB; } else if (strcmp(ev, "error") == 0) { key = SJ_MQTT_ERROR_CB; } else if (strcmp(ev, "close") == 0) { key = SJ_MQTT_CLOSE_CB; } else { rcode = v7_throwf(v7, "Error", "unsupported protocol"); goto clean; } v7_def(v7, v7_get_this(v7), key, ~0, V7_DESC_ENUMERABLE(0), cb); (void) res; clean: return rcode; }
/* * Subscribes a mqtt client to a topic. */ enum v7_err MQTT_subscribe(struct v7 *v7, v7_val_t *res) { enum v7_err rcode = V7_OK; struct user_data *ud; struct mg_connection *nc; struct mg_mqtt_topic_expression expr; v7_val_t topicv = v7_arg(v7, 0); const char *topic; nc = v7_get_ptr(v7, v7_get(v7, v7_get_this(v7), "_nc", ~0)); if (nc == NULL) { rcode = v7_throwf(v7, "Error", "unsupported protocol"); goto clean; } ud = (struct user_data *) nc->user_data; topic = v7_get_cstring(v7, &topicv); if (topic == NULL || strlen(topic) == 0) { rcode = v7_throwf(v7, "TypeError", "invalid topic"); goto clean; } expr.topic = topic; expr.qos = 0; mg_mqtt_subscribe(nc, &expr, 1, ud->msgid++); *res = v7_mk_boolean(v7, 1); clean: return rcode; }
/* * Publishes a message to a topic. * * Args: * - `topic`: topic, string. * - `message`: message, string. * * Only QOS 0 is implemented at the moment. */ enum v7_err MQTT_publish(struct v7 *v7, v7_val_t *res) { enum v7_err rcode = V7_OK; struct user_data *ud; struct mg_connection *nc; const char *topic; const char *message; size_t message_len; v7_val_t topicv = v7_arg(v7, 0), messagev = v7_arg(v7, 1); (void) res; topic = v7_get_cstring(v7, &topicv); if (topic == NULL || strlen(topic) == 0) { rcode = v7_throwf(v7, "TypeError", "invalid topic"); goto clean; } if (!v7_is_string(messagev)) { rcode = v7_throwf(v7, "TypeError", "invalid message"); goto clean; } message = v7_get_string(v7, &messagev, &message_len); nc = v7_get_ptr(v7, v7_get(v7, v7_get_this(v7), "_nc", ~0)); if (nc == NULL) { rcode = v7_throwf(v7, "Error", "invalid connection"); goto clean; } ud = (struct user_data *) nc->user_data; mg_mqtt_publish(nc, topic, ud->msgid++, MG_MQTT_QOS(0), message, message_len); clean: return rcode; }
enum v7_err fill_ssl_connect_opts(struct v7 *v7, v7_val_t opts, int force_ssl, struct mg_connect_opts *copts) { enum v7_err rcode = V7_OK; v7_val_t v_use_ssl = v7_get(v7, opts, "use_ssl", ~0); v7_val_t v_ca_cert = v7_get(v7, opts, "ssl_ca_cert", ~0); v7_val_t v_cert = v7_get(v7, opts, "ssl_cert", ~0); v7_val_t v_server_name = v7_get(v7, opts, "ssl_server_name", ~0); if (!v7_is_undefined(v_ca_cert) && !v7_is_string(v_ca_cert)) { rcode = v7_throwf(v7, "TypeError", "ssl_ca_cert must be a string"); goto clean; } if (!v7_is_undefined(v_cert) && !v7_is_string(v_cert)) { rcode = v7_throwf(v7, "TypeError", "ssl_cert must be a string"); goto clean; } if (!v7_is_undefined(v_server_name) && !v7_is_string(v_server_name)) { rcode = v7_throwf(v7, "TypeError", "ssl_server_name must be a string"); goto clean; } copts->ssl_ca_cert = v7_get_cstring(v7, &v_ca_cert); copts->ssl_cert = v7_get_cstring(v7, &v_cert); copts->ssl_server_name = v7_get_cstring(v7, &v_server_name); if ((force_ssl || (v7_is_boolean(v_use_ssl) && v7_get_bool(v7, v_use_ssl) != 0)) && copts->ssl_ca_cert == NULL) { /* Defaults to configuration */ copts->ssl_ca_cert = get_cfg()->tls.ca_file; } clean: return rcode; }
static enum v7_err Updater_startupdate(struct v7 *v7, v7_val_t *res) { enum v7_err rcode = V7_OK; v7_val_t manifest_url_v = v7_arg(v7, 0); if (!v7_is_string(manifest_url_v)) { rcode = v7_throwf(v7, "Error", "URL is not a string"); } else { struct update_context *ctx = updater_context_create(); if (ctx == NULL) { rcode = v7_throwf(v7, "Error", "Failed to init updater"); } else if (start_update_download(ctx, v7_get_cstring(v7, &manifest_url_v)) < 0) { rcode = v7_throwf(v7, "Error", ctx->status_msg); } } *res = v7_mk_boolean(v7, rcode == V7_OK); return rcode; }
/* * Make a new MQTT client. * * Arguments: * url: url where to connect to * opts: option object * * Recognized option object properties: * * - clientId: string; mqtt client id. defaults to * Math.random().toString(16).substr(2, 10) * * Example: * * var client = MQTT.connect('mqtt://test.mosquitto.org'); * * client.on('connect', function () { * client.subscribe('presence'); * client.publish('presence', 'Hello mqtt'); * }); * * client.on('message', function (topic, message) { * console.log(message); * }); * * TLS can be enabled by choosing the `mqtts://` protocol. In that * case the default port is 8883 as defined by IANA. * * The API is modeled after https://www.npmjs.com/package/mqtt. * */ enum v7_err sj_mqtt_connect(struct v7 *v7, v7_val_t *res) { enum v7_err rcode = V7_OK; const char *url; size_t len; struct mg_str host, scheme; unsigned int port; struct mg_connection *nc; struct user_data *ud; char *url_with_port = NULL; int use_ssl = 0; v7_val_t urlv = v7_arg(v7, 0), opts = v7_arg(v7, 1); v7_val_t client_id; v7_val_t proto = v7_get(v7, v7_get(v7, v7_get_global(v7), "MQTT", ~0), "proto", ~0); if (!v7_is_string(urlv)) { rcode = v7_throwf(v7, "Error", "invalid url string"); goto clean; } url = v7_get_string(v7, &urlv, &len); if (mg_parse_uri(mg_mk_str(url), &scheme, NULL, &host, &port, NULL, NULL, NULL) < 0) { rcode = v7_throwf(v7, "Error", "invalid url string"); goto clean; } if (mg_vcmp(&scheme, "mqtt") == 0) { url += sizeof("mqtt://") - 1; } else if (mg_vcmp(&scheme, "mqtts") == 0) { url += sizeof("mqtts://") - 1; use_ssl = 1; } else { rcode = v7_throwf(v7, "Error", "unsupported protocol"); goto clean; } client_id = v7_get(v7, opts, "clientId", ~0); if (v7_is_undefined(client_id)) { rcode = v7_exec(v7, "Math.random().toString(16).substr(2,8)", &client_id); if (rcode != V7_OK) { goto clean; } } if (port == 0) { if (asprintf(&url_with_port, "%.*s%s", (int) host.len, host.p, (use_ssl ? ":8883" : ":1883")) < 0) { rcode = v7_throwf(v7, "Error", "Out of memory"); goto clean; } } nc = mg_connect(&sj_mgr, url_with_port ? url_with_port : url, mqtt_ev_handler); if (nc == NULL) { rcode = v7_throwf(v7, "Error", "cannot create connection"); goto clean; } if (use_ssl) { #ifdef MG_ENABLE_SSL mg_set_ssl(nc, NULL, NULL); #else rcode = v7_throwf(v7, "Error", "SSL not enabled"); goto clean; #endif } mg_set_protocol_mqtt(nc); *res = v7_mk_object(v7); v7_set_proto(v7, *res, proto); ud = calloc(1, sizeof(*ud)); if (ud == NULL) { rcode = v7_throwf(v7, "Error", "Out of memory"); goto clean; } ud->v7 = v7; ud->client = *res; ud->client_id = strdup(v7_get_cstring(v7, &client_id)); if (ud->client_id == NULL) { free(ud); rcode = v7_throwf(v7, "Error", "Out of memory"); goto clean; } nc->user_data = ud; v7_own(v7, &ud->client); v7_def(v7, *res, "_nc", ~0, _V7_DESC_HIDDEN(1), v7_mk_foreign(v7, nc)); clean: free(url_with_port); return rcode; }