##############################################################################
# $Id: 70_Jabber.pm 12904 2016-12-29 12:44:32Z BioS $
#
#     70_Jabber.pm
#     An FHEM Perl module for connecting to an Jabber XMPP Server and 
#     send/recieve messages.
#     Thanks to Predictor who had the initial idea for such a module.
#
#     Copyright by BioS
#
#     This file is part of fhem.
#
#     Fhem is free software: you can redistribute it and/or modify
#     it under the terms of the GNU General Public License as published by
#     the Free Software Foundation, either version 2 of the License, or
#     (at your option) any later version.
#
#     Fhem is distributed in the hope that it will be useful,
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#     GNU General Public License for more details.
#
#     You should have received a copy of the GNU General Public License
#     along with fhem.  If not, see <http://www.gnu.org/licenses/>.
#
# Version: 1.7 - 2016-12-29
#
# Changelog:
# v1.7 2016-12-29 Added possibility to change componentname via attr JabberDomain (thx Turbokid)
# v1.6 2016-07-10 Fix log message if otr message is empty (thx spikeh1)
# v1.5 2015-09-17 Added OTR (Off the Record) end to end encryption
#                 Added MUC (Multi-User-Channel) joining and handling
# v1.4 2015-08-27 Fixed broken callback registration in Net::XMPP >= 1.04
# v1.3 2015-01-10 Fixed DNS SRV resolving and resulting wrong to: address
# v1.2 2015-01-09 hardening XML::Stream Process() call and fix of ssl_verify
# v1.1 2014-07-28 Added UTF8 encoding / decoding to Messages
# v1.0 2014-04-10 Stable Release - Housekeeping & Add to SVN
# v0.3 2014-03-19 Fixed SetPresence() & Added extensive debugging capabilities by setting $debug to 1
# v0.2 2014-01-28 Added SSL option in addition to TLS 
# v0.1 2014-01-18 Initial Release
##############################################################################
# You will need the following perl Module and all it's depencies for this to work: 
# Net::Jabber
# For using the SSL features and to connect securly to a Jabber server you also need this perl Module:
# Net::SSLeay
#
# For using OTR you need to compile Crypt::OTR from CPAN on your own
#
# The recommended debian packages to be installed are these: 
# libnet-jabber-perl libnet-xmpp-perl libxml-stream-perl libdigest-sha1-perl libauthen-sasl-perl libnet-ssleay-perl
#
# Have Phun!
#
package main;

use strict;
use warnings;
use utf8;
use Time::HiRes qw(gettimeofday);
use Net::Jabber;
use base qw( Net::XMPP::Namespaces );
use Blocking;

sub Jabber_Set($@);
sub Jabber_Define($$);
sub Jabber_UnDef($$);
sub Jabber_PollMessages($);
sub Jabber_CheckConnection($);

# If you want extended logging and debugging infomations
# in fhem.log please set the following value to 1
my $debug = 0;

my %sets = (
  "msg" => 1,
  "msgmuc" => 1,
  "msgotr" => 1,
  "subscribe" => 1
);

###################################
sub
Jabber_Initialize($)
{
  my ($hash) = @_;

  $hash->{SetFn}      = "Jabber_Set";
  $hash->{DefFn}      = "Jabber_Define";
  $hash->{UndefFn}    = "Jabber_UnDef";
  $hash->{AttrFn}     = "Jabber_Attr";
  $hash->{AttrList}   = "dummy:1,0 loglevel:0,1,2,3,4,5 OnlineStatus:available,unavailable PollTimer RecvWhitelist ResourceName MucJoin MucRecvWhitelist OTREnable OTRSharedSecret JabberDomain ".$readingFnAttributes;
}

###################################
sub Jabber_Set($@)
{
  my ($hash, $name, $cmd, @args) = @_;
  
  if (!defined($sets{$cmd}))
  {
    return "Unknown argument " . $cmd . ", choose one of " . join(" ", sort keys %sets);
  }

  if ($cmd eq 'msg')
  {
    return Jabber_Set_Message($hash, @args);
  }

  if ($cmd eq 'msgmuc')
  {
    return Jabber_Set_MUCMessage($hash, @args);
  }

  if ($cmd eq 'msgotr')
  {
    return Jabber_Set_OTRMessage($hash, @args);
  }


  if ($cmd eq 'subscribe')
  {
    return Jabber_Subcribe_To($hash, @args);
  }

}
###################################
# Set's
###################################
sub Jabber_Set_Message($@)
{
  my ($hash,$dst,@tmpMsg) = @_;
  my $message = join(" ", @tmpMsg);
  utf8::decode($message);
  if (Jabber_CheckConnection($hash)) {

    $hash->{JabberDevice}->MessageSend(to=>$dst,
                      body=>$message,
                      type=>"chat",
                      priority=>10);  
  }
}

###################################
sub Jabber_Set_MUCMessage($@)
{
  my ($hash,$dst,@tmpMsg) = @_;
  my $message = join(" ", @tmpMsg);
  utf8::decode($message);
  if (Jabber_CheckConnection($hash)) {

    #convert the groupchat id to a short id else this would be a private message which is not allowed via "groupchat" type
    my $JID = new Net::Jabber::JID($dst);
    my $senderShort = $JID->GetJID("base");
    $hash->{JabberDevice}->MessageSend(to=>$senderShort,
                      body=>$message,
                      type=>"groupchat",
                      priority=>10);
  }
}

###################################
sub Jabber_Set_OTRMessage($@)
{
  my ($hash,$dst,@tmpMsg) = @_;
  my $message = join(" ", @tmpMsg);
  utf8::decode($message);
  if (Jabber_CheckConnection($hash)) {
    if ($hash->{helper}{otractive}) {
      my $JID = new Net::Jabber::JID($dst);

      if (defined($hash->{helper}{otrJIDs}{$JID->GetJID("full")}) && defined($hash->{helper}{otrJIDs}{$JID->GetJID("full")}{verified}) ) {
        #send a encrypted message as we have an connection
        if (my $ciphertext = $hash->{OTR}->encrypt($JID->GetJID("full"), $message)) {
            Log 0, "$hash->{NAME} Secure sending to ".$JID->GetJID("full") if $debug;
            Jabber_Set_Message($hash,$dst,$ciphertext);
        } else {
            Log 0, "$hash->{NAME} Your message was not sent - no encrypted conversation is established" if $debug;
        }
      } else {
        #establish a secure connection and send the message then.
        Log 0, "$hash->{NAME} No secure connection to ".$JID->GetJID("full")." - establishing and sending message later..." if $debug;
        #send it later
        push @{ $hash->{helper}{otrJIDs}{$JID->GetJID("full")}{waitingMsgs} }, {"jid" => $JID->GetJID("full"), "msg" => $message} ;
        #establish...
        $hash->{OTR}->establish($JID->GetJID("full"));
      }          
    } else {
      return "OTR not activated. Activate by using  'attr $hash->{NAME} OTREnable 1'"; 
    }
  }
}

###################################
sub Jabber_Subcribe_To($@)
{
  my ($hash,$dst) = @_;
  if (Jabber_CheckConnection($hash)) {
    #respond with authorization so they can see our online state
    $hash->{JabberDevice}->Subscription(type=>"subscribed",
                   to=>$dst);
    #ask for authorization also so we can also see their online state (for future)
    $hash->{JabberDevice}->Subscription(type=>"subscribe",
                   to=>$dst);
  }
}

