b5a672059795273b508a9825db7b2b753b701667
[freeside.git] / fs_selfservice / FS-SelfService / cgi / selfservice.cgi
1 #!/usr/bin/perl -Tw
2
3 use strict;
4 use vars qw($DEBUG $cgi $session_id $form_max $template_dir);
5 use subs qw(do_template);
6 use CGI;
7 use CGI::Carp qw(fatalsToBrowser);
8 use Text::Template;
9 use HTML::Entities;
10 use Date::Format;
11 use Number::Format 1.50;
12 use FS::SelfService qw( login customer_info invoice
13                         payment_info process_payment 
14                         process_prepay
15                         list_pkgs order_pkg signup_info order_recharge
16                         part_svc_info provision_acct provision_external
17                         unprovision_svc change_pkg domainselector
18                         list_svcs list_svc_usage list_support_usage
19                         myaccount_passwd
20                       );
21
22 $template_dir = '.';
23
24 $DEBUG = 1;
25
26 $form_max = 255;
27
28 $cgi = new CGI;
29
30 unless ( defined $cgi->param('session') ) {
31   do_template('login',{});
32   exit;
33 }
34
35 if ( $cgi->param('session') eq 'login' ) {
36
37   $cgi->param('username') =~ /^\s*([a-z0-9_\-\.\&]{0,$form_max})\s*$/i
38     or die "illegal username";
39   my $username = $1;
40
41   $cgi->param('domain') =~ /^\s*([\w\-\.]{0,$form_max})\s*$/
42     or die "illegal domain";
43   my $domain = $1;
44
45   $cgi->param('password') =~ /^(.{0,$form_max})$/
46     or die "illegal password";
47   my $password = $1;
48
49   my $rv = login(
50     'username' => $username,
51     'domain'   => $domain,
52     'password' => $password,
53   );
54   if ( $rv->{error} ) {
55     do_template('login', {
56       'error'    => $rv->{error},
57       'username' => $username,
58       'domain'   => $domain,
59     } );
60     exit;
61   } else {
62     $cgi->param('session' => $rv->{session_id} );
63     $cgi->param('action'  => 'myaccount' );
64   }
65 }
66
67 $session_id = $cgi->param('session');
68
69 #order|pw_list XXX ???
70 $cgi->param('action') =~
71     /^(myaccount|view_invoice|make_payment|make_ach_payment|payment_results|ach_payment_results|recharge_prepay|recharge_results|logout|change_bill|change_ship|customer_order_pkg|process_order_pkg|customer_change_pkg|process_change_pkg|process_order_recharge|provision|provision_svc|process_svc_acct|process_svc_external|delete_svc|view_usage|view_usage_details|view_support_details|change_password|process_change_password)$/
72   or die "unknown action ". $cgi->param('action');
73 my $action = $1;
74
75 warn "calling $action sub\n"
76   if $DEBUG;
77 $FS::SelfService::DEBUG = $DEBUG;
78 my $result = eval "&$action();";
79 die $@ if $@;
80
81 if ( $result->{error} eq "Can't resume session"
82   || $result->{error} eq "Expired session" ) { #ick
83
84   do_template('login',{});
85   exit;
86 }
87
88 #warn $result->{'open_invoices'};
89 #warn scalar(@{$result->{'open_invoices'}});
90
91 warn "processing template $action\n"
92   if $DEBUG;
93 do_template($action, {
94   'session_id' => $session_id,
95   'action'     => $action, #so the menu knows what tab we're on...
96   %{$result}
97 });
98
99 #--
100
101 sub myaccount { customer_info( 'session_id' => $session_id ); }
102
103 sub view_invoice {
104
105   $cgi->param('invnum') =~ /^(\d+)$/ or die "illegal invnum";
106   my $invnum = $1;
107
108   invoice( 'session_id' => $session_id,
109            'invnum'     => $invnum,
110          );
111
112 }
113
114 sub customer_order_pkg {
115   my $init_data = signup_info( 'customer_session_id' => $session_id );
116   return $init_data if ( $init_data->{'error'} );
117
118   my $customer_info = customer_info( 'session_id' => $session_id );
119   return $customer_info if ( $customer_info->{'error'} );
120
121   return {
122     ( map { $_ => $init_data->{$_} }
123           qw( part_pkg security_phrase svc_acct_pop ),
124     ),
125     %$customer_info,
126   };
127 }
128
129 sub customer_change_pkg {
130   my $init_data = signup_info( 'customer_session_id' => $session_id );
131   return $init_data if ( $init_data->{'error'} );
132
133   my $customer_info = customer_info( 'session_id' => $session_id );
134   return $customer_info if ( $customer_info->{'error'} );
135
136   return {
137     ( map { $_ => $init_data->{$_} }
138           qw( part_pkg security_phrase svc_acct_pop ),
139     ),
140     ( map { $_ => $cgi->param($_) }
141         qw( pkgnum pkg )
142     ),
143     %$customer_info,
144   };
145 }
146
147 sub process_order_pkg {
148
149   my $results = '';
150
151   unless ( length($cgi->param('_password')) ) {
152     my $init_data = signup_info( 'customer_session_id' => $session_id );
153     $results = { 'error' => $init_data->{msgcat}{empty_password} };
154     $results = { 'error' => $init_data->{error} } if($init_data->{error});
155   }
156   if ( $cgi->param('_password') ne $cgi->param('_password2') ) {
157     my $init_data = signup_info( 'customer_session_id' => $session_id );
158     $results = { 'error' => $init_data->{msgcat}{passwords_dont_match} };
159     $results = { 'error' => $init_data->{error} } if($init_data->{error});
160     $cgi->param('_password', '');
161     $cgi->param('_password2', '');
162   }
163
164   $results ||= order_pkg (
165     'session_id' => $session_id,
166     map { $_ => $cgi->param($_) }
167         qw( custnum pkgpart username _password _password2 sec_phrase popnum )
168   );
169
170
171   if ( $results->{'error'} ) {
172     $action = 'customer_order_pkg';
173     return {
174       $cgi->Vars,
175       %{customer_order_pkg()},
176       'error' => '<FONT COLOR="#FF0000">'. $results->{'error'}. '</FONT>',
177     };
178   } else {
179     return $results;
180   }
181
182 }
183
184 sub process_change_pkg {
185
186   my $results = '';
187
188   $results ||= change_pkg (
189     'session_id' => $session_id,
190     map { $_ => $cgi->param($_) }
191         qw( pkgpart pkgnum )
192   );
193
194
195   if ( $results->{'error'} ) {
196     $action = 'customer_change_pkg';
197     return {
198       $cgi->Vars,
199       %{customer_change_pkg()},
200       'error' => '<FONT COLOR="#FF0000">'. $results->{'error'}. '</FONT>',
201     };
202   } else {
203     return $results;
204   }
205
206 }
207
208 sub process_order_recharge {
209
210   my $results = '';
211
212   $results ||= order_recharge (
213     'session_id' => $session_id,
214     map { $_ => $cgi->param($_) }
215         qw( svcnum )
216   );
217
218
219   if ( $results->{'error'} ) {
220     $action = 'view_usage';
221     if ($results->{'error'} eq '_decline') {
222       $results->{'error'} = "There has been an error processing your account.  Please contact customer support."
223     }
224     return {
225       $cgi->Vars,
226       %{view_usage()},
227       'error' => '<FONT COLOR="#FF0000">'. $results->{'error'}. '</FONT>',
228     };
229   } else {
230     return $results;
231   }
232
233 }
234
235 sub make_payment {
236   payment_info( 'session_id' => $session_id );
237 }
238
239 sub payment_results {
240
241   use Business::CreditCard;
242
243   #we should only do basic checking here for DoS attacks and things
244   #that couldn't be constructed by the web form...  let process_payment() do
245   #the rest, it gives better error messages
246
247   $cgi->param('amount') =~ /^\s*(\d+(\.\d{2})?)\s*$/
248     or die "Illegal amount: ". $cgi->param('amount'); #!!!
249   my $amount = $1;
250
251   my $payinfo = $cgi->param('payinfo');
252   $payinfo =~ s/\D//g;
253   $payinfo =~ /^(\d{13,16})$/
254     #or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
255     or die "illegal card"; #!!!
256   $payinfo = $1;
257   validate($payinfo)
258     #or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
259     or die "invalid card"; #!!!
260
261   if ( $cgi->param('card_type') ) {
262     cardtype($payinfo) eq $cgi->param('card_type')
263       #or $error ||= $init_data->{msgcat}{not_a}. $cgi->param('CARD_type');
264       or die "not a ". $cgi->param('card_type');
265   }
266
267   $cgi->param('paycvv') =~ /^\s*(.{0,4})\s*$/ or die "illegal CVV2";
268   my $paycvv = $1;
269
270   $cgi->param('month') =~ /^(\d{2})$/ or die "illegal month";
271   my $month = $1;
272   $cgi->param('year') =~ /^(\d{4})$/ or die "illegal year";
273   my $year = $1;
274
275   $cgi->param('payname') =~ /^(.{0,80})$/ or die "illegal payname";
276   my $payname = $1;
277
278   $cgi->param('address1') =~ /^(.{0,80})$/ or die "illegal address1";
279   my $address1 = $1;
280
281   $cgi->param('address2') =~ /^(.{0,80})$/ or die "illegal address2";
282   my $address2 = $1;
283
284   $cgi->param('city') =~ /^(.{0,80})$/ or die "illegal city";
285   my $city = $1;
286
287   $cgi->param('state') =~ /^(.{2})$/ or die "illegal state";
288   my $state = $1;
289
290   $cgi->param('zip') =~ /^(.{0,10})$/ or die "illegal zip";
291   my $zip = $1;
292
293   my $save = 0;
294   $save = 1 if $cgi->param('save');
295
296   my $auto = 0;
297   $auto = 1 if $cgi->param('auto');
298
299   $cgi->param('paybatch') =~ /^([\w\-\.]+)$/ or die "illegal paybatch";
300   my $paybatch = $1;
301
302   process_payment(
303     'session_id' => $session_id,
304     'payby'      => 'CARD',
305     'amount'     => $amount,
306     'payinfo'    => $payinfo,
307     'paycvv'     => $paycvv,
308     'month'      => $month,
309     'year'       => $year,
310     'payname'    => $payname,
311     'address1'   => $address1,
312     'address2'   => $address2,
313     'city'       => $city,
314     'state'      => $state,
315     'zip'        => $zip,
316     'save'       => $save,
317     'auto'       => $auto,
318     'paybatch'   => $paybatch,
319   );
320
321 }
322
323 sub make_ach_payment {
324   payment_info( 'session_id' => $session_id );
325 }
326
327 sub ach_payment_results {
328
329   #we should only do basic checking here for DoS attacks and things
330   #that couldn't be constructed by the web form...  let process_payment() do
331   #the rest, it gives better error messages
332
333   $cgi->param('amount') =~ /^\s*(\d+(\.\d{2})?)\s*$/
334     or die "illegal amount"; #!!!
335   my $amount = $1;
336
337   my $payinfo1 = $cgi->param('payinfo1');
338   $payinfo1=~ /^(\d+)$/
339     or die "illegal account"; #!!!
340   $payinfo1= $1;
341
342   my $payinfo2 = $cgi->param('payinfo2');
343   $payinfo2=~ /^(\d+)$/
344     or die "illegal ABA/routing code"; #!!!
345   $payinfo2= $1;
346
347   $cgi->param('payname') =~ /^(.{0,80})$/ or die "illegal payname";
348   my $payname = $1;
349
350   $cgi->param('paystate') =~ /^(.{0,2})$/ or die "illegal paystate";
351   my $paystate = $1;
352
353   $cgi->param('paytype') =~ /^(.{0,80})$/ or die "illegal paytype";
354   my $paytype = $1;
355
356   $cgi->param('ss') =~ /^(.{0,80})$/ or die "illegal ss";
357   my $ss = $1;
358
359   $cgi->param('stateid') =~ /^(.{0,80})$/ or die "illegal stateid";
360   my $stateid = $1;
361
362   $cgi->param('stateid_state') =~ /^(.{0,2})$/ or die "illegal stateid_state";
363   my $stateid_state = $1;
364
365   my $save = 0;
366   $save = 1 if $cgi->param('save');
367
368   my $auto = 0;
369   $auto = 1 if $cgi->param('auto');
370
371   $cgi->param('paybatch') =~ /^([\w\-\.]+)$/ or die "illegal paybatch";
372   my $paybatch = $1;
373
374   process_payment(
375     'session_id' => $session_id,
376     'payby'      => 'CHEK',
377     'amount'     => $amount,
378     'payinfo1'   => $payinfo1,
379     'payinfo2'   => $payinfo2,
380     'month'      => '12',
381     'year'       => '2037',
382     'payname'    => $payname,
383     'paytype'    => $paytype,
384     'paystate'   => $paystate,
385     'ss'         => $ss,
386     'stateid'    => $stateid,
387     'stateid_state' => $stateid_state,
388     'save'       => $save,
389     'auto'       => $auto,
390     'paybatch'   => $paybatch,
391   );
392
393 }
394
395 sub recharge_prepay {
396   customer_info( 'session_id' => $session_id );
397 }
398
399 sub recharge_results {
400
401   my $prepaid_cardnum = $cgi->param('prepaid_cardnum');
402   $prepaid_cardnum =~ s/\W//g;
403   $prepaid_cardnum =~ /^(\w*)$/ or die "illegal prepaid card number";
404   $prepaid_cardnum = $1;
405
406   process_prepay ( 'session_id'     => $session_id,
407                    'prepaid_cardnum' => $prepaid_cardnum,
408                  );
409 }
410
411 sub logout {
412   FS::SelfService::logout( 'session_id' => $session_id );
413 }
414
415 sub provision {
416   my $result = list_pkgs( 'session_id' => $session_id );
417   die $result->{'error'} if exists $result->{'error'} && $result->{'error'};
418   $result;
419 }
420
421 sub provision_svc {
422
423   my $result = part_svc_info(
424     'session_id' => $session_id,
425     map { $_ => $cgi->param($_) } qw( pkgnum svcpart ),
426   );
427   die $result->{'error'} if exists $result->{'error'} && $result->{'error'};
428
429   $result->{'svcdb'} =~ /^svc_(.*)$/
430     #or return { 'error' => 'Unknown svcdb '. $result->{'svcdb'} };
431     or die 'Unknown svcdb '. $result->{'svcdb'};
432   $action .= "_$1";
433
434   $result;
435 }
436
437 sub process_svc_acct {
438
439   my $result = provision_acct (
440     'session_id' => $session_id,
441     map { $_ => $cgi->param($_) } qw(
442       pkgnum svcpart username domsvc _password _password2 sec_phrase popnum )
443   );
444
445   if ( exists $result->{'error'} && $result->{'error'} ) { 
446     #warn "$result $result->{'error'}"; 
447     $action = 'provision_svc_acct';
448     return {
449       $cgi->Vars,
450       %{ part_svc_info( 'session_id' => $session_id,
451                         map { $_ => $cgi->param($_) } qw( pkgnum svcpart )
452                       )
453       },
454       'error' => $result->{'error'},
455     };
456   } else {
457     #warn "$result $result->{'error'}"; 
458     return $result;
459   }
460
461 }
462
463 sub process_svc_external {
464   provision_external (
465     'session_id' => $session_id,
466     map { $_ => $cgi->param($_) } qw( pkgnum svcpart )
467   );
468 }
469
470 sub delete_svc {
471   unprovision_svc(
472     'session_id' => $session_id,
473     'svcnum'     => $cgi->param('svcnum'),
474   );
475 }
476
477 sub view_usage {
478   list_svcs(
479     'session_id'  => $session_id,
480     'svcdb'       => 'svc_acct',
481     'ncancelled'  => 1,
482   );
483 }
484
485 sub view_usage_details {
486   list_svc_usage(
487     'session_id'  => $session_id,
488     'svcnum'      => $cgi->param('svcnum'),
489     'beginning'   => $cgi->param('beginning') || '',
490     'ending'      => $cgi->param('ending') || '',
491   );
492 }
493
494 sub view_support_details {
495   list_support_usage(
496     'session_id'  => $session_id,
497     'svcnum'      => $cgi->param('svcnum'),
498     'beginning'   => $cgi->param('beginning') || '',
499     'ending'      => $cgi->param('ending') || '',
500   );
501 }
502
503 sub change_password {
504   list_svcs(
505     'session_id' => $session_id,
506     'svcdb'      => 'svc_acct',
507   );
508 };
509
510 sub process_change_password {
511
512   my $result = myaccount_passwd(
513     'session_id'    => $session_id,
514     map { $_ => $cgi->param($_) } qw( svcnum new_password new_password2 )
515   );
516
517   if ( exists $result->{'error'} && $result->{'error'} ) { 
518
519     $action = 'change_password';
520     return {
521       $cgi->Vars,
522       %{ list_svcs( 'session_id' => $session_id,
523                     'svcdb'      => 'svc_acct',
524                   )
525        },
526       #'svcnum' => $cgi->param('svcnum'),
527       'error'  => $result->{'error'}
528     };
529
530  } else {
531
532    return $result;
533
534  }
535
536 }
537
538 #--
539
540 sub do_template {
541   my $name = shift;
542   my $fill_in = shift;
543
544   $cgi->delete_all();
545   $fill_in->{'selfurl'} = $cgi->self_url;
546   $fill_in->{'cgi'} = \$cgi;
547
548   my $template = new Text::Template( TYPE    => 'FILE',
549                                      SOURCE  => "$template_dir/$name.html",
550                                      DELIMITERS => [ '<%=', '%>' ],
551                                      UNTAINT => 1,                    )
552     or die $Text::Template::ERROR;
553
554   print $cgi->header( '-expires' => 'now' ),
555         $template->fill_in( PACKAGE => 'FS::SelfService::_selfservicecgi',
556                             HASH    => $fill_in
557                           );
558 }
559
560 #*FS::SelfService::_selfservicecgi::include = \&Text::Template::fill_in_file;
561
562 package FS::SelfService::_selfservicecgi;
563
564 #use FS::SelfService qw(regionselector expselect popselector);
565 use HTML::Entities;
566 use FS::SelfService qw(popselector domainselector);
567
568 #false laziness w/agent.cgi
569 sub include {
570   my $name = shift;
571   my $template = new Text::Template( TYPE   => 'FILE',
572                                      SOURCE => "$main::template_dir/$name.html",
573                                      DELIMITERS => [ '<%=', '%>' ],
574                                      UNTAINT => 1,                   
575                                    )
576     or die $Text::Template::ERROR;
577
578   $template->fill_in( PACKAGE => 'FS::SelfService::_selfservicecgi',
579                       #HASH    => $fill_in
580                     );
581
582 }
583