+ # Init file
+ #XXX: we use this file to store the requested domains on our side
+ #XXX: see bug https://github.com/letsencrypt/boulder/issues/3335 and https://community.letsencrypt.org/t/acmev2-orders-list/51662
+ my $file = $self->{req}{pending}.'/'.(((sha256_base64(join(',', ($self->{domain}{domain}, @{$self->{domain}{domains}})))) =~ s/=+\z//r) =~ tr[+/][-_]r);
+
+ # Init content
+ my $content = undef;
+
+ # Load account content or post a new one
+ if (
+ #XXX: use eval to workaround a fatal in from_json
+ ! defined eval {
+ # Check that file exists
+ -f $file &&
+ # Read it
+ ($content = read_file($file)) &&
+ # Decode it
+ ($content = from_json($content))
+ # Check expiration
+ } || (str2time($content->{expires}) <= time()+3600)
+ ) {
+ # Init tied payload
+ #XXX: tie to Tie::IxHash to keep a stable ordering of hash keys
+ #XXX: https://www.perlmonks.org/?node_id=1215976
+ #XXX: optional notBefore, notAfter, see https://ietf-wg-acme.github.io/acme/draft-ietf-acme-acme.html#applying-for-certificate-issuance
+ tie(my %payload, 'Tie::IxHash', 'profile' => 'classic', identifiers => []);
+
+ # Loop on domains
+ map {
+ # With public ip
+ if (is_public_ip($_)) {
+ # Set shortlived profile
+ $payload{profile} = 'shortlived';
+
+ # Tie in a stable hash and append to identifiers array
+ #XXX: tie to Tie::IxHash to keep a stable ordering of hash keys
+ tie(%{$payload{identifiers}[scalar @{$payload{identifiers}}]}, 'Tie::IxHash', type => 'ip', value => $_);
+ # With fqdn
+ } else {
+ # Tie in a stable hash and append to identifiers array
+ #XXX: tie to Tie::IxHash to keep a stable ordering of hash keys
+ tie(%{$payload{identifiers}[scalar @{$payload{identifiers}}]}, 'Tie::IxHash', type => 'dns', value => $_);
+ }
+ } ($self->{domain}{domain}, @{$self->{domain}{domains}});
+
+ # Post new order request
+ my $res = $self->_post($self->{req}{'newOrder'}, \%payload);
+
+ # Handle error
+ unless ($res->is_success) {
+ confess('POST '.$self->{req}{'newOrder'}.' failed: '.$res->status_line);
+ }
+
+ # Handle error
+ unless ($res->content) {
+ confess('POST '.$self->{req}{'newOrder'}.' empty content: '.$res->status_line);
+ }
+
+ # Handle error
+ unless ($res->headers->{location}) {
+ confess('POST '.$self->{req}{'newOrder'}.' missing location: '.$res->status_line);
+ }
+
+ # Extract content
+ $content = from_json($res->content);
+
+ # Check status
+ unless ($content->{status} eq 'ready' or $content->{status} eq 'pending') {
+ confess('POST '.$self->{req}{'newOrder'}.' invalid status: '.$content->{status}.': '.$res->status_line);
+ }
+
+ # Store location
+ # XXX: used with async response
+ $content->{location} = $res->headers->{location};
+
+ # Store retry after
+ $content->{retryafter} = (defined $res->headers->{'retry-after'} and $res->headers->{'retry-after'}) ? $res->headers->{'retry-after'} : 1;
+
+ # Write to file
+ write_file($file, to_json($content));
+ }
+
+ # Save the authorizations
+ $self->{req}{authorizations} = [ keys %{{ map { $_ => undef } @{$content->{authorizations}} }} ];
+