###################################
sub
Jabber_Define($$)
{
  my ($hash, $def) = @_;
  my $name = $hash->{NAME};
  my @args = split("[ \t]+", $def);
  
  if (int(@args) < 8)
  {
    return "Invalid number of arguments: define <name> Jabber <server> <port> <username> <password> <tls> <ssl>";
  }
  my ($tmp1,$tmp2,$server, $port, $username, $password, $tls, $ssl) = @args;
  
  $hash->{STATE} = 'Initialized';
  
  #defaults:
  $attr{$name}{PollTimer}=2;
  $attr{$name}{ResourceName}='FHEM';
  $attr{$name}{RecvWhitelist}='.*';
  $attr{$name}{OnlineStatus}='available';

  if(defined($server) && defined($port) && defined($username) && defined($password) && defined($tls) && defined($ssl))
  {    
    $hash->{helper}{server} = $server;
    $hash->{helper}{username} = $username;
    $hash->{helper}{password} = $password;
    $hash->{helper}{port} = $port;
    $hash->{helper}{tls} = $tls;
    $hash->{helper}{ssl} = $ssl;
    $hash->{helper}{otractive} = 0;
    $hash->{helper}{otrJIDs} = {}; #hash
    
    if ($tls == 1 || $ssl == 1) {
       if(!eval("require Net::SSLeay;")) {
          $hash->{STATE} = "Disconnected (Module error)";
          $hash->{CONNINFO} = "Missing perl Module Net::SSLeay for TLS or SSL connection.";
          return undef;
       }
    }
    if(!eval("require Authen::SASL;")) {
       $hash->{STATE} = "Disconnected (Module error)";
       $hash->{CONNINFO} = "Missing perl Module Authen::SASL for Jabber Authentication.";
       return undef;
    }    
    Jabber_CheckConnection($hash) if($init_done);
    InternalTimer(gettimeofday()+$attr{$name}{PollTimer}, "Jabber_PollMessages", $hash,0);
    return undef;
  }
  else
  {
    return "define not correct: define <name> Jabber <server> <port> <username> <password> <tls> <ssl>";
  }  
}

###################################
sub
Jabber_UnDef($$)
{
  my ($hash, $name) = @_;
  RemoveInternalTimer($hash);
  $hash->{JabberDevice}->Disconnect();
  return undef;
}

###################################
# Attrib
sub
Jabber_Attr(@)
{
	my ($cmd,$name,$aName,$aVal) = @_;
	my $hash = $defs{$name};
	# $cmd can be "del" or "set"
	# $name is device name
	# aName and aVal are Attribute name and value
  if ($cmd eq "set") {
    if ($aName eq "OnlineStatus") {
      if (defined($aVal) && defined($hash->{JabberDevice}) && $init_done) {
        #Send Presence type only if we do not want to be available
        if ($aVal ne "available") {
          $hash->{JabberDevice}->PresenceSend(type=>$aVal);
        } else {
          $hash->{JabberDevice}->PresenceSend();
        }
      }
    } elsif ($aName eq "MucJoin") {
      #Join the MUC
      if (defined($aVal) && defined($hash->{JabberDevice}) && $init_done) {
        Jabber_MUCs_Join($hash,$aVal);
      }
    } elsif ($aName eq "OTREnable") {
      #We dont care if Jabber is not connected already
      if (defined($aVal) && $init_done) {
        #OTR Enabled, init OTR
        if ($aVal == 1) {
          Jabber_OTR_Init($hash);
        }
      }
    } elsif ($aName eq "OTRSharedSecret") {
      #Nothing special to do will be used later..
    } elsif ($aName eq "JabberDomain" && $init_done) {
      #restart connection...
      $hash->{JabberDevice}->Disconnect();
    }
  }
	return undef;
}

##########################################
# Joins a MUC and save the nick/name for later processing
sub
Jabber_MUCs_Join($$)
{
  my ($hash,$MUCJID) = @_;
  my $name = $hash->{NAME};

  #find rooms to leave
  my %oldrooms;
  if (defined($hash->{helper}{myMUCJIDs})) {
    foreach my $oldroom (@{$hash->{helper}{myMUCJIDs}}) {
      $oldrooms{$oldroom} = 1;
    }
    
  }
  $hash->{helper}{myMUCJIDs} = ();
  
  #format of line: room@server/nick:pass,room2@server/nick2:pass
  my @rooms = split /,/, $MUCJID;
  
  foreach my $roompass (@rooms) {
    my ($room,$pass) = split /:/,$roompass;

    #add room to array 
    push @{ $hash->{helper}{myMUCJIDs} }, $room;

    #remove from rooms to leave
    if (exists($oldrooms{$room})) {
      delete $oldrooms{$room};
    }
    #create new presence object
    my $presence = Net::Jabber::Presence->new;
    $presence->SetTo($room);
    my $muc = $presence->NewChild('http://jabber.org/protocol/muc');

    if($pass) {
      $muc->SetPassword($pass);
    }
    #remove history
    my $hist = $muc->AddHistory();
    $hist->SetMaxChars(0);

    #join the room (or change the nick)
    $hash->{JabberDevice}->Send($presence);
  }

  #leave old rooms
  foreach my $room (keys %oldrooms) {
    $hash->{JabberDevice}->PresenceSend(to => $room, type => 'unavailable');
  }

}

##########################################
# Checking for waiting Messages from the Jabber Server
sub
Jabber_PollMessages($)
{
  my ($hash) = @_;
  my $name = $hash->{NAME};
  my $connectiondied = 0;
  RemoveInternalTimer($hash);
  
  if(!$init_done) {
    InternalTimer(gettimeofday()+$attr{$name}{PollTimer}, "Jabber_PollMessages", $hash,0);  
    return undef; # exit if FHEM is not ready yet.
  }
  
  if (Jabber_CheckConnection($hash)) {
    Log 0, "$hash->{NAME} Jabber PollMessages" if $debug;
    #We need to manually do what XML::Stream normally do on 'Process()' as we do not want to block it for too long.
    #They only accept a multiple of second as timeout, but that is too much if we block FHEM every second a second
    #If we find that there is something to do we call Process()
    my $doProcess = 0;
    
    #If there is something to read from the XMPP Server
    if (defined($hash->{JabberDevice}->{STREAM}->{SELECT}->can_read(0.01))) {
      $doProcess = 1;
    }
    
    #From XML::Stream - Check if a connection needs a keepalive or has been timed out.
    #Again, we would block for at least one second (every 10 seconds) if we call Process() here
    if ($doProcess == 0) {
  
      #From XML::Stream - Check if a connection needs a keepalive, and send that keepalive.
      foreach my $sid (keys(%{$hash->{JabberDevice}->{STREAM}->{SIDS}}))
      {
        next if ($sid eq "default");
        next if ($sid =~ /^server/);
        next if ($hash->{JabberDevice}->{STREAM}->{SIDS}->{$sid}->{status} == -1);
        if ((time - $hash->{JabberDevice}->{STREAM}->{SIDS}->{$sid}->{keepalive}) > 10)
        {
          $hash->{JabberDevice}->{STREAM}->IgnoreActivity($sid,1);
          $hash->{JabberDevice}->{STREAM}->{SIDS}->{$sid}->{status} = -1 if !defined($hash->{JabberDevice}->{STREAM}->Send($sid," "));
          if (! $hash->{JabberDevice}->{STREAM}->{SIDS}->{$sid}->{status} == 1)
          {
            #Keep-Alive failed - we must call Process() to handle it
            $doProcess = 1;
            Log 0, "$hash->{NAME} Keep Alive Failed" if $debug;
          }
          $hash->{JabberDevice}->{STREAM}->IgnoreActivity($sid,0);
        }
      }
      
      #From XML::Stream - Check if a connection timed out, if not respond, if it timed out, call Process()
      foreach my $sid (keys(%{$hash->{JabberDevice}->{STREAM}->{SIDS}}))
      {
        next if ($sid eq "default");
        next if ($sid =~ /^server/);

        $hash->{JabberDevice}->{STREAM}->Respond($sid)
          if (exists($hash->{JabberDevice}->{STREAM}->{SIDS}->{$sid}->{activitytimeout}) && 
             defined($hash->{JabberDevice}->{STREAM}->GetRoot($sid)));        
        
        $doProcess = 1
          if (exists($hash->{JabberDevice}->{STREAM}->{SIDS}->{$sid}->{activitytimeout}) &&
            ((time - $hash->{JabberDevice}->{STREAM}->{SIDS}->{$sid}->{activitytimeout}) > 10) &&
            ($hash->{JabberDevice}->{STREAM}->{SIDS}->{$sid}->{status} != 1));
      }
    }

    #We do Process() - if the connection has died we reconnect. 
    if ($doProcess == 1) {
      Log 0, "$hash->{NAME} DoProcess Call" if $debug;

      #Check for previous errors in process(), before XMPP::Connection will break down FHEM
      $connectiondied = 0;
      if (defined($hash->{JabberDevice})) {
        if (exists($hash->{JabberDevice}->{PROCESSERROR}) && ($hash->{JabberDevice}->{PROCESSERROR} == 1)) {
          #XMPP::Connection would kill FHEM now.. But we try to handle it.
          $hash->{STATE} = "Disconnected";
          $hash->{CONNINFO} = "Jabber connection error (Previous XMPP Process() error!)";
          Log 0, "$hash->{NAME} Jabber connection error (Previous XMPP Process() error!)" if $debug;
          $connectiondied = 1;
        } else {
	  #Do Process(), if it is undef, connection is gone or some other problem...
          if (!defined($hash->{JabberDevice}->Process(1))) {
            $hash->{STATE} = "Disconnected";
            $hash->{CONNINFO} = "Jabber connection died";
            Log 0, "$hash->{NAME} Jabber connection error (Process() is undef!)" if $debug;
            $connectiondied = 1;
          }
        }
      } else {
        $connectiondied = 1;
      }

      if ($connectiondied == 1) {
        #connection died
        Log 0, "$hash->{NAME} Connection died" if $debug;
        $hash->{JabberDevice} = undef;
        if (Jabber_CheckConnection($hash)) {
          #Send Presence type only if we do not want to be availible
          if ($attr{$name}{OnlineStatus} ne "available") {
            $hash->{JabberDevice}->PresenceSend(type=>$attr{$name}{OnlineStatus});
          } else {
            $hash->{JabberDevice}->PresenceSend();
          }
        }
      }
    }
    Log 0, "$hash->{NAME} Poll End" if $debug;
  }
  InternalTimer(gettimeofday()+$attr{$name}{PollTimer}, "Jabber_PollMessages", $hash,0);
}

