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