1 package FS::upload_target;
4 use base qw( FS::Record );
5 use FS::Record qw( qsearch qsearchs );
6 use FS::Misc qw(send_email);
9 use vars qw($me $DEBUG);
15 FS::upload_target - Object methods for upload_target records
19 use FS::upload_target;
21 $record = new FS::upload_target \%hash;
22 $record = new FS::upload_target { 'column' => 'value' };
24 $error = $record->insert;
26 $error = $new_record->replace($old_record);
28 $error = $record->delete;
30 $error = $record->check;
34 An FS::upload_target object represents a destination to deliver files (such
35 as invoice batches) by FTP, SFTP, or email. FS::upload_target inherits from
40 =item targetnum - primary key
42 =item agentnum - L<FS::agent> foreign key; can be null
44 =item protocol - 'ftp', 'sftp', or 'email'.
46 =item hostname - the DNS name of the FTP site, or the domain name of the
49 =item port - the TCP port number, if it's not standard.
51 =item username - username
53 =item password - password
55 =item path - for FTP/SFTP, the working directory to change to upon connecting.
57 =item subject - for email, the Subject: header
59 =item handling - a string naming an additional process to apply to
60 the file before sending it.
70 sub table { 'upload_target'; }
74 Creates a new FTP target. To add it to the database, see L<"insert">.
78 Adds this record to the database. If there is an error, returns the error,
79 otherwise returns false.
83 Delete this record from the database.
85 =item replace OLD_RECORD
87 Replaces the OLD_RECORD with this one in the database. If there is an error,
88 returns the error, otherwise returns false.
92 Checks all fields to make sure this is a valid example. If there is
93 an error, returns the error, otherwise returns false. Called by the insert
101 my $protocol = lc($self->protocol);
102 if ( $protocol eq 'email' ) {
103 $self->set(password => '');
104 $self->set(port => '');
105 $self->set(path => '');
106 } elsif ( $protocol eq 'sftp' ) {
107 $self->set(port => 22) unless $self->get('port');
108 $self->set(subject => '');
109 } elsif ( $protocol eq 'ftp' ) {
110 $self->set('port' => 21) unless $self->get('port');
111 $self->set(subject => '');
113 return "protocol '$protocol' not supported";
115 $self->set(protocol => $protocol); # lowercase it
118 $self->ut_numbern('targetnum')
119 || $self->ut_foreign_keyn('agentnum', 'agent', 'agentnum')
120 || $self->ut_text('hostname')
121 || $self->ut_text('username')
122 || $self->ut_textn('password')
123 || $self->ut_numbern('port')
124 || $self->ut_textn('path')
125 || $self->ut_textn('subject')
126 || $self->ut_enum('handling', [ $self->handling_types ])
128 return $error if $error;
133 =item put LOCALNAME [ REMOTENAME ]
135 Uploads the file named LOCALNAME, optionally changing its name to REMOTENAME
136 on the target. For FTP/SFTP, this opens a connection, changes to the working
137 directory (C<path>), and PUTs the file. For email, it composes an empty
138 message and attaches the file.
140 Returns an error message if anything goes wrong.
146 my $localname = shift;
147 my @s = File::Spec->splitpath($localname);
148 my $remotename = shift || $s[-1];
150 my $conf = FS::Conf->new;
151 if ( $self->protocol eq 'ftp' or $self->protocol eq 'sftp' ) {
152 # could cache this if we ever want to reuse it
154 my $connection = eval { $self->connect };
156 $connection->put($localname, $remotename);
157 return $connection->error || '';
158 } elsif ( $self->protocol eq 'email' ) {
160 my $to = join('@', $self->username, $self->hostname);
161 # XXX if we were smarter, this could use a message template for the
162 # message subject, body, and source address
163 # (maybe use only the raw content, so that we don't have to supply a
164 # customer for substitutions? ewww.)
166 'from' => $conf->config('invoice_from'),
168 'subject' => $self->subject,
171 { Path => $localname,
172 Type => 'application/octet-stream',
173 Encoding => 'base64',
174 Filename => $remotename,
175 Disposition => 'attachment',
179 return send_email(%message);
182 return "unknown protocol '".$self->protocol."'";
188 Creates a Net::FTP or Net::SFTP::Foreign object (according to the setting
189 of the 'secure' flag), connects to 'hostname', attempts to log in with
190 'username' and 'password', and changes the working directory to 'path'.
191 On success, returns the object. On failure, dies with an error message.
193 Always returns an error for email targets.
199 if ( $self->protocol eq 'sftp' ) {
200 eval "use Net::SFTP::Foreign;";
203 user => $self->username,
205 autodie => 0, #we're doing this anyway
207 # Net::SFTP::Foreign does not deal well with args that are defined
209 $args{port} = $self->port if $self->port and $self->port != 22;
210 $args{password} = $self->password if length($self->password) > 0;
211 $args{more} = '-v' if $DEBUG;
212 my $sftp = Net::SFTP::Foreign->new($self->hostname, %args);
213 $sftp->setcwd($self->path);
216 elsif ( $self->protocol eq 'ftp') {
217 eval "use Net::FTP;";
222 Passive => 1,# optional?
224 my $ftp = Net::FTP->new($self->hostname, %args)
225 or die "connect to ".$self->hostname." failed: $@";
226 $ftp->login($self->username, $self->password)
227 or die "login to ".$self->username.'@'.$self->hostname." failed: $@";
228 $ftp->binary; #optional?
229 $ftp->cwd($self->path)
230 or ($self->path eq '/')
231 or die "cwd to ".$self->hostname.'/'.$self->path." failed: $@";
235 return "can't connect() to a target of type '".$self->protocol."'";
241 Returns a descriptive label for this target.
247 $self->targetnum . ': ' . $self->username . '@' . $self->hostname;
252 Returns a list of values for the "handling" field, corresponding to the
253 known ways to preprocess a file before uploading. Currently those are
254 implemented somewhat crudely in L<FS::Cron::upload>.
260 #'billco', #not implemented this way yet
269 Handling methods should be here, but instead are in FS::Cron.
273 L<FS::Record>, schema.html from the base documentation.