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