##########################################
# Checking the Connection to the Jabber Server
sub Jabber_CheckConnection($)
{
  my ($hash) = @_;
  my $name = $hash->{NAME};
  
  if (!defined($hash->{JabberDevice})) {
    #Not defined, create a new Jabber connection
    my $dev = undef;
    if ($debug > 0) {
      $dev = new Net::Jabber::Client(debuglevel=>2,debugfile=>"/tmp/jabberdebug.log"); 
    } else {
      $dev = new Net::Jabber::Client();
    }
    $hash->{JabberDevice} = $dev;

    #Default to SSL = nonverify (0x00) - this has been changed in XML::Stream 1.23_04 and cause problems because you need a CA verify list.
    if (defined($hash->{JabberDevice}->{STREAM}->{SIDS}->{default}->{ssl_verify})) {
      $hash->{JabberDevice}->{STREAM}->{SIDS}->{default}->{ssl_verify} = 0x00;
    }

    #Default to to SRV lookups, ugly hack because older versions of XMPP::Connection dont call the respective value in XML::Stream..
    $hash->{JabberDevice}->{STREAM}->{SIDS}->{default}->{srv} = "_xmpp-client._tcp";

    #fix for weak callbacks, since Net::XMPP v.1.05 they "weaken" the reference to prevent *possible* memory problems,
    #but that causes the callbacks to not work anymore, so we unweaken it here by initializing the callbacks again :)
    $hash->{JabberDevice}->InitCallbacks();

    #For MUC we need to check if the history function is in the namespace, if not our libraries are old and we need to hack it in
    #I found this option in NET::Jabber::Owl
    if (!exists($Net::XMPP::Namespaces::NS{'__netjabber__:iq:muc:history'})) {
      $hash->{JabberDevice}->AddNamespace(ns    => '__netjabber__:iq:muc:history',
             tag   => 'history',
             xpath => {
                       MaxChars   => { path => '@maxchars' },
                       MaxStanzas => { path => '@maxstanzas' },
                       Seconds    => { path => '@seconds' },
                       Since      => { path => '@since' }
            },
            docs  => {
                      module => 'Net::Jabber',
            },
      );
      #patch the already existing muc namespace to support the history function so we can call "AddHistory" later...
      $Net::XMPP::Namespaces::NS{'http://jabber.org/protocol/muc'}->{xpath}->{History} = {
          type  => 'child',
          path  => 'history',
          child => { ns => '__netjabber__:iq:muc:history' },
          calls => ['Add', 'Get', 'Set', 'Defined' ],
          };
    }

    #Needed for Message handling:
    $hash->{JabberDevice}->SetMessageCallBacks(normal => sub { \&Jabber_INC_Message($hash,@_) }, chat => sub { \&Jabber_INC_Message($hash,@_) }, groupchat => sub { \&Jabber_INC_MUCMessage($hash,@_) } );
    #Needed if someone wants to subscribe to us and is on the WhiteList
    $hash->{JabberDevice}->SetPresenceCallBacks(
                           subscribe => sub { \&Jabber_INC_Subscribe($hash,@_) }, 
                           subscribed => sub { \&Jabber_INC_Subscribe($hash,@_) },
                           available => sub { \&Jabber_INC_Subscribe($hash,@_) },
                           unavailable => sub { \&Jabber_INC_Subscribe($hash,@_) },
                           unsubscribe => sub { \&Jabber_INC_Subscribe($hash,@_) },
                           unsubscribed => sub { \&Jabber_INC_Subscribe($hash,@_) },
                           error => sub { \&Jabber_INC_Subscribe($hash,@_) }
                         );
    if(exists($attr{$name}{OTREnable}) && $attr{$name}{OTREnable} == 1) {
      Jabber_OTR_Init($hash);
    }
  }

  if (!$hash->{JabberDevice}->Connected()) {

    my $componentname = $hash->{helper}{server};
    if(exists($attr{$name}{JabberDomain}) && $attr{$name}{JabberDomain} ne '') {
       $componentname = $attr{$name}{JabberDomain};
    }
    

    my $connectionstatus = $hash->{JabberDevice}->Connect(
                            hostname=>$hash->{helper}{server}, 
                            port=>$hash->{helper}{port}, 
                            tls=>$hash->{helper}{tls},
                            ssl=>$hash->{helper}{ssl},
                            componentname=>$componentname
                            );
                            
    if (!defined($connectionstatus)) {
      $hash->{STATE} = "Disconnected";
      $hash->{CONNINFO} = "Jabber connect error ($!)";
      return 0;
    }
    my @authresult = $hash->{JabberDevice}->AuthSend(username=>$hash->{helper}{username},
                                 password=>$hash->{helper}{password},
                                 resource=>$attr{$name}{ResourceName});

    if (!defined($authresult[0])) {
      $hash->{STATE} = "Disconnected";
      $hash->{CONNINFO} = "Jabber authentication error: Cannot Authenticate for an unknown reason. Connectionstatus is: $connectionstatus";
      return 0;      
    }

    if ($authresult[0] ne "ok") {
      $hash->{STATE} = "Disconnected";
      $hash->{CONNINFO} = "Jabber authentication error: @authresult";
      return 0;
    }    
    $hash->{STATE} = "Connected";
    $hash->{CONNINFO} = "Connected to $hash->{helper}{server} with username $hash->{helper}{username}";
    $hash->{JabberDevice}->RosterRequest();
    #join MUCs
    if (defined($attr{$name}{MucJoin})) {
      Jabber_MUCs_Join($hash,$attr{$name}{MucJoin}) if $attr{$name}{MucJoin} ne "";
    }

    #Send Presence type only if we do not want to be availible
    if ($attr{$name}{OnlineStatus} ne "available") {
      $hash->{JabberDevice}->PresenceSend(type=>$attr{$name}{OnlineStatus});
    } else {
      $hash->{JabberDevice}->PresenceSend();
    }
  }
  if (!$hash->{JabberDevice}->Connected()) {
    $hash->{STATE} = "Disconnected";
    $hash->{CONNINFO} = "Cannot connect for an unknown reason";
    return 0;
  } else {
    return 1;
  }
  
}

