Added preliminary Symbian support.
[silc.git] / lib / silcutil / unix / silcunixsocketstream.c
index 1eded3d36b351e8a76a0c49f2fc9bd7363ad8b93..1b9d66295036a22721ed963d1e44d262aed27bd4 100644 (file)
 
 #include "silc.h"
 
+/************************ Static utility functions **************************/
+
+/* The IO process callback that calls the notifier callback to upper layer. */
+
+SILC_TASK_CALLBACK(silc_socket_stream_io)
+{
+  SilcSocketStream stream = context;
+
+  if (silc_unlikely(!stream->notifier))
+    return;
+
+  switch (type) {
+  case SILC_TASK_READ:
+    stream->notifier(stream, SILC_STREAM_CAN_READ, stream->notifier_context);
+    break;
+
+  case SILC_TASK_WRITE:
+    stream->notifier(stream, SILC_STREAM_CAN_WRITE, stream->notifier_context);
+    break;
+
+  default:
+    break;
+  }
+}
+
+/**************************** Stream Operations *****************************/
+
 /* QoS read handler, this will call the read and write events to indicate
    that data is available again after a timeout. */
 
@@ -168,8 +195,10 @@ int silc_socket_stream_write(SilcStream stream, const unsigned char *data,
   }
 
   SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
-  silc_schedule_set_listen_fd(sock->schedule, sock->sock,
-                             SILC_TASK_READ, FALSE);
+  if (silc_schedule_get_fd_events(sock->schedule, sock->sock) &
+      SILC_TASK_WRITE)
+    silc_schedule_set_listen_fd(sock->schedule, sock->sock,
+                               SILC_TASK_READ, FALSE);
 
   return ret;
 }
@@ -188,19 +217,13 @@ int silc_socket_udp_stream_write(SilcStream stream, const unsigned char *data,
                                 SilcUInt32 data_len)
 {
   SilcSocketStream sock = stream;
-  int ret;
 
-  SILC_LOG_DEBUG(("Writing data to UDP socket %d", sock->sock));
-
-  ret = send(sock->sock, data, data_len, 0);
-  if (ret < 0) {
-    /* Ignore error and return success */
-    SILC_LOG_DEBUG(("Cannot write to UDP socket: %s", strerror(errno)));
-    return data_len;
-  }
+  /* In connectionless state check if remote IP and port is provided */
+  if (!sock->connected && sock->ip && sock->port)
+    return silc_net_udp_send(stream, sock->ip, sock->port, data, data_len);
 
-  SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
-  return ret;
+  /* In connected state use normal writing to socket. */
+  return silc_socket_stream_write(stream, data, data_len);
 }
 
 #if 0
@@ -223,3 +246,90 @@ SilcBool silc_socket_get_error(SilcStream sock, char *error,
   return TRUE;
 }
 #endif /* 0 */
+
+/* Closes socket */
+
+SilcBool silc_socket_stream_close(SilcStream stream)
+{
+  SilcSocketStream socket_stream = stream;
+
+  if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
+      !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
+    return FALSE;
+
+  silc_schedule_unset_listen_fd(socket_stream->schedule, socket_stream->sock);
+  silc_net_close_connection(socket_stream->sock);
+
+  return TRUE;
+}
+
+/* Destroys the stream */
+
+void silc_socket_stream_destroy(SilcStream stream)
+{
+  SilcSocketStream socket_stream = stream;
+
+  if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
+      !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
+    return;
+
+  silc_socket_stream_close(socket_stream);
+  silc_free(socket_stream->ip);
+  silc_free(socket_stream->hostname);
+  if (socket_stream->schedule)
+    silc_schedule_task_del_by_fd(socket_stream->schedule, socket_stream->sock);
+
+  if (socket_stream->qos) {
+    silc_schedule_task_del_by_context(socket_stream->schedule,
+                                     socket_stream->qos);
+    if (socket_stream->qos->buffer) {
+      memset(socket_stream->qos->buffer, 0,
+            socket_stream->qos->read_limit_bytes);
+      silc_free(socket_stream->qos->buffer);
+    }
+    silc_free(socket_stream->qos);
+  }
+
+  if (socket_stream->schedule)
+    silc_schedule_wakeup(socket_stream->schedule);
+
+  silc_free(socket_stream);
+}
+
+/* Sets stream notification callback for the stream */
+
+void silc_socket_stream_notifier(SilcStream stream,
+                                SilcSchedule schedule,
+                                SilcStreamNotifier callback,
+                                void *context)
+{
+  SilcSocketStream socket_stream = stream;
+
+  if (!SILC_IS_SOCKET_STREAM(socket_stream) &&
+      !SILC_IS_SOCKET_STREAM_UDP(socket_stream))
+    return;
+
+  SILC_LOG_DEBUG(("Setting stream notifier callback"));
+
+  socket_stream->notifier = callback;
+  socket_stream->notifier_context = context;
+  socket_stream->schedule = schedule;
+
+  if (socket_stream->notifier) {
+    /* Add the socket to scheduler.  Safe to call if already added. */
+    silc_schedule_task_add_fd(socket_stream->schedule, socket_stream->sock,
+                             silc_socket_stream_io, socket_stream);
+
+    /* Initially set socket for reading */
+    silc_schedule_set_listen_fd(socket_stream->schedule, socket_stream->sock,
+                               SILC_TASK_READ, FALSE);
+    silc_schedule_wakeup(socket_stream->schedule);
+  } else {
+    /* Unschedule the socket */
+    silc_schedule_unset_listen_fd(socket_stream->schedule,
+                                 socket_stream->sock);
+    silc_schedule_task_del_by_fd(socket_stream->schedule,
+                                socket_stream->sock);
+    silc_schedule_wakeup(socket_stream->schedule);
+  }
+}