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