99d1ff16f4ae9ae815f46ffa4775c40a3b1b5130
[framework/uifw/eet.git] / src / lib / eet_connection.c
1 /*
2  * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
3  */
4
5 #ifdef HAVE_CONFIG_H
6 # include <config.h>
7 #endif
8
9 #ifdef HAVE_ALLOCA_H
10 # include <alloca.h>
11 #elif defined __GNUC__
12 # define alloca __builtin_alloca
13 #elif defined _AIX
14 # define alloca __alloca
15 #elif defined _MSC_VER
16 # include <malloc.h>
17 # define alloca _alloca
18 #else
19 # include <stddef.h>
20 # ifdef  __cplusplus
21 extern "C"
22 # endif
23 void *alloca (size_t);
24 #endif
25
26 #include <string.h>
27 #include <stdlib.h>
28
29 #ifdef HAVE_NETINET_IN_H
30 # include <netinet/in.h>
31 #endif
32
33 #ifdef _WIN32
34 # include <winsock2.h>
35 #endif
36
37 #include <Eina.h>
38
39 #include "Eet.h"
40 #include "Eet_private.h"
41
42 #define MAGIC_EET_DATA_PACKET 0x4270ACE1
43
44 typedef struct _Eet_Message Eet_Message;
45 struct _Eet_Message {
46    int magic;
47    int size;
48 };
49
50 struct _Eet_Connection
51 {
52    Eet_Read_Cb *eet_read_cb;
53    Eet_Write_Cb *eet_write_cb;
54    void *user_data;
55
56    size_t allocated;
57    size_t size;
58    size_t received;
59
60    void *buffer;
61 };
62
63 Eet_Connection *
64 eet_connection_new(Eet_Read_Cb *eet_read_cb,
65                    Eet_Write_Cb *eet_write_cb,
66                    const void *user_data)
67 {
68    Eet_Connection *conn;
69
70    if (!eet_read_cb || !eet_write_cb)
71      return NULL;
72
73    conn = calloc(1, sizeof (Eet_Connection));
74    if (!conn)
75      return NULL;
76
77    conn->eet_read_cb = eet_read_cb;
78    conn->eet_write_cb = eet_write_cb;
79    conn->user_data = (void*) user_data;
80
81    return conn;
82 }
83
84 int
85 eet_connection_received(Eet_Connection *conn, const void *data, size_t size)
86 {
87    if (!conn || !data || !size)
88      return size;
89
90    do {
91       size_t copy_size;
92
93       if (conn->size == 0)
94         {
95            const Eet_Message *msg;
96            size_t packet_size;
97
98            if (size < sizeof (Eet_Message))
99              break;
100
101            msg = data;
102            /* Check the magic */
103            if (ntohl(msg->magic) != MAGIC_EET_DATA_PACKET)
104              break;
105
106            packet_size = ntohl(msg->size);
107            /* Message should always be under 64K */
108            if (packet_size > 64 * 1024)
109              break;
110
111            data = (void*) (msg + 1);
112            size -= sizeof (msg);
113            if ((size_t) packet_size <= size)
114              {
115                 /* Not a partial receive, go the quick way. */
116                 if (!conn->eet_read_cb(data, packet_size, conn->user_data))
117                   break;
118
119                 data = (void*) ((char *) data + packet_size);
120                 size -= packet_size;
121
122                 conn->received = 0;
123                 continue;
124              }
125
126            conn->size = packet_size;
127            if (conn->allocated < conn->size)
128              {
129                 void *tmp;
130
131                 tmp = realloc(conn->buffer, conn->size);
132                 if (!tmp)
133                   break;
134
135                 conn->buffer = tmp;
136                 conn->allocated = conn->size;
137              }
138         }
139
140       /* Partial receive */
141       copy_size = (conn->size - conn->received >= size) ? size : conn->size - conn->received;
142       memcpy((char*) conn->buffer + conn->received, data, copy_size);
143
144       conn->received += copy_size;
145       data = (void*)((char *) data + copy_size);
146       size -= copy_size;
147
148       if (conn->received == conn->size)
149         {
150            size_t data_size;
151
152            data_size = conn->size;
153            conn->size = 0;
154            conn->received = 0;
155
156            /* Completed a packet. */
157            if (!conn->eet_read_cb(conn->buffer, data_size, conn->user_data))
158              {
159                 /* Something goes wrong. Stop now. */
160                 size += data_size;
161                 break;
162              }
163         }
164    } while (size > 0);
165
166    return size;
167 }
168
169 static Eina_Bool
170 _eet_connection_raw_send(Eet_Connection *conn, void *data, int data_size)
171 {
172    Eet_Message *message;
173
174    /* Message should never be above 64K */
175    if (data_size > 64 * 1024)
176      return EINA_FALSE;
177
178    message = alloca(data_size + sizeof (Eet_Message));
179    message->magic = htonl(MAGIC_EET_DATA_PACKET);
180    message->size = htonl(data_size);
181
182    memcpy(message + 1, data, data_size);
183
184    conn->eet_write_cb(message, data_size + sizeof (Eet_Message), conn->user_data);
185    return EINA_TRUE;
186 }
187
188 Eina_Bool
189 eet_connection_send(Eet_Connection *conn, Eet_Data_Descriptor *edd, const void *data_in, const char *cipher_key)
190 {
191    void *flat_data;
192    int data_size;
193    Eina_Bool ret = EINA_FALSE;
194
195    flat_data = eet_data_descriptor_encode_cipher(edd, data_in, cipher_key, &data_size);
196    if (!flat_data)
197      return EINA_FALSE;
198
199    if (_eet_connection_raw_send(conn, flat_data, data_size))
200      ret = EINA_TRUE;
201
202    free(flat_data);
203    return ret;
204 }
205
206 Eina_Bool
207 eet_connection_node_send(Eet_Connection *conn, Eet_Node *node, const char *cipher_key)
208 {
209    void *data;
210    int data_size;
211    Eina_Bool ret = EINA_FALSE;
212
213    data = eet_data_node_encode_cipher(node, cipher_key, &data_size);
214    if (!data)
215      return EINA_FALSE;
216
217    if (_eet_connection_raw_send(conn, data, data_size))
218      ret = EINA_TRUE;
219
220    free(data);
221    return ret;
222 }
223
224 void *
225 eet_connection_close(Eet_Connection *conn, Eina_Bool *on_going)
226 {
227    void *user_data;
228
229    if (!conn)
230      return NULL;
231
232    if (on_going)
233      *on_going = conn->received == 0 ? EINA_FALSE : EINA_TRUE;
234
235    user_data = conn->user_data;
236
237    free(conn->buffer);
238    free(conn);
239
240    return user_data;
241 }