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