[project @ 2005-03-11 17:58:13 by unknown_lamer]
[clinton/bobotpp.git] / source / Socket.C
1 // Socket.C -*- C++ -*-
2 // Copyright (C) 2002 Clinton Ebadi
3 // Copyright (c) 1997, 1998 Etienne BERNARD
4
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // any later version.
9
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
18
19 #include "Socket.H"
20 #include "Bot.H"
21 #include <string>
22
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <netdb.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <errno.h>
32
33 Socket::Socket()
34 : remotePort(0), remoteAddress(0),
35 localPort(0), localAddress(0)
36 {
37 fd = new s_fd;
38 fd->fd = socket(AF_INET, SOCK_STREAM, 0);
39 }
40
41 Socket::Socket(int newfd, unsigned long newAddress, int newPort)
42 : remotePort(newPort), remoteAddress(newAddress),
43 localPort(0), localAddress(0)
44 {
45 fd = new s_fd;
46 fd->fd = newfd;
47 }
48
49 Socket::Socket(const Socket &s)
50 : remotePort(s.remotePort), remoteAddress(s.remoteAddress),
51 localPort(s.localPort), localAddress(s.localAddress)
52 {
53 s.fd->n++;
54 fd = s.fd;
55 }
56
57 Socket::~Socket()
58 {
59 if (--fd->n == 0) {
60 close();
61 delete fd;
62 }
63 }
64
65 int
66 Socket::getRemotePort() const
67 {
68 return remotePort;
69 }
70
71 int
72 Socket::getLocalPort() const
73 {
74 return localPort;
75 }
76
77 int
78 Socket::getFileDescriptor() const
79 {
80 return fd->fd;
81 }
82
83 bool
84 Socket::isConnected()
85 {
86 return fd->fd != -1;
87 }
88
89 bool
90 Socket::setRemoteHostname(String hostname)
91 {
92 struct hostent *host;
93
94 if ((host = gethostbyname((const char *)hostname)) == 0)
95 return false;
96
97 memcpy(&remoteAddress, host->h_addr, host->h_length);
98 remoteAddress = ntohl(remoteAddress);
99
100 return true;
101 }
102
103 bool
104 Socket::setRemoteIP(unsigned long address)
105 {
106 remoteAddress = address;
107 return true;
108 }
109
110 bool
111 Socket::setRemotePort(int p)
112 {
113 remotePort = p;
114
115 return true;
116 }
117
118 bool
119 Socket::setLocalHostname(String hostname)
120 {
121 struct hostent *host;
122
123 if ((host = gethostbyname((const char *)hostname)) == 0)
124 return false;
125
126 memcpy(&localAddress, host->h_addr, host->h_length);
127
128 struct sockaddr_in local_addr;
129 local_addr.sin_family = AF_INET;
130 local_addr.sin_port = htons(localPort);
131 local_addr.sin_addr.s_addr = htonl(localAddress);
132 if (bind(fd->fd, (struct sockaddr *)(&local_addr), sizeof(local_addr)) < 0)
133 return false;
134
135 return true;
136 }
137
138 bool
139 Socket::setLocalIP(unsigned long address)
140 {
141 localAddress = address;
142
143 struct sockaddr_in local_addr;
144 local_addr.sin_family = AF_INET;
145 local_addr.sin_port = htons(localPort);
146 local_addr.sin_addr.s_addr = htonl(localAddress);
147 if (bind(fd->fd, (struct sockaddr *)(&local_addr), sizeof(local_addr)) < 0)
148 return false;
149
150 return true;
151 }
152
153 bool
154 Socket::setLocalPort(int p)
155 {
156 localPort = p;
157
158 struct sockaddr_in local_addr;
159 local_addr.sin_family = AF_INET;
160 local_addr.sin_port = htons(localPort);
161 local_addr.sin_addr.s_addr = htonl(localAddress);
162 if (bind(fd->fd, (struct sockaddr *)(&local_addr), sizeof(local_addr)) < 0)
163 return false;
164
165 return true;
166 }
167
168 bool
169 Socket::setNonBlocking(bool nonblock)
170 {
171 long flags;
172
173 if (fd->fd == -1)
174 return false;
175
176 // We first get the file descriptor's flags
177 if (!fcntl(fd->fd, F_GETFL, &flags))
178 return false;
179
180 if (nonblock)
181 return fcntl(fd->fd, F_SETFL, flags | O_NONBLOCK);
182 else
183 return fcntl(fd->fd, F_SETFL, flags & ~O_NONBLOCK);
184 }
185
186 bool
187 Socket::connect()
188 {
189 struct sockaddr_in addr;
190 memset(&addr, 0, sizeof(addr));
191 addr.sin_family = AF_INET;
192 addr.sin_addr.s_addr = htonl(remoteAddress);
193 addr.sin_port = htons(remotePort);
194 if (::connect(fd->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
195 {
196 // Bot::logLine (String("Socket Error: ")
197 // + strerror (errno) + String("\n"));
198 return false;
199 }
200
201 return true;
202 }
203
204 bool
205 Socket::listen(int backlog)
206 {
207 return ::listen(fd->fd, backlog) == 0;
208 }
209
210 Socket
211 Socket::accept()
212 {
213 struct sockaddr_in addr;
214 socklen_t addrlen=sizeof(addr);
215 int newfd = ::accept(fd->fd, (sockaddr *)&addr, &addrlen);
216 if (newfd == -1)
217 return Socket(-1, 0, 0);
218 unsigned long newRemoteAddress = ntohl(addr.sin_addr.s_addr);
219 int newRemotePort = ntohs(addr.sin_port);
220 return Socket(newfd, newRemoteAddress, newRemotePort);
221 }
222
223 void
224 Socket::close()
225 {
226 ::close(fd->fd);
227 fd->fd = -1;
228 }
229
230 bool
231 Socket::write(String s, bool m)
232 {
233 if (fd->fd == -1)
234 return false;
235
236 if (m) {
237 if (::write(fd->fd, (const char *)s, s.length()) +
238 ::write(fd->fd, "\n", 1) != s.length() + 1)
239 return false;
240 }
241 else
242 if (::write(fd->fd, (const char *)s, s.length()) +
243 ::write(fd->fd, "\r\n", 2) != s.length() + 2)
244 return false;
245
246 return true;
247 }
248
249 String
250 Socket::readLine()
251 {
252 // fixme: this could probably be sped up (use some faster way of
253 // reading from the socket than reading a char at a time)
254
255 // We allocate the buffer statically to keep from having to overhead
256 // of reallocating a new buffer every time we call this (which is
257 // every time anything happens on IRC so the overhead of
258 // re-allocating a buffer every time is potentially too large).
259 //
260 // Since it is static, and the length of lines will differ for each
261 // read, an embedded \0 is inserted after the last character of the
262 // line and then a copy of the the c_str is returned as a String so
263 // that only the line that was just read is returned.
264 static std::string buf (512, ' ');
265
266 int pos = 0; // pos in buffer
267 int nb; // number of bytes read by ::read
268 char r; // temp var for storing output of read into
269 std::string::size_type length = buf.length ();
270
271 do
272 {
273 nb = ::read(fd->fd, &r, 1);
274 switch (nb) {
275 case 0:
276 return String("");
277 case -1:
278 if (errno != EINTR && errno != EAGAIN)
279 return String("");
280 sleep(1);
281 }
282
283 if (nb != -1)
284 if (pos < length)
285 buf[pos++] = r;
286 else
287 {
288 buf.resize (length * 2);
289 length = buf.length ();
290 buf[pos++] = r;
291 }
292 } while (r != '\n');
293
294 if (pos > 1 && buf[pos-2] == '\r')
295 buf[pos-2] = '\0';
296 else
297 buf[pos-1] = '\0';
298
299 // c_str () is used because the String constructor for std::string
300 // will copy the entire std::string into it when we only want it to
301 // copy up to the first null.
302 return String (buf.c_str ());
303 }
304
305 String
306 Socket::readChar()
307 {
308 char r[2];
309 int nb;
310 nb = ::read(fd->fd, &r, 1);
311 switch (nb) {
312 case 0:
313 return String("");
314 case -1:
315 if(errno != EINTR && errno != EAGAIN)
316 return String("");
317 sleep(1);
318 }
319 r[1] = '\0';
320 return String(r);
321 }
322
323
324 // // Here we use a circular buffer to read from the socket
325 // String
326 // Socket::readLine()
327 // {
328 // // The result string
329 // char result[512];
330 // // Position in the result string
331 // int pos = 0;
332 // // Number of chars read from the socket
333 // int nb;
334
335 // // First, we read from the socket
336 // beginning:
337 // if (end <= begin) {
338 // nb = ::read(fd->fd, buf+end, begin - end);
339 // } else {
340 // nb = ::read(fd->fd, buf+end, 1024 - end);
341 // }
342
343 // if (nb == -1 && errno != EINTR && errno != EAGAIN)
344 // return String("");
345
346 // end = (end + nb) % 1024;
347
348 // // Fine, now we read our string from the buffer
349 // while (buf[begin] != '\r' && buf[begin] != '\n') {
350 // result[pos++] = buf[begin++];
351 // begin %= 1024;
352 // if (begin == end)
353 // goto beginning;
354 // }
355
356 // result[pos] = '\0';
357
358 // // Now we skip the final '\r' and '\n'
359 // if (buf[begin] == '\r')
360 // begin = (begin + 1) % 1024;
361 // if (buf[begin] == '\n')
362 // begin = (begin + 1) % 1024;
363
364 // // And we return the result
365 // return String(result);
366 // }
367
368 // bool
369 // Socket::hasData()
370 // {
371 // cout << "DEBUG hasData = " << (begin != end) << endl;
372 // return begin != end;
373 // }