1 package FS::domain_record;
5 #use FS::Record qw( qsearch qsearchs );
6 use FS::Record qw( qsearchs dbh );
13 FS::domain_record - Object methods for domain_record records
17 use FS::domain_record;
19 $record = new FS::domain_record \%hash;
20 $record = new FS::domain_record { 'column' => 'value' };
22 $error = $record->insert;
24 $error = $new_record->replace($old_record);
26 $error = $record->delete;
28 $error = $record->check;
32 An FS::domain_record object represents an entry in a DNS zone.
33 FS::domain_record inherits from FS::Record. The following fields are currently
38 =item recnum - primary key
40 =item svcnum - Domain (see L<FS::svc_domain>) of this entry
42 =item reczone - partial (or full) zone for this entry
44 =item recaf - address family for this entry, currently only `IN' is recognized.
46 =item rectype - record type for this entry (A, MX, etc.)
48 =item recdata - data for this entry
58 Creates a new entry. To add the example to the database, see L<"insert">.
60 Note that this stores the hash reference, not a distinct copy of the hash it
61 points to. You can ask the object for a copy with the I<hash> method.
65 sub table { 'domain_record'; }
69 Adds this record to the database. If there is an error, returns the error,
70 otherwise returns false.
77 local $SIG{HUP} = 'IGNORE';
78 local $SIG{INT} = 'IGNORE';
79 local $SIG{QUIT} = 'IGNORE';
80 local $SIG{TERM} = 'IGNORE';
81 local $SIG{TSTP} = 'IGNORE';
82 local $SIG{PIPE} = 'IGNORE';
84 my $oldAutoCommit = $FS::UID::AutoCommit;
85 local $FS::UID::AutoCommit = 0;
88 my $error = $self->SUPER::insert;
90 $dbh->rollback if $oldAutoCommit;
94 unless ( $self->rectype =~ /^(SOA|_mstr)$/ ) {
95 my $error = $self->increment_serial;
97 $dbh->rollback if $oldAutoCommit;
102 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
110 Delete this record from the database.
117 local $SIG{HUP} = 'IGNORE';
118 local $SIG{INT} = 'IGNORE';
119 local $SIG{QUIT} = 'IGNORE';
120 local $SIG{TERM} = 'IGNORE';
121 local $SIG{TSTP} = 'IGNORE';
122 local $SIG{PIPE} = 'IGNORE';
124 my $oldAutoCommit = $FS::UID::AutoCommit;
125 local $FS::UID::AutoCommit = 0;
128 my $error = $self->SUPER::delete;
130 $dbh->rollback if $oldAutoCommit;
134 unless ( $self->rectype =~ /^(SOA|_mstr)$/ ) {
135 my $error = $self->increment_serial;
137 $dbh->rollback if $oldAutoCommit;
142 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
148 =item replace OLD_RECORD
150 Replaces the OLD_RECORD with this one in the database. If there is an error,
151 returns the error, otherwise returns false.
158 local $SIG{HUP} = 'IGNORE';
159 local $SIG{INT} = 'IGNORE';
160 local $SIG{QUIT} = 'IGNORE';
161 local $SIG{TERM} = 'IGNORE';
162 local $SIG{TSTP} = 'IGNORE';
163 local $SIG{PIPE} = 'IGNORE';
165 my $oldAutoCommit = $FS::UID::AutoCommit;
166 local $FS::UID::AutoCommit = 0;
169 my $error = $self->SUPER::replace(@_);
171 $dbh->rollback if $oldAutoCommit;
175 unless ( $self->rectype eq 'SOA' ) {
176 my $error = $self->increment_serial;
178 $dbh->rollback if $oldAutoCommit;
183 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
191 Checks all fields to make sure this is a valid example. If there is
192 an error, returns the error, otherwise returns false. Called by the insert
197 # the check method should currently be supplied - FS::Record contains some
198 # data checking routines
204 $self->ut_numbern('recnum')
205 || $self->ut_number('svcnum')
207 return $error if $error;
209 return "Unknown svcnum (in svc_domain)"
210 unless qsearchs('svc_domain', { 'svcnum' => $self->svcnum } );
212 $self->reczone =~ /^(@|[a-z0-9\.\-\*]+)$/i
213 or return "Illegal reczone: ". $self->reczone;
216 $self->recaf =~ /^(IN)$/ or return "Illegal recaf: ". $self->recaf;
219 $self->rectype =~ /^(SOA|NS|MX|A|PTR|CNAME|_mstr)$/
220 or return "Illegal rectype (only SOA NS MX A PTR CNAME recognized): ".
224 return "Illegal reczone for ". $self->rectype. ": ". $self->reczone
225 if $self->rectype !~ /^MX$/i && $self->reczone =~ /\*/;
227 if ( $self->rectype eq 'SOA' ) {
228 my $recdata = $self->recdata;
229 $recdata =~ s/\s+/ /g;
230 $recdata =~ /^([a-z0-9\.\-]+ [\w\-\+]+\.[a-z0-9\.\-]+ \( (\d+ ){5}\))$/i
231 or return "Illegal data for SOA record: $recdata";
233 } elsif ( $self->rectype eq 'NS' ) {
234 $self->recdata =~ /^([a-z0-9\.\-]+)$/i
235 or return "Illegal data for NS record: ". $self->recdata;
237 } elsif ( $self->rectype eq 'MX' ) {
238 $self->recdata =~ /^(\d+)\s+([a-z0-9\.\-]+)$/i
239 or return "Illegal data for MX record: ". $self->recdata;
240 $self->recdata("$1 $2");
241 } elsif ( $self->rectype eq 'A' ) {
242 $self->recdata =~ /^((\d{1,3}\.){3}\d{1,3})$/
243 or return "Illegal data for A record: ". $self->recdata;
245 } elsif ( $self->rectype eq 'PTR' ) {
246 $self->recdata =~ /^([a-z0-9\.\-]+)$/i
247 or return "Illegal data for PTR record: ". $self->recdata;
249 } elsif ( $self->rectype eq 'CNAME' ) {
250 $self->recdata =~ /^([a-z0-9\.\-]+)$/i
251 or return "Illegal data for CNAME record: ". $self->recdata;
253 } elsif ( $self->rectype eq '_mstr' ) {
254 $self->recdata =~ /^((\d{1,3}\.){3}\d{1,3})$/
255 or return "Illegal data for _master pseudo-record: ". $self->recdata;
263 =item increment_serial
267 sub increment_serial {
270 my $soa = qsearchs('domain_record', {
271 svcnum => $self->svcnum,
272 reczone => '@', #or full domain ?
275 } ) or return "soa record not found; can't increment serial";
277 my $data = $soa->recdata;
278 $data =~ s/(\(\D*)(\d+)/$1.($2+1)/e; #well, it works.
280 my %hash = $soa->hash;
281 $hash{recdata} = $data;
282 my $new = new FS::domain_record \%hash;
290 $Id: domain_record.pm,v 1.8 2002-05-22 18:44:01 ivan Exp $
294 The data validation doesn't check everything it could. In particular,
295 there is no protection against bad data that passes the regex, duplicate
296 SOA records, forgetting the trailing `.', impossible IP addersses, etc. Of
297 course, it's still better than editing the zone files directly. :)
301 L<FS::Record>, schema.html from the base documentation.