[browser] fixed menu skin to set the size automatically.
[vuplus_dvbapp] / lib / base / socketbase.cpp
1 #include <unistd.h>
2 #include <fcntl.h>
3 #include <sys/socket.h>
4 #include <sys/select.h>
5 #include <arpa/inet.h>
6 #include <netdb.h>
7
8 #include <vector>
9 #include <string>
10
11 #include "socketbase.h"
12
13 #include <lib/base/ebase.h>
14 #include <lib/base/eerror.h>
15
16 int eSocketBase::select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
17 {
18         int retval;
19         fd_set rset, wset, xset;
20         timeval interval;
21
22         /* make a backup of all fd_set's and timeval struct */
23         if (readfds) rset = *readfds;
24         if (writefds) wset = *writefds;
25         if (exceptfds) xset = *exceptfds;
26         if (timeout)
27         {
28                 interval = *timeout;
29         }
30         else
31         {
32                 /* make gcc happy... */
33                 timerclear(&interval);
34         }
35
36         while (1)
37         {
38                 retval = ::select(maxfd, readfds, writefds, exceptfds, timeout);
39
40                 if (retval < 0)
41                 {
42                         /* restore the backup before we continue */
43                         if (readfds) *readfds = rset;
44                         if (writefds) *writefds = wset;
45                         if (exceptfds) *exceptfds = xset;
46                         if (timeout) *timeout = interval;
47                         if (errno == EINTR) continue;
48                         eDebug("eSocketBase::select error (%m)");
49                         break;
50                 }
51
52                 break;
53         }
54         return retval;
55 }
56
57 ssize_t eSocketBase::singleRead(int fd, void *buf, size_t count)
58 {
59         int retval;
60         while (1)
61         {
62                 retval = ::read(fd, buf, count);
63                 if (retval < 0)
64                 {
65                         if (errno == EINTR) continue;
66                         eDebug("eSocketBase::singleRead error (%m)");
67                 }
68                 return retval;
69         }
70 }
71
72 ssize_t eSocketBase::timedRead(int fd, void *buf, size_t count, int initialtimeout, int interbytetimeout)
73 {
74         fd_set rset;
75         struct timeval timeout;
76         int result;
77         size_t totalread = 0;
78
79         while (totalread < count)
80         {
81                 FD_ZERO(&rset);
82                 FD_SET(fd, &rset);
83                 if (totalread == 0)
84                 {
85                         timeout.tv_sec = initialtimeout/1000;
86                         timeout.tv_usec = (initialtimeout%1000) * 1000;
87                 }
88                 else
89                 {
90                         timeout.tv_sec = interbytetimeout / 1000;
91                         timeout.tv_usec = (interbytetimeout%1000) * 1000;
92                 }
93                 if ((result = select(fd + 1, &rset, NULL, NULL, &timeout)) < 0) return -1; /* error */
94                 if (result == 0) break;
95                 if ((result = singleRead(fd, ((char*)buf) + totalread, count - totalread)) < 0)
96                 {
97                         return -1;
98                 }
99                 if (result == 0) break;
100                 totalread += result;
101         }
102         return totalread;
103 }
104
105 ssize_t eSocketBase::readLine(int fd, char** buffer, size_t* bufsize)
106 {
107         size_t i = 0;
108         int result;
109         while (1) 
110         {
111                 if (i >= *bufsize) 
112                 {
113                         char *newbuf = (char*)realloc(*buffer, (*bufsize)+1024);
114                         if (newbuf == NULL)
115                                 return -ENOMEM;
116                         *buffer = newbuf;
117                         *bufsize = (*bufsize) + 1024;
118                 }
119                 result = timedRead(fd, (*buffer) + i, 1, 3000, 100);
120                 if (result <= 0 || (*buffer)[i] == '\n')
121                 {
122                         (*buffer)[i] = '\0';
123                         return result <= 0 ? -1 : i;
124                 }
125                 if ((*buffer)[i] != '\r') i++;
126         }
127         return -1;
128 }
129
130 int eSocketBase::connect(const char *hostname, int port, int timeoutsec)
131 {
132         int sd = -1;
133         std::vector<struct addrinfo *> addresses;
134         struct addrinfo *info = NULL;
135         struct addrinfo hints;
136         memset(&hints, 0, sizeof(hints));
137         hints.ai_family = AF_UNSPEC; /* both ipv4 and ipv6 */
138         hints.ai_socktype = SOCK_STREAM;
139         hints.ai_protocol = 0; /* any */
140 #ifdef AI_ADDRCONFIG
141         hints.ai_flags = AI_ADDRCONFIG; /* only return ipv6 if we have an ipv6 address ourselves, and ipv4 if we have an ipv4 address ourselves */
142 #else
143         hints.ai_flags = 0; /* we have only IPV4 support, if AI_ADDRCONFIG is not available */
144 #endif
145         char portstring[15];
146         /* this is suboptimal, but the alternative would require us to mess with the memberdata of an 'abstract' addressinfo struct */
147         snprintf(portstring, sizeof(portstring), "%d", port);
148         if (getaddrinfo(hostname, portstring, &hints, &info) || !info) return -1;
149         struct addrinfo *ptr = info;
150         while (ptr)
151         {
152                 addresses.push_back(ptr);
153                 ptr = ptr->ai_next;
154         }
155
156         for (unsigned int i = 0; i < addresses.size(); i++)
157         {
158                 sd = ::socket(addresses[i]->ai_family, addresses[i]->ai_socktype, addresses[i]->ai_protocol);
159                 if (sd < 0) break;
160                 int flags;
161                 bool setblocking = false;
162                 if ((flags = fcntl(sd, F_GETFL, 0)) < 0)
163                 {
164                         ::close(sd);
165                         sd = -1;
166                         continue;
167                 }
168                 if (!(flags & O_NONBLOCK))
169                 {
170                         /* set socket nonblocking, to allow for our own timeout on a nonblocking connect */
171                         flags |= O_NONBLOCK;
172                         if (fcntl(sd, F_SETFL, flags) < 0)
173                         {
174                                 ::close(sd);
175                                 sd = -1;
176                                 continue;
177                         }
178                         /* remember to restore O_NONBLOCK when we're connected */
179                         setblocking = true;
180                 }
181                 int connectresult;
182                 while (1)
183                 {
184                         connectresult = ::connect(sd, addresses[i]->ai_addr, addresses[i]->ai_addrlen);
185                         if (connectresult < 0)
186                         {
187                                 if (errno == EINTR || errno == EINPROGRESS)
188                                 {
189                                         int error;
190                                         socklen_t len = sizeof(error);
191                                         timeval timeout;
192                                         fd_set wset;
193                                         FD_ZERO(&wset);
194                                         FD_SET(sd, &wset);
195
196                                         timeout.tv_sec = timeoutsec;
197                                         timeout.tv_usec = 0;
198
199                                         if (select(sd + 1, NULL, &wset, NULL, &timeout) <= 0) break;
200
201                                         if (getsockopt(sd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) break;
202
203                                         if (error) break;
204                                         /* we are connected */
205                                         connectresult = 0;
206                                         break;
207                                 }
208                         }
209                         break;
210                 }
211                 if (connectresult < 0)
212                 {
213                         ::close(sd);
214                         sd = -1;
215                         continue;
216                 }
217                 if (setblocking)
218                 {
219                         /* set socket blocking again */
220                         flags &= ~O_NONBLOCK;
221                         if (fcntl(sd, F_SETFL, flags) < 0)
222                         {
223                                 ::close(sd);
224                                 sd = -1;
225                                 continue;
226                         }
227                 }
228                 if (sd >= 0)
229                 {
230 #ifdef SO_NOSIGPIPE
231                         int val = 1;
232                         setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, (char*)&val, sizeof(val));
233 #endif
234                         /* we have a working connection */
235                         break;
236                 }
237         }
238         freeaddrinfo(info);
239         return sd;
240 }
241
242 ssize_t eSocketBase::writeAll(int fd, const void *buf, size_t count)
243 {
244         int retval;
245         char *ptr = (char*)buf;
246         size_t handledcount = 0;
247         while (handledcount < count)
248         {
249                 retval = ::write(fd, &ptr[handledcount], count - handledcount);
250
251                 if (retval == 0) return -1;
252                 if (retval < 0)
253                 {
254                         if (errno == EINTR) continue;
255                         eDebug("eSocketBase::writeAll error (%m)");
256                         return retval;
257                 }
258                 handledcount += retval;
259         }
260         return handledcount;
261 }