##########################################
# Incoming Subscribe events
sub Jabber_INC_Subscribe
{
  my($hash,$session_id, $presence) = @_;
  my $name = $hash->{NAME};
  Log 0, "$hash->{NAME} INC_Subscribe: Recv presence from: " . $presence->GetFrom() . " Type: ". $presence->GetType() if $debug;
  
  my $sender = $presence->GetFrom();
  my $JID = new Net::Jabber::JID($sender);
  my $senderShort = $JID->GetJID("base");
  my $senderLong = $JID->GetJID("full");
  my $mucRegexMatch = 0;

  #Check the Whitelist if the sender is allowed to send us.
  if (defined($attr{$name}{MucRecvWhitelist})) {
    if ($senderLong =~ m/$attr{$name}{MucRecvWhitelist}/) {
      $mucRegexMatch = 1;
    }
  }
  if ($senderShort =~ m/$attr{$name}{RecvWhitelist}/ || $mucRegexMatch) {
    Log 0, "$hash->{NAME} Regex m/$attr{$name}{RecvWhitelist}/ matched" if $debug && $senderShort =~ m/$attr{$name}{RecvWhitelist}/;
    Log 0, "$hash->{NAME} Regex (MUC) m/$attr{$name}{MucRecvWhitelist}/ matched" if $debug && $mucRegexMatch && $senderLong =~ m/$attr{$name}{MucRecvWhitelist}/;

    if ($presence->GetType() eq "subscribe") {
      #respond with authorization so they can see our online state
      $hash->{JabberDevice}->Subscription(type=>"subscribed",
                     to=>$presence->GetFrom());
      #ask for authorization also so we can also see their online state (for future)
      $hash->{JabberDevice}->Subscription(type=>"subscribe",
                     to=>$presence->GetFrom());      
    }
  } else {
    Log 0, "$hash->{NAME} Regex m/$attr{$name}{RecvWhitelist}/ and m/$attr{$name}{MucRecvWhitelist}/ did not match" if $debug;
  }
}

##########################################
# Incoming Private (encrypted or plaintext) Message
sub Jabber_INC_Message {
  my($hash,$session_id, $xmpp_message) = @_;
  my $name = $hash->{NAME};
  
  my $sender = $xmpp_message->GetFrom();
  my $message = $xmpp_message->GetBody();
  utf8::encode($message);
  Log 0, "$hash->{NAME} INC_Message: $sender: $message" if $debug;
  my $JID = new Net::Jabber::JID($sender);
  my $senderShort = $JID->GetJID("base");
  
  #Check the Whitelist if the sender is allowed to send us, but not the "shortname" as this will strip the sendernickname off
  if ($senderShort =~ m/$attr{$name}{RecvWhitelist}/) {
    Log 0, "$hash->{NAME} Regex m/$attr{$name}{RecvWhitelist}/ matched" if $debug;
    
    # check to see if this is a OTR Message, if OTR is enabled
    my $otr_message_recv = 0;
    if ($hash->{helper}{otractive}) {
      if ($message =~ m/^\?OTR/) {
        #try to decrypt it
        my $discard_msg = 0;
        ($message, $discard_msg) = $hash->{OTR}->decrypt($JID->GetJID("full"), $message);
        if (!$discard_msg && $message ne "") {
          Log 0, "$hash->{NAME} INC_Message [OTR]: $sender: $message" if $debug;
          utf8::encode($message);
          $otr_message_recv = 1;
        } elsif(!$discard_msg && $message eq "") {
          Log 0, "$hash->{NAME} INC_Message [OTR]: We received an encrypted message from $senderShort but were unable to decrypt it (maybe also a control message)" if $debug;
        } 
      }
    }
    #When we have got no message after the OTR decrypt, we can leave this function.

    if (!defined($message) || $message eq "") {
      Log 0, "$hash->{NAME} Message is empty after OTR decrypt." if $debug;
      return undef;
    }

    # Some IM clients send HTML, we need
    # to convert it to plain text
    # remove tags
    $message =~ s/<(.|\n)+?>//g;
    # convert "'s
    $message =~ s/"/\\"/g;
    # trim whitespaces at beginning and end
    $message =~ s/^[\n\r\s]+|[\n\r\s]+$//g;

    #now, if the message is empty, (or was only filled with xml tags for various status infos), drop it
    if ($message ne "") {
     
      readingsBeginUpdate($hash);
      if ($otr_message_recv) {
        readingsBulkUpdate($hash,"OTRMessage","$sender: $message");
        readingsBulkUpdate($hash,"OTRLastSenderJID","$sender");
        readingsBulkUpdate($hash,"OTRLastMessage","$message");
        Log 0, "$hash->{NAME} ReadingsUpdate [OTR]: $message" if $debug;
      } else {
        readingsBulkUpdate($hash,"Message","$sender: $message");
        readingsBulkUpdate($hash,"LastSenderJID","$sender");
        readingsBulkUpdate($hash,"LastMessage","$message");
        Log 0, "$hash->{NAME} ReadingsUpdate: $message" if $debug;
      }

      readingsEndUpdate($hash, 1);
    } else {
      Log 0, "$hash->{NAME} Message was empty or full of xml tags - no readings update" if $debug;
    }
  } else {
    Log 0, "$hash->{NAME} Regex m/$attr{$name}{RecvWhitelist}/ did not match" if $debug;
  }
}

##########################################
# Incoming MUC (Multi-User-Channel) Message
sub Jabber_INC_MUCMessage {
  my($hash,$session_id, $xmpp_message) = @_;

  my $name = $hash->{NAME};

  my $sender = $xmpp_message->GetFrom();
  my $message = $xmpp_message->GetBody();
  utf8::encode($message);
  Log 0, "$hash->{NAME} INC_MUCMessage: $sender: $message\n" if $debug;
  my $JID = new Net::Jabber::JID($sender);
  my $senderShort = $JID->GetJID("base");
  my $senderLong = $JID->GetJID("full");

  #filter MUC messages (ie subject set on join)
  #filter own messages to prevent loop
  foreach my $muc (@{$hash->{helper}{myMUCJIDs}}) {
    my $mucJID = new Net::Jabber::JID($muc);
    if ($JID->GetJID("full") eq $mucJID->GetJID("base")) {
      #room send something to us
      Log 0, "$hash->{NAME} ignoring message from room $senderShort" if $debug;
      return undef;
    } elsif ($JID->GetJID("full") eq $mucJID->GetJID("full")) {
      #we received our own message
      Log 0, "$hash->{NAME} ignoring message from ourself" if $debug;
      return undef;
    }
  }

  #Check the Whitelist if the sender is allowed to send us, but not the "shortname" as this will strip the sendernickname off
  if ($senderLong =~ m/$attr{$name}{MucRecvWhitelist}/) {
    Log 0, "$hash->{NAME} Regex (MUC) m/$attr{$name}{MucRecvWhitelist}/ matched" if $debug;

    # Some IM clients send HTML, we need
    # to convert it to plain text
    # remove tags
    $message =~ s/<(.|\n)+?>//g;
    # convert "'s
    $message =~ s/"/\\"/g;
    # trim whitespaces at beginning and end
    $message =~ s/^[\n\r\s]+|[\n\r\s]+$//g;

    #now, if the message is empty, (or was only filled with xml tags for various status infos), drop it
    if ($message ne "") {
      readingsBeginUpdate($hash);

      readingsBulkUpdate($hash,"MucMessage","$sender: $message");
      readingsBulkUpdate($hash,"MucLastSenderJID","$sender");
      readingsBulkUpdate($hash,"MucLastMessage","$message");
      Log 0, "$hash->{NAME} ReadingsUpdate: $message" if $debug;

      readingsEndUpdate($hash, 1);
    } else {
      Log 0, "$hash->{NAME} Message was empty or full of xml tags - no readings update" if $debug;
    }
  } else {
    Log 0, "$hash->{NAME} Regex (MUC) m/$attr{$name}{MucRecvWhitelist}/ did not match" if $debug;
  }
}

