change title of summary page; remove windows linefeeds
[raven.git] / apps / gacksadmin / gacks_functions.php
1 <?php
2
3 require_once 'form.php';
4
5 define(INFINITY, 2147483647);
6
7 define(STATE_PENDING, 0);
8 define(STATE_APPLY, 1);
9 define(STATE_APPLIED, 2);
10 define(STATE_INVOICED, 3);
11
12 define(KIND_CYCLE_CHARGE, 0);
13 define(KIND_SLOT_CHARGE, 1);
14 define(KIND_CYCLE_AGGREGATE, 2);
15 define(KIND_INVOICE, 10);
16 define(KIND_INVOICE_BEST_EFFORT, 11);
17 define(KIND_INVOICE_RESV, 12);
18 define(KIND_ADJUSTMENT, 20);
19 define(KIND_PAYMENT, 30);
20
21 function l_gacks_accounts ()       { return "/db/gacks/accounts.php"; }
22 function l_gacks_add_account ()    { return "/db/gacks/add_account.php"; }
23 function l_gacks_actions ()        { return "/db/gacks/action.php"; }
24 function l_gacks_account ($acctname, $acctkind)    { return "/db/gacks/account.php?account_name=$acctname&account_kind=$acctkind"; }
25 function l_gacks_reservations ()   { return "/db/gacks/reservations.php"; }
26 function l_gacks_reservation_set ()    { return "/db/gacks/reservation_set.php"; }
27 function l_gacks_invoice($name, $token, $state, $start_date, $end_date, $parent) { return "/db/gacks/invoices.php?account_name=$name&account_token=$token&state=$state&start_date=$start_date&end_date=$end_date&parent_id=$parent"; }
28 function l_gacks_all_invoices ()   { return l_gacks_invoice(NULL, NULL, STATE_INVOICED, time() - 30*24*60*60, NULL, NULL); }
29 function l_gacks_weekly ($y=NULL, $m=NULL, $d=NULL)         { return "/db/gacks/weekly.php?invoiceyear=$y&invoicemonth=$m&invoiceday=$d"; }
30 function l_gacks_node_status ()    { return "/db/gacks/nodestatus.php"; }
31 function l_gacks_wizard ()         { return "/db/gacks/wizard.php"; }
32 function l_gacks_user_reservations() { return "/db/gacks/user_reservations.php"; }
33 function l_gacks_edit($item)       { return "/db/gacks/edit.php"; }
34 function l_gacks_delete($item)     { $timeStart=$item['timeStart']; $timeStop=$item['timeStop']; $id=$item['id']; $lastAllocator=end($item['allocatorHRNs']); return "/db/gacks/action.php?action=delete-reservation&timeStart=$timeStart&timeStop=$timeStop&resource_id=$id&lastAllocator=$lastAllocator"; }
35 function l_gacks_service_level()   { return "/db/gacks/user_service_level.php"; }
36 function l_gacks_confirm_service($acct, $level, $autoRenew, $freezeUnreserved) { return "/db/gacks/user_confirm_service.php?account_name=$acct&level=$level&autoRenew=$autoRenew&freezeUnreserved=$freezeUnreserved"; }
37 function l_gacks_add_reservation() { return "/db/gacks/user_add_reservation.php"; }
38 function l_gacks_persons()         { return "/db/gacks/user_persons.php"; }
39 function l_gacks_advanced($value, $url)        { $eurl=urlencode($url); return "/db/gacks/action.php?action=set-advanced&value=$value&url=$eurl"; }
40 function l_gacks_summary()         { return "/db/gacks/user_summary.php"; }
41
42 function gacksid($x) { return "gacksid:" . $x; }
43
44 function gackshrn($x) { return "hrn:" . $x; }
45
46 function slice_to_hrn($slice)
47 {
48    $slice = str_replace("_", ".", $slice);
49    $hrn = "plc.vicci." . $slice;
50    return $hrn;
51 }
52
53 function is_time_unimportant($x)
54 {
55     if (($x==KIND_CYCLE_CHARGE) || ($x==KIND_SLOT_CHARGE)) {
56         return FALSE;
57     } else {
58         return TRUE;
59     }
60 }
61
62 function gacks_config()
63 {
64     return parse_ini_file("/etc/gacks/gacks.conf", true);     // see also in gacksclient.php
65 }
66
67 function handle_as_string($handle)
68 {
69     $id = $handle['id'];
70     $unitStart = $handle['unitStart'];
71     $unitStop = $handle['unitStop'];
72     $timeStart = $handle['timeStart'];
73     $timeStop = $handle['timeStop'];
74
75     return "$id#$unitStart-$unitStop#$timeStart-$timeStop";
76 }
77
78 function verify_account_token($account_name, $account_token) {
79     //$handle = fopen("/root/gacks_account_secret.txt", "r");
80     //$secret = fgets($handle, 4096);
81     //fclose($handle);
82
83     $config = gacks_config();
84     $secret = $config['gacks']['account_secret'];
85
86     $secret = trim($secret);
87     $token = sha1($secret . $account_name);
88     $token = substr($token, 0, 8);
89
90     //print $secret . " " . $token . " " . $account_name . " " . $account_token;
91
92     return ($token == $account_token);
93 }
94
95 function gacksid_guess_prefix($x) {
96     if ((strpos($x, ".") !== false) and (strpos($x, "_") == false)) {
97         return "hrn:" . $x;
98     } else if ((strpos($x, ".") == false) and (strpos($x, "_") == true)) {
99         return "slice:" . $x;
100     } else {
101         // XXX this doesn't display an error to the user
102         throw new Exception("unable to guess whether name is a slice or hrn");
103     }
104 }
105
106 function level_selectors($value)
107 {
108     $level_selectors = array();
109     $level_selectors []= array("display"=>"Default", "value"=>"default");
110     $level_selectors []= array("display"=>"Silver", "value"=>"silver");
111     $level_selectors []= array("display"=>"Gold", "value"=>"gold");
112
113     foreach ($level_selectors as &$sel) {
114         if ($sel["value"] == $value) {
115             $sel["selected"] = True;
116         }
117     }
118
119     return $level_selectors;
120 }
121
122 function charge_kind_selectors($value)
123 {
124     $kind_selectors = array();
125     $kind_selectors []= array("display"=>"Cycle Charge", "value"=>"0");
126     $kind_selectors []= array("display"=>"Slot Charge", "value"=>"1");
127     $kind_selectors []= array("display"=>"Invoice Charge", "value"=>"10");
128     $kind_selectors []= array("display"=>"Adjustment", "value"=>"20");
129     $kind_selectors []= array("display"=>"Payment", "value"=>"30");
130
131     foreach ($kind_selectors as &$sel) {
132         if ($sel["value"] == $value) {
133             $sel["selected"] = True;
134         }
135     }
136
137     return $kind_selectors;
138 }
139
140 function bool_text($value, $true, $false)
141 {
142     if ($value) {
143         return $true;
144     } else {
145         return $false;
146     }
147 }
148
149 function bool_selectors($value)
150 {
151     if ($value) {
152         $value = 1;
153     } else {
154         $value = 0;
155     }
156
157     $bool_selectors = array();
158     $bool_selectors []= array("display"=>"Yes", "value"=>"1");
159     $bool_selectors []= array("display"=>"No", "value"=>"0");
160
161     foreach ($bool_selectors as &$sel) {
162         if ($sel["value"] == $value) {
163             $sel["selected"] = True;
164         }
165     }
166
167     return $bool_selectors;
168 }
169
170 function aggregate_records($records)
171 {
172    $aggs = array();
173
174    foreach ($records as $rec) {
175        $rec['key'] = $rec['id'] + '#' + $rec['timeStart'] + "-" + $rec['timeStop'] + ":" + implode(",", $rec['allocatorHRNs']) + ">" + $rec['consumerHRN'];
176        $rec['qty'] = $rec['unitStop'] - $rec['unitStart'];
177
178        $found = False;
179        foreach ($aggs as &$agg) {
180            if ($agg['key'] == $rec['key']) {
181                $agg['qty'] = $agg['qty'] + $rec['qty'];
182                $found = True;
183            }
184        }
185        if (!$found) {
186            $aggs []= $rec;
187        }
188    }
189
190    return $aggs;
191 }
192
193 function record_table($records, $timezone_seconds)
194 {
195     $headers=array();
196     $headers["id"] = "string";
197     $headers["start"] = "string";
198     $headers["stop"] = "string";
199     $headers["lastAllocator"] = "string";
200     $headers["consumer"] = "string";
201     $headers["qty"] = "int";
202     $headers["actions"] = "string";
203
204     $sort_column = 1;
205     $table = new PlekitTable("reservations", $headers, $sort_column);
206     $table->start();
207
208     $aggs = aggregate_records($records);
209
210     foreach ( $aggs as $item ) {
211             $table->row_start();
212             $table->cell($item['id']);
213             $table->cell(strftime("%Y-%m-%d %H:%M:%S", $item['timeStart']+$timezone_seconds));
214             $table->cell(strftime("%Y-%m-%d %H:%M:%S", $item['timeStop']+$timezone_seconds));
215             $table->cell(end($item['allocatorHRNs']));
216             $table->cell($item['consumerHRN']);
217             $table->cell($item['qty']);
218             $table->cell(href(l_gacks_delete($item), "delete")); // href(l_gacks_edit($item), "edit") . " "
219             $table->row_end();
220     }
221
222     $table->end();
223 }
224
225 function charge_detail($charge)
226 {
227     $details = new PlekitDetails(False);
228
229     $details->start();
230
231     $details->form_start(l_gacks_actions(), array());
232
233     $details->th_td("id", $charge["id"]);
234     $details->th_td("object/desc", $charge["object_desc"]);
235     $details->th_td("kind", $charge["kind_id"]);
236     $details->th_td("date", $charge["date"]);
237     $details->th_td("amount", $charge["amount"]);
238     $details->th_td("state", $charge["state"]);
239     $details->th_td("parent_id", $charge["parent_id"]);
240     $details->th_td("account_name", $charge["account_name"]);
241
242     $details->end();
243     $details->form_end();
244 }
245
246 function dollars($amount)
247 {
248     return "$ " . number_format($amount, 2);
249 }
250
251 function invoice_table($invoice, $account_name, $account_token, $options)
252 {
253     $invoice_kind = array();
254     $invoice_kind[KIND_CYCLE_CHARGE] = "best effort";
255     $invoice_kind[KIND_SLOT_CHARGE] = "reservation";
256     $invoice_kind[KIND_CYCLE_AGGREGATE] = "best effort";
257     $invoice_kind[KIND_INVOICE] = "daily invoice";
258     $invoice_kind[KIND_INVOICE_BEST_EFFORT] = "daily invoice";
259     $invoice_kind[KIND_INVOICE_RESV] = "daily invoice";
260     $invoice_kind[KIND_ADJUSTMENT] = "adjustment";
261     $invoice_kind[KIND_PAYMENT] = "payment";
262
263     $headers=array();
264     $headers["id"] = "string";
265     $headers["account_name"] = "string";
266     $headers["date"] = "string";
267     $headers["kind"] = "string";
268     $headers["description"] = "string";
269     $headers["amount"] = "int";
270
271     // ideally it would be nice if we could sort by 3 columns,
272     //    account_name, date, description
273     if ($account_name) {
274         $sort_column = 2; // date
275     } else {
276         $sort_column = 1; // account name
277     }
278
279     $table = new PlekitTable("invoices", $headers, $sort_column);
280     $table->start();
281
282     $charges = $invoice['charges'];
283
284     foreach ( $charges as $item ) {
285             //if ($options['date_only']) {
286             if (is_time_unimportant($item['kind_id'])) {
287                 $date = explode(' ', $item['date']);
288                 $date = $date[0];
289             } else {
290                 $date = $item['date'];
291             }
292             $table->row_start();
293             $table->cell(href(l_gacks_invoice($account_name, $account_token, NULL, NULL, NULL, $item['id']), $item['id']));
294             $table->cell($item['account_name']);
295             $table->cell($date);
296             $table->cell($invoice_kind[$item['kind_id']]);
297             $table->cell($item['object_desc']);
298             $table->cell(dollars($item['amount']), array("align"=>"right"));
299             $table->row_end();
300     }
301
302     $table->end();
303 }
304
305 # from http://cool-php-tutorials.blogspot.com/2010/02/easy-php-date-picker.html
306 function date_picker($name, $y, $m, $d)
307 {
308     $startyear = 2012;
309     $endyear = date("Y");
310
311     $months=array('','January','February','March','April','May',
312     'June','July','August', 'September','October','November','December');
313
314     // Month dropdown
315     $html="<select name=\"".$name."month\">";
316
317     for($i=1;$i<=12;$i++)
318     {
319        $html.="<option value='$i'";
320        if ($i==$m) {
321            $html.=" selected=true";
322        }
323        $html.=">$months[$i]</option>";
324     }
325     $html.="</select> ";
326
327     // Day dropdown
328     $html.="<select name=\"".$name."day\">";
329     for($i=1;$i<=31;$i++)
330     {
331        $html.="<option value='$i'";
332        if ($i==$d) {
333            $html.=" selected=true";
334        }
335        $html.=">$i</option>";
336     }
337     $html.="</select> ";
338
339     // Year dropdown
340     $html.="<select name=\"".$name."year\">";
341
342     for($i=$startyear;$i<=$endyear;$i++)
343     {
344       $html.="<option value='$i'";
345       if ($i==$y) {
346           $html.=" selected=true";
347       }
348       $html.=">$i</option>";
349     }
350     $html.="</select> ";
351
352     return $html;
353 }
354
355 function get_slices($plcapi, $person_id, $fields)
356 {
357     if (!in_array("name", $fields)) {
358         $fields []= "name";
359     }
360
361     $persons = $plcapi->GetPersons(array('person_id'=>$person_id),array('person_id','email','slice_ids','role_ids','site_ids'));
362     $person=$persons[0];
363     $slice_filter=array('slice_id' => $person['slice_ids']);
364     $user_slices=$plcapi->GetSlices($slice_filter,$fields);
365
366     $slices = array();
367     foreach ($user_slices as $slice) {
368         $slices[$slice['name']] = $slice;
369     }
370
371     $is_pi = in_array( 20, $person['role_ids']);
372     if ($is_pi) {
373         $slice_filter=array('site_id'=>$person['site_ids'][0]);
374         $pi_slices = $plcapi->GetSlices($slice_filter, $fields);
375
376         foreach ($pi_slices as $slice) {
377             if (!array_key_exists($slice['name'], $slices)) {
378                 $slices[$slice['name']] = $slice;
379             }
380         }
381     }
382
383     return $slices;
384 }
385
386 function get_account_names($plcapi, $person_id)
387 {
388     $slices = get_slices($plcapi, $person_id, array("name"));
389     $slicenames = array();
390     foreach ($slices as $name => $slice) {
391         $slicenames []= $name;
392     }
393
394     return $slicenames;
395 }
396
397 function account_selectors($plcapi, $person_id, $account_name)
398 {
399     $slicenames = get_account_names($plcapi, $person_id);
400
401     $selectors = array();
402     foreach ($slicenames as $slicename) {
403         if ($slicename == $account_name) {
404             $selectors []= array("display"=>$slicename, "value"=>$slicename, "selected"=>True);
405         } else {
406             $selectors []= array("display"=>$slicename, "value"=>$slicename);
407         }
408     }
409
410     return $selectors;
411 }
412
413 function find_selector($needle, $selectors)
414 {
415     foreach ($selectors as $selector) {
416         if ($selector['value']==$needle) {
417             return $selector;
418         }
419     }
420     return NULL;
421 }
422
423 function get_gacks_session()
424 {
425     if (!array_key_exists("gacks", $_SESSION)) {
426         return array("account_name" => NULL);
427     }
428     return $_SESSION['gacks'];
429 }
430
431 function update_gacks_session($updated_session)
432 {
433     $session = get_gacks_session();
434
435     foreach ($updated_session as $key => $value) {
436         $session[$key] = $value;
437     }
438
439     $_SESSION['gacks'] = $session;
440 }
441
442 function get_account_name($api, $_person_id, $form=NULL, $url=NULL)
443 {
444     $session = get_gacks_session();
445     $account_name = $session['account_name'];
446
447 // this would be if we want to do it like how plc does loging...
448 //    if ($account_name == NULL) {
449 //        Header("Location: /db/gacks/set_account.php?url=" . urlencode($url));
450 //        exit();
451 //    }
452
453     drupal_add_js("misc/popup.js");
454
455     if ($form == NULL) {
456         $form = new PlekitForm(l_gacks_actions(), array("action"=>"set-account",
457                                                         "url"=>$_SERVER['REQUEST_URI']));
458         $form->start();
459         $needFormEnd = TRUE;
460     } else {
461         $needFormEnd = FALSE;
462     }
463
464     $selectors = account_selectors($api, $_person_id, $account_name);
465
466     if (($account_name != NULL) && (!find_selector($account_name, $selectors))) {
467         $account_name = NULL;
468     }
469
470     if ($account_name == NULL) {
471         print "Slice name:<br>";
472         print $form->select_html("account_name", $selectors);
473         print $form->submit_html("submit", "submit");
474     } else {
475         print "Slice name:<br>";
476         if ($url != NULL) {
477             $onChange = "PopupMessage(); this.form.action=\"$url\"; this.form.submit();";
478         } else {
479             $onChange = "PopupMessage(); this.form.submit();";
480         }
481         print $form->select_html("account_name", $selectors, array("onChange"=> $onChange));
482     }
483
484     if ($needFormEnd) {
485         $form->end();
486     }
487
488     return $account_name;
489 }
490
491 // This makes a combobox that when it is changed, it will pop up a "Please Wait"
492 // message and refresh the page.
493 //
494 // &$value is passed by reference, so that it will automatically be set to the
495 // first item in the combobox if it is NULL.
496
497 function auto_refresh_combo($form, $name, $selectors, &$value, $url)
498 {
499     foreach ($selectors as &$sel) {
500         if ($sel["value"] == $value) {
501             $sel["selected"] = True;
502         } else {
503             $sel["selected"] = False;
504         }
505     }
506
507     if (($value!=NULL) && (!find_selector($value, $selectors))) {
508         $value = NULL;
509     }
510
511     if ($value==NULL) {
512         if (count($selectors)>=1) {
513             $value = $selectors[0]['value'];
514         }
515     }
516
517     $onChange = "PopupMessage(); this.form.action=\"$url\"; this.form.method=\"get\"; this.form.submit();";
518     return $form->select_html($name, $selectors, array("onChange"=> $onChange));
519 }
520
521 // Combobox for Gacks account names. In addition to the features supported by
522 // auto_refresh_combo, it automatically populates the selectors with the slices
523 // that the user has access to, and gets and sets the account_name in the
524 // session variables.
525
526 function account_name_combo($api, $_person_id, $form, &$account_name, $url)
527 {
528     $session = get_gacks_session();
529     if ($account_name==NULL) {
530         $account_name = $session['account_name'];
531     } else {
532         // If a new $account_name was passed in, then update the session
533         // variable.
534         if ($account_name!=$session['account_name']) {
535              update_gacks_session(array("account_name" => $account_name));
536         }
537     }
538
539     $selectors = account_selectors($api, $_person_id, $account_name);
540
541     return auto_refresh_combo($form, "account_name", $selectors, $account_name, $url);
542 }
543
544 function timezone_combo($form, &$timezone, $url)
545 {
546     $session = get_gacks_session();
547     if ($timezone==NULL) {
548         $timezone = $session['timezone'];
549     } else {
550         // If a new $timezone was passed in, then update the session
551         // variable.
552         if ($timezone!=$session['timezone']) {
553              update_gacks_session(array("timezone" => $timezone));
554         }
555     }
556
557     $selectors = array();
558     for($i=-12;$i<=14;$i++) {
559         if ($i<0) {
560             $selectors []= array("display" => "GMT" . $i, "value" => $i);
561         } else {
562             $selectors []= array("display" => "GMT+" . $i, "value" => $i);
563         }
564     }
565
566     return auto_refresh_combo($form, "timezone", $selectors, $timezone, $url);
567 }
568
569 function account_table($plcapi, $gacksapi, $person_id)
570 {
571     $slices = get_slices($plcapi, $person_id, array("name","person_ids","node_ids"));
572
573     $accounts = array();
574     foreach ($slices as $slicename => $slice) {
575          $accounts[$slicename] = $gacksapi->get_account(gacksid($slicename), "user");
576     }
577
578     $headers=array();
579     $headers["name"] = "string";
580     $headers["level"] = "string";
581     $headers["be"] = "string";
582     $headers["users"] = "integer";
583     $headers["slivers"] = "integer";
584     $headers["billingContacts"] = "string";
585
586     $table = new PlekitTable("accounts", $headers, 0);
587     $table->start();
588
589     foreach ($accounts as $name => $account) {
590         $slice = $slices[$name];
591         $table->row_start();
592         $table->cell($name);
593         $table->cell($account["level"]);
594         $table->cell(bool_text(!$account["freezeUnreserved"], "enabled", "disabled"));
595         $table->cell(count($slice["person_ids"]));
596         $table->cell(count($slice["node_ids"]));
597         $table->cell($account["billingContacts"]);
598
599         $table->row_end();
600     }
601
602     $table->end();
603
604 }
605
606 function date_combo($form, $name, &$y, &$m, &$d, $url)
607 {
608     $month_selectors = array();
609     $month_selectors []= array("display" => "Jan", "value" => 1);
610     $month_selectors []= array("display" => "Feb", "value" => 2);
611     $month_selectors []= array("display" => "Mar", "value" => 3);
612     $month_selectors []= array("display" => "Apr", "value" => 4);
613     $month_selectors []= array("display" => "May", "value" => 5);
614     $month_selectors []= array("display" => "Jun", "value" => 6);
615     $month_selectors []= array("display" => "Jul", "value" => 7);
616     $month_selectors []= array("display" => "Aug", "value" => 8);
617     $month_selectors []= array("display" => "Sep", "value" => 9);
618     $month_selectors []= array("display" => "Oct", "value" => 10);
619     $month_selectors []= array("display" => "Nov", "value" => 11);
620     $month_selectors []= array("display" => "Dec", "value" => 12);
621
622     $year_selectors = array();
623     for($i=2012;$i<=2022;$i++) {
624         $year_selectors []= array("display" => $i, "value" => $i);
625     }
626
627     $day_selectors = array();
628     for($i=1;$i<=31;$i++) {
629         $day_selectors []= array("display" => $i, "value" => $i);
630     }
631
632     $m_combo = auto_refresh_combo($form, $name . "_m", $month_selectors, $m, $url);
633     $d_combo = auto_refresh_combo($form, $name . "_d", $day_selectors, $d, $url);
634     $y_combo = auto_refresh_combo($form, $name . "_y", $year_selectors, $y, $url);
635
636     return $m_combo . $d_combo . $y_combo;
637 }
638
639 function get_slice_image($api, $slicename)
640 {
641     $image_tags = $api->GetSliceTags(array("tagname" => "vref", "name" => $slicename));
642     if (count($image_tags) > 0) {
643         return $image_tags[0]['value'];
644     } else {
645         return NULL;
646     }
647 }
648
649 function get_slice_headnode($api, $slicename)
650 {
651     $ipList_tags = $api->GetSliceTags(array("tagname" => "vsys_ipList", "name" => $slicename));
652     if (count($ipList_tags) > 0) {
653         $ipList = $ipList_tags[0]['value'];
654         $ips = explode(",", $ipList);
655         if (count($ips)>0) {
656             return $ips[0];
657         } else {
658             return NULL;
659         }
660     } else {
661         return NULL;
662     }
663 }
664
665 function time_selectors($start_date, $min_date)
666 {
667     for($i=0;$i<24;$i++) {
668         if ($start_date+$i*60*60 >= $min_date) {
669             $selectors []= array("display" => "$i:00:00", "value"=>$i);
670         }
671     }
672
673     return $selectors;
674 }
675
676 function get_timezone()
677 {
678     if (isset($_GET['timezone_offset_minutes'])) {
679         $minutes = $_GET['timezone_offset_minutes'];
680 /*        echo "GMT offset (in minutes, from the browser): ". $minutes ."<br />\n";
681         echo "GMT: ". gmdate("Y-m-d H:i:s") ."<br />\n";
682
683         $local = gmmktime(gmdate("H"),gmdate("i")-$minutes); // adjust GMT by client's offset
684
685         echo "Calculated client's date/time: ". gmdate("Y-m-d h:i:s a",$local) ."<br />\n";*/
686
687         return $minutes;
688     } else {
689         if  (!strchr($_SERVER['REQUEST_URI'], "?")) {
690             $url = $_SERVER['REQUEST_URI'] . "?";
691         } else {
692             $url = $_SERVER['REQUEST_URI'] . "&";
693         }
694
695         echo "<script language='javascript'>\n";
696         echo "var d = new Date();\n";
697         echo "location.href=\"${url}timezone_offset_minutes=\" + (-d.getTimezoneOffset());\n";
698         echo "</script>\n";
699         exit();
700     }
701 }