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