##########################################
# OTR Specific functions
##########################################
sub
Jabber_OTR_Init($)
{
  my ($hash) = @_;
  #check if Crypt::OTR is installed
  if(!eval("require Crypt::OTR;")) {
    $hash->{helper}{otractive} = 0;
    $hash->{STATE} = $hash->{STATE}. " (OTR Error)";
    $hash->{OTR_STATE} = "Missing perl Module Crypt::OTR, OTR disabled.";
#    $hash->{CONNINFO} = "Missing perl Module Crypt::OTR, OTR disabled.";
    return undef;
  }

  #find if the current directory is writeable to store our key, if not we will use /tmp as this is the most availible one.
  my $otrCfgDir = AttrVal('global','modpath','.')."/log";
  $otrCfgDir = "/tmp" if ! -e $otrCfgDir || ! -w $otrCfgDir;
  
  Crypt::OTR->init;
  my $otr_accname = $hash->{NAME};
  my $otr_proto = "FHEMJabber";
  my $otr = new Crypt::OTR(
        account_name  => $otr_accname,
        protocol      => $otr_proto, 
        config_dir    => $otrCfgDir, 
    );

  $hash->{OTR} = $otr;
  $hash->{OTR}->set_callback('inject' => sub { \&Jabber_OTR_inject($hash,@_) });
  $hash->{OTR}->set_callback('otr_message' => sub { \&Jabber_OTR_system_message($hash,@_) });
  $hash->{OTR}->set_callback('verified' => sub { \&Jabber_OTR_connected_verified($hash,@_) });
  $hash->{OTR}->set_callback('unverified' => sub { \&Jabber_OTR_connected_unverified($hash,@_) });
  $hash->{OTR}->set_callback('disconnect' => sub { \&Jabber_OTR_disconnected($hash,@_) });
  $hash->{OTR}->set_callback('smp_request' => sub { \&Jabber_OTR_smprequest($hash,@_) });
  

  if (! -e $otrCfgDir."/otr.private_key-".lc($otr_accname)."-".lc($otr_proto)) {
    #say we are genning Private key, and log the info, then execute it in another fork of fhem to prevent a 2h block
    Log 0, "$hash->{NAME} Generating OTR private key, be prepared that this will take 2 hours or more. Check OTR_STATE (this is a one-time task)";
    $hash->{OTR_STATE} = "Generating OTR private key...";
    if(!exists($hash->{helper}{OTR_GENKEY_PID})) {
      $hash->{helper}{OTR_GENKEY_PID} = BlockingCall("Jabber_OTR_GenPrivateKey", $hash->{NAME}."|".$otr_accname."|".$otr_proto."|".$otrCfgDir, "Jabber_OTR_GenPrivateKeyComplete");
    } else {
      $hash->{OTR_STATE} = "Still generating OTR private key. Please be patient!";
    }
  } else {
    if(!exists($hash->{helper}{OTR_GENKEY_PID})) {
      #Private key is there, everything is fine.
      Log 0, "$hash->{NAME} OTR found privatekey, good!" if $debug;
      $hash->{helper}{otractive} = 1;
      $hash->{OTR_STATE} = "OTR enabled and active";
      Log 3, "$hash->{NAME} OTR successfully enabled and active" if $debug;
    } else {
       $hash->{OTR_STATE} = "Still generating OTR private key. Please be patient!";
       Log 3, "$hash->{NAME} OTR still generating OTR private key. Please be patient!";
    }
  }
}
##########################################
# Generating private key in another process via Blocking.pm
sub Jabber_OTR_GenPrivateKey($) 
{
  my ($callargs) = @_;
  my ($hashname, $otr_accname,$otr_proto,$otrCfgDir) = split("\\|", $callargs);
  my $otr = new Crypt::OTR(
        account_name  => $otr_accname,
        protocol      => $otr_proto,
        config_dir    => $otrCfgDir, 
    );
      
  $otr->load_privkey();
  return "$hashname";
}

##########################################
# Completing generating private key
sub Jabber_OTR_GenPrivateKeyComplete($) 
{
  my ($hashname) = @_;
  return unless(defined($hashname));
  
  my $hash = $defs{$hashname};
  $hash->{OTR_STATE} = "Finished generating OTR private key. OTR is now active.";
  Log 0, "$hash->{NAME} Finished generating OTR private key";
  
  delete($hash->{helper}{OTR_GENKEY_PID});
  
  $hash->{helper}{otractive} = 1;
  log 0, "$hash->{NAME} OTR successfully enabled and active" if $debug;  
}

##########################################
# called when OTR is ready to send a message after function calls (e.g. decrypt / smp / etc).
sub Jabber_OTR_inject {
  my ($hash, $self, $account_name, $protocol, $dest_account, $message) = @_;
  Log 0, "$hash->{NAME} [OTR Inject] Inject called: $message" if $debug;
  Jabber_Set_Message($hash, $dest_account, $message); 
  #most times we await an answer, ignore the polltimer and check for new messages immediantly
  Jabber_PollMessages($hash);
}

##########################################
# called to display an OTR control message for a particular user or protocol
sub Jabber_OTR_system_message {
  my ($hash, $self, $account_name, $protocol, $other_user, $otr_message) = @_;
  Log 0, "$hash->{NAME} [OTR Sys MSG] $otr_message" if $debug;
  return 1;
}

##########################################
#called when an OTR Session has been established and has been verified by the SMP Protocol
sub Jabber_OTR_connected_verified {
  my ($hash, $self, $from_account) = @_;
  Log 0, "$hash->{NAME} [OTR] verified connection with $from_account established" if $debug;
  $hash->{helper}{otrJIDs}{$from_account}{verified} = 1;

  #after this callback, OTR sends another message before the connection is fully established, so we have to delay that again for 5 secs before sending the actual message
  my %h = (hash => $hash, from_account => $from_account);
  InternalTimer(gettimeofday()+5, "Jabber_OTRDelaySend", \%h,0);
}

##########################################
#called when an OTR Session has been established and is not verified
sub Jabber_OTR_connected_unverified {
  my ($hash, $self, $from_account) = @_;
  Log 0, "$hash->{NAME} [OTR] unverified connection with $from_account established" if $debug;
  $hash->{helper}{otrJIDs}{$from_account}{verified} = 0;

  
  #after this callback, OTR sends another message before the connection is fully established, so we have to delay that again for 5 secs before sending the actual message
  my %h = (hash => $hash, from_account => $from_account);
  InternalTimer(gettimeofday()+5, "Jabber_OTRDelaySend", \%h,0);
}

##########################################
#called when the other end want to verify our identity
sub Jabber_OTR_smprequest {
  my ($hash, $self, $account_name, $other_user) = @_;

  my $name = $hash->{NAME};
  Log 0, "$hash->{NAME} [OTR] User $other_user wants to verify our identity, sending shared secret response" if $debug;
  if (defined($attr{$name}{OTRSharedSecret})) {
    $hash->{OTR}->continue_smp($other_user, $attr{$name}{OTRSharedSecret});
  } else {
    Log 0, "$hash->{NAME} [OTR] Error, no shared secret defined, sending bogus secret" if $debug;
    $hash->{OTR}->continue_smp($other_user, "ImaBogusSecretBecauseNothingDefined");
  }
}

##########################################
# Delayed sending message after established a secure connection
# This is needed because AFTER the function "un/verified" is called
# the system is still in the process of opening the encrypted communication
# 
sub Jabber_OTRDelaySend($$) {
  my $h = shift;
  my $hash = $h->{hash};
  my $from_account = $h->{from_account};
  
  #if we have unsent messages, send them now.
  if (defined($hash->{helper}{otrJIDs}{$from_account}{waitingMsgs})) {
    foreach my $waitingMsg (@{$hash->{helper}{otrJIDs}{$from_account}{waitingMsgs}}) {
       Jabber_Set_OTRMessage($hash,$waitingMsg->{jid},$waitingMsg->{msg});
    }
  }
  
}

##########################################
#called when an OTR Session has been disconnected
sub Jabber_OTR_disconnected {
  my ($hash, $self, $from_account) = @_;
  Log 0, "$hash->{NAME} [OTR] $from_account disconnected secure channel" if $debug;
  delete ($hash->{helper}{otrJIDs}{$from_account});
}

1;


=pod
=item device
=item summary connect FHEM to the Jabber Network, send and receiving messages
=item summary_DE verbindet FHEM and Jabber Netz, kann Nachrichten senden und empfangen
=begin html

