]> git.mxchange.org Git - friendica.git/blob - mod/dfrn_poll.php
1e07242875086506355f90822dd338d5122f4fd1
[friendica.git] / mod / dfrn_poll.php
1 <?php
2 /**
3  * @file mod/dfrn_poll.php
4  */
5 use Friendica\App;
6 use Friendica\Core\Config;
7 use Friendica\Core\System;
8 use Friendica\Database\DBM;
9 use Friendica\Protocol\DFRN;
10 use Friendica\Protocol\OStatus;
11
12 require_once 'include/items.php';
13 require_once 'include/auth.php';
14
15 function dfrn_poll_init(App $a) {
16         $dfrn_id         = ((x($_GET,'dfrn_id'))         ? $_GET['dfrn_id']              : '');
17         $type            = ((x($_GET,'type'))            ? $_GET['type']                 : 'data');
18         $last_update     = ((x($_GET,'last_update'))     ? $_GET['last_update']          : '');
19         $destination_url = ((x($_GET,'destination_url')) ? $_GET['destination_url']      : '');
20         $challenge       = ((x($_GET,'challenge'))       ? $_GET['challenge']            : '');
21         $sec             = ((x($_GET,'sec'))             ? $_GET['sec']                  : '');
22         $dfrn_version    = ((x($_GET,'dfrn_version'))    ? (float) $_GET['dfrn_version'] : 2.0);
23         $perm            = ((x($_GET,'perm'))            ? $_GET['perm']                 : 'r');
24         $quiet                   = ((x($_GET,'quiet'))                   ? true                                                  : false);
25
26         // Possibly it is an OStatus compatible server that requests a user feed
27         if (($a->argc > 1) && ($dfrn_id == '') && !strstr($_SERVER["HTTP_USER_AGENT"], 'Friendica')) {
28                 $nickname = $a->argv[1];
29                 header("Content-type: application/atom+xml");
30                 echo OStatus::feed($a, $nickname, $last_update, 10);
31                 killme();
32         }
33
34         $direction = (-1);
35
36
37         if(strpos($dfrn_id,':') == 1) {
38                 $direction = intval(substr($dfrn_id,0,1));
39                 $dfrn_id   = substr($dfrn_id,2);
40         }
41
42         $hidewall = false;
43
44         if(($dfrn_id === '') && (! x($_POST,'dfrn_id'))) {
45                 if((Config::get('system','block_public')) && (! local_user()) && (! remote_user())) {
46                         http_status_exit(403);
47                 }
48
49                 $user = '';
50                 if($a->argc > 1) {
51                         $r = q("SELECT `hidewall`,`nickname` FROM `user` WHERE `user`.`nickname` = '%s' LIMIT 1",
52                                 dbesc($a->argv[1])
53                         );
54                         if (!$r)
55                                 http_status_exit(404);
56
57                         $hidewall = ($r[0]['hidewall'] && !local_user());
58
59                         $user = $r[0]['nickname'];
60                 }
61
62                 logger('dfrn_poll: public feed request from ' . $_SERVER['REMOTE_ADDR'] . ' for ' . $user);
63                 header("Content-type: application/atom+xml");
64                 echo DFRN::feed('', $user,$last_update, 0, $hidewall);
65                 killme();
66         }
67
68         if(($type === 'profile') && (! strlen($sec))) {
69
70                 $sql_extra = '';
71                 switch($direction) {
72                         case (-1):
73                                 $sql_extra = sprintf(" AND ( `dfrn-id` = '%s' OR `issued-id` = '%s' ) ", dbesc($dfrn_id),dbesc($dfrn_id));
74                                 $my_id = $dfrn_id;
75                                 break;
76                         case 0:
77                                 $sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
78                                 $my_id = '1:' . $dfrn_id;
79                                 break;
80                         case 1:
81                                 $sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
82                                 $my_id = '0:' . $dfrn_id;
83                                 break;
84                         default:
85                                 goaway(System::baseUrl());
86                                 break; // NOTREACHED
87                 }
88
89                 $r = q("SELECT `contact`.*, `user`.`username`, `user`.`nickname`
90                         FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`
91                         WHERE `contact`.`blocked` = 0 AND `contact`.`pending` = 0
92                         AND `user`.`nickname` = '%s' $sql_extra LIMIT 1",
93                         dbesc($a->argv[1])
94                 );
95
96                 if (DBM::is_result($r)) {
97
98                         $s = fetch_url($r[0]['poll'] . '?dfrn_id=' . $my_id . '&type=profile-check');
99
100                         logger("dfrn_poll: old profile returns " . $s, LOGGER_DATA);
101
102                         if(strlen($s)) {
103
104                                 $xml = parse_xml_string($s);
105
106                                 if((int) $xml->status == 1) {
107                                         $_SESSION['authenticated'] = 1;
108                                         if(! x($_SESSION,'remote'))
109                                                 $_SESSION['remote'] = array();
110
111                                         $_SESSION['remote'][] = array('cid' => $r[0]['id'],'uid' => $r[0]['uid'],'url' => $r[0]['url']);
112
113                                         $_SESSION['visitor_id'] = $r[0]['id'];
114                                         $_SESSION['visitor_home'] = $r[0]['url'];
115                                         $_SESSION['visitor_handle'] = $r[0]['addr'];
116                                         $_SESSION['visitor_visiting'] = $r[0]['uid'];
117                                         if(!$quiet)
118                                                 info( sprintf(t('%1$s welcomes %2$s'), $r[0]['username'] , $r[0]['name']) . EOL);
119                                         // Visitors get 1 day session.
120                                         $session_id = session_id();
121                                         $expire = time() + 86400;
122                                         q("UPDATE `session` SET `expire` = '%s' WHERE `sid` = '%s'",
123                                                 dbesc($expire),
124                                                 dbesc($session_id)
125                                         );
126                                 }
127                         }
128                         $profile = $r[0]['nickname'];
129                         goaway((strlen($destination_url)) ? $destination_url : System::baseUrl() . '/profile/' . $profile);
130                 }
131                 goaway(System::baseUrl());
132
133         }
134
135         if($type === 'profile-check' && $dfrn_version < 2.2 ) {
136
137                 if((strlen($challenge)) && (strlen($sec))) {
138
139                         q("DELETE FROM `profile_check` WHERE `expire` < " . intval(time()));
140                         $r = q("SELECT * FROM `profile_check` WHERE `sec` = '%s' ORDER BY `expire` DESC LIMIT 1",
141                                 dbesc($sec)
142                         );
143                         if (! DBM::is_result($r)) {
144                                 xml_status(3, 'No ticket');
145                                 // NOTREACHED
146                         }
147                         $orig_id = $r[0]['dfrn_id'];
148                         if(strpos($orig_id, ':'))
149                                 $orig_id = substr($orig_id,2);
150
151                         $c = q("SELECT * FROM `contact` WHERE `id` = %d LIMIT 1",
152                                 intval($r[0]['cid'])
153                         );
154                         if (! DBM::is_result($c)) {
155                                 xml_status(3, 'No profile');
156                         }
157                         $contact = $c[0];
158
159                         $sent_dfrn_id = hex2bin($dfrn_id);
160                         $challenge    = hex2bin($challenge);
161
162                         $final_dfrn_id = '';
163
164                         if(($contact['duplex']) && strlen($contact['prvkey'])) {
165                                 openssl_private_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['prvkey']);
166                                 openssl_private_decrypt($challenge,$decoded_challenge,$contact['prvkey']);
167                         }
168                         else {
169                                 openssl_public_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['pubkey']);
170                                 openssl_public_decrypt($challenge,$decoded_challenge,$contact['pubkey']);
171                         }
172
173                         $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.'));
174
175                         if(strpos($final_dfrn_id,':') == 1)
176                                 $final_dfrn_id = substr($final_dfrn_id,2);
177
178                         if($final_dfrn_id != $orig_id) {
179                                 logger('profile_check: ' . $final_dfrn_id . ' != ' . $orig_id, LOGGER_DEBUG);
180                                 // did not decode properly - cannot trust this site
181                                 xml_status(3, 'Bad decryption');
182                         }
183
184                         header("Content-type: text/xml");
185                         echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?><dfrn_poll><status>0</status><challenge>$decoded_challenge</challenge><sec>$sec</sec></dfrn_poll>";
186                         killme();
187                         // NOTREACHED
188                 }
189                 else {
190                                 // old protocol
191
192                         switch($direction) {
193                                 case 1:
194                                         $dfrn_id = '0:' . $dfrn_id;
195                                         break;
196                                 case 0:
197                                         $dfrn_id = '1:' . $dfrn_id;
198                                         break;
199                                 default:
200                                         break;
201                         }
202
203
204                         q("DELETE FROM `profile_check` WHERE `expire` < " . intval(time()));
205                         $r = q("SELECT * FROM `profile_check` WHERE `dfrn_id` = '%s' ORDER BY `expire` DESC",
206                                 dbesc($dfrn_id));
207                         if (DBM::is_result($r)) {
208                                 xml_status(1);
209                                 return; // NOTREACHED
210                         }
211                         xml_status(0);
212                         return; // NOTREACHED
213                 }
214         }
215
216 }
217
218
219
220 function dfrn_poll_post(App $a) {
221
222         $dfrn_id      = ((x($_POST,'dfrn_id'))      ? $_POST['dfrn_id']              : '');
223         $challenge    = ((x($_POST,'challenge'))    ? $_POST['challenge']            : '');
224         $url          = ((x($_POST,'url'))          ? $_POST['url']                  : '');
225         $sec          = ((x($_POST,'sec'))          ? $_POST['sec']                  : '');
226         $ptype        = ((x($_POST,'type'))         ? $_POST['type']                 : '');
227         $dfrn_version = ((x($_POST,'dfrn_version')) ? (float) $_POST['dfrn_version'] : 2.0);
228         $perm         = ((x($_POST,'perm'))         ? $_POST['perm']                 : 'r');
229
230         if($ptype === 'profile-check') {
231
232                 if((strlen($challenge)) && (strlen($sec))) {
233
234                         logger('dfrn_poll: POST: profile-check');
235
236                         q("DELETE FROM `profile_check` WHERE `expire` < " . intval(time()));
237                         $r = q("SELECT * FROM `profile_check` WHERE `sec` = '%s' ORDER BY `expire` DESC LIMIT 1",
238                                 dbesc($sec)
239                         );
240                         if (! DBM::is_result($r)) {
241                                 xml_status(3, 'No ticket');
242                                 // NOTREACHED
243                         }
244                         $orig_id = $r[0]['dfrn_id'];
245                         if(strpos($orig_id, ':'))
246                                 $orig_id = substr($orig_id,2);
247
248                         $c = q("SELECT * FROM `contact` WHERE `id` = %d LIMIT 1",
249                                 intval($r[0]['cid'])
250                         );
251                         if (! DBM::is_result($c)) {
252                                 xml_status(3, 'No profile');
253                         }
254                         $contact = $c[0];
255
256                         $sent_dfrn_id = hex2bin($dfrn_id);
257                         $challenge    = hex2bin($challenge);
258
259                         $final_dfrn_id = '';
260
261                         if(($contact['duplex']) && strlen($contact['prvkey'])) {
262                                 openssl_private_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['prvkey']);
263                                 openssl_private_decrypt($challenge,$decoded_challenge,$contact['prvkey']);
264                         }
265                         else {
266                                 openssl_public_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['pubkey']);
267                                 openssl_public_decrypt($challenge,$decoded_challenge,$contact['pubkey']);
268                         }
269
270                         $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.'));
271
272                         if(strpos($final_dfrn_id,':') == 1)
273                                 $final_dfrn_id = substr($final_dfrn_id,2);
274
275                         if($final_dfrn_id != $orig_id) {
276                                 logger('profile_check: ' . $final_dfrn_id . ' != ' . $orig_id, LOGGER_DEBUG);
277                                 // did not decode properly - cannot trust this site
278                                 xml_status(3, 'Bad decryption');
279                         }
280
281                         header("Content-type: text/xml");
282                         echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?><dfrn_poll><status>0</status><challenge>$decoded_challenge</challenge><sec>$sec</sec></dfrn_poll>";
283                         killme();
284                         // NOTREACHED
285                 }
286
287         }
288
289         $direction    = (-1);
290         if(strpos($dfrn_id,':') == 1) {
291                 $direction = intval(substr($dfrn_id,0,1));
292                 $dfrn_id   = substr($dfrn_id,2);
293         }
294
295
296         $r = q("SELECT * FROM `challenge` WHERE `dfrn-id` = '%s' AND `challenge` = '%s' LIMIT 1",
297                 dbesc($dfrn_id),
298                 dbesc($challenge)
299         );
300
301         if (! DBM::is_result($r)) {
302                 killme();
303         }
304
305         $type = $r[0]['type'];
306         $last_update = $r[0]['last_update'];
307
308         $r = q("DELETE FROM `challenge` WHERE `dfrn-id` = '%s' AND `challenge` = '%s'",
309                 dbesc($dfrn_id),
310                 dbesc($challenge)
311         );
312
313
314         $sql_extra = '';
315         switch($direction) {
316                 case (-1):
317                         $sql_extra = sprintf(" AND `issued-id` = '%s' ", dbesc($dfrn_id));
318                         $my_id = $dfrn_id;
319                         break;
320                 case 0:
321                         $sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
322                         $my_id = '1:' . $dfrn_id;
323                         break;
324                 case 1:
325                         $sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
326                         $my_id = '0:' . $dfrn_id;
327                         break;
328                 default:
329                         goaway(System::baseUrl());
330                         break; // NOTREACHED
331         }
332
333
334         $r = q("SELECT * FROM `contact` WHERE `blocked` = 0 AND `pending` = 0 $sql_extra LIMIT 1");
335
336
337         if (! DBM::is_result($r)) {
338                 killme();
339         }
340
341         $contact = $r[0];
342         $owner_uid = $r[0]['uid'];
343         $contact_id = $r[0]['id'];
344
345
346         if($type === 'reputation' && strlen($url)) {
347                 $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1",
348                         dbesc($url),
349                         intval($owner_uid)
350                 );
351                 $reputation = 0;
352                 $text = '';
353
354                 if (DBM::is_result($r)) {
355                         $reputation = $r[0]['rating'];
356                         $text = $r[0]['reason'];
357
358                         if($r[0]['id'] == $contact_id) {        // inquiring about own reputation not allowed
359                                 $reputation = 0;
360                                 $text = '';
361                         }
362                 }
363
364                 echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
365                 <reputation>
366                         <url>$url</url>
367                         <rating>$reputation</rating>
368                         <description>$text</description>
369                 </reputation>
370                 ";
371                 killme();
372                 // NOTREACHED
373         }
374         else {
375
376                 // Update the writable flag if it changed
377                 logger('dfrn_poll: post request feed: ' . print_r($_POST,true),LOGGER_DATA);
378                 if($dfrn_version >= 2.21) {
379                         if($perm === 'rw')
380                                 $writable = 1;
381                         else
382                                 $writable = 0;
383
384                         if($writable !=  $contact['writable']) {
385                                 q("UPDATE `contact` SET `writable` = %d WHERE `id` = %d",
386                                         intval($writable),
387                                         intval($contact_id)
388                                 );
389                         }
390                 }
391
392                 header("Content-type: application/atom+xml");
393                 $o = DFRN::feed($dfrn_id, $a->argv[1], $last_update, $direction);
394                 echo $o;
395                 killme();
396
397         }
398 }
399
400 function dfrn_poll_content(App $a) {
401
402         $dfrn_id         = ((x($_GET,'dfrn_id'))         ? $_GET['dfrn_id']              : '');
403         $type            = ((x($_GET,'type'))            ? $_GET['type']                 : 'data');
404         $last_update     = ((x($_GET,'last_update'))     ? $_GET['last_update']          : '');
405         $destination_url = ((x($_GET,'destination_url')) ? $_GET['destination_url']      : '');
406         $sec             = ((x($_GET,'sec'))             ? $_GET['sec']                  : '');
407         $dfrn_version    = ((x($_GET,'dfrn_version'))    ? (float) $_GET['dfrn_version'] : 2.0);
408         $perm            = ((x($_GET,'perm'))            ? $_GET['perm']                 : 'r');
409         $quiet           = ((x($_GET,'quiet'))           ? true                          : false);
410
411         $direction = (-1);
412         if(strpos($dfrn_id,':') == 1) {
413                 $direction = intval(substr($dfrn_id,0,1));
414                 $dfrn_id = substr($dfrn_id,2);
415         }
416
417
418         if($dfrn_id != '') {
419                 // initial communication from external contact
420                 $hash = random_string();
421
422                 $status = 0;
423
424                 $r = q("DELETE FROM `challenge` WHERE `expire` < " . intval(time()));
425
426                 if($type !== 'profile') {
427                         $r = q("INSERT INTO `challenge` ( `challenge`, `dfrn-id`, `expire` , `type`, `last_update` )
428                                 VALUES( '%s', '%s', '%s', '%s', '%s' ) ",
429                                 dbesc($hash),
430                                 dbesc($dfrn_id),
431                                 intval(time() + 60 ),
432                                 dbesc($type),
433                                 dbesc($last_update)
434                         );
435                 }
436                 $sql_extra = '';
437                 switch($direction) {
438                         case (-1):
439                                 if($type === 'profile')
440                                         $sql_extra = sprintf(" AND ( `dfrn-id` = '%s' OR `issued-id` = '%s' ) ", dbesc($dfrn_id),dbesc($dfrn_id));
441                                 else
442                                         $sql_extra = sprintf(" AND `issued-id` = '%s' ", dbesc($dfrn_id));
443                                 $my_id = $dfrn_id;
444                                 break;
445                         case 0:
446                                 $sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
447                                 $my_id = '1:' . $dfrn_id;
448                                 break;
449                         case 1:
450                                 $sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
451                                 $my_id = '0:' . $dfrn_id;
452                                 break;
453                         default:
454                                 goaway(System::baseUrl());
455                                 break; // NOTREACHED
456                 }
457
458                 $nickname = $a->argv[1];
459
460                 $r = q("SELECT `contact`.*, `user`.`username`, `user`.`nickname`
461                         FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`
462                         WHERE `contact`.`blocked` = 0 AND `contact`.`pending` = 0
463                         AND `user`.`nickname` = '%s' $sql_extra LIMIT 1",
464                         dbesc($nickname)
465                 );
466
467                 if (DBM::is_result($r)) {
468
469                         $challenge = '';
470                         $encrypted_id = '';
471                         $id_str = $my_id . '.' . mt_rand(1000,9999);
472
473                         if(($r[0]['duplex'] && strlen($r[0]['pubkey'])) || (! strlen($r[0]['prvkey']))) {
474                                 openssl_public_encrypt($hash,$challenge,$r[0]['pubkey']);
475                                 openssl_public_encrypt($id_str,$encrypted_id,$r[0]['pubkey']);
476                         }
477                         else {
478                                 openssl_private_encrypt($hash,$challenge,$r[0]['prvkey']);
479                                 openssl_private_encrypt($id_str,$encrypted_id,$r[0]['prvkey']);
480                         }
481
482                         $challenge = bin2hex($challenge);
483                         $encrypted_id = bin2hex($encrypted_id);
484                 }
485                 else {
486                         $status = 1;
487                         $challenge = '';
488                         $encrypted_id = '';
489                 }
490
491                 if(($type === 'profile') && (strlen($sec))) {
492
493                         // URL reply
494
495                         if($dfrn_version < 2.2) {
496                                 $s = fetch_url($r[0]['poll']
497                                         . '?dfrn_id=' . $encrypted_id
498                                         . '&type=profile-check'
499                                         . '&dfrn_version=' . DFRN_PROTOCOL_VERSION
500                                         . '&challenge=' . $challenge
501                                         . '&sec=' . $sec
502                                 );
503                         }
504                         else {
505                                 $s = post_url($r[0]['poll'], array(
506                                         'dfrn_id' => $encrypted_id,
507                                         'type' => 'profile-check',
508                                         'dfrn_version' => DFRN_PROTOCOL_VERSION,
509                                         'challenge' => $challenge,
510                                         'sec' => $sec
511                                 ));
512                         }
513
514                         $profile = ((DBM::is_result($r) && $r[0]['nickname']) ? $r[0]['nickname'] : $nickname);
515
516                         switch($destination_url) {
517                                 case 'profile':
518                                         $dest = System::baseUrl() . '/profile/' . $profile . '?f=&tab=profile';
519                                         break;
520                                 case 'photos':
521                                         $dest = System::baseUrl() . '/photos/' . $profile;
522                                         break;
523                                 case 'status':
524                                 case '':
525                                         $dest = System::baseUrl() . '/profile/' . $profile;
526                                         break;
527                                 default:
528                                         $appendix = (strstr($destination_url, '?') ? '&f=&redir=1' : '?f=&redir=1');
529                                         $dest = $destination_url . $appendix;
530                                         break;
531                         }
532
533                         logger("dfrn_poll: sec profile: " . $s, LOGGER_DATA);
534
535                         if(strlen($s) && strstr($s,'<?xml')) {
536
537                                 $xml = parse_xml_string($s);
538
539                                 logger('dfrn_poll: profile: parsed xml: ' . print_r($xml,true), LOGGER_DATA);
540
541                                 logger('dfrn_poll: secure profile: challenge: ' . $xml->challenge . ' expecting ' . $hash);
542                                 logger('dfrn_poll: secure profile: sec: ' . $xml->sec . ' expecting ' . $sec);
543
544
545                                 if(((int) $xml->status == 0) && ($xml->challenge == $hash)  && ($xml->sec == $sec)) {
546                                         $_SESSION['authenticated'] = 1;
547                                         if(! x($_SESSION,'remote'))
548                                                 $_SESSION['remote'] = array();
549                                         $_SESSION['remote'][] = array('cid' => $r[0]['id'],'uid' => $r[0]['uid'],'url' => $r[0]['url']);
550                                         $_SESSION['visitor_id'] = $r[0]['id'];
551                                         $_SESSION['visitor_home'] = $r[0]['url'];
552                                         $_SESSION['visitor_visiting'] = $r[0]['uid'];
553                                         if(!$quiet)
554                                                 info( sprintf(t('%1$s welcomes %2$s'), $r[0]['username'] , $r[0]['name']) . EOL);
555                                         // Visitors get 1 day session.
556                                         $session_id = session_id();
557                                         $expire = time() + 86400;
558                                         q("UPDATE `session` SET `expire` = '%s' WHERE `sid` = '%s'",
559                                                 dbesc($expire),
560                                                 dbesc($session_id)
561                                         );
562                                 }
563
564                                 goaway($dest);
565                         }
566                         goaway($dest);
567                         // NOTREACHED
568
569                 }
570                 else {
571                         // XML reply
572                         header("Content-type: text/xml");
573                         echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n"
574                                 . '<dfrn_poll>' . "\r\n"
575                                 . "\t" . '<status>' . $status . '</status>' . "\r\n"
576                                 . "\t" . '<dfrn_version>' . DFRN_PROTOCOL_VERSION . '</dfrn_version>' . "\r\n"
577                                 . "\t" . '<dfrn_id>' . $encrypted_id . '</dfrn_id>' . "\r\n"
578                                 . "\t" . '<challenge>' . $challenge . '</challenge>' . "\r\n"
579                                 . '</dfrn_poll>' . "\r\n" ;
580                         killme();
581                         // NOTREACHED
582                 }
583         }
584 }