From 680b972a5b882d9997fe9a9ab6f44d71bdbf678a Mon Sep 17 00:00:00 2001 From: gandy92 Date: Fri, 8 May 2015 19:04:39 +0000 Subject: [PATCH] MSG: Replace file and mail related code with delegates MSGFile: Assimilate file related code from 75_MSG to make it standalone, without requiring MSG device MSGMail: Assimilate mail related code from 75_MSG to make it standalone, without requiring MSG device git-svn-id: https://svn.fhem.de/fhem/trunk@8546 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 5 + fhem/FHEM/75_MSG.pm | 216 ++++------------------------- fhem/FHEM/76_MSGFile.pm | 244 ++++++++++++++++++-------------- fhem/FHEM/76_MSGMail.pm | 300 +++++++++++++++++++++++++++++----------- 4 files changed, 389 insertions(+), 376 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index f98dc9ce9..729ae1951 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,10 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - change: MSG: Replace file and mail related code with delegates + - change: MSGFile: Assimilate file related code from 75_MSG + to make it standalone, without requiring MSG device + - change: MSGMail: Assimilate mail related code from 75_MSG + to make it standalone, without requiring MSG device - bugfix: MSG: Determine which SSL implementation to use for sending mail (required for libnet-3.06) - change: MilightDevice/MilightBridge: Fixes, features, changes (by MarkusM) diff --git a/fhem/FHEM/75_MSG.pm b/fhem/FHEM/75_MSG.pm index 75579ebb3..071fbe8e4 100644 --- a/fhem/FHEM/75_MSG.pm +++ b/fhem/FHEM/75_MSG.pm @@ -4,6 +4,9 @@ # # History: # +# 2015-05-09: Move mail related code to MSGMail, +# and file related code to MSGFile, +# rewrite to use delegates for compatibility # 2015-05-07: Determine which SSL implementation to use # 2015-05-06: Tidy up code for restructuring # 2015-05-05: Remove dependency on Switch @@ -13,19 +16,12 @@ package main; use strict; use warnings; -use MIME::Lite; -#use Net::SMTP::SSL; - -use Net::SMTP; # libnet-3.06 has SSL included, so we need to check the version my %sets = ( "send" => "MSG", "write" => "MSG", ); -my $MSGMail_SSL = 0; -my $MSGMail_SMTP = 0; - sub MSG_Initialize($) { my ($hash) = @_; @@ -33,54 +29,6 @@ sub MSG_Initialize($) $hash->{SetFn} = "MSG_Set"; $hash->{DefFn} = "MSG_Define"; $hash->{AttrList} = "loglevel:0,1,2,3,4,5,6"; - - my $name = "MSG"; - - # check version of libnet - if < 3.00, try to load Net::SMTP::SSL - $MSGMail_SMTP = $Net::SMTP::VERSION; - if ($Net::SMTP::VERSION >= 3) - { - $MSGMail_SSL = 1; - } - else - { - eval "use Net::SMTP::SSL"; - if ($@) - { - Log 0, $@ if($@); - $MSGMail_SSL = 0; - } - else - { - $MSGMail_SSL = 1; - } - } - Log 2, "$name: SSL is ".(($MSGMail_SSL) ? ("available, provided by Net::SMTP".(($MSGMail_SMTP<3.00)?"::SSL":"")):"not available"); -} - -sub MSGMail_conn($) -{ - my ($hash) = @_; - my ($name) = $hash->{NAME}; - - my $smtphost = AttrVal($name, "smtphost", ""); - my $smtpport = AttrVal($name, "smtpport", "465"); # 465 is the default port - - if ($MSGMail_SSL) - { - if ($MSGMail_SMTP < 3.00) - { - Log3 $name, 3, "$name: try to connect with Net::SMTP::SSL"; - return Net::SMTP::SSL->new($smtphost, Port => $smtpport); - } - else - { - Log3 $name, 3, "$name: try to connect with Net::SMTP"; - return Net::SMTP->new(Host=>$smtphost, Port=>$smtpport, SSL=>1); - } - } - Log3 $name, 0, "$name: SSL not available. Connection will fail"; - return undef; } sub MSG_Set($@) @@ -106,126 +54,14 @@ sub MSG_Set($@) { return "TYPE for $defs{$a[1]} not defined"; } - - #################################################################################################### - ## - ## M S G F i l e - ## - #################################################################################################### elsif ($defs{ $a[1] }{TYPE} eq "MSGFile") { - - return "No filename specified, use attr filename $a[1] $defs{$a[1]}{TYPE}" - if (!AttrVal($a[1], "filename", "")); - - # open the file, new = delete file before create it - # append = add lines to the existing file contents - if (AttrVal($a[1], "filemode", "") eq "new") - { - open(FHEMMSGFILE, ">" . AttrVal($a[1], "filename", "")) - || return "Can not open the file: $!"; - } - else - { - open(FHEMMSGFILE, ">>" . AttrVal($a[1], "filename", "")) - || return "Can not open the file: $!"; - } - - # loop thru the stored lines and write them to the file - # number of lines are in the Readings / msgcount - my $i; - if (ReadingsVal($a[1], "msgcount", 0) > 0) - { - for ($i = 0 ; $i < ReadingsVal($a[1], "msgcount", 0) ; $i++) - { - print FHEMMSGFILE $data{ $a[1] }{$i} - || return "Can not write to the file: $!"; - } - } - - # close the file and send a message to the log - close(FHEMMSGFILE); - Log 1, " write to File: " . AttrVal($a[1], "filename", ""); - } # END MSGFile - - #################################################################################################### - ## - ## M S G M a i l - ## - ## We use MAIL::Lite to compose the message, because it works very well at this and - ## we use Net::SMTP::SSL to connect to the smtp host and manage the authenification, - ## because MAIL:Lite is not very easy to manage with SSL connections - #################################################################################################### - + fhem("set $a[1] write"); + } elsif ($defs{ $a[1] }{TYPE} eq "MSGMail") { - # check all required data - my $from = AttrVal($a[1], "from", ""); - return "No address specified, use attr $a[1] from " - if (!$from); - my $to = AttrVal($a[1], "to", ""); - return "No address specified, use attr $a[1] to " - if (!$to); - my $subject = AttrVal($a[1], "subject", ""); - return "No specified, use attr $a[1] subject " - if (!$subject); - my $authfile = AttrVal($a[1], "authfile", ""); - return "No specified, use attr $a[1] authfile " - if (!$authfile); - my $smtphost = AttrVal($a[1], "smtphost", ""); - return "No name specified, use attr $a[1] sntphost " - if (!$smtphost); - my $smtpport = AttrVal($a[1], "smtpport", "465"); # 465 is the default port - my $cc = AttrVal($a[1], "cc", ""); # Carbon Copy - open(FHEMAUTHFILE, "<" . $authfile) - || return "Can not open authfile $authfile: $!"; - my @auth = ; - close(FHEMAUTHFILE); - chomp(@auth); - - # Log 1, "MSG User = <" . @auth[0] . "> Passwort = <" . @auth[1] . ">"; - - # compose message - my $i; - my $mess = ""; - for ($i = 0 ; $i < ReadingsVal($a[1], "msgcount", 0) ; $i++) - { - $mess .= $data{ $a[1] }{$i}; - } - - my $mailmsg = MIME::Lite->new( - From => $from, - To => $to, - Subject => $subject, - Type => 'text/plain; charset=UTF-8', #'multipart/mixed', # was 'text/plain' - Data => $mess - ); - - # login to the SMTP Host using SSL and send the message - my $smtp; - my $smtperrmsg = "SMTP Error: "; - - #$smtp = Net::SMTP::SSL->new($smtphost, Port => $smtpport) - $smtp = MSGMail_conn($defs{ $a[1] }) - or return $smtperrmsg . " Can't connect to host $smtphost"; - $smtp->auth($auth[0], $auth[1]) - or return $smtperrmsg . " Can't authenticate: " . $smtp->message(); - $smtp->mail($from) or return $smtperrmsg . $smtp->message(); - $smtp->to($to) or return $smtperrmsg . $smtp->message(); - if ($cc ne '') - { - Log 1, "CC = $cc"; - $smtp->cc($cc) or return $smtperrmsg . $smtp->message(); - } - $smtp->data() or return $smtperrmsg . $smtp->message(); - $smtp->datasend($mailmsg->as_string) - or return $smtperrmsg . $smtp->message(); - $smtp->dataend() or return $smtperrmsg . $smtp->message(); - $smtp->quit() or return $smtperrmsg . $smtp->message(); - - Log 1, " send EMail: <$subject>"; - - } ###> END MSGMail + fhem("set $a[1] send"); + } else { return "MSG Filetype $defs{$a[1]}{TYPE} unknown"; @@ -263,16 +99,22 @@ sub MSG_Define($$)

