#!/usr/bin/perl -w use strict; $|=1; my ($config, %optctl); my ($nf, $c); my ($mysql_dbh, $mssql_dbh); sub debug { print "[$0] @_\n" if $config->{debug} } use Jabber::Connection; use Jabber::NodeFactory; use Jabber::NS qw(:all); use DBI; use DBD::Sybase; use XML::Simple; use Getopt::Long; use Encode; #use Encode::Guess qw/ascii cp1250 cp1252 cp850 cp852 iso-8859-1 iso-8859-15 iso-8859-2 utf8/; use HTML::Entities qw(encode_entities_numeric); use Data::Dumper; $Data::Dumper::Indent=1; $Data::Dumper::Useqq=1; $Data::Dumper::Terse=1; # Fetch command-line options $optctl{config} = ""; &GetOptions(\%optctl, "config=s"); $optctl{config} = undef unless ($optctl{config} ne ""); # Read config file $config = XMLin($optctl{config}); # "trap" sigint sub terminate { my $signame = shift; warn "Somebody sent me a SIG$signame. Terminating...\n"; $c->disconnect; $mysql_dbh->disconnect; $mssql_dbh->disconnect; die "\n"; } $SIG{INT} = \&terminate; $SIG{TERM} = \&terminate; # Create a new node factory $nf = new Jabber::NodeFactory; # Open connection to Jabber server $c = new Jabber::Connection( server => $config->{'jabber'}->{'server'}, log => $config->{'log'} || 0, debug => $config->{'debug'} || 0, ); $c->connect or die "oops: cannot connect to $config->{jabber}->{server}". "last error: ". Dumper($c->lastError); # Register handlers $c->register_beat(300, \&get_valid_ids); $c->register_beat(10, \&slurp_and_pump); $c->register_handler('message', \&incoming_message); # Identify and authenticate with the server $c->auth( $config->{jabber}->{user}, $config->{jabber}->{password}, $config->{jabber}->{resource} ) or die "Cannot authenticate with Jabber server\n"; # Send presence $c->send(''); # Create a new db connection &mysql_db_connect; &mssql_db_connect; &get_valid_ids; # Start processing loop $c->start(); # # db_connect - connect to the database # sub mssql_db_connect { my $dsn = "DBI:Sybase:server=" . $config->{mssql}->{server}. ";database=" . $config->{mssql}->{database}; $mssql_dbh = DBI->connect($dsn, $config->{mssql}->{user}, $config->{mssql}->{password}, {PrintError => 0}) or die "Cannot connect to MS SQL ($!), error: $DBI::errstr\n"; } sub mysql_db_connect { my $dsn = "DBI:mysql:database=".$config->{mysql}->{database}. ";host=".$config->{mysql}->{server}; $mysql_dbh = DBI->connect($dsn, $config->{mysql}->{user}, $config->{mysql}->{password}, {PrintError => 0}) or die "Cannot connect to MS SQL ($!), error: $DBI::errstr\n"; } my @valid_ids; sub get_valid_ids { debug "Refreshing Valid Logins\n"; my $db_h = DBI->connect( "DBI:mysql:database=".$config->{mysql}->{jabberdb}, $config->{mysql}->{user}, $config->{mysql}->{password}, {PrintError => 0} ) or do { warn "get_valid_ids connect error\n"; return }; my $query = "SELECT `username` FROM `authreg`"; my $sth = $db_h->prepare($query) or do { warn "get_valid_ids prepare error\n"; return }; $sth->execute() or do { warn "get_valid_ids execute error\n"; return }; my $row5; my @new_ids; while($row5 = $sth->fetchrow_hashref) { debug "[". $row5->{'username'} . "]\n"; my $tmp = $row5->{'username'}; chomp $tmp; push @new_ids, $tmp; } @valid_ids = @new_ids; $sth->finish(); $db_h->disconnect(); } sub valid_login { my $in = shift; chomp $in; $in = lc $in; for(@valid_ids) { $in eq $_ and return 1; } return 0; } # # OKJabber - mark particular message as done # sub OKJabber { my $id = shift; $id == int($id) or do { warn "OKJabber: expecting int, got $id\n"; return}; $mssql_dbh->ping or &mssql_db_connect; my $sth = $mssql_dbh->prepare("exec OKJabber $id") or return; $sth->execute(); } # # ErrJabber - mark particular message as wrong (?) # sub ErrJabber { my ($id, $reason) = (shift, shift); warn "ErrJabber: $id -> $reason\n"; $id == int($id) or do { warn "ErrJabber: expecting int, got $id\n"; return}; $mssql_dbh->ping or &mssql_db_connect; my $sth = $mssql_dbh->prepare("exec ErrJabber $id") or return; $sth->execute(); } # # get_em - get external messages and archive them # sub get_em { debug "Getting External Queued Messages\n"; my $row; # check if we still have a valid database connection(s) $mysql_dbh->ping or &mysql_db_connect; $mssql_dbh->ping or &mssql_db_connect; # select messages from external queue my $select_em = $mssql_dbh->prepare("exec GetJabber") or do { warn "cannot prepare() GetJabber\n"; return; }; $select_em->execute() or do { warn "cannot execute() GetJabber\n"; return; }; do { while($row = $select_em->fetchrow_hashref) { # WE ARE READING FROM STORED PROCEDURE - RTFM next unless $select_em->{syb_result_type} == CS_ROW_RESULT; debug "Got row:\n", Dumper($row), "\n"; my $emid = $row->{'Id'}; # integer Id? $emid == int($emid) or do { ErrJabber($emid, "Expecting int, got $emid"); next; }; my $login = $row->{'Login'}; my $komunikat = $row->{'Komunikat'}; # strip NT domain part, incl. backslash $login =~ s/^\w+\\//; # strip '@' and after $login =~ s/\@.*$//; # existing login? debug "checking if login $login is valid\n"; valid_login($login) or do { ErrJabber($emid, "No such login: $login"); next; }; debug "archiving ext_id $emid\n"; $komunikat = decode($config->{'mssql'}->{'charset'}, $komunikat); debug "decoded from ".$config->{'mssql'}->{'charset'}.": ", Dumper($komunikat); $komunikat = encode($config->{'mysql'}->{'charset'}, $komunikat); debug "encoded to ".$config->{'mysql'}->{'charset'}.": ", Dumper($komunikat); my $archive_query = 'INSERT INTO `messages`(`ext_id`, `do`, `text`, `sent`) '. "VALUES( $emid, '".$login."', '".$komunikat."', 0)"; # DBD::mysql - escaping backslash ! $archive_query =~ s/\\/\\\\/g; my $archive_em = $mysql_dbh->prepare($archive_query) or do { ErrJabber($emid, "cannot prepare for archiving" ); next; }; $archive_em->execute() or do { ErrJabber($emid, "cannot execute query: $archive_query"); next; }; # mark as done debug "calling OKJabber for emid $emid\n"; OKJabber($emid) or warn "problem with OKJabber($emid)\n"; } } while($select_em->{syb_more_results}); } # # send_m - send archived messages # sub send_m { debug "Sending Archived Messages\n"; my ($row, $mess); # check if we still have a valid database connection(s) $mysql_dbh->ping or &mysql_db_connect; # fetch outgoing messages my $send_m = $mysql_dbh->prepare( 'SELECT `id`, `do`, `text` FROM `messages` WHERE `sent` = 0 LIMIT 0,10'); my $mark_sent_m = $mysql_dbh->prepare( 'UPDATE `messages` set `sent` = `sent` + 1 WHERE `id`=?'); $send_m->execute() or return; while ($row = $send_m->fetchrow_hashref) { debug "Got row:\n", Dumper($row), "\n"; my $to = $row->{'do'}; # append @realm $to .= '@'.$config->{'jabber'}->{'server'}; # create new node $mess = $nf->newNode('message'); $mess->attr('type', 'normal'); $mess->attr('to', $to); # this is set automagically: #$m->attr('from', $config->{admin}.'/perlscript'); # we must encode the message :(( # because there are some XML parser errors # TODO: lossless encoding (Unicode?) (HTML entities?) #$mess->insertTag('body')->data( encode("ascii", $row->{'text'}) ); my $body_text = $row->{'text'}; $body_text = decode($config->{'mysql'}->{'charset'}, $body_text); debug "decoded from ".$config->{'mysql'}->{'charset'}.": ", Dumper($body_text); encode_entities_numeric($body_text); debug "encoded to HTML entities: ", Dumper($body_text); $mess->insertTag('body')->insertTag('html')->rawdata($body_text); # send out the message $c->send($mess) or do { warn "cannot send message"; next }; # mark message as sent in the queue debug "marking ID $row->{'id'} as sent\n"; $mark_sent_m->execute($row->{'id'}) or warn "cannot mark message as sent"; } } sub incoming_message { my ($msg, $parcel) = (shift, shift); debug "handling incoming message $msg\n"; my $body_data = $msg->getTag('body')->data; debug "Got body data: ", Dumper($body_data); encode_entities_numeric($body_data); debug "Got htmlized body data: ", Dumper($body_data); # rewrite and send ( with HTMLized body ) my $omsg = $nf->newNode('message'); $omsg->attr('to', $config->{'admin'}); $omsg->insertTag('body')->insertTag('html')->rawdata( $body_data ); $c->send($omsg); } sub slurp_and_pump { get_em; send_m; } exit 0;