IRC.pm 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package Cpanel::iContact::Provider::IRC;
  2. use strict;
  3. use warnings;
  4. use parent 'Cpanel::iContact::Provider';
  5. sub send {
  6. my ($self) = @_;
  7. my @missing = grep { !defined $self->{'contact'}{$_} } qw{IRCSERVER};
  8. die "Kit not complete! Missing: " . join( ", ", @missing ) if scalar( @missing );
  9. my $args_hr = $self->{'args'};
  10. my @errs;
  11. my $subject_copy = $args_hr->{'subject'};
  12. my $body_copy = ${ $args_hr->{'text_body'} };
  13. require Encode;
  14. my $subject = Encode::decode_utf8( $subject_copy, $Encode::FB_QUIET );
  15. my $body = Encode::decode_utf8( $body_copy, $Encode::FB_QUIET );
  16. local $@;
  17. eval {
  18. my $response;
  19. $self->_send(
  20. 'channels' => @{ $args_hr->{'to'} },
  21. 'subject' => $subject,
  22. 'content' => $body
  23. );
  24. };
  25. push( @errs, $@ ) if $@;
  26. if (@errs) {
  27. die "One or more notification attempts failed. Details below:\n"
  28. . join( "\n", @errs );
  29. }
  30. return 1;
  31. }
  32. my $conf;
  33. my $conn;
  34. sub _send {
  35. my ( $self, %args ) = @_;
  36. if( $ENV{'AUTHOR_TESTS'} ) {
  37. my $debugmsg = "# Attempting connection to $self->{'contact'}{'IRCSERVER'}:$self->{'contact'}{'IRCPORT'} as $self->{'contact'}{'IRCNICK'} in channel $args{'destination'}";
  38. $debugmsg .= " using SSL" if $self->{'contact'}{'IRCUSESSL'};
  39. print $debugmsg, "\n";
  40. }
  41. my @message_lines = _format_message_for_irc( $args{'subject'}, $args{'content'}, $args{'destination'} );
  42. require IO::Socket::INET;
  43. require IO::Socket::SSL;
  44. alarm(10);
  45. $conn = IO::Socket::INET->new("$self->{'contact'}{'IRCSERVER'}:$self->{'contact'}{'IRCPORT'}") or die $!;
  46. if( $self->{'contact'}{'IRCUSESSL'} ) {
  47. print "# Upgrading connection to use SSL...\n" if $ENV{'AUTHOR_TESTS'};
  48. IO::Socket::SSL->start_SSL( $conn, 'SSL_HOSTNAME' => $self->{'contact'}{'IRCSERVER'}, 'SSL_verify_mode' => 0 ) or die $IO::Socket::SSL::ERROR;
  49. }
  50. print "# [SENT] NICK $self->{'contact'}{'IRCNICK'}\r\n" if $ENV{'AUTHOR_TESTS'};
  51. print $conn "NICK $self->{'contact'}{'IRCNICK'}\r\n";
  52. print "# [SENT] USER cpsaurus * 8 :cPanel & WHM Notification Bot v0.1 (github.com/troglodyne/iContact-cPanel-Plugins)\r\n" if $ENV{'AUTHOR_TESTS'};
  53. print $conn "USER cpsaurus * 8 :cPanel & WHM Notification Bot v0.1 (github.com/troglodyne/iContact-cPanel-Plugins)\r\n";
  54. my %got;
  55. while( $conn ) {
  56. # Print your message
  57. if( $got{'366'} && $got{'332'} ) {
  58. my $shake_line = shift @message_lines;
  59. print "# [SENT] $shake_line" if $ENV{'AUTHOR_TESTS'};
  60. print $conn $shake_line if scalar(@message_lines);
  61. last if !scalar(@message_lines);
  62. next;
  63. }
  64. my $line = readline( $conn );
  65. $line =~ s/^[^[:print:]]+$//; # Collapse blank lines
  66. if( !$line ) {
  67. print "# [GOT][0] (Sleeping 1s...)\n" if $ENV{'AUTHOR_TESTS'};
  68. sleep 1;
  69. next;
  70. }
  71. print "# [GOT][" . length($line) . "] $line" if $ENV{'AUTHOR_TESTS'};
  72. my @msgparts = split( ' ', $line );
  73. $msgparts[1] ||= '';
  74. # PING handler
  75. if( $msgparts[0] eq 'PING' ) {
  76. print "# [SENT] PONG $msgparts[1]\r\n" if $ENV{'AUTHOR_TESTS'};
  77. print $conn "PONG $msgparts[1]\r\n";
  78. next;
  79. }
  80. # MOTD/JOIN handler
  81. if( grep { $_ eq $msgparts[1] } qw{376 422} ) {
  82. print "# [SENT] JOIN $args{'destination'}\r\n" if $ENV{'AUTHOR_TESTS'};
  83. print $conn "JOIN $args{'destination'}\r\n";
  84. next;
  85. }
  86. # Channel join handler, gotta wait for NAMES and TOPIC
  87. if( grep { $_ eq $msgparts[1] } qw{366 332} ) {
  88. print "# [INFO] Noticed we got $msgparts[1] above. Noting that so we can know when to start spamming messages.\n" if $ENV{'AUTHOR_TESTS'};
  89. $got{"$msgparts[1]"} = 1 ;
  90. next;
  91. }
  92. }
  93. print $conn "QUIT : Done sending notification\r\n";
  94. $conn->shutdown(2);
  95. return;
  96. }
  97. # https://tools.ietf.org/html/rfc2812#section-2.3
  98. sub _format_message_for_irc {
  99. my ( $subj, $body, $chan ) = @_;
  100. my @msg_lines;
  101. my $prefix = "NOTICE $chan :";
  102. my $suffix = "\r\n"; # 2 chars
  103. my $msglen = 510 - length $prefix; # 512 chars total
  104. # Subject is one line
  105. while( $subj ) {
  106. if( length $subj <= 510 ) {
  107. push( @msg_lines, $prefix . $subj . $suffix );
  108. undef $subj;
  109. } else {
  110. push( @msg_lines, $prefix . substr( $subj, 0, 510, "" ) . $suffix );
  111. }
  112. }
  113. # Body is multiline
  114. my @body_lines = split( "\n", $body );
  115. foreach my $line (@body_lines) {
  116. while( $line ) {
  117. if( length $line <= 510 ) {
  118. push( @msg_lines, $prefix . $line . $suffix );
  119. undef $line;
  120. } else {
  121. push( @msg_lines, $prefix . substr( $line, 0, 510, "" ) . $suffix );
  122. }
  123. }
  124. }
  125. return @msg_lines;
  126. }
  127. sub DESTROY {
  128. $conn->shutdown(2) if $conn;
  129. }
  130. 1;