The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
## @file
# File storage methods for notifications

## @class
# File storage methods for notifications
package Lemonldap::NG::Portal::Notification::File;

use strict;
use MIME::Base64;

our $VERSION = '1.1.1';

## @method boolean prereq()
# Check if parameters are set and if storage directory exists.
# @return true if all is OK
sub prereq {
    my $self = shift;
    unless ( $self->{dirName} ) {
        $Lemonldap::NG::Portal::Notification::msg =
          '"dirName" is required in "File" notification type !';
        return 0;
    }
    if ( $self->{table} ) {
        $self->{dirName} =~ s/\/conf\/?$//;
        $self->{dirName} .= "/$self->{table}";
    }
    unless ( -d $self->{dirName} ) {
        $Lemonldap::NG::Portal::Notification::msg =
          "Directory \"$self->{dirName}\" does not exist !";
        return 0;
    }

    # Configure file name separator (_ by default)
    $self->{fileNameSeparator} ||= "_";

    1;
}

## @method hashref get(string uid,string ref)
# Returns notifications corresponding to the user $uid.
# If $ref is set, returns only notification corresponding to this reference.
# @param $uid UID
# @param $ref Notification reference
# @return hashref where keys are filenames and values are XML strings
sub get {
    my ( $self, $uid, $ref ) = @_;
    return () unless ($uid);
    opendir D, $self->{dirName};
    my @notif;
    my $fns = $self->{fileNameSeparator};
    unless ($ref) {
        @notif = grep /^\d{8}${fns}${uid}${fns}\S*\.xml$/, readdir(D);
    }
    else {
        my $tmp = encode_base64( $ref, '' );
        @notif = grep /^\d{8}${fns}${uid}${fns}${tmp}${fns}?\S*\.xml$/,
          readdir(D);
    }
    close D;
    my $files;
    foreach my $file (@notif) {
        unless ( open F, $self->{dirName} . "/$file" ) {
            $self->lmLog( "Unable to read notification $self->{dirName}/$file",
                'error' );
            next;
        }
        $files->{$file} = join( '', <F> );
    }
    return $files;
}

## @method hashref getAll()
# Return all messages not notified.
# @return hashref where keys are internal reference and values are hashref with
# keys date, uid and ref.
sub getAll {
    my $self = shift;
    opendir D, $self->{dirName};
    my @notif;
    my $fns = $self->{fileNameSeparator};
    @notif = grep /^\S*\.xml$/, readdir(D);
    my %h = map {
/^(\d{8})${fns}([^\s${fns}]+)${fns}([^\s${fns}]+)(?:${fns}([^\s${fns}]+))?\.xml$/
          ? (
            $_ => {
                date      => $1,
                uid       => $2,
                ref       => decode_base64($3),
                condition => decode_base64($4)
            }
          )
          : ()
    } @notif;
    return \%h;
}

## @method boolean delete(string myref)
# Mark a notification as done.
# @param $myref identifier returned by get() or getAll()
sub delete {
    my ( $self, $myref ) = @_;
    my $new = ( $myref =~ /(.*?)(?:\.xml)$/ )[0] . '.done';
    return rename( $self->{dirName} . "/$myref", $self->{dirName} . "/$new" );
}

## @method boolean purge(string myref)
# Purge notification (really delete record)
# @param $myref identifier returned by get() or getAll()
# @return true if something was deleted
sub purge {
    my ( $self, $myref ) = @_;
    return unlink( $self->{dirName} . "/$myref" );
}

## @method boolean newNotif(string date, string uid, string ref, string xml)
# Insert a new notification
# @param date Date
# @param uid UID
# @param ref Reference of the notification
# @param xml XML notification
# @return true if succeed
sub newNotif {
    my ( $self, $date, $uid, $ref, $condition, $xml ) = @_;
    my $fns = $self->{fileNameSeparator};
    $date =~ s/-//g;
    return ( 0, "Bad date" ) unless ( $date =~ /^\d{8}/ );
    my $filename =
        $self->{dirName}
      . "/${date}${fns}${uid}${fns}"
      . encode_base64( $ref, '' );
    $filename .= "${fns}" . encode_base64( $condition, '' ) if $condition;
    $filename .= ".xml";

    return ( 0, 'This notification still exists' ) if ( -e $filename );
    my $old = ( $filename =~ /(.*?)(?:\.xml)$/ )[0] . '.done';
    return ( 0, 'This notification has been done' ) if ( -e $old );
    open my $F, ">$filename" or return ( 0, "Unable to create $filename ($!)" );
    binmode($F);
    $xml->toFH($F);
    return ( 0, "Unable to close $filename ($!)" ) unless ( close $F );
    return 1;
}

## @method hashref getDone()
# Returns a list of notification that have been done
# @return hashref where keys are internal reference and values are hashref with
# keys notified, uid and ref.
sub getDone {
    my ($self) = @_;
    opendir D, $self->{dirName};
    my @notif;
    my $fns = $self->{fileNameSeparator};
    @notif = grep /^\d{8}${fns}\S*\.done$/, readdir(D);
    my $res;
    foreach my $file (@notif) {
        my ( $u, $r ) =
          ( $file =~
/^\d+${fns}([^${fns}]+)${fns}([^${fns}]+)${fns}?([^${fns}]+)\.done$/
          );
        die unless ( -f "$self->{dirName}/$file" );
        my $time = ( stat("$self->{dirName}/$file") )[10];
        $res->{$file} = {
            'uid'      => $u,
            'ref'      => decode_base64($r),
            'notified' => $time,
        };
    }
    return $res;
}

1;