assign a size to svc_phone.sms_account
[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 Date::Parse 'str2time';
12 use Number::Format 1.50;
13 use FS::SelfService qw(
14   access_info login_info login customer_info edit_info invoice
15   payment_info process_payment realtime_collect process_prepay
16   list_pkgs order_pkg signup_info order_recharge
17   part_svc_info provision_acct provision_external provision_phone
18   unprovision_svc change_pkg suspend_pkg domainselector
19   list_svcs list_svc_usage list_cdr_usage list_support_usage
20   myaccount_passwd list_invoices create_ticket get_ticket did_report
21   adjust_ticket_priority
22   mason_comp port_graph
23 );
24
25 $template_dir = '.';
26
27 $DEBUG = 0;
28
29 $form_max = 255;
30
31 $cgi = new CGI;
32
33 unless ( defined $cgi->param('session') ) {
34   my $login_info = login_info( 'agentnum' => scalar($cgi->param('agentnum')) );
35
36   do_template('login', $login_info );
37   exit;
38 }
39
40 if ( $cgi->param('session') eq 'login' ) {
41
42   $cgi->param('username') =~ /^\s*([a-z0-9_\-\.\&]{0,$form_max})\s*$/i
43     or die "illegal username";
44   my $username = $1;
45
46   $cgi->param('domain') =~ /^\s*([\w\-\.]{0,$form_max})\s*$/
47     or die "illegal domain";
48   my $domain = $1;
49
50   $cgi->param('password') =~ /^(.{0,$form_max})$/
51     or die "illegal password";
52   my $password = $1;
53
54   my $rv = login(
55     'username' => $username,
56     'domain'   => $domain,
57     'password' => $password,
58   );
59   if ( $rv->{error} ) {
60     my $login_info = login_info( 'agentnum' => $cgi->param('agentnum') );
61     do_template('login', {
62       'error'    => $rv->{error},
63       'username' => $username,
64       'domain'   => $domain,
65       %$login_info,
66     } );
67     exit;
68   } else {
69     $cgi->param('session' => $rv->{session_id} );
70     $cgi->param('action'  => 'myaccount' );
71   }
72 }
73
74 $session_id = $cgi->param('session');
75
76 #order|pw_list XXX ???
77 my @actions = ( qw(
78   myaccount
79   tktcreate
80   tktview
81   ticket_priority
82   didreport
83   invoices
84   view_invoice
85   make_payment
86   make_ach_payment
87   make_term_payment
88   make_thirdparty_payment
89   post_thirdparty_payment
90   payment_results
91   ach_payment_results
92   recharge_prepay
93   recharge_results
94   logout
95   change_bill
96   change_ship
97   change_pay
98   process_change_bill
99   process_change_ship
100   process_change_pay
101   customer_order_pkg
102   process_order_pkg
103   customer_change_pkg
104   process_change_pkg
105   process_order_recharge
106   provision
107   provision_svc
108   process_svc_acct
109   process_svc_phone
110   process_svc_external
111   delete_svc
112   view_usage
113   view_usage_details
114   view_cdr_details
115   view_support_details
116   view_port_graph
117   real_port_graph
118   change_password
119   process_change_password
120   customer_suspend_pkg
121   process_suspend_pkg
122 ));
123  
124 $cgi->param('action') =~ ( '^(' . join('|', @actions) . ')$' )
125   or die "unknown action ". $cgi->param('action');
126 my $action = $1;
127
128 warn "calling $action sub\n"
129   if $DEBUG;
130 $FS::SelfService::DEBUG = $DEBUG;
131 my $result = eval "&$action();";
132 die $@ if $@;
133
134 warn Dumper($result) if $DEBUG;
135
136 if ( $result->{error} && ( $result->{error} eq "Can't resume session"
137   || $result->{error} eq "Expired session") ) { #ick
138
139   my $login_info = login_info();
140   do_template('login', $login_info);
141   exit;
142 }
143
144 #warn $result->{'open_invoices'};
145 #warn scalar(@{$result->{'open_invoices'}});
146
147 warn "processing template $action\n"
148   if $DEBUG;
149 do_template($action, {
150   'session_id' => $session_id,
151   'action'     => $action, #so the menu knows what tab we're on...
152   #%{ payment_info( 'session_id' => $session_id ) },  # cust_paybys for the menu
153   %{$result}
154 });
155
156 #--
157
158 use Data::Dumper;
159 sub myaccount { 
160   customer_info( 'session_id' => $session_id ); 
161 }
162
163 sub change_bill { my $payment_info =
164                     payment_info( 'session_id' => $session_id );
165                   return $payment_info if ( $payment_info->{'error'} );
166                   my $customer_info =
167                     customer_info( 'session_id' => $session_id );
168                   return { 
169                     %$payment_info,
170                     %$customer_info,
171                   };
172                 }
173 sub change_ship { change_bill(@_); }
174 sub change_pay { change_bill(@_); }
175
176 sub _process_change_info { 
177   my ($erroraction, @fields) = @_;
178
179   my $results = '';
180
181   $results ||= edit_info (
182     'session_id' => $session_id,
183     map { ($_ => $cgi->param($_)) } grep { defined($cgi->param($_)) } @fields,
184   );
185
186
187   if ( $results->{'error'} ) {
188     no strict 'refs';
189     $action = $erroraction;
190     return {
191       $cgi->Vars,
192       %{&$action()},
193       'error' => '<FONT COLOR="#FF0000">'. $results->{'error'}. '</FONT>',
194     };
195   } else {
196     return $results;
197   }
198 }
199
200 sub process_change_bill {
201         _process_change_info( 'change_bill', 
202           qw( first last company address1 address2 city state
203               county zip country daytime night fax )
204         );
205 }
206
207 sub process_change_ship {
208         my @list = map { "ship_$_" }
209                      qw( first last company address1 address2 city state
210                          county zip country daytime night fax 
211                        );
212         if ($cgi->param('same') eq 'Y') {
213           foreach (@list) { $cgi->param($_, '') }
214         }
215
216         _process_change_info( 'change_ship', @list );
217 }
218
219 sub process_change_pay {
220         my $postal = $cgi->param( 'postal_invoicing' );
221         my $payby  = $cgi->param( 'payby' );
222         my @list =
223           qw( payby payinfo payinfo1 payinfo2 month year payname
224               address1 address2 city county state zip country auto paytype
225               paystate ss stateid stateid_state invoicing_list
226             );
227         push @list, 'postal_invoicing' if $postal;
228         unless (    $payby ne 'BILL'
229                  || $postal
230                  || $cgi->param( 'invoicing_list' )
231                )
232         {
233           $action = 'change_pay';
234           return {
235             %{&change_pay()},
236             $cgi->Vars,
237             'error' => '<FONT COLOR="#FF0000">Postal or email required.</FONT>',
238           };
239         }
240         _process_change_info( 'change_pay', @list );
241 }
242
243 sub view_invoice {
244
245   $cgi->param('invnum') =~ /^(\d+)$/ or die "illegal invnum";
246   my $invnum = $1;
247
248   invoice( 'session_id' => $session_id,
249            'invnum'     => $invnum,
250          );
251
252 }
253
254 sub invoices {
255   list_invoices( 'session_id' => $session_id, );
256 }
257
258 sub tktcreate {
259   my $customer_info = customer_info( 'session_id' => $session_id );
260   return $customer_info if ( $customer_info->{'error'} );
261
262   my $requestor = "";
263   if ( $customer_info->{'invoicing_list'} ) {
264     my @requestor = split( /\s*\,\s*/, $customer_info->{'invoicing_list'} );
265     $requestor = $requestor[0] if scalar(@requestor);
266   }
267
268   return { 'requestor' => $requestor }
269     unless ($cgi->param('subject') && $cgi->param('message') &&
270         length($cgi->param('subject')) && length($cgi->param('message')));
271     
272  create_ticket( 'session_id' => $session_id,
273                         'subject' => $cgi->param('subject'),
274                         'message' => $cgi->param('message'), 
275                         'requestor' => $requestor,
276             );
277 }
278
279 sub tktview {
280  get_ticket(    'session_id' => $session_id,
281                 'ticket_id' => ($cgi->param('ticket_id') || ''),
282                 'subject'   => ($cgi->param('subject') || ''),
283                 'reply'     => ($cgi->param('reply') || ''),
284             );
285 }
286
287 sub ticket_priority {
288   my %values;
289   foreach ( $cgi->param ) {
290     if ( /^ticket(\d+)$/ ) {
291       # a 'ticket1001' param implies the existence of a 'priority1001' param
292       # but if that's empty, we need to send it as empty rather than forget
293       # it.
294       $values{$1} = $cgi->param("priority$1") || '';
295     }
296   }
297   $action = 'myaccount';
298   # this returns an updated customer_info for myaccount
299   adjust_ticket_priority( 'session_id' => $session_id,
300                           'values'     => \%values );
301 }
302
303 sub customer_order_pkg {
304   my $init_data = signup_info( 'customer_session_id' => $session_id );
305   return $init_data if ( $init_data->{'error'} );
306
307   my $customer_info = customer_info( 'session_id' => $session_id );
308   return $customer_info if ( $customer_info->{'error'} );
309
310   my $pkgselect = mason_comp(
311     'session_id' => $session_id,
312     'comp'       => '/edit/cust_main/first_pkg/select-part_pkg.html',
313     'args'       => [ 'password_verify' => 1,
314                       'onchange'        => 'enable_order_pkg()',
315                       'relurls'         => 1,
316                       'empty_label'     => 'Select package',
317                     ],
318   );
319
320   $pkgselect = $pkgselect->{'error'} || $pkgselect->{'output'};
321
322   return {
323     ( map { $_ => $init_data->{$_} }
324           qw( part_pkg security_phrase svc_acct_pop ),
325     ),
326     %$customer_info,
327     'pkg_selector' => $pkgselect,
328   };
329 }
330
331 sub customer_change_pkg {
332   my $init_data = signup_info( 'customer_session_id' => $session_id );
333   return $init_data if ( $init_data->{'error'} );
334
335   my $customer_info = customer_info( 'session_id' => $session_id );
336   return $customer_info if ( $customer_info->{'error'} );
337
338   return {
339     ( map { $_ => $init_data->{$_} }
340           qw( part_pkg security_phrase svc_acct_pop ),
341     ),
342     ( map { $_ => $cgi->param($_) }
343         qw( pkgnum pkg )
344     ),
345     %$customer_info,
346   };
347 }
348
349 sub process_order_pkg {
350
351   my $results = '';
352
353   my @params = (qw( custnum pkgpart ));
354   my $svcdb = '';
355   if ( $cgi->param('pkgpart_svcpart') =~ /^(\d+)_(\d+)$/ ) {
356     $cgi->param('pkgpart', $1);
357     $cgi->param('svcpart', $2);
358     push @params, 'svcpart';
359     $svcdb = $cgi->param('svcdb');
360     push @params, 'domsvc' if $svcdb eq 'svc_acct';
361   } else {
362     $svcdb = 'svc_acct';
363   }
364
365   if ( $svcdb eq 'svc_acct' ) {
366
367     push @params, qw( username _password _password2 sec_phrase popnum );
368
369     unless ( length($cgi->param('_password')) ) {
370       my $init_data = signup_info( 'customer_session_id' => $session_id );
371       $results = { 'error' => $init_data->{msgcat}{empty_password} };
372       $results = { 'error' => $init_data->{error} } if($init_data->{error});
373     }
374     if ( $cgi->param('_password') ne $cgi->param('_password2') ) {
375       my $init_data = signup_info( 'customer_session_id' => $session_id );
376       $results = { 'error' => $init_data->{msgcat}{passwords_dont_match} };
377       $results = { 'error' => $init_data->{error} } if($init_data->{error});
378       $cgi->param('_password', '');
379       $cgi->param('_password2', '');
380     }
381
382   } elsif ( $svcdb eq 'svc_phone' ) {
383
384     push @params, qw( phonenum sip_password pin phone_name );
385
386   } else {
387     die "$svcdb not handled on process_order_pkg yet";
388   }
389
390   $results ||= order_pkg (
391     'session_id' => $session_id,
392     map { $_ => $cgi->param($_) } @params
393   );
394
395
396   if ( $results->{'error'} ) {
397     $action = 'customer_order_pkg';
398     return {
399       $cgi->Vars,
400       %{customer_order_pkg()},
401       'error' => '<FONT COLOR="#FF0000">'. $results->{'error'}. '</FONT>',
402     };
403   } else {
404     return $results;
405   }
406
407 }
408
409 sub process_change_pkg {
410
411   my $results = '';
412
413   $results ||= change_pkg (
414     'session_id' => $session_id,
415     map { $_ => $cgi->param($_) }
416         qw( pkgpart pkgnum )
417   );
418
419
420   if ( $results->{'error'} ) {
421     $action = 'customer_change_pkg';
422     return {
423       $cgi->Vars,
424       %{customer_change_pkg()},
425       'error' => '<FONT COLOR="#FF0000">'. $results->{'error'}. '</FONT>',
426     };
427   } else {
428     return $results;
429   }
430
431 }
432
433 sub process_suspend_pkg {
434   my $results = '';
435   $results = suspend_pkg (
436     'session_id' => $session_id,
437     map { $_ => $cgi->param($_) } 
438       qw( pkgnum )
439     );
440   if ( $results->{'error'} ) {
441     $action = 'provision';
442     return {
443       'error' => '<FONT COLOR="#FF0000">'. $results->{'error'}. '</FONT>',
444     }
445   }
446   else {
447     return $results;
448   }
449 }
450
451 sub process_order_recharge {
452
453   my $results = '';
454
455   $results ||= order_recharge (
456     'session_id' => $session_id,
457     map { $_ => $cgi->param($_) }
458         qw( svcnum )
459   );
460
461
462   if ( $results->{'error'} ) {
463     $action = 'view_usage';
464     if ($results->{'error'} eq '_decline') {
465       $results->{'error'} = "There has been an error processing your account.  Please contact customer support."
466     }
467     return {
468       $cgi->Vars,
469       %{view_usage()},
470       'error' => '<FONT COLOR="#FF0000">'. $results->{'error'}. '</FONT>',
471     };
472   } else {
473     return $results;
474   }
475
476 }
477
478 sub make_payment {
479
480   my $payment_info = payment_info( 'session_id' => $session_id );
481
482   my $tr_amount_fee = mason_comp(
483     'session_id' => $session_id,
484     'comp'       => '/elements/tr-amount_fee.html',
485     'args'       => [ 'amount' => $payment_info->{'balance'},
486                     ],
487   );
488
489   $tr_amount_fee = $tr_amount_fee->{'error'} || $tr_amount_fee->{'output'};
490
491   $payment_info->{'tr_amount_fee'} = $tr_amount_fee;
492
493   $payment_info;
494 }
495
496 sub payment_results {
497
498   use Business::CreditCard 0.30;
499
500   #we should only do basic checking here for DoS attacks and things
501   #that couldn't be constructed by the web form...  let process_payment() do
502   #the rest, it gives better error messages
503
504   $cgi->param('amount') =~ /^\s*(\d+(\.\d{2})?)\s*$/
505     or die "Illegal amount: ". $cgi->param('amount'); #!!!
506   my $amount = $1;
507
508   my $payinfo = $cgi->param('payinfo');
509   $payinfo =~ s/[^\dx]//g;
510   $payinfo =~ /^([\dx]{13,16}|[\dx]{8,9})$/
511     #or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
512     or die "illegal card"; #!!!
513   $payinfo = $1;
514   unless ( $payinfo =~ /x/ ) {
515     validate($payinfo)
516       #or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
517       or die "invalid card"; #!!!
518   }
519
520   if ( $cgi->param('card_type') ) {
521     cardtype($payinfo) eq $cgi->param('card_type')
522       #or $error ||= $init_data->{msgcat}{not_a}. $cgi->param('CARD_type');
523       or die "not a ". $cgi->param('card_type');
524   }
525
526   $cgi->param('paycvv') =~ /^\s*(.{0,4})\s*$/ or die "illegal CVV2";
527   my $paycvv = $1;
528
529   $cgi->param('month') =~ /^(\d{2})$/ or die "illegal month";
530   my $month = $1;
531   $cgi->param('year') =~ /^(\d{4})$/ or die "illegal year";
532   my $year = $1;
533
534   $cgi->param('payname') =~ /^(.{0,80})$/ or die "illegal payname";
535   my $payname = $1;
536
537   $cgi->param('address1') =~ /^(.{0,80})$/ or die "illegal address1";
538   my $address1 = $1;
539
540   $cgi->param('address2') =~ /^(.{0,80})$/ or die "illegal address2";
541   my $address2 = $1;
542
543   $cgi->param('city') =~ /^(.{0,80})$/ or die "illegal city";
544   my $city = $1;
545
546   $cgi->param('state') =~ /^(.{0,80})$/ or die "illegal state";
547   my $state = $1;
548
549   $cgi->param('zip') =~ /^(.{0,10})$/ or die "illegal zip";
550   my $zip = $1;
551
552   $cgi->param('country') =~ /^(.{0,2})$/ or die "illegal country";
553   my $country = $1;
554
555   my $save = 0;
556   $save = 1 if $cgi->param('save');
557
558   my $auto = 0;
559   $auto = 1 if $cgi->param('auto');
560
561   $cgi->param('paybatch') =~ /^([\w\-\.]+)$/ or die "illegal paybatch";
562   my $paybatch = $1;
563
564   $cgi->param('discount_term') =~ /^(\d*)$/ or die "illegal discount_term";
565   my $discount_term = $1;
566
567
568   process_payment(
569     'session_id' => $session_id,
570     'payby'      => 'CARD',
571     'amount'     => $amount,
572     'payinfo'    => $payinfo,
573     'paycvv'     => $paycvv,
574     'month'      => $month,
575     'year'       => $year,
576     'payname'    => $payname,
577     'address1'   => $address1,
578     'address2'   => $address2,
579     'city'       => $city,
580     'state'      => $state,
581     'zip'        => $zip,
582     'country'    => $country,
583     'save'       => $save,
584     'auto'       => $auto,
585     'paybatch'   => $paybatch,
586     'discount_term' => $discount_term,
587   );
588
589 }
590
591 sub make_ach_payment {
592   payment_info( 'session_id' => $session_id );
593 }
594
595 sub ach_payment_results {
596
597   #we should only do basic checking here for DoS attacks and things
598   #that couldn't be constructed by the web form...  let process_payment() do
599   #the rest, it gives better error messages
600
601   $cgi->param('amount') =~ /^\s*(\d+(\.\d{2})?)\s*$/
602     or die "illegal amount"; #!!!
603   my $amount = $1;
604
605   my $payinfo1 = $cgi->param('payinfo1');
606   $payinfo1 =~ s/[^\dx]//g;
607   $payinfo1 =~ /^([\dx]+)$/
608     or die "illegal account"; #!!!
609   $payinfo1 = $1;
610
611   my $payinfo2 = $cgi->param('payinfo2');
612   $payinfo2 =~ s/[^\dx]//g;
613   $payinfo2 =~ /^([\dx]+)$/
614     or die "illegal ABA/routing code"; #!!!
615   $payinfo2 = $1;
616
617   $cgi->param('payname') =~ /^(.{0,80})$/ or die "illegal payname";
618   my $payname = $1;
619
620   $cgi->param('paystate') =~ /^(.{0,2})$/ or die "illegal paystate";
621   my $paystate = $1;
622
623   $cgi->param('paytype') =~ /^(.{0,80})$/ or die "illegal paytype";
624   my $paytype = $1;
625
626   $cgi->param('ss') =~ /^(.{0,80})$/ or die "illegal ss";
627   my $ss = $1;
628
629   $cgi->param('stateid') =~ /^(.{0,80})$/ or die "illegal stateid";
630   my $stateid = $1;
631
632   $cgi->param('stateid_state') =~ /^(.{0,2})$/ or die "illegal stateid_state";
633   my $stateid_state = $1;
634
635   my $save = 0;
636   $save = 1 if $cgi->param('save');
637
638   my $auto = 0;
639   $auto = 1 if $cgi->param('auto');
640
641   $cgi->param('paybatch') =~ /^([\w\-\.]+)$/ or die "illegal paybatch";
642   my $paybatch = $1;
643
644   process_payment(
645     'session_id' => $session_id,
646     'payby'      => 'CHEK',
647     'amount'     => $amount,
648     'payinfo1'   => $payinfo1,
649     'payinfo2'   => $payinfo2,
650     'month'      => '12',
651     'year'       => '2037',
652     'payname'    => $payname,
653     'paytype'    => $paytype,
654     'paystate'   => $paystate,
655     'ss'         => $ss,
656     'stateid'    => $stateid,
657     'stateid_state' => $stateid_state,
658     'save'       => $save,
659     'auto'       => $auto,
660     'paybatch'   => $paybatch,
661   );
662
663 }
664
665 sub make_thirdparty_payment {
666   payment_info('session_id' => $session_id);
667 }
668
669 sub post_thirdparty_payment {
670   $cgi->param('payby_method') =~ /^(CC|ECHECK|PAYPAL)$/
671     or die "illegal payby method";
672   my $method = $1;
673   $cgi->param('amount') =~ /^(\d+(\.\d*)?)$/
674     or die "illegal amount";
675   my $amount = $1;
676   # realtime_collect() returns the result from FS::cust_main->realtime_collect
677   # which returns realtime_bop()
678   # which returns a hashref of popup_url, collectitems, and reference
679   my $result = realtime_collect( 
680     'session_id' => $session_id,
681     'method' => $method, 
682     'amount' => $amount,
683   );
684   $result;
685 }
686
687 sub make_term_payment {
688   $cgi->param('amount') =~ /^(\d+\.\d{2})$/
689     or die "illegal payment amount";
690   my $balance = $1;
691   $cgi->param('discount_term') =~ /^(\d+)$/
692     or die "illegal discount term";
693   my $discount_term = $1;
694   $action = 'make_payment';
695   ({ %{payment_info( 'session_id' => $session_id )},
696     'balance' => $balance,
697     'discount_term' => $discount_term,
698   })
699 }
700
701 sub recharge_prepay {
702   customer_info( 'session_id' => $session_id );
703 }
704
705 sub recharge_results {
706
707   my $prepaid_cardnum = $cgi->param('prepaid_cardnum');
708   $prepaid_cardnum =~ s/\W//g;
709   $prepaid_cardnum =~ /^(\w*)$/ or die "illegal prepaid card number";
710   $prepaid_cardnum = $1;
711
712   process_prepay ( 'session_id'     => $session_id,
713                    'prepaid_cardnum' => $prepaid_cardnum,
714                  );
715 }
716
717 sub logout {
718   FS::SelfService::logout( 'session_id' => $session_id );
719 }
720
721 sub didreport {
722   my $result = did_report( 'session_id' => $session_id, 
723             'format' => $cgi->param('type'),
724             'recentonly' => $cgi->param('recentonly'),
725         );
726   die $result->{'error'} if exists $result->{'error'} && $result->{'error'};
727   $result;
728 }
729
730 sub provision {
731   my $result = list_pkgs( 'session_id' => $session_id );
732   die $result->{'error'} if exists $result->{'error'} && $result->{'error'};
733   $result->{'pkgpart'} = $cgi->param('pkgpart') if $cgi->param('pkgpart');
734   $result->{'filter'} = $cgi->param('filter') if $cgi->param('filter');
735   $result;
736 }
737
738 sub provision_svc {
739
740   my $result = part_svc_info(
741     'session_id' => $session_id,
742     map { $_ => $cgi->param($_) } qw( pkgnum svcpart svcnum ),
743   );
744   die $result->{'error'} if exists $result->{'error'} && $result->{'error'};
745
746   $result->{'svcdb'} =~ /^svc_(.*)$/
747     #or return { 'error' => 'Unknown svcdb '. $result->{'svcdb'} };
748     or die 'Unknown svcdb '. $result->{'svcdb'};
749   $action .= "_$1";
750
751   $result->{'numavail'} = $cgi->param('numavail');
752   $result->{'lnp'} = $cgi->param('lnp');
753
754   $result;
755 }
756
757 sub process_svc_phone {
758     my @bulkdid = $cgi->param('bulkdid');
759     my $phonenum = $cgi->param('phonenum');
760     my $lnp = $cgi->param('lnp');
761
762     my $result;
763     if($lnp) {
764         $result = provision_phone (
765             'session_id' => $session_id,
766             'countrycode' => '1',
767              map { $_ => $cgi->param($_) } qw( pkgnum svcpart phonenum 
768                 lnp_desired_due_date lnp_other_provider 
769                 lnp_other_provider_account )
770         );
771     } else {
772         $result = provision_phone (
773             'session_id' => $session_id,
774             'bulkdid' => [ @bulkdid ],
775             'countrycode' => '1',
776              map { $_ => $cgi->param($_) } qw( pkgnum svcpart phonenum svcnum email forwarddst )
777         );
778     }
779
780     if ( exists $result->{'error'} && $result->{'error'} ) { 
781         $action = 'provision_svc_phone';
782         return {
783           $cgi->Vars,
784           %{ part_svc_info( 'session_id' => $session_id,
785                         map { $_ => $cgi->param($_) } qw( pkgnum svcpart svcnum )
786               )
787           },
788           'error' => $result->{'error'},
789         };
790   }
791
792   $result;
793 }
794
795 sub process_svc_acct {
796
797   my $result = provision_acct (
798     'session_id' => $session_id,
799     map { $_ => $cgi->param($_) } qw(
800       pkgnum svcpart username domsvc _password _password2 sec_phrase popnum )
801   );
802
803   if ( exists $result->{'error'} && $result->{'error'} ) { 
804     #warn "$result $result->{'error'}"; 
805     $action = 'provision_svc_acct';
806     return {
807       $cgi->Vars,
808       %{ part_svc_info( 'session_id' => $session_id,
809                         map { $_ => $cgi->param($_) } qw( pkgnum svcpart )
810                       )
811       },
812       'error' => $result->{'error'},
813     };
814   } else {
815     #warn "$result $result->{'error'}"; 
816     return $result;
817   }
818
819 }
820
821 sub process_svc_external {
822   provision_external (
823     'session_id' => $session_id,
824     map { $_ => $cgi->param($_) } qw( pkgnum svcpart )
825   );
826 }
827
828 sub delete_svc {
829   unprovision_svc(
830     'session_id' => $session_id,
831     'svcnum'     => $cgi->param('svcnum'),
832   );
833 }
834
835 sub view_usage {
836   list_svcs(
837     'session_id'  => $session_id,
838     'svcdb'       => [ 'svc_acct', 'svc_phone', 'svc_port', ],
839     'ncancelled'  => 1,
840   );
841 }
842
843 sub real_port_graph {
844     my $svcnum = $cgi->param('svcnum');
845     my $res = port_graph(
846             'session_id'  => $session_id,
847             'svcnum'      => $svcnum,
848             'beginning'   => str2time($cgi->param('start')." 00:00:00"),
849             'ending'      => str2time($cgi->param('end')  ." 23:59:59"),
850             );
851     my @usage = @{$res->{'usage'}};
852     my $png = $usage[0]->{'png'};
853     { 'content' => $png, 'format' => 'png' };
854 }
855
856 sub view_port_graph {
857     my $svcnum = $cgi->param('svcnum');
858     { 'svcnum' => $svcnum,
859       'start' => $cgi->param($svcnum.'_start'),
860       'end' => $cgi->param($svcnum.'_end'),
861     }
862 }
863
864 sub view_usage_details {
865       list_svc_usage(
866         'session_id'  => $session_id,
867         'svcnum'      => $cgi->param('svcnum'),
868         'beginning'   => $cgi->param('beginning') || '',
869         'ending'      => $cgi->param('ending') || '',
870       );
871 }
872
873 sub view_cdr_details {
874   list_cdr_usage(
875     'session_id'  => $session_id,
876     'svcnum'      => $cgi->param('svcnum'),
877     'beginning'   => $cgi->param('beginning') || '',
878     'ending'      => $cgi->param('ending') || '',
879     'inbound'     => $cgi->param('inbound') || 0,
880   );
881 }
882
883 sub view_support_details {
884   list_support_usage(
885     'session_id'  => $session_id,
886     'svcnum'      => $cgi->param('svcnum'),
887     'beginning'   => $cgi->param('beginning') || '',
888     'ending'      => $cgi->param('ending') || '',
889   );
890 }
891
892 sub change_password {
893   list_svcs(
894     'session_id' => $session_id,
895     'svcdb'      => 'svc_acct',
896   );
897 };
898
899 sub process_change_password {
900
901   my $result = myaccount_passwd(
902     'session_id'    => $session_id,
903     map { $_ => $cgi->param($_) } qw( svcnum new_password new_password2 )
904   );
905
906   if ( exists $result->{'error'} && $result->{'error'} ) { 
907
908     $action = 'change_password';
909     return {
910       $cgi->Vars,
911       %{ list_svcs( 'session_id' => $session_id,
912                     'svcdb'      => 'svc_acct',
913                   )
914        },
915       #'svcnum' => $cgi->param('svcnum'),
916       'error'  => $result->{'error'}
917     };
918
919  } else {
920
921    return $result;
922
923  }
924
925 }
926
927 #--
928
929 sub do_template {
930   my $name = shift;
931   my $fill_in = shift;
932
933   $cgi->delete_all();
934   $fill_in->{'selfurl'} = $cgi->self_url;
935   $fill_in->{'cgi'} = \$cgi;
936
937   my $access_info = $session_id
938                       ? access_info( 'session_id' => $session_id )
939                       : {};
940   $fill_in->{$_} = $access_info->{$_} foreach keys %$access_info;
941
942   
943     if($result && ref($result) && $result->{'format'} && $result->{'content'}
944         && $result->{'format'} eq 'csv') {
945         print $cgi->header('-expires' => 'now',
946                 '-Content-Type' => 'text/csv',
947                 '-Content-Disposition' => "attachment;filename=output.csv",
948                 ),
949             $result->{'content'};
950     }
951     elsif($result && ref($result) && $result->{'format'} && $result->{'content'}
952          && $result->{'format'} eq 'xls') {
953         print $cgi->header('-expires' => 'now',
954                     '-Content-Type' => 'application/vnd.ms-excel',
955                     '-Content-Disposition' => "attachment;filename=output.xls",
956                     '-Content-Length' => length($result->{'content'}),
957                     ),
958                     $result->{'content'};
959     }
960     elsif($result && ref($result) && $result->{'format'} && $result->{'content'}
961          && $result->{'format'} eq 'png') {
962         print $cgi->header('-expires' => 'now',
963                     '-Content-Type' => 'image/png',
964                     ),
965                     $result->{'content'};
966     }
967     else {
968         my $source = "$template_dir/$name.html";
969         my $template = new Text::Template( TYPE       => 'FILE',
970                                          SOURCE     => $source,
971                                          DELIMITERS => [ '<%=', '%>' ],
972                                          UNTAINT    => 1,
973                                        )
974         or die $Text::Template::ERROR;
975
976         my $data = $template->fill_in( 
977             PACKAGE => 'FS::SelfService::_selfservicecgi',
978             HASH    => $fill_in,
979           ) || "Error processing template $source"; # at least print _something_
980           print $cgi->header( '-expires' => 'now' );
981           print $data;
982     }
983  }
984
985 #*FS::SelfService::_selfservicecgi::include = \&Text::Template::fill_in_file;
986
987 package FS::SelfService::_selfservicecgi;
988
989 use HTML::Entities;
990 use FS::SelfService qw(
991     regionselector popselector domainselector location_form didselector
992 );
993
994 #false laziness w/agent.cgi
995 use vars qw(@INCLUDE_ARGS);
996 sub include {
997   my $name = shift;
998
999   @INCLUDE_ARGS = @_;
1000
1001   my $template = new Text::Template( TYPE   => 'FILE',
1002                                      SOURCE => "$main::template_dir/$name.html",
1003                                      DELIMITERS => [ '<%=', '%>' ],
1004                                      UNTAINT => 1,                   
1005                                    )
1006     or die $Text::Template::ERROR;
1007
1008   $template->fill_in( PACKAGE => 'FS::SelfService::_selfservicecgi',
1009                       #HASH    => $fill_in
1010                     );
1011
1012 }
1013
1014 1;