DaZeus  2.0
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros
server.cpp
Go to the documentation of this file.
1 
6 #include <cassert>
7 #include <iostream>
8 #include <sstream>
9 
10 // libircclient.h needs cstdlib, don't remove the inclusion
11 #include <cstdio>
12 #include <cstdlib>
13 #include <cstring>
14 #include <libircclient.h>
15 
16 #include "server.h"
17 
18 // #define DEBUG
19 
20 #define IRC (irc_session_t*)irc_
21 
22 std::string dazeus::Server::toString(const Server *s)
23 {
24  std::stringstream res;
25  res << "Server[";
26  if(s == 0 ) {
27  res << "0";
28  } else {
29  const ServerConfig *sc = s->config();
30  res << sc->host << ":" << sc->port;
31  }
32  res << "]";
33 
34  return res.str();
35 }
36 
38 : config_(c)
39 , motd_()
40 , network_(n)
41 , irc_(0)
42 , in_whois_for_()
43 , whois_identified_(false)
44 , in_names_()
45 {
46 }
47 
49 {
50  irc_destroy_session(IRC);
51 }
52 
54 {
55  return config_;
56 }
57 
59 {
60  std::string reasonString;
61  switch( reason )
62  {
64  reasonString = "Shutting down";
65  break;
67  reasonString = "Reloading configuration";
68  break;
70  reasonString = "Switching servers";
71  break;
73  reasonString = "Timeout";
74  break;
76  reasonString = "Unknown error";
77  break;
79  reasonString = "An admin asked me to disconnect";
80  break;
82  default:
83  reasonString = "See you around!";
84  }
85 
86  quit( reasonString );
87 }
88 
89 
90 std::string dazeus::Server::motd() const
91 {
92  fprintf(stderr, "MOTD cannot be retrieved.\n");
93  return std::string();
94 }
95 
96 void dazeus::Server::quit( const std::string &reason ) {
97  irc_cmd_quit(IRC, reason.c_str());
98 }
99 
100 void dazeus::Server::whois( const std::string &destination ) {
101  irc_cmd_whois(IRC, destination.c_str());
102 }
103 
109 void dazeus::Server::ircEventMe( const std::string &eventname, const std::string &destination, const std::string &message) {
110  std::vector<std::string> parameters;
111  parameters.push_back(destination);
112  parameters.push_back(message);
113  slotIrcEvent(eventname, network_->nick(), parameters);
114 }
115 
116 void dazeus::Server::ctcpAction( const std::string &destination, const std::string &message ) {
117  ircEventMe("ACTION_ME", destination, message);
118  irc_cmd_me(IRC, destination.c_str(), message.c_str());
119 }
120 
121 void dazeus::Server::names( const std::string &channel ) {
122  irc_cmd_names(IRC, channel.c_str());
123 }
124 
125 void dazeus::Server::ctcpRequest( const std::string &destination, const std::string &message ) {
126  ircEventMe("CTCP_ME", destination, message);
127  irc_cmd_ctcp_request(IRC, destination.c_str(), message.c_str());
128 }
129 
130 void dazeus::Server::join( const std::string &channel, const std::string &key ) {
131  irc_cmd_join(IRC, channel.c_str(), key.c_str());
132 }
133 
134 void dazeus::Server::part( const std::string &channel, const std::string &) {
135  // TODO: also use "reason" here (patch libircclient for this)
136  irc_cmd_part(IRC, channel.c_str());
137 }
138 
139 void dazeus::Server::message( const std::string &destination, const std::string &message ) {
140  std::stringstream ss(message);
141  std::string line;
142  while(std::getline(ss, line)) {
143  ircEventMe("PRIVMSG_ME", destination, message);
144  irc_cmd_msg(IRC, destination.c_str(), line.c_str());
145  }
146 }
147 
149  irc_send_raw(IRC, "PING");
150 }
151 
152 void dazeus::Server::addDescriptors(fd_set *in_set, fd_set *out_set, int *maxfd) {
153  irc_add_select_descriptors(IRC, in_set, out_set, maxfd);
154 }
155 
156 void dazeus::Server::processDescriptors(fd_set *in_set, fd_set *out_set) {
157  irc_process_select_descriptors(IRC, in_set, out_set);
158 }
159 
160 void dazeus::Server::slotNumericMessageReceived( const std::string &origin, unsigned int code,
161  const std::vector<std::string> &args )
162 {
163  assert( network_ != 0 );
164  assert( network_->activeServer() == this );
165  // Also send out some other interesting events
166  if(code == 311) {
167  in_whois_for_ = args[1];
168  assert( !whois_identified_ );
169  }
170  // TODO: should use CAP IDENTIFY_MSG for this:
171  else if(code == 307 || code == 330) // 330 means "logged in as", but doesn't check whether nick is grouped
172  {
173  whois_identified_ = true;
174  }
175  else if(code == 318)
176  {
177  network_->slotWhoisReceived( origin, in_whois_for_, whois_identified_ );
178  std::vector<std::string> parameters;
179  parameters.push_back(in_whois_for_);
180  parameters.push_back(whois_identified_ ? "true" : "false");
181  slotIrcEvent( "WHOIS", origin, parameters );
182  whois_identified_ = false;
183  in_whois_for_.clear();
184  }
185  // part of NAMES
186  else if(code == 353)
187  {
188  std::vector<std::string> names;
189  std::stringstream ss(args.back());
190  std::string name;
191  while(std::getline(ss, name, ' ')) {
192  in_names_.push_back(name);
193  }
194  }
195  else if(code == 366)
196  {
197  network_->slotNamesReceived( origin, args.at(1), in_names_, args.at(0) );
198  std::vector<std::string> parameters;
199  parameters.push_back(args.at(1));
200  std::vector<std::string>::const_iterator it;
201  for(it = in_names_.begin(); it != in_names_.end(); ++it) {
202  parameters.push_back(*it);
203  }
204  slotIrcEvent( "NAMES", origin, parameters );
205  in_names_.clear();
206  }
207  else if(code == 332)
208  {
209  std::vector<std::string> parameters;
210  parameters.push_back(args.at(1));
211  parameters.push_back(args.at(2));
212  slotIrcEvent( "TOPIC", origin, parameters );
213  }
214  std::stringstream codestream;
215  codestream << code;
216  std::vector<std::string> params;
217  params.push_back(codestream.str());
218  std::vector<std::string>::const_iterator it;
219  for(it = args.begin(); it != args.end(); ++it) {
220  params.push_back(*it);
221  }
222  slotIrcEvent( "NUMERIC", origin, params );
223 }
224 
226 {
227  network_->onFailedConnection();
228 }
229 
230 void dazeus::Server::slotIrcEvent(const std::string &event, const std::string &origin, const std::vector<std::string> &args)
231 {
232  assert(network_ != 0);
233  assert(network_->activeServer() == this);
234  network_->slotIrcEvent(event, origin, args);
235 }
236 
237 void irc_eventcode_callback(irc_session_t *s, unsigned int event, const char *origin, const char **p, unsigned int count) {
238  dazeus::Server *server = (dazeus::Server*) irc_get_ctx(s);
239  std::vector<std::string> params;
240  for(unsigned int i = 0; i < count; ++i) {
241  params.push_back(std::string(p[i]));
242  }
243  server->slotNumericMessageReceived(std::string(origin), event, params);
244 }
245 
246 void irc_callback(irc_session_t *s, const char *e, const char *o, const char **params, unsigned int count) {
247  dazeus::Server *server = (dazeus::Server*) irc_get_ctx(s);
248 
249  std::string event(e);
250  // From libircclient docs, but CHANNEL_* is bullshit...
251  if(event == "CHANNEL_NOTICE") {
252  event = "NOTICE";
253  } else if(event == "CHANNEL") {
254  event = "PRIVMSG";
255  }
256 
257  // for now, keep these std::strings:
258  std::string origin;
259  if(o != NULL)
260  origin = std::string(o);
261  size_t exclamMark = origin.find('!');
262  if(exclamMark != std::string::npos) {
263  origin = origin.substr(0, exclamMark);
264  }
265 
266  std::vector<std::string> arguments;
267  for(unsigned int i = 0; i < count; ++i) {
268  arguments.push_back(std::string(params[i]));
269  }
270 
271 #ifdef DEBUG
272  fprintf(stderr, "%s - %s from %s\n", dazeus::Server::toString(server).c_str(), event.c_str(), origin.c_str());
273 #endif
274 
275  // TODO: handle disconnects nicely (probably using some ping and LIBIRC_ERR_CLOSED
276  if(event == "ERROR") {
277  fprintf(stderr, "Error received from libircclient; origin=%s.\n", origin.c_str());
278  server->slotDisconnected();
279  } else if(event == "CONNECT") {
280  printf("Connected to server: %s\n", dazeus::Server::toString(server).c_str());
281  }
282 
283  server->slotIrcEvent(event, origin, arguments);
284 }
285 
287 {
288  printf("Connecting to server: %s\n", toString(this).c_str());
289  assert( !config_->network->nickName.length() == 0 );
290 
291  irc_callbacks_t callbacks;
292  memset(&callbacks, 0, sizeof(irc_callbacks_t));
293  callbacks.event_connect = irc_callback;
294  callbacks.event_nick = irc_callback;
295  callbacks.event_quit = irc_callback;
296  callbacks.event_join = irc_callback;
297  callbacks.event_part = irc_callback;
298  callbacks.event_mode = irc_callback;
299  callbacks.event_umode = irc_callback;
300  callbacks.event_topic = irc_callback;
301  callbacks.event_kick = irc_callback;
302  callbacks.event_channel = irc_callback;
303 #if LIBIRC_VERSION_HIGH > 1 || LIBIRC_VERSION_LOW >= 6
304  callbacks.event_channel_notice = irc_callback;
305 #endif
306  callbacks.event_privmsg = irc_callback;
307  callbacks.event_notice = irc_callback;
308  callbacks.event_invite = irc_callback;
309  callbacks.event_ctcp_req = irc_callback;
310  callbacks.event_ctcp_rep = irc_callback;
311  callbacks.event_ctcp_action = irc_callback;
312  callbacks.event_unknown = irc_callback;
313  callbacks.event_numeric = irc_eventcode_callback;
314 
315  irc_ = (void*)irc_create_session(&callbacks);
316  if(!irc_) {
317  std::cerr << "Couldn't create IRC session in Server.";
318  abort();
319  }
320  irc_set_ctx(IRC, this);
321 
322  assert( config_->network->nickName.length() != 0 );
323  std::string host = config_->host;
324  if(config_->ssl) {
325 #if defined(LIBIRC_OPTION_SSL_NO_VERIFY)
326  host = "#" + host;
327  if(!config_->ssl_verify) {
328  std::cerr << "Warning: connecting without SSL certificate verification." << std::endl;
329  irc_option_set(IRC, LIBIRC_OPTION_SSL_NO_VERIFY);
330  }
331 #else
332  std::cerr << "Error: Your version of libircclient does not support SSL. Failing connection." << std::endl;
333  slotDisconnected();
334  return;
335 #endif
336  }
337  irc_connect(IRC, host.c_str(),
338  config_->port,
339  config_->network->password.c_str(),
340  config_->network->nickName.c_str(),
341  config_->network->userName.c_str(),
342  config_->network->fullName.c_str());
343 }