X-Git-Url: https://git.proxmox.com/?p=pve-common.git;a=blobdiff_plain;f=src%2FPVE%2FTools.pm;h=8c7f3733f80aa0c0338a09c6d9d314122138a342;hp=d6b501edd672d93cc22dd876ed121e9965318ec0;hb=0a7de8204ea8a99dd723660438ae1ffef46549af;hpb=d0229d1d821ad022a455df12eaa27d7caf51d146 diff --git a/src/PVE/Tools.pm b/src/PVE/Tools.pm index d6b501e..8c7f373 100644 --- a/src/PVE/Tools.pm +++ b/src/PVE/Tools.pm @@ -75,7 +75,8 @@ use constant {CLONE_NEWNS => 0x00020000, CLONE_NEWPID => 0x20000000, CLONE_NEWNET => 0x40000000}; -use constant O_PATH => 0x00200000; +use constant {O_PATH => 0x00200000, + O_TMPFILE => 0x00410000}; # This includes O_DIRECTORY sub run_with_timeout { my ($timeout, $code, @param) = @_; @@ -677,7 +678,7 @@ my $keymaphash = { }; my $kvmkeymaparray = []; -foreach my $lc (keys %$keymaphash) { +foreach my $lc (sort keys %$keymaphash) { push @$kvmkeymaparray, $keymaphash->{$lc}->[1]; } @@ -1213,4 +1214,112 @@ sub sync_mountpoint { return $result; } +# support sending multi-part mail messages with a text and or a HTML part +# mailto may be a single email string or an array of receivers +sub sendmail { + my ($mailto, $subject, $text, $html, $mailfrom, $author) = @_; + + $mailto = [ $mailto ] if !ref($mailto); + + my $rcvrarg = ''; + foreach my $r (@$mailto) { + $rcvrarg .= " '$r'"; + } + my $rcvrtxt = join (', ', @$mailto); + + $mailfrom = $mailfrom || "root"; + $author = $author || 'Proxmox VE'; + + open (MAIL,"|sendmail -B 8BITMIME -f $mailfrom $rcvrarg") || + die "unable to open 'sendmail' - $!"; + + # multipart spec see https://www.ietf.org/rfc/rfc1521.txt + my $boundary = "----_=_NextPart_001_".int(time).$$; + + print MAIL "Content-Type: multipart/alternative;\n"; + print MAIL "\tboundary=\"$boundary\"\n"; + print MAIL "MIME-Version: 1.0\n"; + + print MAIL "FROM: $author <$mailfrom>\n"; + print MAIL "TO: $rcvrtxt\n"; + print MAIL "SUBJECT: $subject\n"; + print MAIL "\n"; + print MAIL "This is a multi-part message in MIME format.\n\n"; + print MAIL "--$boundary\n"; + + if (defined($text)) { + print MAIL "Content-Type: text/plain;\n"; + print MAIL "\tcharset=\"UTF8\"\n"; + print MAIL "Content-Transfer-Encoding: 8bit\n"; + print MAIL "\n"; + + # avoid 'remove extra line breaks' issue (MS Outlook) + my $fill = ' '; + $text =~ s/^/$fill/gm; + + print MAIL $text; + + print MAIL "\n--$boundary\n"; + } + + if (defined($html)) { + print MAIL "Content-Type: text/html;\n"; + print MAIL "\tcharset=\"UTF8\"\n"; + print MAIL "Content-Transfer-Encoding: 8bit\n"; + print MAIL "\n"; + + print MAIL $html; + + print MAIL "\n--$boundary--\n"; + } + + close(MAIL); +} + +sub tempfile { + my ($perm, %opts) = @_; + + # default permissions are stricter than with file_set_contents + $perm = 0600 if !defined($perm); + + my $dir = $opts{dir} // '/tmp'; + my $mode = $opts{mode} // O_RDWR; + $mode |= O_EXCL if !$opts{allow_links}; + + my $fh = IO::File->new($dir, $mode | O_TMPFILE, $perm) + or die "failed to create tempfile: $!\n"; + return $fh; +} + +sub tempfile_contents { + my ($data, $perm, %opts) = @_; + + my $fh = tempfile($perm, %opts); + eval { + die "unable to write to tempfile: $!\n" if !print {$fh} $data; + die "unable to flush to tempfile: $!\n" if !defined($fh->flush()); + }; + if (my $err = $@) { + close $fh; + die $err; + } + + return ("/proc/$$/fd/".$fh->fileno, $fh); +} + +sub validate_ssh_public_keys { + my ($raw) = @_; + my @lines = split(/\n/, $raw); + + foreach my $line (@lines) { + next if $line =~ m/^\s*$/; + eval { + my ($filename, $handle) = tempfile_contents($line); + run_command(["ssh-keygen", "-l", "-f", $filename], + outfunc => sub {}, errfunc => sub {}); + }; + die "SSH public key validation error\n" if $@; + } +} + 1;