diff --git a/fhem/CHANGED b/fhem/CHANGED index e8cc3cac6..e3c5ff6e1 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,4 +1,5 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. + - feature: new module 32_mailcheck.pm (by justme1968) - feature: TCM: new commands added - bugfix: TCM: TCM_ReadAnswer(): response error corrected - feature: readingsGroup: new attribute nolinks diff --git a/fhem/FHEM/32_mailcheck.pm b/fhem/FHEM/32_mailcheck.pm new file mode 100755 index 000000000..d3acdab1b --- /dev/null +++ b/fhem/FHEM/32_mailcheck.pm @@ -0,0 +1,412 @@ + +# $Id$ + +# basic idea from https://github.com/justinribeiro/idlemailcheck + +package main; + +use strict; +use warnings; + +use Mail::IMAPClient; +use IO::Socket::SSL; +use IO::File; +use IO::Handle; + +sub +mailcheck_Initialize($) +{ + my ($hash) = @_; + + $hash->{ReadFn} = "mailcheck_Read"; + + $hash->{DefFn} = "mailcheck_Define"; + $hash->{NotifyFn} = "mailcheck_Notify"; + $hash->{UndefFn} = "mailcheck_Undefine"; + #$hash->{SetFn} = "mailcheck_Set"; + $hash->{GetFn} = "mailcheck_Get"; + $hash->{AttrFn} = "mailcheck_Attr"; + $hash->{AttrList} = "debug:1 ". + "delete_message:1 ". + "disable:1 ". + "intervall ". + "logfile ". + $readingFnAttributes; +} + +##################################### + +sub +mailcheck_Define($$) +{ + my ($hash, $def) = @_; + + my @a = split("[ \t][ \t]*", $def); + + return "Usage: define mailcheck host user password [folder]" if(@a < 5); + + my $name = $a[0]; + + my $host = $a[2]; + my $user = $a[3]; + my $password = $a[4]; + my $folder = $a[5]; + + $hash->{tag} = undef; + + $hash->{NAME} = $name; + + $hash->{Host} = $host; + $hash->{User} = $user; + $hash->{helper}{PASS} = $password; + + $hash->{Folder} = "INBOX"; + $hash->{Folder} = $folder if( $folder ); + + if( $hash->{STATE} eq "???" ) { + $hash->{STATE} = "Initialized"; + } elsif( $hash->{STATE} ne "???" ) { + mailcheck_Disconnect($hash); + mailcheck_Connect($hash); + } + + return undef; +} + +sub +mailcheck_Notify($$) +{ + my ($hash,$dev) = @_; + + if( grep(m/^INITIALIZED$/, @{$dev->{CHANGED}}) ) { + delete $hash->{NotifyFn}; + + mailcheck_Connect($hash); + } +} + +sub +mailcheck_Connect($) +{ + my ($hash) = @_; + my $name = $hash->{NAME}; + + return undef if( AttrVal($name, "disable", 0 ) == 1 ); + + my $socket = IO::Socket::SSL->new( + PeerAddr => $hash->{Host}, + PeerPort => 993, #AttrVal($name, "port", 993). + ); + + if($socket) { + $hash->{STATE} = "Connected"; + $hash->{LAST_CONNECT} = FmtDateTime( gettimeofday() ); + + $hash->{FD} = $socket->fileno(); + $hash->{CD} = $socket; # sysread / close won't work on fileno + $hash->{CONNECTS}++; + $selectlist{$name} = $hash; + Log3 $name, 3, "$name: connected to $hash->{Host}"; + + my $client = Mail::IMAPClient->new( + Socket => $socket, + KeepAlive => 'true', + User => $hash->{User}, + Password => $hash->{helper}{PASS}, + ); + + $client->Debug(AttrVal($name, "debug", 0)) if( $client ); + $client->Debug_fh($hash->{FH}) if( $client && defined($hash->{FH}) ); + + if( $client && $client->IsConnected && $client->IsAuthenticated ) { + $hash->{STATE} = "Logged in"; + $hash->{LAST_LOGIN} = FmtDateTime( gettimeofday() ); + + $hash->{CLIENT} = $client; + Log3 $name, 3, "$name: logged in to $hash->{User}"; + + $hash->{HAS_IDLE} = $client->has_capability("IDLE"); + + my $intervall = AttrVal($name, "intervall", 0); + $intervall = $hash->{HAS_IDLE}?60*10:60*1 if( !$intervall ); + $hash->{INTERVAL} = $intervall; + + RemoveInternalTimer($hash); + InternalTimer(gettimeofday()+$hash->{INTERVAL}, "mailcheck_poll", $hash, 1); + + #if( !$client->has_capability("IDLE") ) { + #mailcheck_Disconnect($hash); + #$hash->{STATE} = "IDLE not supported"; + #return undef; + #} + + $client->Uid(0); + $client->select($hash->{Folder}); + + $hash->{tag} = $client->idle; + + } else { + mailcheck_Disconnect($hash); + } + } +} +sub +mailcheck_Disconnect($) +{ + my ($hash) = @_; + my $name = $hash->{NAME}; + + RemoveInternalTimer($hash); + + return if( !$hash->{CD} ); + + my $client = $hash->{CLIENT}; + $client->done if($client && $client->IsAuthenticated ); + $client->logout if($client && $client->IsConnected); + delete $hash->{CLIENT}; + $hash->{tag} = undef; + Log3 $name, 3, "$name: logged out"; + + close($hash->{CD}) if($hash->{CD}); + delete($hash->{FD}); + delete($hash->{CD}); + delete($selectlist{$name}); + $hash->{STATE} = "Disconnected"; + Log3 $name, 3, "$name: Disconnected"; + $hash->{LAST_DISCONNECT} = FmtDateTime( gettimeofday() ); +} + +sub +mailcheck_Undefine($$) +{ + my ($hash, $arg) = @_; + + mailcheck_Disconnect($hash); + close( $hash->{FH} ); + + return undef; +} + +sub +mailcheck_Set($$@) +{ + my ($hash, $name, $cmd) = @_; + + my $list = ""; + return "Unknown argument $cmd, choose one of $list"; +} + +sub +mailcheck_poll($) +{ + my ($hash) = @_; + + RemoveInternalTimer($hash); + InternalTimer(gettimeofday()+$hash->{INTERVAL}, "mailcheck_poll", $hash, 1); + + my $client = $hash->{CLIENT}; + if( $client && $client->IsConnected && $client->IsAuthenticated ) { + $client->done; + $client->select($hash->{Folder}); + $hash->{tag} = $client->idle; + $hash->{LAST_POLL} = FmtDateTime( gettimeofday() ); + } +} + + +sub +mailcheck_Get($$@) +{ + my ($hash, $name, $cmd) = @_; + + my $list = "folders:noArg update:noArg"; + + my $client = $hash->{CLIENT}; + if( $cmd eq "folders" ) { + if( $client && $client->IsConnected && $client->IsAuthenticated ) { + $client->done; + my @folders = $client->folders; + $hash->{tag} = $client->idle; + return join( "\n", @folders ); + } + return "not connected"; + } elsif( $cmd eq "update" ) { + mailcheck_poll($hash); + return undef; + } + + return "Unknown argument $cmd, choose one of $list"; +} + +sub +mailcheck_Attr($$$) +{ + my ($cmd, $name, $attrName, $attrVal) = @_; + + my $orig = $attrVal; + $attrVal = int($attrVal) if($attrName eq "intervall"); + $attrVal = 60 if($attrName eq "intervall" && $attrVal < 60 && $attrVal != 0); + + if( $attrName eq "debug" ) { + $attrVal = 1 if($attrVal); + + my $hash = $defs{$name}; + my $client = $hash->{CLIENT}; + $client->Debug($attrVal) if( $client ); + } elsif( $attrName eq "logfile" ) { + my $hash = $defs{$name}; + + close( $hash->{FH} ); + delete $hash->{FH}; + delete $hash->{currentlogfile}; + + if( $cmd eq "set" ) { + my @t = localtime; + my $f = ResolveDateWildcards($attrVal, @t); + my $fh = new IO::File ">>$f"; + if( defined($fh) ) { + Log3 $name, 3, "$name: logging to $f"; + + $fh->autoflush(1); + + $hash->{FH} = $fh; + $hash->{currentlogfile} = $f; + + my $client = $hash->{CLIENT}; + $client->Debug_fh($fh) if( $client ); + } else { + Log3 $name, 3, "$name: can't open log file $f"; + + my $client = $hash->{CLIENT}; + $client->Debug_fh(*STDERR) if( $client ); + } + } + } + + if( $cmd eq "set" ) { + if( $orig ne $attrVal ) { + $attr{$name}{$attrName} = $attrVal; + return $attrName ." set to ". $attrVal; + } + } + + return; +} + +sub +mailcheck_Read($) +{ + my ($hash) = @_; + my $name = $hash->{NAME}; + + my $client = $hash->{CLIENT}; + + my $ret = $client->idle_data(); + #Log3 $name, 4, Dumper $ret; + + if( !defined($ret) || !$ret ) { + $hash->{tag} = undef; + $ret = $client->done; + } + + foreach my $resp (@$ret) { + $resp =~ s/\015?\012$//; + if ( $resp =~ /^\*\s+(\d+)\s+(EXISTS)\b/ ) { + $resp =~ s/\D//g; + + $client->done; + + my $msg_count = $client->unseen_count||0; + if ($msg_count > 0) { + my $from = $client->get_header($resp, "From"); + $from =~ s/<[^>]*>//g; #strip the email, only display the sender's name + + my $subject = $client->get_header($resp, "Subject"); + + readingsSingleUpdate($hash, "Subject", $subject, 1 ); + + $client->delete_message( $resp ) if( AttrVal($name, "delete_message", 0) == 1 ); + } + + $client->idle; + + } elsif ( $resp =~ /^\*\s+(BYE)/ ) { + mailcheck_Disconnect($hash); + mailcheck_Connect($hash); + + return undef; + } + } + + $hash->{tag} ||= $client->idle; + + unless ( $client->IsConnected ) { + mailcheck_Disconnect($hash); + mailcheck_Connect($hash); + } +} + + +1; + +=pod +=begin html + + +

mailcheck

+ + +=end html +=cut diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt index 17f46bc88..ed523c6ba 100644 --- a/fhem/MAINTAINER.txt +++ b/fhem/MAINTAINER.txt @@ -85,6 +85,7 @@ FHEM/30_HUEBridge.pm justme1968 http://forum.fhem.de Sonstige FHEM/31_HUEDevice.pm justme1968 http://forum.fhem.de Sonstige Systeme FHEM/31_LightScene.pm justme1968 http://forum.fhem.de Automatisierung FHEM/32_SYSSTAT.pm justme1968 http://forum.fhem.de Unterstützende Dienste +FHEM/32_mailcheck.pm justme1968 http://forum.fhem.de Automatisierung FHEM/33_readingsGroup.pm justme1968 http://forum.fhem.de Frontends FHEM/32_speedtest.pm justme1968 http://forum.fhem.de Sonstiges FHEM/34_panStamp.pm justme1968 http://forum.fhem.de Sonstiges Systeme