2 # Copyright (c) 2023 The Exim Maintainers
3 # SPDX-License-Identifier: GPL-2.0-or-later
4 # See the file NOTICE for conditions of use and distribution.
6 # Utility for one-time upgrage/downgrade between exim message-id formats,
7 # around the 4.97 transition
10 # This variables should be set by the building process
11 my $spool = 'SPOOL_DIRECTORY'; # may be overridden later
26 my $b62 = '[0-9A-Za-z]';
28 if ( !getopts('hudv', \%opt)
30 || !$opt{v} && !$opt{u} && !$opt{d}
35 print "exim_id_update:\n",
36 "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n",
37 "perl(runtime): $]\n";
41 $spool = $ARGV[0] if ($ARGV[0]);
42 $mode_upgrade = $opt{u};
46 Utility for one-time down/upgrade of Exim message-id formats
47 in spool files. Only the filenames and first-line ID tag values
48 are affected; not message content such as Message-ID fields.
49 Only -H, -D and -J files are handled.
51 Syntax: exim_id_update [-d | -u | -h | -v] [spooldir]
58 Exactly one of -d or -u must be given.
59 The spool directory defaults to the build-time value,
60 or can be given as a command-line argument.
65 # - Check exim not running
66 # - Wipe any wait-hints DBs, buy just removing the files.
67 # For all queue (main and named), with split-spool if needed, for each file identifiable
68 # as a spoolfile (name starts with an ID, ends with -H -D -J -K)
69 # XXX are there only subsets we can handle - eg. a -H + a -D ?
70 # mainline code sequence is -D (locks msg) -H ?-J
71 # mainline locking sequence (spool_open_datafile()) is
73 # - fnctl F_LOCK (amount = first line of file)
75 # The -H and -D files contain the ID as their initial line.
77 # - records successful deliveries, as insurance vs. crashes
78 # - has lines with mail addresses
80 # - is a temp for DKIM'd delivery when a transport-filter is in use
81 # - contains the message that would have been put on the wire (except for encryption)
82 # - the transport, with tpt-filter, writes the file - and then reads it
83 # so as to generate the DKIM signature. Then it sends the message, with
84 # generated headers and reading the file again, down the wire.
85 # And then it deletes it.
86 # - unclear if we really want to rewrite these files, if we do see then
89 # - if old-format name:
91 # - generate new files, in safe sequence
92 # - remove old files (do we need to archive?)
95 # loop for default Q, named Qs
96 # loop for plain, split-spool
98 # if is -H, and -D exists
100 # create new ID string from old
107 # rename old -J to new -J
114 chdir $spool or die "failed cd to $spool";
117 if ($_ =~ ($mode_upgrade ? "${b62}{6}-${b62}{6}-${b62}{2}-D" : "${b62}{6}-${b62}{11}-${b62}{4}-D") );
124 my $old_dfile = shift;
125 my $old_prefix = $old_dfile;
126 my ($old_hfile , $new_prefix);
130 $old_prefix =~ s/-D$//;
131 $old_hfile = $old_prefix . '-H';
133 # The -H file must also exist
134 return if (! -e $old_hfile);
136 $new_prefix = $old_prefix;
138 $new_prefix =~ s/^([^-]*)-([^-]*)-(.*)$/$1-00000$2-${3}00/;
140 $new_prefix =~ s/^([^-]*)-.....([^-]*)-(..)..$/$1-$2-${3}/;
143 ####### create the new -D file
145 open $d_old, '+<', $old_dfile
146 or die "Can't open file: $!\n";
148 # lock the old -D file
149 dfile_lock($d_old, $mode_upgrade ? 16 : 23);
150 # seek past the first line
153 # create the new -D file
154 $d_new = f_create($new_prefix . '-D');
156 # lock the new -D file
157 dfile_lock($d_new, $mode_upgrade ? 23 : 16);
159 # write the new message-id to the first line
160 print $d_new "$new_prefix-D\n";
162 # copy the rest of the -D file
163 while ($line = <$d_old>) {
167 ####### create the new -H file
169 open my $h_old, '<', $old_hfile
170 or die "Can't open file: $!\n";
173 my $h_new = f_create($new_prefix . '-H');
174 print $h_new "$new_prefix-H\n";
175 while ($line = <$h_old>) {
179 ###### rename a journal file if it exists
181 rename $old_prefix . '-J', $new_prefix . '-J' if (-e $old_prefix . '-J');
186 unlink $old_hfile or die "failed to remove $old_hfile";
188 unlink $old_dfile or die "failed to remove $old_dfile";
190 dfile_unlock($d_new, $mode_upgrade ? 23 : 16);
199 my $fs = new File::FcntlLock;
201 $fs->l_type( F_WRLCK );
202 $fs->l_whence( SEEK_CUR );
204 $fs->l_len( $nbytes );
206 $fs->lock( $fh, F_SETLK )
207 or die "Locking failed: " . $fs->error . "\n";
213 my $fs = new File::FcntlLock;
215 $fs->l_type( F_UNLCK );
216 $fs->l_whence( SEEK_CUR );
218 $fs->l_len( $nbytes );
219 $fs->lock( $fh, F_SETLK )
220 or die "Unlocking failed: " . $fs->error . "\n";
224 my $filename = shift;
225 sysopen(my $fh, $filename, O_RDWR|O_CREAT|O_EXCL)
226 or die "Can't create $filename: $!";
229 # TODO: chown, chgrp exim; chmod 0640