216 |
216 |
217 } |
217 } |
218 } |
218 } |
219 |
219 |
220 /** |
220 /** |
|
221 * :nm MODE <target> [<stuff> [ ... ]] |
|
222 * |
|
223 * If target is ourselves, handle as a private umode, otherwise, propagate to channel |
|
224 */ |
|
225 static void irc_net_on_MODE (const struct irc_line *line, void *arg) |
|
226 { |
|
227 struct irc_net *net = arg; |
|
228 |
|
229 if (irc_conn_self(net->conn, line->args[0])) |
|
230 // don't really care about our umode... |
|
231 log_info("mode set: %s -> %s %s %s...", line->source->nickname, line->args[1], line->args[2], line->args[3]); |
|
232 |
|
233 else |
|
234 // it's a channel mode |
|
235 irc_net_propagate_chan(net, line, line->args[0]); |
|
236 |
|
237 } |
|
238 |
|
239 /** |
221 * Our irc_cmd handler list |
240 * Our irc_cmd handler list |
222 */ |
241 */ |
223 static struct irc_cmd_handler _cmd_handlers[] = { |
242 static struct irc_cmd_handler _cmd_handlers[] = { |
224 // propagate certain messages to the appropriate channel |
243 // propagate certain messages to the appropriate channel |
225 { "QUIT", &irc_net_on_chanuser }, |
244 { "QUIT", &irc_net_on_chanuser }, |
226 { "JOIN", &irc_net_on_chan0 }, |
245 { "JOIN", &irc_net_on_chan0 }, |
227 { "PART", &irc_net_on_chan0 }, |
246 { "PART", &irc_net_on_chan0 }, |
228 { "MODE", &irc_net_on_chan0 }, |
247 { "MODE", &irc_net_on_MODE }, |
229 { "TOPIC", &irc_net_on_chan0 }, |
248 { "TOPIC", &irc_net_on_chan0 }, |
230 { "KICK", &irc_net_on_chan0 }, |
249 { "KICK", &irc_net_on_chan0 }, |
231 { IRC_RPL_NAMREPLY, &irc_net_on_chan2 }, |
250 { IRC_RPL_NAMREPLY, &irc_net_on_chan2 }, |
232 { IRC_RPL_ENDOFNAMES, &irc_net_on_chan1 }, |
251 { IRC_RPL_ENDOFNAMES, &irc_net_on_chan1 }, |
233 { "CTCP ACTION", &irc_net_on_chan0 }, |
252 { "CTCP ACTION", &irc_net_on_chan0 }, |
240 |
259 |
241 { NULL, NULL } |
260 { NULL, NULL } |
242 }; |
261 }; |
243 |
262 |
244 /** |
263 /** |
245 * The given socket is now connected, so create the irc_conn and bind it in to us |
264 * The given socket is now connected, so create the irc_conn and bind it in to us. |
|
265 * |
|
266 * If this fails, this will clean up any partial state, including sock. |
246 */ |
267 */ |
247 static err_t irc_net_connected (struct irc_net *net, struct sock_stream *sock, struct error_info *err) |
268 static err_t irc_net_connected (struct irc_net *net, struct sock_stream *sock, struct error_info *err) |
248 { |
269 { |
249 // create the irc connection state |
270 // create the irc connection state |
250 if (irc_conn_create(&net->conn, sock, &_conn_callbacks, net, err)) |
271 if (irc_conn_create(&net->conn, sock, &_conn_callbacks, net, err)) |
251 return ERROR_CODE(err); |
272 goto error; |
252 |
273 |
253 // add our command handlers |
274 // add our command handlers |
254 if ((ERROR_CODE(err) = irc_conn_add_cmd_handlers (net->conn, _cmd_handlers, net))) |
275 if ((ERROR_CODE(err) = irc_conn_add_cmd_handlers (net->conn, _cmd_handlers, net))) |
255 return ERROR_CODE(err); |
276 goto error; |
256 |
277 |
257 // register |
278 // register |
258 if ((ERROR_CODE(err) = irc_conn_register(net->conn, &net->info.register_info))) |
279 if ((ERROR_CODE(err) = irc_conn_register(net->conn, &net->info.register_info))) |
259 return ERROR_CODE(err); |
280 goto error; |
260 |
281 |
261 // ok |
282 // ok |
262 return SUCCESS; |
283 return SUCCESS; |
263 } |
284 |
264 |
285 error: |
|
286 if (!net->conn) { |
|
287 // cleanup sock ourselves |
|
288 sock_stream_release(sock); |
|
289 |
|
290 } else { |
|
291 // cleanup the partial stuff |
|
292 irc_conn_destroy(net->conn); |
|
293 |
|
294 net->conn = NULL; |
|
295 } |
|
296 |
|
297 return ERROR_CODE(err); |
|
298 } |
|
299 |
|
300 /** |
|
301 * Our sock_*_connect_async callback |
|
302 */ |
265 static void irc_net_on_connect (struct sock_stream *sock, struct error_info *conn_err, void *arg) |
303 static void irc_net_on_connect (struct sock_stream *sock, struct error_info *conn_err, void *arg) |
266 { |
304 { |
267 struct irc_net *net = arg; |
305 struct irc_net *net = arg; |
268 struct error_info err; |
306 struct error_info err; |
269 |
307 |
278 FATAL_ERROR(&err, "irc_net_connected failed"); |
316 FATAL_ERROR(&err, "irc_net_connected failed"); |
279 |
317 |
280 // XXX: cleanup |
318 // XXX: cleanup |
281 } |
319 } |
282 |
320 |
283 err_t irc_net_create (struct irc_net **net_ptr, const struct irc_net_info *info, struct error_info *err) |
321 /** |
284 { |
322 * Connect and create a new irc_conn based on our irc_net_info. |
285 struct irc_net *net; |
323 */ |
|
324 static err_t irc_net_connect (struct irc_net *net, struct error_info *err) |
|
325 { |
|
326 struct irc_net_info *info = &net->info; |
286 struct sock_stream *sock = NULL; |
327 struct sock_stream *sock = NULL; |
287 |
328 |
288 // allocate |
329 // sanity check |
289 if ((net = calloc(1, sizeof(*net))) == NULL) |
330 assert(!net->conn); |
290 return SET_ERROR(err, ERR_CALLOC); |
331 |
291 |
332 // connect based on what's known |
292 // initialize |
|
293 // XXX: info shouldn't be copied directly |
|
294 net->info = *info; |
|
295 TAILQ_INIT(&net->channels); |
|
296 LIST_INIT(&net->users); |
|
297 |
|
298 if (info->raw_sock) { |
333 if (info->raw_sock) { |
299 log_debug("connected using raw socket: %p", info->raw_sock); |
334 log_debug("connected using raw socket: %p", info->raw_sock); |
300 |
335 |
301 // direct sock_stream connection |
336 // direct sock_stream connection |
302 sock = info->raw_sock; |
337 sock = info->raw_sock; |
306 goto error; |
341 goto error; |
307 |
342 |
308 } else if (info->ssl_cred) { |
343 } else if (info->ssl_cred) { |
309 // aquire a ref |
344 // aquire a ref |
310 // NOTE: before any error handling |
345 // NOTE: before any error handling |
311 sock_ssl_client_cred_get(net->info.ssl_cred); |
346 sock_ssl_client_cred_get(info->ssl_cred); |
312 |
347 |
313 log_debug("connecting to [%s]:%s using SSL", info->hostname, info->service); |
348 log_debug("connecting to [%s]:%s using SSL", info->hostname, info->service); |
314 |
349 |
315 // connect |
350 // connect |
316 if (sock_ssl_connect_async(&sock, info->hostname, info->service, net->info.ssl_cred, &irc_net_on_connect, net, err)) |
351 if (sock_ssl_connect_async(&sock, info->hostname, info->service, info->ssl_cred, &irc_net_on_connect, net, err)) |
317 goto error; |
352 goto error; |
318 |
353 |
319 } else { |
354 } else { |
320 log_debug("connecting to [%s]:%s", info->hostname, info->service); |
355 log_debug("connecting to [%s]:%s", info->hostname, info->service); |
321 |
356 |
324 goto error; |
359 goto error; |
325 |
360 |
326 } |
361 } |
327 |
362 |
328 // ok |
363 // ok |
|
364 return SUCCESS; |
|
365 |
|
366 error: |
|
367 return ERROR_CODE(err); |
|
368 } |
|
369 |
|
370 err_t irc_net_create (struct irc_net **net_ptr, const struct irc_net_info *info, struct error_info *err) |
|
371 { |
|
372 struct irc_net *net; |
|
373 |
|
374 // allocate |
|
375 if ((net = calloc(1, sizeof(*net))) == NULL) |
|
376 return SET_ERROR(err, ERR_CALLOC); |
|
377 |
|
378 // initialize |
|
379 // XXX: we need to copy *info's fields |
|
380 net->info = *info; |
|
381 TAILQ_INIT(&net->channels); |
|
382 LIST_INIT(&net->users); |
|
383 |
|
384 // initial connect |
|
385 if (irc_net_connect(net, err)) |
|
386 goto error; |
|
387 |
|
388 // ok |
329 *net_ptr = net; |
389 *net_ptr = net; |
330 |
390 |
331 return SUCCESS; |
391 return SUCCESS; |
332 |
392 |
333 error: |
393 error: |
334 if (sock && !net->conn) |
|
335 // we need to clean up the socket ourself |
|
336 sock_stream_release(sock); |
|
337 |
|
338 // cleanup main |
394 // cleanup main |
339 irc_net_destroy(net); |
395 irc_net_destroy(net); |
340 |
396 |
341 return ERROR_CODE(err); |
397 return ERROR_CODE(err); |
342 } |
398 } |