<a name="Jabber"></a>
<h3>Jabber</h3>
<ul>
  This Module allows FHEM to connect to the Jabber Network, send and receiving messages from and to a Jabber server.<br>
  <br> 
  Jabber is another description for (XMPP) - a communications protocol for message-oriented middleware based 
  on XML and - depending on the server - encrypt the communications channels.<br> 
  For the user it is similar to other instant messaging Platforms like Facebook Chat, ICQ or Google's Hangouts 
  but free, Open Source and by default encrypted between the Jabber servers.<br>
  <br> 
  You need an account on a Jabber Server, you can find free services and more information on <a href="http://www.jabber.org/">jabber.org</a><br>
  Discuss the module in the <a href="http://forum.fhem.de/index.php/topic,18967.0.html">specific thread here</a>.<br>
  <br>
  This Module requires the following perl Modules to be installed (using SSL):<br>
  <ul>
    <li>Net::Jabber</li>
    <li>Net::XMPP</li>
    <li>Authen::SASL</li>
    <li>XML::Stream</li>
    <li>Net::SSLeay</li>
  </ul>
  <br>
  Since version 1.5 it allows FHEM also to join MUC (Multi-User-Channels) and the use of OTR for end to end encryption<br>
  If you want to use OTR you must compile and install Crypt::OTR from CPAN on your own.<br>
  <br>
  <br>
  <a name="JabberDefine"></a>
  <b>Define</b>
  <ul>
    <code>define &lt;name&gt; Jabber &lt;server&gt; &lt;port&gt; &lt;username&gt; &lt;password&gt; &lt;TLS&gt; &lt;SSL&gt;</code><br>
    <br>
    You have to create an account on a free Jabber server or setup your own Jabber server.<br>
    <br>
    Example:
    <ul>
      <code>define JabberClient1 Jabber jabber.org 5222 myusername mypassword 1 0</code>
    </ul>
    <br>
  </ul>
  <br>
  <a name="JabberSet"></a>
  <b>Set</b>
  <ul>
    <li>
      <code>set &lt;name&gt; msg &lt;username&gt; &lt;msg&gt;</code>
      <br>
      sends a message to the specified username
      <br>
      Examples:
      <ul>
        <code>set JabberClient1 msg myname@jabber.org It is working!</code><br>
      </ul>
    </li>
    <br>
    <li>
      <code>set &lt;name&gt; msgmuc &lt;channel&gt; &lt;msg&gt;</code>
      <br>
      sends a message to the specified MUC group channel
      <br>
      Examples:
      <ul>
        <code>set JabberClient1 msgmuc roomname@jabber.org Woot!</code><br>
      </ul>
    </li>
    <br>
    <li>
      <code>set &lt;name&gt; msgotr &lt;username&gt; &lt;msg&gt;</code>
      <br>
      sends an Off-the-Record encrypted message to the specified username, if no OTR session is currently established it is being tried to esablish an OTR session with the specified user.<br>
      If the user does not have OTR support the message is discarded.
      <br>
      Examples:
      <ul>
        <code>set JabberClient1 msgotr myname@jabber.org Meet me at 7pm at the place today :*</code><br>
      </ul>
    </li>
    <br>    
    <li>
      <code>set &lt;name&gt; subscribe &lt;username&gt;</code>
      <br>
      asks the username for authorization (not needed normally)
      <br>
      Example:
      <ul>
        <code>set JabberClient1 subscribe myname@jabber.org</code><br>
      </ul>
    </li>
  </ul>  
  <br>
  <b>Get</b> <ul>N/A</ul><br>
  <a name="JabberAttr"></a>
  <b>Attributes</b>
  <ul>
    <a name="OnlineStatus"></a>
    <li><code>OnlineStatus available|unavailable</code><br>
        Sets the online status of the client, available (online in Clients) or unavailable (offline in Clients)<br>
        It is possible, on some servers, that FHEM can even recieve messages if the status is unavailable<br>
        <br>
        Default: <code>available</code>
    </li><br>
    <a name="ResourceName"></a>
    <li><code>ResourceName &lt;name&gt;</code><br>
        In XMPP/Jabber you can have multiple clients connected with the same username. <br>
        The resource name finally makes the Jabber-ID unique to each client.<br>
        Here you can define the resource name.<br>
        <br>
        Default: <code>FHEM</code>
    </li><br>
    <a name="PollTimer"></a>
    <li><code>PollTimer &lt;seconds&gt;</code><br>
        This is the interval in seconds at which the jabber server get polled.<br>
        Every interval the client checks if there are messages waiting and checks the connection to the server.<br>
        Don't set it over 10 seconds, as the client could get disconnected.<br>
         <br>
        Default: <code>2</code>
    </li><br>    
    <a name="RecvWhitelist"></a>
    <li><code>RecvWhitelist &lt;Regex&gt;</code><br>
        Only if the Regex match, the client accepts and interpret the message. Everything else will be discarded.<br>
        <br>
        Default: <code>.*</code><br>
        Examples:<br>
        <ul>
          <code>myname@jabber.org</code><br>
          <code>(myname1@jabber.org|myname2@xmpp.de)</code><br>
        </ul>
    </li><br>
    <a name="MucJoin"></a>
    <li><code>MucJoin channel1@server.com/mynick[:password]</code><br>
        Allows you to join one or more MUC's (Multi-User-Channel) with a specific Nick and a optional Password<br>
        <br>
        Default: empty (no messages accepted)<br>
        Examples:<br>
        <ul>
          Join a channel: <code>channel1@server.com/mynick</code><br>
          Join more channels: <code>channel1@server.com/mynick,channel2@server.com/myothernick</code><br>
          Join a channel with a password set: <code>channel1@server.com/mynick:password</code><br>
        </ul>
    </li><br>  
    <a name="MucRecvWhitelist"></a>
    <li><code>MucRecvWhitelist &lt;Regex&gt;</code><br>
        Same as RecvWhitelist but for MUC: Only if the Regex match, the client accepts and interpret the message. Everything else will be discarded.<br>
        <br>
        Default: empty (no messages accepted)<br>
        Examples:<br>
        <ul>
          All joined channels allowed: <code>.*</code><br>
          Specific channel allowed only: <code>mychannel@jabber.org</code><br>
          Specific Nick in channel allowed only: <code>mychannel@jabber.org/NickOfFriend</code><br>
        </ul>
    </li><br>  
    <a name="OTREnable"></a>
    <li><code>OTREnable 1|0</code><br>
        Enabled the use of Crypt::OTR for end to end encryption between a device and FHEM<br>
        You must have Crypt::OTR installed and a private key is being generated the first time you enable this option<br>
        Key generation can take more than 2 hours on a quiet system but will not block FHEM instead it will inform you if it has been finished<br>
        Key generation is a one-time-task<br>
         <br>
        Default: empty (OTR disabled)
    </li><br>  
    <a name="OTRSharedSecret"></a>
    <li><code>OTRSharedSecret aSecretKeyiOnlyKnow@@*</code><br>
        Optional shared secret to allow the other end to start a trust verification against FHEM with this shared key.<br>
        If the user starts a trust verification process the fingerprint of the FHEM private key will be saved at the user's device and the connection is trusted.<br>
        This will allow to inform the user if the private key has changed (ex. in Man-in-the-Middle attacks)<br>
         <br>
        Default: empty, please define a shared secret on your own.
    </li><br>  
  </ul>
  <br>
  <a name="JabberReadings"></a>
  <b>Generated Readings/Events:</b>
  <ul>
     <li>Private Messages
      <ul>
        <li><b>Message</b> - Complete message including JID and text</li>
        <li><b>LastMessage</b> - Only text portion of the Message</li>
        <li><b>LastSenderJID</b> - Only JID portion of the Message</li>
      </ul>
     </li><br>
     <li>Encrypted Private Messages (if OTREnable=1)
      <ul>
        <li><b>OTRMessage</b> - Complete decrypted message including JID and text</li>
        <li><b>OTRLastMessage</b> - Only text portion of the Message</li>
        <li><b>OTRLastSenderJID</b> - Only JID portion of the Message</li>
      </ul>
     </li><br>
     <li>MUC Room Messages  (if MUCJoin is set)
      <ul>
        <li><b>MucMessage</b> - Complete message including room's JID and text</li>
        <li><b>MucLastMessage</b> - Only text portion of the Message</li>
        <li><b>MucLastSenderJID</b> - Only JID portion of the Message</li>
      </ul>
     </li>
  </ul>
  <br>
  <a name="JabberNotes"></a>
  <b>Author's Notes:</b>
    <ul>
      <li>You can react and reply on incoming private messages with a notify like this:<br>
        <pre><code>define Jabber_Notify notify JabberClient1:Message.* {
  my $lastsender=ReadingsVal("JabberClient1","LastSenderJID","0");
  my $lastmsg=ReadingsVal("JabberClient1","LastMessage","0");
  my $temperature=ReadingsVal("BU_Temperatur","temperature","0");
  fhem("set JabberClient1 msg ". $lastsender . " Temp: ".$temperature);
}
        </code></pre>
      </li>
      <li>You can react and reply on MUC messages with a notify like this, be aware that the nickname in $lastsender is stripped off in the msgmuc function<br>
        <pre><code>define Jabber_Notify notify JabberClient1:MucMessage.* {
  my $lastsender=ReadingsVal("JabberClient1","LastSenderJID","0");
  my $lastmsg=ReadingsVal("JabberClient1","LastMessage","0");
  my $temperature=ReadingsVal("BU_Temperatur","temperature","0");
  fhem("set JabberClient1 msgmuc ". $lastsender . " Temp: ".$temperature);
}
        </code></pre>
      </li>
      <li>You can react and reply on OTR private messages with a notify like this:<br>
        <pre><code>define Jabber_Notify notify JabberClient1:OTRMessage.* {
  my $lastsender=ReadingsVal("JabberClient1","LastSenderJID","0");
  my $lastmsg=ReadingsVal("JabberClient1","LastMessage","0");
  my $temperature=ReadingsVal("BU_Temperatur","temperature","0");
  fhem("set JabberClient1 msgotr ". $lastsender . " Temp: ".$temperature);
}
        </code></pre>
      </li>
    </ul>    
