Added preliminary Symbian support.
[silc.git] / lib / silcutil / symbian / silcsymbiansocketstream.cpp
1 \r
2 /*\r
3 \r
4   silcsymbiansocketstream.cpp\r
5 \r
6   Author: Pekka Riikonen <priikone@silcnet.org>\r
7 \r
8   Copyright (C) 2006 Pekka Riikonen\r
9 \r
10   This program is free software; you can redistribute it and/or modify\r
11   it under the terms of the GNU General Public License as published by\r
12   the Free Software Foundation; version 2 of the License.\r
13 \r
14   This program is distributed in the hope that it will be useful,\r
15   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
17   GNU General Public License for more details.\r
18 \r
19 */\r
20 \r
21 /* In this implementation the sockets are in blocking mode, except that\r
22    on Symbian the blocking mode is actually asynchronous, which semantically\r
23    translates into non-blocking mode.  The non-blocking mode just is not\r
24    explicitly set because it would require us also to explicitly poll for the\r
25    socket, which is done automatically by the Active Scheduler in blocking\r
26    mode. */\r
27 \r
28 #include "silc.h"\r
29 #include "silcsymbiansocketstream.h"\r
30 \r
31 /***************************** Socket Classes *******************************/\r
32 \r
33 /* Socket stream sender */\r
34 \r
35 class SilcSymbianSocketSend : public CActive {\r
36 public:\r
37   /* Constructor */\r
38   SilcSymbianSocketSend() : CActive(CActive::EPriorityStandard)\r
39   {\r
40     CActiveScheduler::Add(this);\r
41   }\r
42 \r
43   /* Destructor */\r
44   ~SilcSymbianSocketSend()\r
45   {\r
46     Cancel();\r
47   }\r
48 \r
49   /* Send data */\r
50   void Send(const TDesC8& buf, TSockXfrLength& ret_len)\r
51   {\r
52     s->sock->Send(buf, 0, iStatus, ret_len);\r
53     SetActive();\r
54   }\r
55 \r
56   /* Send data */\r
57   void Send(const TDesC8& buf, TSockXfrLength& ret_len,\r
58             const char *remote_ip, int remote_port)\r
59   {\r
60     TInetAddr remote;\r
61     TBuf<64> tmp;\r
62 \r
63     remote = TInetAddr(remote_port);\r
64     tmp = (TText *)remote_ip;\r
65     if (remote.Input(tmp) == KErrNone) {\r
66       s->sock->SendTo(buf, remote, 0, iStatus, ret_len);\r
67       SetActive();\r
68     }\r
69   }\r
70 \r
71   /* Sending callback */\r
72   void RunL()\r
73   {\r
74     if (iStatus != KErrNone) {\r
75       if (iStatus == KErrEof)\r
76         s->eof = 1;\r
77       else\r
78         s->error = 1;\r
79       return;\r
80     }\r
81 \r
82     /* Call stream callback */\r
83     if (s->stream && s->stream->notifier)\r
84       s->stream->notifier(s->stream, SILC_STREAM_CAN_WRITE,\r
85                           s->stream->notifier_context);\r
86   }\r
87 \r
88   /* Cancel */\r
89   void DoCancel()\r
90   {\r
91     s->sock->CancelWrite();\r
92   }\r
93 \r
94   SilcSymbianSocket *s;\r
95 };\r
96 \r
97 /* Socket stream receiver */\r
98 \r
99 class SilcSymbianSocketReceive : public CActive {\r
100 public:\r
101   /* Constructor */\r
102   SilcSymbianSocketReceive() : CActive(CActive::EPriorityStandard)\r
103   {\r
104     CActiveScheduler::Add(this);\r
105   }\r
106 \r
107   /* Destructor */\r
108   ~SilcSymbianSocketReceive()\r
109   {\r
110     Cancel();\r
111   }\r
112 \r
113   /* Read data */\r
114   void Read()\r
115   {\r
116     if (!s->stream || s->stream->connected)\r
117       s->sock->RecvOneOrMore(inbuf, 0, iStatus, inbuf_len);\r
118     else\r
119       s->sock->RecvFrom(inbuf, remote, 0, iStatus);\r
120     SetActive();\r
121   }\r
122 \r
123   /* Reading callback */\r
124   void RunL()\r
125   {\r
126     if (iStatus != KErrNone) {\r
127       if (iStatus == KErrEof)\r
128         s->eof = 1;\r
129       else\r
130         s->error = 1;\r
131       return;\r
132     }\r
133 \r
134     if (!inbuf_ptr)\r
135       inbuf_ptr = inbuf.Ptr();\r
136     inbuf_len = inbuf.Length();\r
137 \r
138     /* Call stream callback */\r
139     if (s->stream && s->stream->notifier)\r
140       s->stream->notifier(s->stream, SILC_STREAM_CAN_READ,\r
141                           s->stream->notifier_context);\r
142 \r
143     /* Read more */\r
144     Read();\r
145   }\r
146 \r
147   /* Cancel */\r
148   void DoCancel()\r
149   {\r
150     s->sock->CancelRecv();\r
151   }\r
152 \r
153   TBuf8<8192> inbuf;\r
154   const unsigned char *inbuf_ptr;\r
155   TSockXfrLength inbuf_len;\r
156   SilcSymbianSocket *s;\r
157   TInetAddr remote;\r
158 };\r
159 \r
160 /* Creates symbian socket stream context */\r
161 \r
162 SilcSymbianSocket *silc_create_symbian_socket(RSocket *sock,\r
163                                               RSocketServ *ss)\r
164 {\r
165   SilcSymbianSocket *stream;\r
166 \r
167   stream = (SilcSymbianSocket *)silc_calloc(1, sizeof(*stream));\r
168   if (!stream)\r
169     return NULL;\r
170   stream->sock = sock;\r
171   stream->ss = ss;\r
172 \r
173   stream->send = new SilcSymbianSocketSend;\r
174   if (!stream->send) {\r
175     silc_free(stream);\r
176     return NULL;\r
177   }\r
178 \r
179   stream->receive = new SilcSymbianSocketReceive;\r
180   if (!stream->receive) {\r
181     delete stream->send;\r
182     silc_free(stream);\r
183     return NULL;\r
184   }\r
185 \r
186   return stream;\r
187 }\r
188 \r
189 /***************************** SILC Stream API ******************************/\r
190 \r
191 /* Stream read operation */\r
192 \r
193 int silc_socket_stream_read(SilcStream stream, unsigned char *buf,\r
194                             SilcUInt32 buf_len)\r
195 {\r
196   SilcSocketStream socket_stream = (SilcSocketStream)stream;\r
197   SilcSymbianSocket *s = (SilcSymbianSocket *)socket_stream->sock;\r
198   SilcSymbianSocketReceive *recv = s->receive;\r
199   int len;\r
200 \r
201   if (s->error || !s->stream)\r
202     return -2;\r
203   if (s->eof)\r
204     return 0;\r
205   if (!recv->inbuf_len() || !recv->inbuf_ptr)\r
206     return -1;\r
207 \r
208   len = recv->inbuf_len();\r
209   if (buf_len < len)\r
210     len = buf_len;\r
211 \r
212   /* Copy the read data */\r
213   memcpy(buf, recv->inbuf_ptr, len);\r
214 \r
215   recv->inbuf_ptr = NULL;\r
216   if (len < recv->inbuf_len())\r
217     recv->inbuf_ptr += len;\r
218 \r
219   return len;\r
220 }\r
221 \r
222 /* Stream write operation */\r
223 \r
224 int silc_socket_stream_write(SilcStream stream, const unsigned char *data,\r
225                              SilcUInt32 data_len)\r
226 {\r
227   SilcSocketStream socket_stream = (SilcSocketStream)stream;\r
228   SilcSymbianSocket *s = (SilcSymbianSocket *)socket_stream->sock;\r
229   SilcSymbianSocketSend *send = s->send;\r
230   TSockXfrLength ret_len;\r
231   TPtrC8 write_buf(data, data_len);\r
232 \r
233   if (s->would_block)\r
234     return -1;\r
235   if (s->error || !s->stream)\r
236     return -2;\r
237   if (s->eof)\r
238     return 0;\r
239 \r
240   /* Send data */\r
241   send->Send(write_buf, ret_len);\r
242   if (send->iStatus.Int() != KErrNone) {\r
243     if (send->iStatus.Int() == KErrEof)\r
244       return 0;\r
245     return -2;\r
246   }\r
247 \r
248   if (!ret_len())\r
249     return -1;\r
250 \r
251   s->would_block = 0;\r
252   if (ret_len() < data_len)\r
253     s->would_block = 1;\r
254 \r
255   return ret_len();\r
256 }\r
257 \r
258 /* Receive UDP packet, connected socket. */\r
259 \r
260 int silc_socket_udp_stream_read(SilcStream stream, unsigned char *buf,\r
261                                 SilcUInt32 buf_len)\r
262 {\r
263   return silc_net_udp_receive(stream, NULL, 0, NULL, buf, buf_len);\r
264 }\r
265 \r
266 /* Send UDP packet, connected socket. */\r
267 \r
268 int silc_socket_udp_stream_write(SilcStream stream, const unsigned char *data,\r
269                                  SilcUInt32 data_len)\r
270 {\r
271   SilcSocketStream sock = (SilcSocketStream)stream;\r
272 \r
273   /* In connectionless state check if remote IP and port is provided */\r
274   if (!sock->connected && sock->ip && sock->port)\r
275     return silc_net_udp_send(stream, sock->ip, sock->port, data, data_len);\r
276 \r
277   /* In connected state use normal writing to socket. */\r
278   return silc_socket_stream_write(stream, data, data_len);\r
279 }\r
280 \r
281 /* Receive UDP packet, connectionless socket */\r
282 \r
283 int silc_net_udp_receive(SilcStream stream, char *remote_ip_addr,\r
284                          SilcUInt32 remote_ip_addr_size, int *remote_port,\r
285                          unsigned char *buf, SilcUInt32 buf_len)\r
286 {\r
287   SilcSocketStream socket_stream = (SilcSocketStream)stream;\r
288   SilcSymbianSocket *s = (SilcSymbianSocket *)socket_stream->sock;\r
289   SilcSymbianSocketReceive *recv = s->receive;\r
290   int len;\r
291 \r
292   if (s->eof)\r
293     return 0;\r
294   if (!recv->inbuf_len() || !recv->inbuf_ptr)\r
295     return -1;\r
296 \r
297   len = recv->inbuf_len();\r
298   if (buf_len < len)\r
299     len = buf_len;\r
300 \r
301   /* Copy the read data */\r
302   memcpy(buf, recv->inbuf_ptr, len);\r
303 \r
304   recv->inbuf_ptr = NULL;\r
305   if (len < recv->inbuf_len())\r
306     recv->inbuf_ptr += len;\r
307 \r
308   if (remote_ip_addr && remote_ip_addr_size && remote_port) {\r
309     TBuf<64> ip;\r
310     recv->remote.Output(ip);\r
311     silc_strncat(remote_ip_addr, remote_ip_addr_size, (const char *)ip.Ptr(),\r
312                  ip.Length());\r
313     *remote_port = recv->remote.Port();\r
314   }\r
315 \r
316   return len;\r
317 }\r
318 \r
319 /* Send UDP packet, connectionless socket  */\r
320 \r
321 int silc_net_udp_send(SilcStream stream,\r
322                       const char *remote_ip_addr, int remote_port,\r
323                       const unsigned char *data, SilcUInt32 data_len)\r
324 {\r
325   SilcSocketStream socket_stream = (SilcSocketStream)stream;\r
326   SilcSymbianSocket *s = (SilcSymbianSocket *)socket_stream->sock;\r
327   SilcSymbianSocketSend *send = s->send;\r
328   TSockXfrLength ret_len;\r
329   TPtrC8 write_buf(data, data_len);\r
330 \r
331   if (s->would_block)\r
332     return -1;\r
333   if (s->eof)\r
334     return 0;\r
335 \r
336   /* Send data */\r
337   send->Send(write_buf, ret_len, remote_ip_addr, remote_port);\r
338   if (send->iStatus.Int() != KErrNone) {\r
339     if (send->iStatus.Int() == KErrEof)\r
340       return 0;\r
341     return -2;\r
342   }\r
343 \r
344   if (!ret_len())\r
345     return -1;\r
346 \r
347   s->would_block = 0;\r
348   if (ret_len() < data_len)\r
349     s->would_block = 1;\r
350 \r
351   return ret_len();\r
352 }\r
353 \r
354 /* Closes socket */\r
355 \r
356 SilcBool silc_socket_stream_close(SilcStream stream)\r
357 {\r
358   SilcSocketStream socket_stream = (SilcSocketStream)stream;\r
359   SilcSymbianSocket *s;\r
360 \r
361   if (!SILC_IS_SOCKET_STREAM(socket_stream) &&\r
362       !SILC_IS_SOCKET_STREAM_UDP(socket_stream))\r
363     return FALSE;\r
364 \r
365   s = (SilcSymbianSocket *)socket_stream->sock;\r
366   s->sock->Close();\r
367 \r
368   return TRUE;\r
369 }\r
370 \r
371 /* Destroys the stream */\r
372 \r
373 void silc_socket_stream_destroy(SilcStream stream)\r
374 {\r
375   SilcSocketStream socket_stream = (SilcSocketStream)stream;\r
376   SilcSymbianSocket *s;\r
377 \r
378   if (!SILC_IS_SOCKET_STREAM(socket_stream) &&\r
379       !SILC_IS_SOCKET_STREAM_UDP(socket_stream))\r
380     return;\r
381 \r
382   s = (SilcSymbianSocket *)socket_stream->sock;\r
383 \r
384   silc_socket_stream_close(stream);\r
385   silc_free(socket_stream->ip);\r
386   silc_free(socket_stream->hostname);\r
387   silc_free(socket_stream);\r
388   delete s->send;\r
389   delete s->receive;\r
390   delete s->sock;\r
391   if (s->ss) {\r
392     s->ss->Close();\r
393     delete s->ss;\r
394   }\r
395   delete s;\r
396 }\r
397 \r
398 /* Sets stream notification callback for the stream */\r
399 \r
400 void silc_socket_stream_notifier(SilcStream stream,\r
401                                  SilcSchedule schedule,\r
402                                  SilcStreamNotifier callback,\r
403                                  void *context)\r
404 {\r
405   SilcSocketStream socket_stream = (SilcSocketStream)stream;\r
406   SilcSymbianSocket *s;\r
407 \r
408   if (!SILC_IS_SOCKET_STREAM(socket_stream) &&\r
409       !SILC_IS_SOCKET_STREAM_UDP(socket_stream))\r
410     return;\r
411 \r
412   s = (SilcSymbianSocket *)socket_stream->sock;\r
413   if (callback)\r
414     s->stream = socket_stream;\r
415   else\r
416     s->stream = NULL;\r
417 \r
418   socket_stream->notifier = callback;\r
419   socket_stream->notifier_context = context;\r
420   socket_stream->schedule = schedule;\r
421 }\r