+/**
+ * Cache a message to be sent once tunnel is online.
+ *
+ * @param t Tunnel to hold the message.
+ * @param msg Message itself (copy will be made).
+ */
+static struct MeshTunnelDelayed *
+queue_data (struct MeshTunnel3 *t, const struct GNUNET_MessageHeader *msg)
+{
+ struct MeshTunnelDelayed *tqd;
+ uint16_t size = ntohs (msg->size);
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "queue data on Tunnel %s\n", GMT_2s (t));
+
+ if (GNUNET_YES == is_ready (t))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ tqd = GNUNET_malloc (sizeof (struct MeshTunnelDelayed) + size);
+
+ tqd->t = t;
+ memcpy (&tqd[1], msg, size);
+ GNUNET_CONTAINER_DLL_insert_tail (t->tq_head, t->tq_tail, tqd);
+ return tqd;
+}
+
+
+
+/**
+ * Sends an already built message on a tunnel, encrypting it and
+ * choosing the best connection.
+ *
+ * @param message Message to send. Function modifies it.
+ * @param t Tunnel on which this message is transmitted.
+ * @param force Force the tunnel to take the message (buffer overfill).
+ * @param cont Continuation to call once message is really sent.
+ * @param cont_cls Closure for @c cont.
+ * @param existing_q In case this a transmission of previously queued data,
+ * this should be TunnelQueue given to the client.
+ * Otherwise, NULL.
+ *
+ * @return Handle to cancel message. NULL if @c cont is NULL.
+ */
+static struct MeshTunnel3Queue *
+send_prebuilt_message (const struct GNUNET_MessageHeader *message,
+ struct MeshTunnel3 *t, int force,
+ GMT_sent cont, void *cont_cls,
+ struct MeshTunnel3Queue *existing_q)
+{
+ struct MeshTunnel3Queue *tq;
+ struct MeshConnection *c;
+ struct GNUNET_MESH_Encrypted *msg;
+ size_t size = ntohs (message->size);
+ char cbuf[sizeof (struct GNUNET_MESH_Encrypted) + size];
+ uint32_t iv;
+ uint16_t type;
+ int fwd;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "GMT Send on Tunnel %s\n", GMT_2s (t));
+
+ if (GNUNET_NO == is_ready (t))
+ {
+ struct MeshTunnelDelayed *tqd;
+ /* A non null existing_q indicates sending of queued data.
+ * Should only happen after tunnel becomes ready.
+ */
+ GNUNET_assert (NULL == existing_q);
+ tqd = queue_data (t, message);
+ if (NULL == cont)
+ return NULL;
+ tq = GNUNET_new (struct MeshTunnel3Queue);
+ tq->tqd = tqd;
+ tqd->tq = tq;
+ tq->cont = cont;
+ tq->cont_cls = cont_cls;
+ return tq;
+ }
+
+ GNUNET_assert (GNUNET_NO == GMT_is_loopback (t));
+
+ iv = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
+ msg = (struct GNUNET_MESH_Encrypted *) cbuf;
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_ENCRYPTED);
+ msg->iv = iv;
+ GNUNET_assert (t_encrypt (t, &msg[1], message, size, iv) == size);
+ msg->header.size = htons (sizeof (struct GNUNET_MESH_Encrypted) + size);
+ c = tunnel_get_connection (t);
+ if (NULL == c)
+ {
+ GNUNET_break (GNUNET_YES == t->destroy);
+ return NULL;
+ }
+ type = ntohs (message->type);
+ switch (type)
+ {
+ case GNUNET_MESSAGE_TYPE_MESH_DATA:
+ case GNUNET_MESSAGE_TYPE_MESH_DATA_ACK:
+ case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_CREATE:
+ case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_DESTROY:
+ case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_ACK:
+ msg->cid = *GMC_get_id (c);
+ msg->ttl = htonl (default_ttl);
+ break;
+ default:
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "unkown type %s\n",
+ GM_m2s (type));
+ GNUNET_break (0);
+ }
+
+ fwd = GMC_is_origin (c, GNUNET_YES);
+
+ if (NULL == cont)
+ {
+ (void) GMC_send_prebuilt_message (&msg->header, c, fwd, force, NULL, NULL);
+ return NULL;
+ }
+ if (NULL == existing_q)
+ {
+ tq = GNUNET_new (struct MeshTunnel3Queue); /* FIXME valgrind: leak*/
+ }
+ else
+ {
+ tq = existing_q;
+ tq->tqd = NULL;
+ }
+ tq->cq = GMC_send_prebuilt_message (&msg->header, c, fwd, force,
+ &message_sent, tq);
+ tq->cont = cont;
+ tq->cont_cls = cont_cls;
+
+ return tq;
+}
+
+