</ul>
=end html
=begin html_DE

<a name="Jabber"></a>
<h3>Jabber</h3>
<ul>
  Dieses Modul verbindet sich mit dem Jabber Netzwerk, sendet und empf&auml;ngt Nachrichten von und zu einem Jabber Server.<br>
  <br> 
  Jabber ist eine andere Beschreibung f&uuml;r "XMPP", ein Kommunikationsprotokoll f&uuml;r Nachrichtenorientierte "middleware", basierend
  auf XML.<br>
  Fester bestandteil des Protokolls ist die Verschl&uuml;sselung zwischen Client und Server.<br> 
  F&uuml;r den Benutzer ist es &auml;hnlich anderer Chat-Plattformen wie zum Beispiel dem facebook Chat, ICQ oder Google Hangouts - 
  jedoch frei Verf&uuml;gbar, open Source und normalerweise Verschl&uuml;sselt (was Serverabh&auml;ngig ist).<br>
  <br> 
  F&uuml;r dieses Modul brauchst du einen Account auf einem Jabber Server. Kostenlose accounts und Server findet man unter <a href="http://www.jabber.org/">jabber.org</a><br>
  Diskussionen zu diesem Modul findet man im <a href="http://forum.fhem.de/index.php/topic,18967.0.html">FHEM Forum hier</a>.<br>
  <br>
  Dieses Modul ben&ouml;tigt die folgenden Perl Module (inkl. SSL M&ouml;glichkeit)<br>
  <ul>
    <li>Net::Jabber</li>
    <li>Net::XMPP</li>
    <li>Authen::SASL</li>
    <li>XML::Stream</li>
    <li>Net::SSLeay</li>
  </ul>
  <br>
  Seit Version 1.5 kann dieses Modul in Multi-User-Channel (sogenannte MUC) beitreten und Off-the-Record (OTR) Ende-zu-Ende Verschl&uuml;sselung benutzen.<br>
  Wenn du OTR benutzen m&ouml;chtest musst du dir Crypt::OTR von CPAN selbst installieren.<br>
  OTR ist nochmal ein zus&auml;tzlicher Sicherheitsrelevater Punkt, da die Kommunikation wirklich von Endger&auml;t zu FHEM verschl&uuml;sselt wird und man sich nicht auf die Jabber Server Transportverschl&uuml;sselung verlassen muss.<br>
  <br>
  <br>
  <a name="JabberDefine"></a>
  <b>Define</b>
  <ul>
    <code>define &lt;name&gt; Jabber &lt;server&gt; &lt;port&gt; &lt;username&gt; &lt;password&gt; &lt;TLS&gt; &lt;SSL&gt;</code><br>
    <br>
    Du ben&ouml;tigst nat&uuml;rlich echte Accountdaten.<br>
    <br>
    Beispiel:
    <ul>
      <code>define JabberClient1 Jabber jabber.org 5222 myusername mypassword 1 0</code>
    </ul>
    <br>
  </ul>
  <br>
  <a name="JabberSet"></a>
  <b>Set</b>
  <ul>
    <li>
      <code>set &lt;name&gt; msg &lt;username&gt; &lt;msg&gt;</code>
      <br>
      Sendet eine Nachricht "msg" an den Jabberuser "username"
      <br>
      Beispiel:
      <ul>
        <code>set JabberClient1 msg myname@jabber.org It is working!</code><br>
      </ul>
    </li>
    <br>
    <li>
      <code>set &lt;name&gt; msgmuc &lt;channel&gt; &lt;msg&gt;</code>
      <br>
      Sendet eine Nachricht "msg" an dieJabber-MUC-Gruppe "channel".<br>
      Dabei wird ein eventuell mitgegebener Nickname von "channel" entfernt, so kann man direkt das Reading LastMessageJID benutzen.<br>
      <br>
      Beispiel:
      <ul>
        <code>set JabberClient1 msgmuc roomname@jabber.org Woot!</code><br>
      </ul>
    </li>
    <br>
    <li>
      <code>set &lt;name&gt; msgotr &lt;username&gt; &lt;msg&gt;</code>
      <br>
      Sendet eine OTR verschl&uuml;sselte Nachricht an den "username", wenn keine aktive OTR Sitzung aufgebaut ist, wird versucht eine aufzubauen.<br>
      Wenn der Empf&auml;nger OTR nicht versteht, wird die Nachricht verworfen, d.h. sie wird auf keinen Fall im Klartext &uuml;bertragen.
      <br>
      Beispiel:
      <ul>
        <code>set JabberClient1 msgotr myname@jabber.org Wir sehen uns heute um 18:00 Uhr :*</code><br>
      </ul>
    </li>
    <br> 
    <li>
      <code>set &lt;name&gt; subscribe &lt;username&gt;</code>
      <br>
      Fr&auml;gt eine Authorisierung beim "username" an (normalerweise wird das nicht ben&ouml;tigt)
      <br>
      Beispiel:
      <ul>
        <code>set JabberClient1 subscribe myname@jabber.org</code><br>
      </ul>
    </li>
  </ul>
  <br>
  <b>Get</b> <ul>N/A</ul><br>
  <a name="JabberAttr"></a>
  <b>Attribute</b>
  <ul>
    <a name="OnlineStatus"></a>
    <li><code>OnlineStatus available|unavailable</code><br>
        Setzt den Online-status, ob der Client anderen gegen&uuml;ber Online ist (available) oder Offline erscheint (unavailable)<br>
        Es ist m&ouml;glich dass einige Server eingehende Nachrichten trotzdem FHEM zustellen obwohl er "unavailable" ist<br>
        <br>
        Standard: <code>available</code>
    </li><br>
    <a name="ResourceName"></a>
    <li><code>ResourceName &lt;name&gt;</code><br>
        In der Jabber-Welt kann ein Client mit einem Usernamen &ouml;fter mit einem Server verbunden sein (z.b. Handy, Computer, FHEM). <br>
        Der "resource name" ergibt die finale Jabber-ID und macht die verschiedenen Verbindungen einzigartig (z.B. bios@jabber.org/FHEM).<br>
        Hier kannst du den "resource name" setzen.<br>
        <br>
        Standard: <code>FHEM</code>
    </li><br>
    <a name="PollTimer"></a>
    <li><code>PollTimer &lt;seconds&gt;</code><br>
        Dies ist der Intervall in der &uuml;berpr&uuml;ft wird ob neue Nachrichten zur Verarbeitung beim Jabber Server anstehen.<br>
        Ebenfalls wird hiermit die Verbindung zum Server &uuml;berpr&uuml;ft (Timeouts, DSL Disconnects etc.).<br>
        Setze es nicht &uuml;ber 10 Sekunden, die Verbindung kann sonst die ganze Zeit getrennt werden, Sie wird zwar wieder aufgebaut, aber nach 10 Sekunden brechen die meisten Server die Verbindung automatisch ab.<br>
         <br>
        Standard: <code>2</code>
    </li><br>      
    <a name="RecvWhitelist"></a>
    <li><code>RecvWhitelist &lt;Regex&gt;</code><br>
        Nur wenn die Jabber-ID einer privaten empfangenen Nachricht auf diese Regex zutrifft, akzeptiert FHEM die Nachricht und gibt sie an Notifys weiter. Alles andere wird verworfen.<br>
        <br>
        Standard: <code>.*</code><br>
        Beispiele:<br>
        <ul>
          <code>myname@jabber.org</code><br>
          <code>(myname1@jabber.org|myname2@xmpp.de)</code><br>
        </ul>
    </li><br>
    <a name="MucJoin"></a>
    <li><code>MucJoin channel1@server.com/mynick[:passwort]</code><br>
        Tritt dem MUC mit dem spezifizierten Nickname und dem optionalem Passwort bei.<br>
        <br>
        Standard: nicht definiert<br>
        Beispiele:<br>
        <ul>
          Einen Raum betreten: <code>channel1@server.com/mynick</code><br>
          Mehrere R&auml;ume betreten: <code>channel1@server.com/mynick,channel2@server.com/myothernick</code><br>
          Einen Raum mit Passwort betreten: <code>channel1@server.com/mynick:password</code><br>
        </ul>
    </li><br>  
    <a name="MucRecvWhitelist"></a>
    <li><code>MucRecvWhitelist &lt;Regex&gt;</code><br>
        Selbe funktion wie RecvWhitelist, aber f&uuml;r Gruppenr&auml;ume: Nur wenn die Regex zutrifft, wird die Nachricht verarbeitet. Alles andere wird ignoriert.<br>
        <br>
        Standard: nicht definiert (keine Nachricht wird akzeptiert)<br>
        Beispiele:<br>
        <ul>
          Alle Nachrichten aller betretenen R&auml;ume erlauben: <code>.*</code><br>
          Alle Nachrichten bestimmter betretenen R&auml;ume erlauben: <code>mychannel@jabber.org</code><br>
          Nur bestimmte User in bestimmten betretenen R&auml;umen erlauben: <code>mychannel@jabber.org/NickOfFriend</code><br>
        </ul>
    </li><br>  
    <a name="OTREnable"></a>
    <li><code>OTREnable 1|0</code><br>
        Schaltet die Verschl&uuml;sselungsfunktionen von Crypt::OTR f&uuml;r sichere Ende-zu-Ende Kummunikation in FHEM an oder aus.<br>
        Es muss zwangsl&auml;ufig daf&uuml;r Crypt::OTR installiert sein.<br>
        <i>Ein Privater Schl&uuml;ssel wird bei Erstbenutzung generiert, das kann mehr als 2 Stunden dauern!</i><br>
        Daf&uuml;r ist das eine einmalige Sache und FHEM wird dadurch nicht blockiert. Im Device sieht man im OTR_STATE wenn der Private Schl&uuml;ssel fertig ist.<br>
        Erst danach ist OTR aktiv.<br>
         <br>
        Default: nicht definiert (OTR deaktiviert)
    </li><br>  
    <a name="OTRSharedSecret"></a>
    <li><code>OTRSharedSecret aSecretKeyiOnlyKnow@@*</code><br>
        Optionales geheimes Passwort, dass man vom Endger&auml;t an FHEM schicken kann um zu beweisen, dass es sich tats&auml;chlich um FHEM handelt und nicht um einen
        Hacker der sich (z.b. bei dem Internetprovider) zwischengeschaltet hat. 
        Normalerweise bekommt das Endger&auml;t eine Warnung wenn sich an einer bereits verifizierten Verbindung etwas ge&auml;ndert hat.<br>
        Diese Warnung sollte man dann sehr ernst nehmen.
         <br>
        Default: nicht definiert, setze hier dein geheimes Passwort.
    </li><br>  
        
  
  </ul>
  <br>
  <a name="JabberReadings"></a>
  <b>Generierte Readings/Events:</b>
  <ul>
     <li>Privat Nachrichten
      <ul>
        <li><b>Message</b> - Komplette Nachricht inkl. JID und Text</li>
        <li><b>LastMessage</b> - Nur der Textteil der Nachricht</li>
        <li><b>LastSenderJID</b> - Nur die Sender-JID der Nachricht</li>
      </ul>
     </li><br>
     <li>Verschl&uuml;sselte Private Nachrichten (wenn OTREnable=1)
      <ul>
        <li><b>OTRMessage</b> - Komplette entschl&uuml;sselte Nachricht inkl. JID und Text</li>
        <li><b>OTRLastMessage</b> - Nur der Textteil der Nachricht</li>
        <li><b>OTRLastSenderJID</b> - Nur die Sender-JID der Nachricht</li>
      </ul>
     </li><br>
     <li>MUC Raum Nachrichten (wenn MUCJoin gesetzt ist)
      <ul>
        <li><b>MucMessage</b> - Komplette Nachricht (Raumname/Nickname und Text)</li>
        <li><b>MucLastMessage</b> - Nur der Textteil der Nachricht</li>
        <li><b>MucLastSenderJID</b> - Nur die Sender-JID der Nachricht</li>
      </ul>
     </li>
  </ul>
  <br>
  <a name="JabberNotes"></a>
  <b>Notizen des Entwicklers:</b>
    <ul>
      <li>Mit folgendem Notify-Beispiel kannst du auf eingehende Nachrichten reagieren, dieses Beispiel schickt das Reading "Temperatur" des Sensors "BU_Temperatur" bei jeder ankommenden Nachricht an den Sender zur&uuml;ck:<br>
        <pre><code>define Jabber_Notify notify JabberClient1:Message.* {
  my $lastsender=ReadingsVal("JabberClient1","LastSenderJID","0");
  my $lastmsg=ReadingsVal("JabberClient1","LastMessage","0");
  my $temperature=ReadingsVal("BU_Temperatur","temperature","0");
  fhem("set JabberClient1 msg ". $lastsender . " Temp: ".$temperature);
}
        </code></pre>
      </li>
      <li>Auf MUC Nachrichten l&auml;sst sich folgend reagieren, Augenmerk darauf legen dass der Nickname aus $lastsender in der msgmuc Funktion entfernt wird, damit die Nachricht an den Raum geht<br>
        <pre><code>define Jabber_Notify notify JabberClient1:MucMessage.* {
  my $lastsender=ReadingsVal("JabberClient1","LastSenderJID","0");
  my $lastmsg=ReadingsVal("JabberClient1","LastMessage","0");
  my $temperature=ReadingsVal("BU_Temperatur","temperature","0");
  fhem("set JabberClient1 msgmuc ". $lastsender . " Temp: ".$temperature);
}
        </code></pre>
      </li>
      <li>Auf OTR Nachrichten wird reagiert, wie auf normale private Nachrichten auch, jedoch wird mit der msgotr Funktion geantwortet:<br>
        <pre><code>define Jabber_Notify notify JabberClient1:OTRMessage.* {
  my $lastsender=ReadingsVal("JabberClient1","LastSenderJID","0");
  my $lastmsg=ReadingsVal("JabberClient1","LastMessage","0");
  my $temperature=ReadingsVal("BU_Temperatur","temperature","0");
  fhem("set JabberClient1 msgotr ". $lastsender . " Temp: ".$temperature);
}
        </code></pre>
      </li>      
    </ul>    
</ul>
=end html_DE
=cut
