void fb_mqtt_write(FbMqtt *mqtt, FbMqttMessage *msg) { const GByteArray *bytes; FbMqttMessagePrivate *mriv; FbMqttPrivate *priv; g_return_if_fail(FB_IS_MQTT(mqtt)); g_return_if_fail(FB_IS_MQTT_MESSAGE(msg)); priv = mqtt->priv; mriv = msg->priv; bytes = fb_mqtt_message_bytes(msg); if (G_UNLIKELY(bytes == NULL)) { fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, _("Failed to format data")); return; } fb_util_debug_hexdump(FB_UTIL_DEBUG_INFO, mriv->bytes, "Writing %d (flags: 0x%0X)", mriv->type, mriv->flags); g_byte_array_append(priv->wbuf, bytes->data, bytes->len); fb_mqtt_cb_write(mqtt, priv->gsc->fd, PURPLE_INPUT_WRITE); if (priv->wev > 0) { priv->wev = purple_input_add(priv->gsc->fd, PURPLE_INPUT_WRITE, fb_mqtt_cb_write, mqtt); } }
/** * Implemented #b_event_handler for #fb_mqtt_timeout(). * * @param data The user defined data, which is #fb_mqtt. * @param fd The event file descriptor. * @param cond The #b_input_condition. * * @return FALSE to prevent continued event handling. **/ static gboolean fb_mqtt_cb_timeout(gpointer data, gint fd, b_input_condition cond) { fb_mqtt_t *mqtt = data; mqtt->tev = 0; fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, "Connection timed out"); return FALSE; }
static gboolean fb_mqtt_cb_timeout(gpointer data) { FbMqtt *mqtt = data; FbMqttPrivate *priv = mqtt->priv; priv->tev = 0; fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, _("Connection timed out")); return FALSE; }
/** * Checks the #fb_mqtt connection. * * @param mqtt The #fb_mqtt. * @param error TRUE to error upon no connection, FALSE otherwise. * * @return TRUE if the #fb_mqtt is connected, FALSE otherwise. **/ gboolean fb_mqtt_connected(fb_mqtt_t *mqtt, gboolean error) { gboolean connected; g_return_val_if_fail(mqtt != NULL, FALSE); connected = (mqtt->ssl != NULL) && mqtt->connected; if (!connected && error) fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, "Not connected"); return connected; }
gboolean fb_mqtt_connected(FbMqtt *mqtt, gboolean error) { FbMqttPrivate *priv; gboolean connected; g_return_val_if_fail(FB_IS_MQTT(mqtt), FALSE); priv = mqtt->priv; connected = (priv->gsc != NULL) && priv->connected; if (!connected && error) { fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, _("Not connected")); } return connected; }
/** * Implemented #ssl_input_function for the connection of #fb_mqtt->ssl. * * @param data The user defined data, which is #fb_mqtt. * @param error The SSL error. (0 on success) * @param ssl The SSL source. * @param cond The #b_input_condition. * * @return TRUE for continued event handling, otherwise FALSE. **/ static gboolean fb_mqtt_cb_open(gpointer data, gint error, gpointer ssl, b_input_condition cond) { fb_mqtt_t *mqtt = data; gint fd; if ((ssl == NULL) || (error != SSL_OK)) { fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, "Failed to connect"); return FALSE; } fb_mqtt_timeout_clear(mqtt); fd = ssl_getfd(mqtt->ssl); mqtt->rev = b_input_add(fd, B_EV_IO_READ, fb_mqtt_cb_read, mqtt); FB_MQTT_FUNC(mqtt, open); return FALSE; }
static void fb_mqtt_cb_write(gpointer data, gint fd, PurpleInputCondition cond) { FbMqtt *mqtt = data; FbMqttPrivate *priv = mqtt->priv; gssize wize; wize = purple_ssl_write(priv->gsc, priv->wbuf->data, priv->wbuf->len); if (wize < 0) { fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, _("Failed to write data")); return; } if (wize > 0) { g_byte_array_remove_range(priv->wbuf, 0, wize); } if (priv->wbuf->len < 1) { priv->wev = 0; } }
/** * Writes a #fb_mqtt_msg to the #fb_mqtt. * * @param mqtt The #fb_mqtt. * @param msg The #fb_mqtt_msg. **/ void fb_mqtt_write(fb_mqtt_t *mqtt, fb_mqtt_msg_t *msg) { const GByteArray *bytes; gint fd; g_return_if_fail(mqtt != NULL); bytes = fb_mqtt_msg_bytes(msg); if (G_UNLIKELY(bytes == NULL)) { fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, "Failed to format data"); return; } fb_util_hexdump(bytes, 2, "Writing %d (flags: 0x%0X)", msg->type, msg->flags); fd = ssl_getfd(mqtt->ssl); g_byte_array_append(mqtt->wbuf, bytes->data, bytes->len); if ((mqtt->wev < 1) && fb_mqtt_cb_write(mqtt, fd, B_EV_IO_WRITE)) mqtt->wev = b_input_add(fd, B_EV_IO_WRITE, fb_mqtt_cb_write, mqtt); }
/** * Implemented #b_event_handler for the writing of #fb_mqtt->fd. * * @param data The user defined data, which is #fb_mqtt. * @param fd The event file descriptor. * @param cond The #b_input_condition. * * @return TRUE for continued event handling, otherwise FALSE. **/ static gboolean fb_mqtt_cb_write(gpointer data, gint fd, b_input_condition cond) { fb_mqtt_t *mqtt = data; gssize wize; wize = ssl_write(mqtt->ssl, (gchar*) mqtt->wbuf->data, mqtt->wbuf->len); if (wize < 0) { fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, "Failed to write data"); return FALSE; } if (wize > 0) g_byte_array_remove_range(mqtt->wbuf, 0, wize); if (mqtt->wbuf->len < 1) { mqtt->wev = 0; return FALSE; } return TRUE; }
void fb_mqtt_read(FbMqtt *mqtt, FbMqttMessage *msg) { FbMqttMessage *nsg; FbMqttPrivate *priv; FbMqttMessagePrivate *mriv; GByteArray *wytes; gchar *str; guint8 chr; guint16 mid; g_return_if_fail(FB_IS_MQTT(mqtt)); g_return_if_fail(FB_IS_MQTT_MESSAGE(msg)); priv = mqtt->priv; mriv = msg->priv; fb_util_debug_hexdump(FB_UTIL_DEBUG_INFO, mriv->bytes, "Reading %d (flags: 0x%0X)", mriv->type, mriv->flags); switch (mriv->type) { case FB_MQTT_MESSAGE_TYPE_CONNACK: if (!fb_mqtt_message_read_byte(msg, NULL) || !fb_mqtt_message_read_byte(msg, &chr)) { break; } if (chr != FB_MQTT_ERROR_SUCCESS) { fb_mqtt_error(mqtt, chr, _("Connection failed (%u)"), chr); return; } priv->connected = TRUE; fb_mqtt_ping(mqtt); g_signal_emit_by_name(mqtt, "connect"); return; case FB_MQTT_MESSAGE_TYPE_PUBLISH: if (!fb_mqtt_message_read_str(msg, &str)) { break; } if ((mriv->flags & FB_MQTT_MESSAGE_FLAG_QOS1) || (mriv->flags & FB_MQTT_MESSAGE_FLAG_QOS2)) { if (mriv->flags & FB_MQTT_MESSAGE_FLAG_QOS1) { chr = FB_MQTT_MESSAGE_TYPE_PUBACK; } else { chr = FB_MQTT_MESSAGE_TYPE_PUBREC; } if (!fb_mqtt_message_read_mid(msg, &mid)) { break; } nsg = fb_mqtt_message_new(chr, 0); fb_mqtt_message_write_u16(nsg, mid); fb_mqtt_write(mqtt, nsg); g_object_unref(nsg); } wytes = g_byte_array_new(); fb_mqtt_message_read_r(msg, wytes); g_signal_emit_by_name(mqtt, "publish", str, wytes); g_byte_array_free(wytes, TRUE); g_free(str); return; case FB_MQTT_MESSAGE_TYPE_PUBREL: if (!fb_mqtt_message_read_mid(msg, &mid)) { break; } nsg = fb_mqtt_message_new(FB_MQTT_MESSAGE_TYPE_PUBCOMP, 0); fb_mqtt_message_write_u16(nsg, mid); /* Message identifier */ fb_mqtt_write(mqtt, nsg); g_object_unref(nsg); return; case FB_MQTT_MESSAGE_TYPE_PINGRESP: fb_mqtt_ping(mqtt); return; case FB_MQTT_MESSAGE_TYPE_PUBACK: case FB_MQTT_MESSAGE_TYPE_PUBCOMP: case FB_MQTT_MESSAGE_TYPE_SUBACK: case FB_MQTT_MESSAGE_TYPE_UNSUBACK: return; default: fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, _("Unknown packet (%u)"), mriv->type); return; } /* Since no case returned, there was a parse error. */ fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, _("Failed to parse message")); }
static void fb_mqtt_cb_read(gpointer data, gint fd, PurpleInputCondition cond) { FbMqtt *mqtt = data; FbMqttMessage *msg; FbMqttPrivate *priv = mqtt->priv; gint res; guint mult; guint8 buf[1024]; guint8 byte; gsize size; gssize rize; if (priv->remz < 1) { /* Reset the read buffer */ g_byte_array_set_size(priv->rbuf, 0); res = purple_ssl_read(priv->gsc, &byte, sizeof byte); g_byte_array_append(priv->rbuf, &byte, sizeof byte); if (res != sizeof byte) { fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, _("Failed to read fixed header")); return; } mult = 1; do { res = purple_ssl_read(priv->gsc, &byte, sizeof byte); g_byte_array_append(priv->rbuf, &byte, sizeof byte); if (res != sizeof byte) { fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, _("Failed to read packet size")); return; } priv->remz += (byte & 127) * mult; mult *= 128; } while ((byte & 128) != 0); } if (priv->remz > 0) { size = MIN(priv->remz, sizeof buf); rize = purple_ssl_read(priv->gsc, buf, size); if (rize < 1) { fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, _("Failed to read packet data")); return; } g_byte_array_append(priv->rbuf, buf, rize); priv->remz -= rize; } if (priv->remz < 1) { msg = fb_mqtt_message_new_bytes(priv->rbuf); priv->remz = 0; if (G_UNLIKELY(msg == NULL)) { fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, _("Failed to parse message")); return; } fb_mqtt_read(mqtt, msg); g_object_unref(msg); } }
/** * Read a #GByteArray to the #fb_mqtt. * * @param mqtt The #fb_mqtt. * @param bytes The #GByteArray. **/ void fb_mqtt_read(fb_mqtt_t *mqtt, fb_mqtt_msg_t *msg) { fb_mqtt_msg_t *nsg; GByteArray *wytes; gchar *str; guint8 chr; guint16 mid; g_return_if_fail(mqtt != NULL); g_return_if_fail(msg != NULL); fb_util_hexdump(msg->bytes, 2, "Reading %d (flags: 0x%0X)", msg->type, msg->flags); switch (msg->type) { case FB_MQTT_MSG_TYPE_CONNACK: if (!fb_mqtt_msg_read_byte(msg, NULL) || !fb_mqtt_msg_read_byte(msg, &chr)) { break; } if (chr != FB_MQTT_ERROR_SUCCESS) { fb_mqtt_error(mqtt, chr, "Connection failed (%u)", chr); return; } mqtt->connected = TRUE; fb_mqtt_ping(mqtt); FB_MQTT_FUNC(mqtt, connack); return; case FB_MQTT_MSG_TYPE_PUBLISH: if (!fb_mqtt_msg_read_str(msg, &str)) break; if ((msg->flags & FB_MQTT_MSG_FLAG_QOS1) || (msg->flags & FB_MQTT_MSG_FLAG_QOS2)) { if (msg->flags & FB_MQTT_MSG_FLAG_QOS1) chr = FB_MQTT_MSG_TYPE_PUBACK; else chr = FB_MQTT_MSG_TYPE_PUBREC; if (!fb_mqtt_msg_read_mid(msg, &mid)) break; nsg = fb_mqtt_msg_new(chr, 0); fb_mqtt_msg_write_u16(nsg, mid); fb_mqtt_write(mqtt, nsg); fb_mqtt_msg_free(nsg); } wytes = g_byte_array_new(); fb_mqtt_msg_read_r(msg, wytes); FB_MQTT_FUNC(mqtt, publish, str, wytes); g_byte_array_free(wytes, TRUE); g_free(str); return; case FB_MQTT_MSG_TYPE_PUBREL: if (!fb_mqtt_msg_read_mid(msg, &mid)) break; nsg = fb_mqtt_msg_new(FB_MQTT_MSG_TYPE_PUBCOMP, 0); fb_mqtt_msg_write_u16(nsg, mid); /* Message identifier */ fb_mqtt_write(mqtt, nsg); fb_mqtt_msg_free(nsg); return; case FB_MQTT_MSG_TYPE_PINGRESP: fb_mqtt_ping(mqtt); return; case FB_MQTT_MSG_TYPE_PUBACK: case FB_MQTT_MSG_TYPE_PUBCOMP: case FB_MQTT_MSG_TYPE_SUBACK: case FB_MQTT_MSG_TYPE_UNSUBACK: return; default: fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, "Unknown packet (%u)", msg->type); return; } /* Since no case returned, there was a parse error. */ fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, "Failed to parse message"); }
/** * Implemented #b_event_handler for the read of #fb_mqtt->fd. * * @param data The user defined data, which is #fb_mqtt. * @param fd The event file descriptor. * @param cond The #b_input_condition. * * @return TRUE for continued event handling, otherwise FALSE. **/ static gboolean fb_mqtt_cb_read(gpointer data, gint fd, b_input_condition cond) { fb_mqtt_t *mqtt = data; fb_mqtt_msg_t *msg; gchar buf[1024]; guint8 byte; guint mult; gssize rize; gint res; if (mqtt->remz < 1) { /* Reset the read buffer */ g_byte_array_set_size(mqtt->rbuf, 0); res = ssl_read(mqtt->ssl, (gchar*) &byte, sizeof byte); g_byte_array_append(mqtt->rbuf, &byte, sizeof byte); if (res != sizeof byte) goto error; mult = 1; do { res = ssl_read(mqtt->ssl, (gchar*) &byte, sizeof byte); g_byte_array_append(mqtt->rbuf, &byte, sizeof byte); if (res != sizeof byte) goto error; mqtt->remz += (byte & 127) * mult; mult *= 128; } while ((byte & 128) != 0); } if (mqtt->remz > 0) { rize = ssl_read(mqtt->ssl, buf, MIN(mqtt->remz, sizeof buf)); if (rize < 1) goto error; g_byte_array_append(mqtt->rbuf, (guint8*) buf, rize); mqtt->remz -= rize; } if (mqtt->remz < 1) { msg = fb_mqtt_msg_new_bytes(mqtt->rbuf); mqtt->remz = 0; if (G_UNLIKELY(msg == NULL)) goto error; fb_mqtt_read(mqtt, msg); fb_mqtt_msg_free(msg); } return TRUE; error: fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, "Short read"); return FALSE; }