Porting Toolkit to Symbian. It should work while some sporadic
[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     SetActive();
134   }
135
136   /* Reading callback */
137   virtual void RunL()
138   {
139     SILC_LOG_DEBUG(("RunL(), iStatus=%d", iStatus));
140
141     if (iStatus != KErrNone) {
142       if (iStatus == KErrEof)
143         s->eof = 1;
144       else
145         s->error = 1;
146
147       /* Call stream callback */
148       if (s->stream && s->stream->notifier)
149         s->stream->notifier(s->stream, SILC_STREAM_CAN_READ,
150                             s->stream->notifier_context);
151       return;
152     }
153
154     if (!s->stream || s->stream->connected)
155       inbuf_len = read_len();
156     else
157       inbuf_len = inbuf.Length();
158
159     if (inbuf_len) {
160       inbuf_ptr = inbuf.Ptr();
161       while (inbuf_ptr) {
162         /* Call stream callback until all has been read */
163         if (s->stream && s->stream->notifier)
164           s->stream->notifier(s->stream, SILC_STREAM_CAN_READ,
165                               s->stream->notifier_context);
166       }
167     }
168
169     /* Read more */
170     Read();
171   }
172
173   /* Cancel */
174   virtual void DoCancel()
175   {
176     s->sock->CancelRecv();
177   }
178
179   TBuf8<8192> inbuf;
180   const unsigned char *inbuf_ptr;
181   TInt inbuf_len;
182   TSockXfrLength read_len;
183   SilcSymbianSocket *s;
184   TInetAddr remote;
185 };
186
187 /* Creates symbian socket stream context */
188
189 SilcSymbianSocket *silc_create_symbian_socket(RSocket *sock,
190                                               RSocketServ *ss)
191 {
192   SilcSymbianSocket *stream;
193
194   stream = (SilcSymbianSocket *)silc_calloc(1, sizeof(*stream));
195   if (!stream)
196     return NULL;
197   stream->sock = sock;
198   stream->ss = ss;
199
200   SILC_LOG_DEBUG(("Create new Symbian socket %p", stream));
201
202   stream->send = new SilcSymbianSocketSend;
203   if (!stream->send) {
204     silc_free(stream);
205     return NULL;
206   }
207   stream->send->s = stream;
208
209   stream->receive = new SilcSymbianSocketReceive;
210   if (!stream->receive) {
211     delete stream->send;
212     silc_free(stream);
213     return NULL;
214   }
215   stream->receive->s = stream;
216   stream->receive->inbuf_ptr = NULL;
217   stream->receive->inbuf_len = 0;
218
219   return stream;
220 }
221
222 /***************************** SILC Stream API ******************************/
223
224 extern "C" {
225
226 /* Stream read operation */
227
228 int silc_socket_stream_read(SilcStream stream, unsigned char *buf,
229                             SilcUInt32 buf_len)
230 {
231   SilcSocketStream socket_stream = (SilcSocketStream)stream;
232   SilcSymbianSocket *s = (SilcSymbianSocket *)socket_stream->sock;
233   SilcSymbianSocketReceive *recv = s->receive;
234   int len;
235
236   SILC_LOG_DEBUG(("Reading from sock %p", s));
237
238   if (s->error || !s->stream) {
239     SILC_LOG_DEBUG(("Error reading from sock %p", s));
240     return -2;
241   }
242   if (s->eof) {
243     SILC_LOG_DEBUG(("EOF from sock %p", s));
244     return 0;
245   }
246   if (!recv->inbuf_len || !recv->inbuf_ptr) {
247     SILC_LOG_DEBUG(("Cannot read now from sock %p", s));
248     return -1;
249   }
250
251   len = recv->inbuf_len;
252   if (buf_len < len)
253     len = buf_len;
254
255   /* Copy the read data */
256   memcpy(buf, recv->inbuf_ptr, len);
257
258   if (len < recv->inbuf_len) {
259     recv->inbuf_ptr += len;
260     recv->inbuf_len -= len;
261   } else {
262     recv->inbuf_ptr = NULL;
263     recv->inbuf_len = 0;
264   }
265
266   SILC_LOG_DEBUG(("Read %d bytes", len));
267
268   return len;
269 }
270
271 /* Stream write operation */
272
273 int silc_socket_stream_write(SilcStream stream, const unsigned char *data,
274                              SilcUInt32 data_len)
275 {
276   SilcSocketStream socket_stream = (SilcSocketStream)stream;
277   SilcSymbianSocket *s = (SilcSymbianSocket *)socket_stream->sock;
278   SilcSymbianSocketSend *send = s->send;
279   TSockXfrLength ret_len;
280   TPtrC8 write_buf(data, data_len);
281
282   SILC_LOG_DEBUG(("Writing to sock %p", s));
283
284   if (s->error || !s->stream) {
285     SILC_LOG_DEBUG(("Error writing to sock %p", s));
286     return -2;
287   }
288   if (s->eof) {
289     SILC_LOG_DEBUG(("EOF from sock %p", s));
290     return 0;
291   }
292   if (s->would_block) {
293     SILC_LOG_DEBUG(("Cannot write now to sock %p", s));
294     return -1;
295   }
296
297   /* Send data */
298   send->Send(write_buf, ret_len);
299   if (send->iStatus.Int() != KErrNone) {
300     if (send->iStatus.Int() == KErrEof) {
301       SILC_LOG_DEBUG(("EOF from sock %p", s));
302       return 0;
303     }
304     SILC_LOG_DEBUG(("Error writing to sock %p, error %d", s,
305                     send->iStatus.Int()));
306     return -2;
307   }
308
309   if (!ret_len())
310     return -1;
311
312   s->would_block = 0;
313   if (ret_len() < data_len)
314     s->would_block = 1;
315
316   SILC_LOG_DEBUG(("Wrote %d bytes", ret_len()));
317
318   return ret_len();
319 }
320
321 /* Receive UDP packet, connected socket. */
322
323 int silc_socket_udp_stream_read(SilcStream stream, unsigned char *buf,
324                                 SilcUInt32 buf_len)
325 {
326   return silc_net_udp_receive(stream, NULL, 0, NULL, buf, buf_len);
327 }
328
329 /* Send UDP packet, connected socket. */
330
331 int silc_socket_udp_stream_write(SilcStream stream, const unsigned char *data,
332                                  SilcUInt32 data_len)
333 {
334   SilcSocketStream sock = (SilcSocketStream)stream;
335
336   /* In connectionless state check if remote IP and port is provided */
337   if (!sock->connected && sock->ip && sock->port)
338     return silc_net_udp_send(stream, sock->ip, sock->port, data, data_len);
339
340   /* In connected state use normal writing to socket. */
341   return silc_socket_stream_write(stream, data, data_len);
342 }
343
344 /* Receive UDP packet, connectionless socket */
345
346 int silc_net_udp_receive(SilcStream stream, char *remote_ip_addr,
347                          SilcUInt32 remote_ip_addr_size, int *remote_port,
348                          unsigned char *buf, SilcUInt32 buf_len)
349 {
350   SilcSocketStream socket_stream = (SilcSocketStream)stream;
351   SilcSymbianSocket *s = (SilcSymbianSocket *)socket_stream->sock;
352   SilcSymbianSocketReceive *recv = s->receive;
353   int len;
354
355   if (s->eof)
356     return 0;
357   if (!recv->inbuf_len || !recv->inbuf_ptr)
358     return -1;
359
360   len = recv->inbuf_len;
361   if (buf_len < len)
362     len = buf_len;
363
364   /* Copy the read data */
365   memcpy(buf, recv->inbuf_ptr, len);
366
367   if (len < recv->inbuf_len) {
368     recv->inbuf_ptr += len;
369     recv->inbuf_len -= len;
370   } else {
371     recv->inbuf_ptr = NULL;
372     recv->inbuf_len = 0;
373   }
374
375   if (remote_ip_addr && remote_ip_addr_size && remote_port) {
376     TBuf<64> ip;
377     recv->remote.Output(ip);
378     silc_strncat(remote_ip_addr, remote_ip_addr_size, (const char *)ip.Ptr(),
379                  ip.Length());
380     *remote_port = recv->remote.Port();
381   }
382
383   return len;
384 }
385
386 /* Send UDP packet, connectionless socket  */
387
388 int silc_net_udp_send(SilcStream stream,
389                       const char *remote_ip_addr, int remote_port,
390                       const unsigned char *data, SilcUInt32 data_len)
391 {
392   SilcSocketStream socket_stream = (SilcSocketStream)stream;
393   SilcSymbianSocket *s = (SilcSymbianSocket *)socket_stream->sock;
394   SilcSymbianSocketSend *send = s->send;
395   TSockXfrLength ret_len;
396   TPtrC8 write_buf(data, data_len);
397
398   if (s->would_block)
399     return -1;
400   if (s->eof)
401     return 0;
402
403   /* Send data */
404   send->Send(write_buf, ret_len, remote_ip_addr, remote_port);
405   if (send->iStatus.Int() != KErrNone) {
406     if (send->iStatus.Int() == KErrEof)
407       return 0;
408     return -2;
409   }
410
411   if (!ret_len())
412     return -1;
413
414   s->would_block = 0;
415   if (ret_len() < data_len)
416     s->would_block = 1;
417
418   return ret_len();
419 }
420
421 /* Closes socket */
422
423 SilcBool silc_socket_stream_close(SilcStream stream)
424 {
425   SilcSocketStream socket_stream = (SilcSocketStream)stream;
426   SilcSymbianSocket *s = (SilcSymbianSocket *)socket_stream->sock;
427   s->sock->Close();
428
429   return TRUE;
430 }
431
432 /* Destroys the stream */
433
434 void silc_socket_stream_destroy(SilcStream stream)
435 {
436   SilcSocketStream socket_stream = (SilcSocketStream)stream;
437   SilcSymbianSocket *s = (SilcSymbianSocket *)socket_stream->sock;
438
439   SILC_LOG_DEBUG(("Destroying sock %p", s));
440
441   silc_socket_stream_close(stream);
442   silc_free(socket_stream->ip);
443   silc_free(socket_stream->hostname);
444   silc_free(socket_stream);
445   delete s->send;
446   delete s->receive;
447   delete s->sock;
448   if (s->ss) {
449     s->ss->Close();
450     delete s->ss;
451   }
452   silc_free(s);
453 }
454
455 /* Sets stream notification callback for the stream */
456
457 SilcBool silc_socket_stream_notifier(SilcStream stream,
458                                      SilcSchedule schedule,
459                                      SilcStreamNotifier callback,
460                                      void *context)
461 {
462   SilcSocketStream socket_stream = (SilcSocketStream)stream;
463   SilcSymbianSocket *s = (SilcSymbianSocket *)socket_stream->sock;
464
465   SILC_LOG_DEBUG(("Setting stream notifier for sock %p", s));
466
467   if (callback)
468     s->stream = socket_stream;
469   else
470     s->stream = NULL;
471
472   socket_stream->notifier = callback;
473   socket_stream->notifier_context = context;
474   socket_stream->schedule = schedule;
475
476   /* Schedule for receiving data by doing one read operation */
477   if (callback)
478     s->receive->Read();
479
480   return TRUE;
481 }
482
483 } /* extern "C" */