1 /*****************************************************************************
2 * HTICK --- FTN Ticker / Request Processor
3 *****************************************************************************
4 * Hatch file to fileecho implementation
5 *
6 * This file is part of HTICK, part of the Husky fidosoft project
7 * http://husky.sourceforge.net
8 *
9 * HTICK is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2, or (at your option) any
12 * later version.
13 *
14 * HTICK is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with HTICK; see the file COPYING. If not, write to the Free
21 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22 *****************************************************************************
23 * $Id$
24 *****************************************************************************/
25
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <errno.h>
32
33 /* compiler.h */
34 #include <huskylib/compiler.h>
35
36 #ifdef HAS_UNISTD_H
37 # include <unistd.h>
38 #endif
39
40 /* smapi */
41 #include <huskylib/huskylib.h>
42
43 /* fidoconf */
44 #include <fidoconf/fidoconf.h>
45 #include <fidoconf/common.h>
46 #include <huskylib/dirlayer.h>
47 #include <huskylib/xstr.h>
48 #include <fidoconf/afixcmd.h>
49 #include <huskylib/recode.h>
50 #include <huskylib/crc.h>
51
52 #include <areafix/areafix.h>
53
54 /* htick */
55 #include <filecase.h>
56 #include <seenby.h>
57 #include <add_desc.h>
58 #include <hatch.h>
59 #include <global.h>
60 #include <fcommon.h>
61 #include <toss.h>
62
63 char *versionStr;
64
65 typedef enum _descrMacro
66 { DESCRERROR = 0, BBSONELINE =
67 1, BBSMLTLINE, DIZONELINE, DIZMLTLINE, FILONELINE, FILMLTLINE } descrMacro;
68
getDescOptions(char * desc,char ** filename)69 static descrMacro getDescOptions( char *desc, char **filename )
70 {
71 byte descOPT = 0;
72
73 if( !desc || !filename )
74 {
75 w_log( LL_CRIT,
76 __FILE__
77 ":: Parameter is NULL: getDescOptions(%s,%s). This is serious error in program, please report to developers.",
78 desc ? "desc" : "NULL", filename ? "filename" : "NULL" );
79 return DESCRERROR;
80 }
81
82 if( stricmp( desc, "@@BBS" ) == 0 )
83 descOPT = BBSONELINE;
84 else if( stricmp( desc, "@@DIZ" ) == 0 )
85 descOPT = DIZONELINE;
86 else if( stricmp( desc, "@BBS" ) == 0 )
87 descOPT = BBSMLTLINE;
88 else if( stricmp( desc, "@DIZ" ) == 0 )
89 descOPT = DIZMLTLINE;
90 else if( desc[0] == '@' )
91 {
92 char *basename = desc;
93
94 basename++;
95 if( *basename == '@' )
96 basename++;
97 if( fexist( basename ) )
98 {
99 descOPT = ( basename - desc == 1 ) ? FILMLTLINE : FILONELINE;
100 *filename = sstrdup( basename );
101 }
102 }
103 return descOPT;
104 }
105
expandDescMacros(s_ticfile * tic,char * hatchedFile)106 static void expandDescMacros( s_ticfile * tic, char *hatchedFile )
107 {
108 char *descr_file_name = NULL;
109 char *basename = NULL;
110 char *ldFileName = NULL;
111 char *sdFileName = NULL;
112 char ctmp;
113 int SdescOPT = 0;
114 int LdescOPT = 0;
115 s_ticfile tmptic;
116 char **tmpArray = NULL;
117 UINT i;
118
119 if( !tic || !hatchedFile )
120 {
121 w_log( LL_CRIT,
122 __FILE__
123 ":: Parameter is NULL: expandDescMacros(%s,%s). This is serious error in program, please report to developers.",
124 tic ? "tic" : "NULL", hatchedFile ? "hatchedFile" : "NULL" );
125 return;
126 }
127
128 memset( &tmptic, 0, sizeof( s_ticfile ) );
129 if( tic->anzdesc > 0 )
130 {
131 SdescOPT = getDescOptions( tic->desc[0], &sdFileName );
132 if( SdescOPT > 0 )
133 {
134 nfree( tic->desc[0] );
135 tic->desc[0] = sstrdup( "-- description missing --" );
136 }
137 }
138 if( tic->anzldesc > 0 )
139 {
140 LdescOPT = getDescOptions( tic->ldesc[0], &ldFileName );
141 if( LdescOPT > 0 )
142 {
143 nfree( tic->ldesc[0] );
144 nfree( tic->ldesc );
145 tic->anzldesc = 0;
146 }
147 }
148
149 if( !SdescOPT && !LdescOPT )
150 goto recode;
151
152 if( SdescOPT == BBSONELINE || LdescOPT == BBSONELINE || SdescOPT == BBSMLTLINE
153 || LdescOPT == BBSMLTLINE )
154 {
155 if( strrchr( tic->file, PATH_DELIM ) )
156 {
157 basename = strrchr( tic->file, PATH_DELIM );
158 ctmp = *basename;
159 *basename = '\0';
160 xscatprintf( &descr_file_name, "%s%c%s", tic->file, PATH_DELIM, config->fileDescription );
161 *basename = ctmp;
162 }
163 else
164 {
165 xstrcat( &descr_file_name, config->fileDescription );
166 }
167 adaptcase( descr_file_name );
168 if( GetDescFormBbsFile( descr_file_name, tic->file, &tmptic ) == 0 )
169 {
170
171 if( LdescOPT == BBSONELINE )
172 {
173 tic->anzldesc = 1;
174 tic->ldesc = scalloc( sizeof( *tic->ldesc ), tic->anzldesc );
175 tic->ldesc[0] = sstrdup( tmptic.desc[0] );
176 LdescOPT = 0;
177 }
178 else if( LdescOPT == BBSMLTLINE )
179 {
180 tic->anzldesc = tmptic.anzdesc;
181 tic->ldesc = scalloc( sizeof( *tic->ldesc ), tic->anzldesc );
182 for( i = 0; i < tic->anzldesc; i++ )
183 {
184 tic->ldesc[i] = sstrdup( tmptic.desc[i] );
185 }
186 LdescOPT = 0;
187 }
188 if( SdescOPT == BBSONELINE )
189 {
190 tic->anzdesc = 1;
191 tic->desc = scalloc( sizeof( *tic->desc ), tic->anzdesc );
192 tic->desc[0] = sstrdup( tmptic.desc[0] );
193 SdescOPT = 0;
194 }
195 else if( SdescOPT == BBSMLTLINE )
196 {
197
198 tic->anzdesc = tmptic.anzdesc;
199 tic->desc = scalloc( sizeof( *tic->desc ), tic->anzdesc );
200 for( i = 0; i < tic->anzdesc; i++ )
201 {
202 tic->desc[i] = sstrdup( tmptic.desc[i] );
203 }
204 SdescOPT = 0;
205 }
206 disposeTic( &tmptic );
207 memset( &tmptic, 0, sizeof( s_ticfile ) );
208 }
209 }
210 if( SdescOPT == DIZONELINE || LdescOPT == DIZONELINE || SdescOPT == DIZMLTLINE
211 || LdescOPT == DIZMLTLINE )
212 {
213 if( GetDescFormDizFile( hatchedFile, &tmptic ) == 1 )
214 {
215 if( SdescOPT == DIZMLTLINE || LdescOPT == DIZMLTLINE )
216 {
217 tmpArray = scalloc( sizeof( char * ), tmptic.anzldesc );
218 for( i = 0; i < tmptic.anzldesc; i++ )
219 {
220 tmpArray[i] = sstrdup( tmptic.ldesc[i] );
221 }
222 }
223 if( LdescOPT == DIZONELINE )
224 {
225 tic->anzldesc = 1;
226 tic->ldesc = scalloc( sizeof( *tic->ldesc ), tic->anzldesc );
227 tic->ldesc[0] = sstrdup( tmptic.ldesc[0] );
228 }
229 else if( LdescOPT == DIZMLTLINE )
230 {
231 tic->anzldesc = tmptic.anzldesc;
232 tic->ldesc = tmpArray;
233 }
234 if( SdescOPT == DIZONELINE )
235 {
236 nfree( tic->desc[0] );
237 tic->desc[0] = sstrdup( tmptic.ldesc[0] );
238 }
239 else if( SdescOPT == DIZMLTLINE )
240 {
241 tic->anzdesc = tmptic.anzldesc;
242 tic->desc = tmpArray;
243 }
244 disposeTic( &tmptic );
245 memset( &tmptic, 0, sizeof( s_ticfile ) );
246 }
247 }
248
249 if( SdescOPT == FILONELINE || SdescOPT == FILMLTLINE )
250 {
251 if( GetDescFormFile( sdFileName, &tmptic ) == 1 )
252 {
253 if( SdescOPT == FILMLTLINE || LdescOPT == FILMLTLINE )
254 {
255 tmpArray = scalloc( sizeof( char * ), tmptic.anzldesc );
256 for( i = 0; i < tmptic.anzldesc; i++ )
257 {
258 tmpArray[i] = sstrdup( tmptic.ldesc[i] );
259 }
260 }
261 if( SdescOPT == FILONELINE )
262 {
263 nfree( tic->desc[0] );
264 tic->desc[0] = sstrdup( tmptic.ldesc[0] );
265 }
266 else if( SdescOPT == FILMLTLINE )
267 {
268 tic->anzdesc = tmptic.anzldesc;
269 tic->desc = tmpArray;
270 }
271 }
272 }
273 if( LdescOPT == FILONELINE || LdescOPT == FILMLTLINE )
274 {
275 if( sdFileName && ldFileName && stricmp( sdFileName, ldFileName ) == 0 )
276 {
277 if( LdescOPT == FILONELINE )
278 {
279 tic->anzldesc = 1;
280 tic->ldesc = scalloc( sizeof( *tic->ldesc ), tic->anzldesc );
281 tic->ldesc[0] = sstrdup( tmptic.ldesc[0] );
282 }
283 else if( LdescOPT == FILMLTLINE )
284 {
285 tic->anzldesc = tmptic.anzldesc;
286 tic->ldesc = tmpArray;
287 }
288 }
289 else
290 {
291 disposeTic( &tmptic );
292 memset( &tmptic, 0, sizeof( s_ticfile ) );
293 if( GetDescFormFile( ldFileName, &tmptic ) == 1 )
294 {
295 if( LdescOPT == FILMLTLINE )
296 {
297 tic->anzldesc = tmptic.anzldesc;
298 tic->ldesc = scalloc( sizeof( char * ), tmptic.anzldesc );
299 for( i = 0; i < tmptic.anzldesc; i++ )
300 {
301 tic->ldesc[i] = sstrdup( tmptic.ldesc[i] );
302 }
303 }
304 else if( LdescOPT == FILONELINE )
305 {
306 tic->anzldesc = 1;
307 tic->ldesc = scalloc( sizeof( *tic->ldesc ), tic->anzldesc );
308 tic->ldesc[0] = sstrdup( tmptic.ldesc[0] );
309 }
310 }
311 }
312 }
313
314 recode:
315 if( config->outtab != NULL )
316 {
317 for( i = 0; i < tic->anzdesc; i++ )
318 {
319 recodeToTransportCharset( tic->desc[i] );
320 }
321 for( i = 0; i < tic->anzldesc; i++ )
322 {
323 recodeToTransportCharset( tic->ldesc[i] );
324 }
325 }
326
327 disposeTic( &tmptic );
328 }
329
hatch()330 HATCH_HATCH_RETURN_CODES hatch( )
331 {
332 s_area *filearea;
333 struct stat stbuf;
334 char *hatchedFile = NULL;
335 char buffer[256] = "";
336
337 w_log( LL_INFO, "Start file hatch..." );
338
339 hatchedFile = sstrdup( hatchInfo->file );
340
341 /* Exist file? */
342 adaptcase( hatchedFile );
343 {
344 FILE *f = fopen( hatchedFile, "rb" );
345
346 if( f )
347 fclose( f );
348 else
349 {
350 w_log( LL_ALERT, "File %s: %s", hatchedFile, strerror( errno ) );
351 return HATCH_ERROR_FILE;
352 }
353 }
354
355 xstrcpy( &hatchInfo->file, GetFilenameFromPathname( hatchedFile ) );
356
357 if( stricmp( hatchedFile, hatchInfo->file ) == 0 ) /* hatch from current dir */
358 {
359 int len = 0;
360
361 getcwd( buffer, 256 );
362 len = strlen( buffer );
363 if( buffer[len - 1] == PATH_DELIM )
364 {
365 Strip_Trailing( buffer, PATH_DELIM );
366 }
367 nfree( hatchedFile );
368 xscatprintf( &hatchedFile, "%s%c%s", buffer, ( char )PATH_DELIM, hatchInfo->file );
369 }
370
371 MakeProperCase( hatchInfo->file );
372
373 filearea = getFileArea( hatchInfo->area );
374
375 if( filearea == NULL )
376 {
377 w_log( '9', "Cannot open or create File Area %s", hatchInfo->area );
378 return HATCH_NOT_FILEAREA;
379 }
380
381 expandDescMacros( hatchInfo, hatchedFile );
382
383 stat( hatchedFile, &stbuf );
384 hatchInfo->size = stbuf.st_size;
385
386 hatchInfo->origin = hatchInfo->from = *filearea->useAka;
387
388 seenbyAdd( &hatchInfo->seenby, &hatchInfo->anzseenby, filearea->useAka );
389
390 if( filearea->description )
391 hatchInfo->areadesc = sstrdup( filearea->description );
392 /* Adding crc */
393 hatchInfo->crc = filecrc32( hatchedFile );
394
395 if( sendToLinks( 0, filearea, hatchInfo, hatchedFile ) == 0 )
396 {
397 doSaveTic( NULL, hatchInfo, filearea );
398 }
399
400 nfree( hatchedFile );
401 return HATCH_OK;
402 }
403
send(char * filename,char * area,char * addr)404 HATCH_SEND_RETURN_CODES send( char *filename, char *area, char *addr )
405 /* 0 - OK */
406 /* 1 - Passthrough filearea */
407 /* 2 - filearea not found */
408 /* 3 - file not found */
409 /* 4 - link not found */
410 /* 5 - put on link error */
411 /* -1 - invalid parameter */
412 {
413 s_ticfile tic;
414 s_link *link = NULL;
415 s_area *filearea;
416 char *sendfile = NULL, *descr_file_name = NULL, *tmpfile = NULL;
417 char timestr[40];
418 struct stat stbuf;
419 time_t acttime;
420 HATCH_SEND_RETURN_CODES rc;
421
422 if( !filename || !area || !addr )
423 {
424 w_log( LL_CRIT,
425 __FILE__
426 ":: Parameter is NULL: send(%s,%s,%s). This is serious error in program, please report to developers.",
427 filename ? "filename" : "NULL", area ? "area" : "NULL", addr ? "addr" : "NULL" );
428 return HATCH_SEND_E_INVALID;
429 }
430
431 w_log( LL_INFO, "Start file send (%s in %s to %s)", filename, area, addr );
432
433 filearea = getFileArea( area );
434 if( filearea == NULL )
435 {
436 if( !quiet )
437 fprintf( stderr, "Error: Filearea not found\n" );
438 return HATCH_SEND_E_FILEAREANOTFOUND;
439 }
440
441 link = getLink( config, addr );
442 if( link == NULL )
443 {
444 if( !quiet )
445 fprintf( stderr, "Error: Link not found\n" );
446 return HATCH_SEND_E_LINKNOTFOUND;
447 }
448
449 memset( &tic, 0, sizeof( tic ) );
450
451 if( filearea->msgbType == MSGTYPE_PASSTHROUGH )
452 sendfile = sstrdup( config->passFileAreaDir );
453 else
454 sendfile = sstrdup( filearea->fileName );
455
456 strLower( sendfile );
457 _createDirectoryTree( sendfile );
458 xstrcat( &sendfile, filename );
459
460 /* Exist file? */
461 adaptcase( sendfile );
462 if( !fexist( sendfile ) )
463 {
464 if( !quiet )
465 fprintf( stderr, "Error: File not found\n" );
466 w_log( '6', "File %s, not found", sendfile );
467 nfree( sendfile );
468 disposeTic( &tic );
469 return HATCH_SEND_E_FILENOTFOUND;
470 }
471
472 tic.file = sstrdup( filename );
473
474 if( filearea->sendorig )
475 {
476 xstrscat( &tmpfile, config->passFileAreaDir, tic.file, NULL );
477 adaptcase( tmpfile );
478
479 if( copy_file( sendfile, tmpfile, 1 ) != 0 )
480 { /* overwrite existing file if not same */
481 adaptcase( sendfile );
482 if( copy_file( sendfile, tmpfile, 1 ) == 0 )
483 { /* overwrite existing file if not same */
484 w_log( '6', "Copied %s to %s", sendfile, tmpfile );
485 }
486 else
487 {
488 w_log( '9', "File %s not found or not copyable", sendfile );
489 disposeTic( &tic );
490 nfree( sendfile );
491 nfree( tmpfile );
492 return HATCH_SEND_E_FILEAREANOTFOUND;
493 }
494 }
495 else
496 {
497 w_log( '6', "Copied %s to %s", sendfile, tmpfile );
498 strcpy( sendfile, tmpfile );
499 }
500 }
501
502 tic.area = sstrdup( area );
503
504 stat( sendfile, &stbuf );
505 tic.size = stbuf.st_size;
506
507 tic.origin = tic.from = *filearea->useAka;
508
509 /* Adding crc */
510 tic.crc = filecrc32( sendfile );
511
512 xstrscat( &descr_file_name, filearea->fileName, config->fileDescription, NULL );
513 adaptcase( descr_file_name );
514
515 GetDescFormBbsFile( descr_file_name, tic.file, &tic );
516
517 /* Adding path */
518 time( &acttime );
519 strcpy( timestr, asctime( gmtime( &acttime ) ) );
520 timestr[strlen( timestr ) - 1] = 0;
521 if( timestr[8] == ' ' )
522 timestr[8] = '0';
523 tic.path = srealloc( tic.path, ( tic.anzpath + 1 ) * sizeof( *tic.path ) );
524 tic.path[tic.anzpath] = NULL;
525 xscatprintf( &tic.path[tic.anzpath], "%s %lu %s UTC %s",
526 aka2str( *filearea->useAka ), ( unsigned long )time( NULL ), timestr, versionStr );
527 tic.anzpath++;
528
529 /* Adding Downlink to Seen-By */
530 seenbyAdd( &tic.seenby, &tic.anzseenby, &link->hisAka );
531 /* Forward file to */
532 if( PutFileOnLink( sendfile, &tic, link ) )
533 rc = HATCH_SEND_OK;
534 else
535 rc = HATCH_SEND_E_PUT_ON_LINK;
536
537 disposeTic( &tic );
538 nfree( sendfile );
539 nfree( tmpfile );
540 nfree( descr_file_name );
541 return rc;
542 }
543
544 /* Return values:
545 * 1 if success
546 * 0 if error
547 */
PutFileOnLink(char * newticedfile,s_ticfile * tic,s_link * downlink)548 int PutFileOnLink( char *newticedfile, s_ticfile * tic, s_link * downlink )
549 {
550 int busy = 0;
551 char *linkfilepath = NULL;
552 char *newticfile = NULL;
553 FILE *flohandle = NULL;
554 hs_addr *aka;
555 char *str_hisAka, *str_Aka;
556
557 if( !newticedfile || !tic || !downlink )
558 {
559 w_log( LL_CRIT,
560 __FILE__
561 ":: Parameter is NULL: PutFileOnLink(%s,%s,%s). This is serious error in program, please report to developers.",
562 newticedfile ? "newticedfile" : "NULL", tic ? "tic" : "NULL",
563 downlink ? "downlink" : "NULL" );
564 return 0;
565 }
566
567 aka = SelectPackAka( downlink );
568 memcpy( &tic->from, downlink->ourAka, sizeof( hs_addr ) );
569 memcpy( &tic->to, &downlink->hisAka, sizeof( hs_addr ) );
570
571 nfree( tic->password );
572 if( downlink->ticPwd != NULL )
573 tic->password = sstrdup( downlink->ticPwd );
574
575 if( createOutboundFileNameAka( downlink, downlink->fileEchoFlavour, FLOFILE, aka ) == 1 )
576 busy = 1;
577
578 if( busy )
579 {
580 xstrcat( &linkfilepath, config->busyFileDir );
581 }
582 else
583 {
584 if( config->separateBundles )
585 {
586 char *point = strrchr( downlink->floFile, '.' );
587
588 *point = '\0';
589 xscatprintf( &linkfilepath, "%s.sep%c", downlink->floFile, PATH_DELIM );
590 *point = '.';
591 }
592 else
593 {
594 xstrcat( &linkfilepath, config->ticOutbound );
595 }
596 }
597
598 /* fileBoxes support */
599 if( needUseFileBoxForLinkAka( config, downlink, aka ) )
600 {
601 nfree( linkfilepath );
602 if( !downlink->fileBox )
603 downlink->fileBox = makeFileBoxNameAka( config, downlink, aka );
604 xstrcat( &linkfilepath, downlink->fileBox );
605 }
606
607 _createDirectoryTree( linkfilepath );
608 /* Don't create TICs for everybody */
609 if( !downlink->noTIC )
610 {
611 newticfile = makeUniqueDosFileName( linkfilepath, "tic", config );
612 if( !writeTic( newticfile, tic ) )
613 {
614 /* try to toss into config->busyFileDir */
615 w_log( LL_CRIT, "Can't write into: %s", linkfilepath );
616 nfree( newticfile );
617 nfree( linkfilepath );
618 xstrcat( &linkfilepath, config->busyFileDir );
619 newticfile = makeUniqueDosFileName( linkfilepath, "tic", config );
620 if( !writeTic( newticfile, tic ) )
621 {
622 w_log( LL_CRIT, "Can't write into: %s", linkfilepath );
623 nfree( newticfile );
624 return 0;
625 }
626 busy = 1; /* do not touch floFile if it's BSO */
627 }
628 }
629 else
630 newticfile = NULL;
631
632 if( needUseFileBoxForLinkAka( config, downlink, aka ) )
633 {
634 /* do not copy file into busyFileDir */
635 if( strcasecmp( linkfilepath, config->busyFileDir ) )
636 {
637 xstrcat( &linkfilepath, tic->file );
638 if( link_file( newticedfile, linkfilepath ) == 0 )
639 {
640 if( copy_file( newticedfile, linkfilepath, 1 ) == 0 )
641 { /* overwrite existing file if not same */
642 w_log( LL_FILESENT, "Copied: %s", newticedfile );
643 w_log( LL_FILESENT, " to: %s", linkfilepath );
644 }
645 else
646 {
647 w_log( '9', "File %s not found or not copyable", newticedfile );
648 if( !busy && downlink->bsyFile )
649 remove( downlink->bsyFile );
650 nfree( downlink->bsyFile );
651 nfree( downlink->floFile );
652 nfree( linkfilepath );
653 }
654 }
655 else
656 {
657 w_log( LL_FILESENT, "Linked: %s", newticedfile );
658 w_log( LL_FILESENT, " to: %s", linkfilepath );
659 }
660 }
661 }
662 else if( !busy )
663 {
664 flohandle = fopen( downlink->floFile, "a" );
665 fprintf( flohandle, "%s\n", newticedfile );
666 if( newticfile != NULL )
667 fprintf( flohandle, "^%s\n", newticfile );
668 fclose( flohandle );
669 }
670 if( !busy || needUseFileBoxForLinkAka( config, downlink, aka ) )
671 {
672
673 str_hisAka = aka2str5d( downlink->hisAka );
674 str_Aka = aka2str5d( *aka );
675
676 if( newticfile != NULL )
677 w_log( LL_LINK, "Forwarding %s with tic %s for %s via %s", tic->file,
678 GetFilenameFromPathname( newticfile ), str_hisAka, str_Aka );
679 else
680 w_log( LL_LINK, "Forwarding %s for %s via %s", tic->file, str_hisAka, str_Aka );
681
682 nfree( str_hisAka );
683 nfree( str_Aka );
684
685 if( !busy && downlink->bsyFile )
686 remove( downlink->bsyFile );
687 }
688 nfree( downlink->bsyFile );
689 nfree( downlink->floFile );
690 nfree( linkfilepath );
691 nfree( newticfile );
692 return 1;
693 }
694