MSG

    - The MSG device is the backend device for all the message handling (I/O-engine). - Under normal conditions only one MSG device is needed to serve multiple frontend - message devices like file or email. + The MSG device is deprecated.

    + It used to be the backend device for I/O-handling of MSGMail + and MSGFile devices. + The MSG device can still be used for this purpose, but actually delegates send and + write commands to the MSGMail and MSGFile + devices, respectively.

    + Before removing a deprecated MSG device from an installation, make sure to change + code in user functions, accordingly.

    Define
      define <name> MSG

      - Specifies the MSG device. A single MSG device could serve multiple MSG frontends. - But, for special conditions there could be defined more than one MSG device. + Specifies the MSG device. This is no longer required since + MSGFile and MSGMail + devices provide all necessary functionality.

    @@ -282,18 +124,12 @@ sub MSG_Define($$)
Notes:
    - To send the data, both send or write could be used.
    - The devicename is the name of a frontenddevice previously - defined. Based on the type of the frontend device, the MSG device - will send out the lines of data. + To process the data, both 'send' and 'write' can be used.
    + The MSG device will delegate the command to the device specified + by 'devicename' and automatically use 'send' for + MSGMail and 'write' for + MSGFile devices.
    - Frontend devices are available for:
    - - For details about this devices, please review the device-definitions.
    - After sending/writing the data, the data stills exists with the - frontend device, MSG do not delete/purge any data, this must be done - by the frontend device.

    Examples:
      @@ -302,12 +138,8 @@ sub MSG_Define($$)
    Attributes -

    diff --git a/fhem/FHEM/76_MSGFile.pm b/fhem/FHEM/76_MSGFile.pm index 34f1219ba..cdba92cf6 100644 --- a/fhem/FHEM/76_MSGFile.pm +++ b/fhem/FHEM/76_MSGFile.pm @@ -2,15 +2,12 @@ # $Id$ ######################################################## # -# Created 2012 by rbente -# -######################################################## -# # History: # -# 2015-05-06: tidy up code for restructuring -# 2015-05-05: remove dependency on Switch -# +# 2015-05-09: Assimilate file related code from 75_MSG +# 2015-05-06: Tidy up code for restructuring +# 2015-05-05: Remove dependency on Switch +# 2012 : Created by rbente # package main; @@ -19,7 +16,8 @@ use warnings; my %sets = ( "add" => "MSGFile", "clear" => "MSGFile", - "list" => "MSGFile" + "list" => "MSGFile", + "write" => "MSGFile" ); ############################################## @@ -41,78 +39,6 @@ sub MSGFile_Initialize($) $hash->{AttrList} = "loglevel:0,1,2,3,4,5,6 filename filemode:new,append CR:0,1"; } -############################################## -# Set Function -# all the data are stored in the global array @data -# as counter we use a READING named msgcount -############################################## -sub MSGFile_Set($@) -{ - my ($hash, @a) = @_; - return "Unknown argument $a[1], choose one of -> " . join(" ", sort keys %sets) - if (!defined($sets{ $a[1] })); - - my $name = shift @a; - - return "no set value specified" if (int(@a) < 1); - return "Unknown argument ?" if ($a[0] eq "?"); - my $v = join(" ", @a); - - # we like to add another line of data - if ($a[0] eq "add") - { - # split the line in command and data - my $mx = shift @a; - my $my = join(" ", @a); - # check if we like to have and CR at the end of the line - if (AttrVal($name, "CR", "0") eq "1") - { - $my = $my . "\n"; - } - # get the highest number of lines, store the line in @data and increase - # the counter, at the end set the status - my $count = $hash->{READINGS}{msgcount}{VAL}; - $data{$name}{$count} = $my; - $hash->{READINGS}{msgcount}{TIME} = TimeNow(); - $hash->{READINGS}{msgcount}{VAL} = $count + 1; - $hash->{STATE} = "addmsg"; - - } - # we like to clear our buffer, first clear all lines of @data - # and then set the counter to 0 and the status to clear - elsif ($a[0] eq "clear") - { - my $i; - for ($i = 0 ; $i < ReadingsVal($name, "msgcount", 0) ; $i++) - { - $data{$name}{$i} = ""; - } - $hash->{READINGS}{msgcount}{TIME} = TimeNow(); - $hash->{READINGS}{msgcount}{VAL} = 0; - $hash->{STATE} = "clear"; - - } - # we like to see the buffer - elsif ($a[0] eq "list") - { - my $i; - my $mess = "---- Lines of data for $name ----\n"; - for ($i = 0 ; $i < ReadingsVal($name, "msgcount", 0) ; $i++) - { - $mess .= $data{$name}{$i}; - } - return "$mess---- End of data for $name ----"; - } - - Log GetLogLevel($name, 2), "messenger set $name $v"; - - # set stats - # $hash->{CHANGED}[0] = $v; - $hash->{READINGS}{state}{TIME} = TimeNow(); - $hash->{READINGS}{state}{VAL} = $v; - return undef; -} - ############################################## # Define Function # set the counter to 0 @@ -155,6 +81,117 @@ sub MSGFile_Undef($$) delete($modules{MSGFile}{defptr}{ $hash->{CODE} }) if ($hash && $hash->{CODE}); return undef; } + +############################################## +# Set Function +# all the data are stored in the global array @data +# as counter we use a READING named msgcount +############################################## +sub MSGFile_Set($@) +{ + my ($hash, @a) = @_; + return "Unknown argument $a[1], choose one of -> " . join(" ", sort keys %sets) + if (!defined($sets{ $a[1] })); + + my $name = shift @a; + + return "no set value specified" if (int(@a) < 1); + return "Unknown argument ?" if ($a[0] eq "?"); + my $v = join(" ", @a); + + # we like to add another line of data + if ($a[0] eq "add") + { + # split the line in command and data + my $mx = shift @a; + my $my = join(" ", @a); + + # check if we like to have and CR at the end of the line + if (AttrVal($name, "CR", "0") eq "1") + { + $my = $my . "\n"; + } + + # get the highest number of lines, store the line in @data and increase + # the counter, at the end set the status + my $count = $hash->{READINGS}{msgcount}{VAL}; + $data{$name}{$count} = $my; + $hash->{READINGS}{msgcount}{TIME} = TimeNow(); + $hash->{READINGS}{msgcount}{VAL} = $count + 1; + $hash->{STATE} = "addmsg"; + + } + + # we like to clear our buffer, first clear all lines of @data + # and then set the counter to 0 and the status to clear + elsif ($a[0] eq "clear") + { + my $i; + for ($i = 0 ; $i < ReadingsVal($name, "msgcount", 0) ; $i++) + { + $data{$name}{$i} = ""; + } + $hash->{READINGS}{msgcount}{TIME} = TimeNow(); + $hash->{READINGS}{msgcount}{VAL} = 0; + $hash->{STATE} = "clear"; + + } + + # we like to see the buffer + elsif ($a[0] eq "list") + { + my $i; + my $mess = "---- Lines of data for $name ----\n"; + for ($i = 0 ; $i < ReadingsVal($name, "msgcount", 0) ; $i++) + { + $mess .= $data{$name}{$i}; + } + return "$mess---- End of data for $name ----"; + } + elsif ($a[0] eq "write") + { + return "No filename specified, use attr filename $name $defs{$name}{TYPE}" + if (!AttrVal($name, "filename", "")); + + # open the file, new = delete file before create it + # append = add lines to the existing file contents + if (AttrVal($name, "filemode", "") eq "new") + { + open(FHEMMSGFILE, ">" . AttrVal($name, "filename", "")) + || return "Can not open the file: $!"; + } + else + { + open(FHEMMSGFILE, ">>" . AttrVal($name, "filename", "")) + || return "Can not open the file: $!"; + } + + # loop thru the stored lines and write them to the file + # number of lines are in the Readings / msgcount + my $i; + if (ReadingsVal($name, "msgcount", 0) > 0) + { + for ($i = 0 ; $i < ReadingsVal($name, "msgcount", 0) ; $i++) + { + print FHEMMSGFILE $data{ $name }{$i} + || return "Can not write to the file: $!"; + } + } + + # close the file and send a message to the log + close(FHEMMSGFILE); + Log 1, " write to File: " . AttrVal($name, "filename", ""); + } # END MSGFile + + Log GetLogLevel($name, 2), "messenger set $name $v"; + + # set stats + # $hash->{CHANGED}[0] = $v; + $hash->{READINGS}{state}{TIME} = TimeNow(); + $hash->{READINGS}{state}{VAL} = $v; + return undef; +} + 1; =pod @@ -163,15 +200,13 @@ sub MSGFile_Undef($$)

    MSGFile

      - The MSGFile device is a frontend device for message handling. - With a MSGFile device data is written to disk (or other media). - Multiple MSGFile devices could be defined. - To write the data to disk, a MSG device is necessary. - A MSGFile device needs the operating systems rights to write to the filesystem. + The MSGFile device is used to write arbitrary data to a file on disk + or other media accessable through the filesystem. In order to write to a file, + the access rights of the FHEM process to the specified file and path are relevant. To set the rights for a directory, please use OS related commands.

      - + Define

        define <name> MSGFile <filename>

        @@ -186,33 +221,38 @@ sub MSGFile_Undef($$) Set
        -
          set <name> add|clear|list [text]
          +
            set <name> add|clear|list|write [text]
            Set is used to manipulate the message buffer of the device. The message buffer is an array of lines of data, stored serial based on the incoming time into the buffer. Lines of data inside the buffer could not be deleted anymore, except of flashing the whole buffer.
            +
              add
              to add lines of data to the message buffer. All data behind "add" will be interpreted as text message. To add a carriage return to the data, please use the CR attribute.
            +
              clear
              to flash the message buffer and set the line counter to 0. All the lines of data are deleted and the buffer is flushed.
            +
              list
              to list the message buffer.

            -

          - Examples: -
            - set myFile add Dies ist Textzeile 1
            - set myFile add Dies ist Textzeile 2
            - set myFile clear

            - Full working example to write two lines of data to a file:
            - define myMsg MSG
            - define myFile MSGFile /tmp/fhemtest.txt
            - attr myFile filemode append
            - set myFile add Textzeile 1
            - set myFile add Textzeile 2
            - set myMsg write myFile
            - set myFile clear
            -

          + +
            write
            to write the message buffer to the associated file.

          +

        + Examples: +
          + set myFile add Dies ist Textzeile 1
          + set myFile add Dies ist Textzeile 2
          + set myFile clear
          +
          + Full working example to write two lines of data to a file:
          + define myFile MSGFile /tmp/fhemtest.txt
          + attr myFile filemode append
          + set myFile add Textzeile 1
          + set myFile add Textzeile 2
          + set myFile write
          + set myFile clear
          +

        Attributes diff --git a/fhem/FHEM/76_MSGMail.pm b/fhem/FHEM/76_MSGMail.pm index 7ef068d13..8e519bd84 100644 --- a/fhem/FHEM/76_MSGMail.pm +++ b/fhem/FHEM/76_MSGMail.pm @@ -2,26 +2,30 @@ # $Id$ ######################################################## # -# Created 2012 by rbente -# -######################################################## -# # History: # -# 2015-05-06: tidy up code for restructuring -# 2015-05-05: remove dependency on Switch -# +# 2015-05-09: Assimilate mail related code from 75_MSG +# 2015-05-06: Tidy up code for restructuring +# 2015-05-05: Remove dependency on Switch +# 2012 : Created by rbente # package main; use strict; use warnings; +use MIME::Lite; +use Net::SMTP; # libnet-3.06 has SSL included, so we need to check the version + my %sets = ( "add" => "MSGMail", "clear" => "MSGMail", - "list" => "MSGMail" + "list" => "MSGMail", + "send" => "MSGMail" ); +my $MSGMail_SSL = 0; +my $MSGMail_SMTP = 0; + ############################################## # Initialize Function # Attributes are: @@ -43,79 +47,33 @@ sub MSGMail_Initialize($) $hash->{DefFn} = "MSGMail_Define"; $hash->{UndefFn} = "MSGMail_Undef"; $hash->{AttrList} = "loglevel:0,1,2,3,4,5,6 authfile smtphost smtpport subject from to cc CR:0,1"; -} -############################################## -# Set Function -# all the data are stored in the global array @data -# as counter we use a READING named msgcount -############################################## -sub MSGMail_Set($@) -{ - my ($hash, @a) = @_; - return "Unknown argument $a[1], choose one of -> " . join(" ", sort keys %sets) - if (!defined($sets{ $a[1] })); - my $name = shift @a; + my $name = "MSGMail"; - return "no set value specified" if (int(@a) < 1); - - # return "Unknown argument ?" if($a[0] eq "?"); - my $v = join(" ", @a); - # we like to add another line of data - if ($a[0] eq "add") + # check version of libnet - if < 3.00, try to load Net::SMTP::SSL + $MSGMail_SMTP = $Net::SMTP::VERSION; + if ($Net::SMTP::VERSION >= 3) { - # split the line in command and data - my $mx = shift @a; - my $my = join(" ", @a); - # check if we like to have and CR at the end of the line - if (AttrVal($name, "CR", "0") eq "1") - { - $my = $my . "\n"; - } - # get the highest number of lines, stored the line in @data and increase - # the counter, at the end set the status - my $count = $hash->{READINGS}{msgcount}{VAL}; - $data{$name}{$count} = $my; - $hash->{READINGS}{msgcount}{TIME} = TimeNow(); - $hash->{READINGS}{msgcount}{VAL} = $count + 1; - $hash->{STATE} = "addmsg"; - + $MSGMail_SSL = 1; } - # we like to clear our buffer, first clear all lines of @data - # and then set the counter to 0 and the status to clear - - elsif ($a[0] eq "clear") + else { - my $i; - for ($i = 0 ; $i < ReadingsVal($name, "msgcount", 0) ; $i++) + eval "use Net::SMTP::SSL"; + if ($@) { - $data{$name}{$i} = ""; + Log 0, $@ if ($@); + $MSGMail_SSL = 0; } - $hash->{READINGS}{msgcount}{TIME} = TimeNow(); - $hash->{READINGS}{msgcount}{VAL} = 0; - $hash->{STATE} = "clear"; - - } - - # we like to see the buffer - - elsif ($a[0] eq "list") - { - my $i; - my $mess = "---- Lines of data for $name ----\n"; - for ($i = 0 ; $i < ReadingsVal($name, "msgcount", 0) ; $i++) + else { - $mess .= $data{$name}{$i}; + $MSGMail_SSL = 1; } - return "$mess---- End of data for $name ----"; } - Log GetLogLevel($name, 2), "messenger set $name $v"; - - # set stats - # $hash->{CHANGED}[0] = $v; - $hash->{READINGS}{state}{TIME} = TimeNow(); - $hash->{READINGS}{state}{VAL} = $v; - return undef; + Log 2, + "$name: SSL is " + . ( ($MSGMail_SSL) + ? ("available, provided by Net::SMTP" . (($MSGMail_SMTP < 3.00) ? "::SSL" : "")) + : "not available"); } ############################################## @@ -130,6 +88,7 @@ sub MSGMail_Define($$) my $name = $hash->{NAME}; return $errmsg if (@a != 6); + # set all the Attributes $attr{$name}{from} = $a[2]; $attr{$name}{to} = $a[3]; @@ -153,6 +112,7 @@ sub MSGMail_Undef($$) { my ($hash, $name) = @_; my $i; + # flush the data for ($i = 0 ; $i < ReadingsVal($name, "msgcount", 0) ; $i++) { @@ -163,6 +123,183 @@ sub MSGMail_Undef($$) return undef; } +############################################## +# Set Function +# all the data are stored in the global array @data +# as counter we use a READING named msgcount +############################################## +sub MSGMail_Set($@) +{ + my ($hash, @a) = @_; + return "Unknown argument $a[1], choose one of -> " . join(" ", sort keys %sets) + if (!defined($sets{ $a[1] })); + my $name = shift @a; + + return "no set value specified" if (int(@a) < 1); + + # return "Unknown argument ?" if($a[0] eq "?"); + my $v = join(" ", @a); + + # we like to add another line of data + if ($a[0] eq "add") + { + # split the line in command and data + my $mx = shift @a; + my $my = join(" ", @a); + + # check if we like to have and CR at the end of the line + if (AttrVal($name, "CR", "0") eq "1") + { + $my = $my . "\n"; + } + + # get the highest number of lines, stored the line in @data and increase + # the counter, at the end set the status + my $count = $hash->{READINGS}{msgcount}{VAL}; + $data{$name}{$count} = $my; + $hash->{READINGS}{msgcount}{TIME} = TimeNow(); + $hash->{READINGS}{msgcount}{VAL} = $count + 1; + $hash->{STATE} = "addmsg"; + } + + # we like to clear our buffer, first clear all lines of @data + # and then set the counter to 0 and the status to clear + + elsif ($a[0] eq "clear") + { + my $i; + for ($i = 0 ; $i < ReadingsVal($name, "msgcount", 0) ; $i++) + { + $data{$name}{$i} = ""; + } + $hash->{READINGS}{msgcount}{TIME} = TimeNow(); + $hash->{READINGS}{msgcount}{VAL} = 0; + $hash->{STATE} = "clear"; + } + + # we like to see the buffer + + elsif ($a[0] eq "list") + { + my $i; + my $mess = "---- Lines of data for $name ----\n"; + for ($i = 0 ; $i < ReadingsVal($name, "msgcount", 0) ; $i++) + { + $mess .= $data{$name}{$i}; + } + return "$mess---- End of data for $name ----"; + } + + elsif ($a[0] eq "send") + { + # check all required data + my $from = AttrVal($name, "from", ""); + my $to = AttrVal($name, "to", ""); + my $subject = AttrVal($name, "subject", ""); + my $authfile = AttrVal($name, "authfile", ""); + my $smtphost = AttrVal($name, "smtphost", ""); + my $smtpport = AttrVal($name, "smtpport", "465"); # 465 is the default port + my $cc = AttrVal($name, "cc", ""); # Carbon Copy + + return "No address specified, use attr $name from " + if (!$from); + return "No address specified, use attr $name to " + if (!$to); + return "No specified, use attr $name subject " + if (!$subject); + return "No specified, use attr $name authfile " + if (!$authfile); + return "No name specified, use attr $name sntphost " + if (!$smtphost); + + open(FHEMAUTHFILE, "<" . $authfile) + || return "Can not open authfile $authfile: $!"; + my @auth = ; + close(FHEMAUTHFILE); + chomp(@auth); + + # Log 1, "MSG User = <" . @auth[0] . "> Passwort = <" . @auth[1] . ">"; + + # compose message + my $i; + my $mess = ""; + for ($i = 0 ; $i < ReadingsVal($name, "msgcount", 0) ; $i++) + { + $mess .= $data{ $name }{$i}; + } + + my $mailmsg = MIME::Lite->new( + From => $from, + To => $to, + Subject => $subject, + Type => 'text/plain; charset=UTF-8', #'multipart/mixed', # was 'text/plain' + Data => $mess + ); + + # login to the SMTP Host using SSL and send the message + my $smtp; + my $smtperrmsg = "SMTP Error: "; + + #$smtp = Net::SMTP::SSL->new($smtphost, Port => $smtpport) + $smtp = MSGMail_conn($defs{ $name }) + or return $smtperrmsg . " Can't connect to host $smtphost"; + $smtp->auth($auth[0], $auth[1]) + or return $smtperrmsg . " Can't authenticate: " . $smtp->message(); + $smtp->mail($from) or return $smtperrmsg . $smtp->message(); + $smtp->to($to) or return $smtperrmsg . $smtp->message(); + if ($cc ne '') + { + Log 1, "CC = $cc"; + $smtp->cc($cc) or return $smtperrmsg . $smtp->message(); + } + $smtp->data() or return $smtperrmsg . $smtp->message(); + $smtp->datasend($mailmsg->as_string) + or return $smtperrmsg . $smtp->message(); + $smtp->dataend() or return $smtperrmsg . $smtp->message(); + $smtp->quit() or return $smtperrmsg . $smtp->message(); + + Log 1, " send EMail: <$subject>"; + + } ###> END MSGMail + + Log GetLogLevel($name, 2), "messenger set $name $v"; + + # set stats + # $hash->{CHANGED}[0] = $v; + $hash->{READINGS}{state}{TIME} = TimeNow(); + $hash->{READINGS}{state}{VAL} = $v; + return undef; +} + +############################################## +# Helper Function to connect to mail server +# Returns a smtp connection (see Net:SMTP) +############################################## +sub MSGMail_conn($) +{ + my ($hash) = @_; + my ($name) = $hash->{NAME}; + + my $smtphost = AttrVal($name, "smtphost", ""); + my $smtpport = AttrVal($name, "smtpport", "465"); # 465 is the default port + + if ($MSGMail_SSL) + { + if ($MSGMail_SMTP < 3.00) + { + Log3 $name, 3, "$name: try to connect with Net::SMTP::SSL"; + return Net::SMTP::SSL->new($smtphost, Port => $smtpport); + } + else + { + Log3 $name, 3, "$name: try to connect with Net::SMTP"; + return Net::SMTP->new(Host => $smtphost, Port => $smtpport, SSL => 1); + } + } + Log3 $name, 0, "$name: SSL not available. Connection will fail"; + return undef; +} + 1; =pod @@ -171,13 +308,12 @@ sub MSGMail_Undef($$)

        MSGMail

          - The MSGMail device is a frontend device for mail message handling. - With a MSGMaildevice data is fowarded to a mail provider and send to a recipent. - Multiple MSGMail devices could be defined. - MSGMail supports by the moment only mail provider, which uses SSL secured connection - like Googlemail, GMX, Yahoo or 1und1 for example. - To send an email, a MSG device is necessary.
          - MAIL::Lite and Net::SMTP::SSL from CPAN is needed to use MSGMail!! + The MSGMail device is used to send mail messages to a recipient by connecting + to a SMTP server. Currently MSGMail supports only servers, that allow SSL secured connections + like Googlemail, GMX, Yahoo or 1und1. + MSGMail requires the perl pacakge MAIL::Lite. + For SSL support, Net::SMTP version 3.06 is required. On systems with an older version of Net::SMTP, + MSGMail requires the package Net::SMTP::SSL.

          @@ -196,7 +332,7 @@ sub MSGMail_Undef($$) Set
          -
            set <name> add|clear|list [text]
            +
              set <name> add|clear|list|send [text]
              Set is used to manipulate the message buffer of the device. The message buffer is an array of lines of data, stored serial based on the incoming time into the buffer. Lines of data inside the buffer could not be deleted @@ -205,9 +341,10 @@ sub MSGMail_Undef($$) "add" will be interpreted as text message. To add a carriage return to the data, please use the CR attribute.
            -
              clear
              to flash the message buffer and set the line counter to 0. +
                clear
                to flush the message buffer and set the line counter to 0. All the lines of data are deleted and the buffer is flushed.
                list
                to list the message buffer.

              +
                send
                to send the message buffer.


              Examples:
                @@ -215,14 +352,13 @@ sub MSGMail_Undef($$) set myMail add Dies ist Textzeile 2
                set myMail clear

                Full working example to send two lines of data to a recipent:
                - define myMsg MSG
                define myMail MSGMail donald.duck@entenhausen.com dagobert.duck@duck-banking.com smtp.entenhausen.net /etc/fhem/msgmailauth
                attr myMail smtpport 9999
                attr myMail subject i need more money
                attr myMail CR 0
                set myMail add Please send me
                set myMail add 1.000.000 Taler
                - set myMsg send myMail
                + set myMail send
                set myMail clear