1 /*===========================================================================
2  *
3  *                            PUBLIC DOMAIN NOTICE
4  *               National Center for Biotechnology Information
5  *
6  *  This software/database is a "United States Government Work" under the
7  *  terms of the United States Copyright Act.  It was written as part of
8  *  the author's official duties as a United States Government employee and
9  *  thus cannot be copyrighted.  This software/database is freely available
10  *  to the public for use. The National Library of Medicine and the U.S.
11  *  Government have not placed any restriction on its use or reproduction.
12  *
13  *  Although all reasonable efforts have been taken to ensure the accuracy
14  *  and reliability of the software and data, the NLM and the U.S.
15  *  Government do not and cannot warrant the performance or results that
16  *  may be obtained by using this software or data. The NLM and the U.S.
17  *  Government disclaim all warranties, express or implied, including
18  *  warranties of performance, merchantability or fitness for any particular
19  *  purpose.
20  *
21  *  Please cite the author in any work or product based on this material.
22  *
23  * ===========================================================================
24  *
25  */
26 
27 #include <kfg/extern.h>
28 
29 #include <kfg/ngc.h> /* KNgcObjRelease */
30 
31 #include <klib/rc.h>
32 #include <klib/text.h>
33 #include <klib/printf.h>
34 #include <klib/data-buffer.h>
35 #include <klib/refcount.h>
36 
37 #include <kfg/ngc.h>
38 
39 #include <kfs/directory.h> /* KDirectoryOpenFileRead */
40 #include <kfs/file.h>
41 #include <kfs/subfile.h>
42 #include <kfs/gzip.h>
43 
44 #include <strtol.h>
45 
46 #include "kfg-priv.h" /* KConfigGetNgcFile */
47 #include "ngc-priv.h"
48 
49 #include <string.h>
50 #include <sysalloc.h>
51 
52 
53 #define MIN_ENC_KEY_LEN 1
54 #define MAX_ENC_KEY_LEN 256
55 #define MIN_DNLD_TICKET_LEN 1
56 #define MAX_DNLD_TICKET_LEN 256
57 #define MIN_DESCRIPTION_LEN 1
58 #define MAX_DESCRIPTION_LEN 256
59 
60 
61 #define RELEASE(type, obj) do { rc_t rc2 = type##Release(obj); \
62     if (rc2 && !rc) { rc = rc2; } obj = NULL; } while (false)
63 
64 
KNgcObjWhack(KNgcObj * self)65 static rc_t KNgcObjWhack ( KNgcObj * self )
66 {
67     KDataBufferWhack ( & self-> buffer );
68     free( self );
69     return 0;
70 }
71 
72 
KNgcObjParseIdKeyTicketDesc(KNgcObj * self,uint32_t offset)73 static rc_t KNgcObjParseIdKeyTicketDesc ( KNgcObj * self, uint32_t offset )
74 {
75     rc_t rc = 0;
76     uint64_t i;
77     uint64_t l = ( self -> buffer . elem_count ) - offset;
78     uint8_t state;
79     const char * ptr = ( const char * ) self -> buffer . base;
80     String projectId;
81     String * dst = & projectId;
82 
83     memset ( & projectId, 0, sizeof projectId );
84 
85     ptr += offset;
86     dst -> addr = ptr;
87     for ( i = 0, state = 0; i < l && state < 4; i++ )
88     {
89         if ( ptr[ i ] == '|' )
90         {
91             dst -> size = dst -> len;
92             switch( state )
93             {
94                 case 0 : dst = & self -> encryptionKey; break;
95                 case 1 : dst = & self -> downloadTicket; break;
96                 case 2 : dst = & self -> description; break;
97             }
98             state ++;
99             if ( state < 4 )
100             {
101                 if ( i < ( l - 1 ) )
102                     dst -> addr = &( ptr[ i + 1 ] );
103             }
104         }
105         else
106         {
107             ( dst -> len )++;
108         }
109     }
110     if ( projectId . addr == NULL ||
111          self -> encryptionKey . addr == NULL ||
112          self -> downloadTicket . addr == NULL ||
113          self -> description . addr == NULL )
114     {
115         rc = RC ( rcKFG, rcFile, rcParsing, rcParam, rcInvalid );
116     }
117     else if ( projectId . len < 1 ||
118                self -> encryptionKey . len < 1 ||
119                self -> downloadTicket . len < 1 ||
120                self -> description . len < 1 )
121     {
122         rc = RC ( rcKFG, rcFile, rcParsing, rcParam, rcInvalid );
123     }
124 
125     /* the following tests may be version depended */
126 
127     /* test to verify that the project ID is a valid Integer */
128     if ( rc == 0 )
129     {
130         char * end;
131         self -> projectId = strtou32 ( projectId . addr, & end, 10 );
132         if ( ( end - ( char* ) projectId . addr ) != projectId . size )
133             rc = RC ( rcKFG, rcFile, rcParsing, rcParam, rcInvalid );
134     }
135 
136     /* test that the download ticket and the encKey do have a minimum and maximum length */
137     if ( rc == 0 )
138     {
139         if ( ( self -> downloadTicket . len < MIN_DNLD_TICKET_LEN ) ||
140              ( self -> downloadTicket . len > MAX_DNLD_TICKET_LEN ) )
141             rc = RC ( rcKFG, rcFile, rcParsing, rcParam, rcInvalid );
142     }
143 
144     if ( rc == 0 )
145     {
146         if ( ( self -> encryptionKey . len < MIN_ENC_KEY_LEN ) ||
147              ( self -> encryptionKey . len > MAX_ENC_KEY_LEN ) )
148             rc = RC ( rcKFG, rcFile, rcParsing, rcParam, rcInvalid );
149     }
150 
151     if ( rc == 0 )
152     {
153         if ( ( self -> description . len < MIN_DESCRIPTION_LEN ) ||
154              ( self -> description . len > MAX_DESCRIPTION_LEN ) )
155             rc = RC ( rcKFG, rcFile, rcParsing, rcParam, rcInvalid );
156     }
157 
158     return rc;
159 }
160 
161 
KNgcParseUntilfound(const char * src,uint32_t l,const char * term,uint32_t term_count,String * dst)162 static bool KNgcParseUntilfound ( const char * src, uint32_t l, const char * term, uint32_t term_count, String * dst )
163 {
164     bool res = false;
165     uint32_t i;
166 
167     dst -> addr = src;
168     for ( i = 0; i < l && !res; ++i )
169     {
170         char * found = string_chr ( term, term_count, src[ i ] );
171         res = ( found != NULL );
172         if ( !res )
173             ( dst -> len )++;
174     }
175     if ( res )
176         dst -> size = dst -> len;
177     else
178         dst -> size = dst -> len = 0;
179     return res;
180 }
181 
182 
KNgcObjParseBuffer(KNgcObj * self)183 static rc_t KNgcObjParseBuffer ( KNgcObj * self )
184 {
185     rc_t rc = 0;
186     uint32_t l = ( uint32_t ) self -> buffer . elem_count;
187     const char * ptr = ( const char * ) self -> buffer . base;
188 
189     const char s_version[] = "version ";
190     const char s_v1_0[] = "1.0";
191 
192     if ( ( size_t ) l != self -> buffer . elem_count )
193         l = INT32_MAX;
194 
195     if ( string_cmp( s_version, sizeof s_version - 1, ptr, l, sizeof s_version - 1 ) != 0 )
196         rc = RC( rcKFG, rcFile, rcParsing, rcFormat, rcUnrecognized );
197     else if ( !KNgcParseUntilfound ( &( ptr[ 8 ] ), l - 8, "\n\r", 2, & self -> version ) )
198         rc = RC( rcKFG, rcFile, rcParsing, rcFormat, rcUnrecognized );
199 
200     if ( rc == 0 )
201     {
202         if ( self -> version . len != 3 )
203             rc = RC( rcKFG, rcFile, rcParsing, rcFormat, rcUnrecognized );
204         else if ( string_cmp( s_v1_0, sizeof s_v1_0 - 1, self -> version . addr,  sizeof s_v1_0 - 1, sizeof s_v1_0 - 1 ) != 0 )
205             rc = RC( rcKFG, rcFile, rcParsing, rcFormat, rcUnrecognized );
206     }
207 
208     if ( rc == 0 )
209     {
210         uint32_t offset = sizeof s_version - 1 + ( self -> version . len ) + 1;
211         rc = KNgcObjParseIdKeyTicketDesc ( self, offset );
212     }
213     return rc;
214 }
215 
216 
KNgcObjInitFromString(KNgcObj * self,const char * line)217 static rc_t KNgcObjInitFromString ( KNgcObj * self, const char * line )
218 {
219     rc_t rc = 0;
220     uint32_t len = string_measure( line, NULL );
221     rc = KDataBufferResize ( &self -> buffer, len + 20 );
222     if ( rc == 0 )
223     {
224         size_t written;
225         rc = string_printf( self -> buffer . base, len + 20, &written, "version 1.0\n%s", line );
226         if ( rc == 0 )
227         {
228             self -> buffer . elem_count = written;
229             rc = KNgcObjParseBuffer ( self );
230         }
231     }
232     return rc;
233 }
234 
235 
KNgcObjInitFromFile(KNgcObj * self,const struct KFile * src)236 static rc_t KNgcObjInitFromFile ( KNgcObj * self, const struct KFile * src )
237 {
238     char hdr [ 8 ];
239     size_t num_read;
240     rc_t rc = KFileReadAll ( src, 0, hdr, sizeof hdr, & num_read );
241     if ( rc == 0 )
242     {
243         if ( num_read != sizeof hdr )
244             rc = RC( rcKFG, rcFile, rcReading, rcFile, rcWrongType );
245         else if ( memcmp( hdr, "ncbi_gap", sizeof hdr ) != 0 )
246             rc = RC( rcKFG, rcFile, rcReading, rcFile, rcWrongType );
247         else
248         {
249             uint64_t src_size;
250             rc = KFileSize ( src, & src_size );
251             if ( rc == 0 )
252             {
253                 const struct KFile * sub;
254                 rc = KFileMakeSubRead ( &sub, src, sizeof hdr, src_size - sizeof hdr );
255                 if ( rc == 0 )
256                 {
257                     const struct KFile * gzip;
258                     rc = KFileMakeGzipForRead ( & gzip, sub );
259                     if ( rc == 0 )
260                     {
261                         size_t to_read = ( src_size * 10 );
262                         /* guessing that the unzip version will not be bigger that 10 x the zipped one */
263                         rc = KDataBufferResize ( & self -> buffer, to_read );
264                         if ( rc == 0 )
265                         {
266                             size_t num_read;
267                             rc = KFileReadAll ( gzip, 0,  self -> buffer . base, to_read, & num_read );
268                             if ( rc == 0 )
269                             {
270                                 self -> buffer . elem_count = num_read;
271                                 rc = KNgcObjParseBuffer ( self );
272                             }
273                         }
274                         KFileRelease ( gzip );
275                     }
276                     KFileRelease ( sub );
277                 }
278             }
279         }
280     }
281     return rc;
282 }
283 
284 
KNgcObjMakeFromString(const KNgcObj ** ngc,const char * line)285 LIB_EXPORT rc_t CC KNgcObjMakeFromString ( const KNgcObj **ngc, const char * line )
286 {
287     rc_t rc;
288     if ( ngc == NULL || line == NULL )
289         rc = RC ( rcKFG, rcMgr, rcAllocating, rcParam, rcNull );
290     else
291     {
292         struct KNgcObj * f = calloc ( 1, sizeof * f );
293         if ( f == NULL )
294             rc = RC ( rcKFG, rcMgr, rcAllocating, rcMemory, rcExhausted );
295         else
296         {
297             KRefcountInit ( & f -> refcount, 1, "KNgcObj", "init", "kfg" );
298             memset ( & f -> buffer, 0, sizeof f -> buffer );
299             rc = KDataBufferMakeBytes ( & f -> buffer, 0 );
300             if ( rc == 0 )
301             {
302                 rc = KNgcObjInitFromString( f, line );
303                 if ( rc == 0 )
304                 {
305                     * ngc = f;
306                     return rc;
307                 }
308             }
309             KNgcObjWhack ( f );
310         }
311         * ngc = NULL;
312     }
313     return rc;
314 }
315 
316 
KNgcObjMakeFromFile(const KNgcObj ** ngc,const struct KFile * src)317 LIB_EXPORT rc_t CC KNgcObjMakeFromFile ( const KNgcObj **ngc, const struct KFile * src )
318 {
319     rc_t rc;
320     if ( ngc == NULL || src == NULL )
321         rc = RC ( rcKFG, rcFile, rcAllocating, rcParam, rcNull );
322     else
323     {
324         struct KNgcObj * f = calloc ( 1, sizeof * f );
325         if ( f == NULL )
326             rc = RC ( rcKFG, rcFile, rcAllocating, rcMemory, rcExhausted );
327         else
328         {
329             KRefcountInit ( & f -> refcount, 1, "KNgcObj", "init", "kfg" );
330             memset ( & f -> buffer, 0, sizeof f -> buffer );
331             rc = KDataBufferMakeBytes ( & f -> buffer, 0 );
332             if ( rc == 0 )
333             {
334                 rc = KNgcObjInitFromFile( f, src );
335                 if ( rc == 0 )
336                 {
337                     * ngc = f;
338                     return rc;
339                 }
340             }
341             KNgcObjWhack ( f );
342         }
343         * ngc = NULL;
344     }
345     return rc;
346 }
347 
348 
KNgcObjAddRef(const KNgcObj * self)349 LIB_EXPORT rc_t CC KNgcObjAddRef ( const KNgcObj *self )
350 {
351     if ( self != NULL )
352     {
353         switch ( KRefcountAdd( &self->refcount, "KNgcObj" ) )
354         {
355         case krefLimit:
356             return RC ( rcKFG, rcFile, rcAttaching, rcRefcount, rcExcessive );
357         case krefNegative:
358             return RC ( rcKFG, rcFile, rcAttaching, rcRefcount, rcInvalid );
359         }
360     }
361     return 0;
362 }
363 
364 
KNgcObjRelease(const KNgcObj * self)365 LIB_EXPORT rc_t CC KNgcObjRelease ( const KNgcObj *self )
366 {
367     if ( self != NULL )
368     {
369         switch ( KRefcountDrop ( & self -> refcount, "KNgcObj" ) )
370         {
371         case krefWhack:
372             return KNgcObjWhack ( ( KNgcObj * ) self );
373         case krefNegative:
374             return RC ( rcKFG, rcFile, rcReleasing, rcRefcount, rcInvalid );
375         }
376     }
377     return 0;
378 }
379 
380 
KNgcObjPrint(const KNgcObj * self,char * buffer,size_t buffer_size,size_t * written)381 LIB_EXPORT rc_t CC KNgcObjPrint ( const KNgcObj *self, char * buffer, size_t buffer_size, size_t * written )
382 {
383     rc_t rc = 0;
384     if ( self == NULL )
385         rc = RC ( rcKFG, rcFile, rcFormatting, rcSelf, rcNull );
386     else if ( buffer == NULL )
387         rc = RC ( rcKFG, rcFile, rcFormatting, rcParam, rcNull );
388     else
389         rc = string_printf( buffer, buffer_size, written,
390                             "Vers: '%S', ID:'%u', Key:'%S', Ticket:'%S', Desc:'%S'",
391                             &self -> version, self -> projectId, &self -> encryptionKey,
392                             &self -> downloadTicket, &self -> description );
393     return rc;
394 }
395 
396 
KNgcObjWriteToFile(const KNgcObj * self,struct KFile * dst)397 LIB_EXPORT rc_t CC KNgcObjWriteToFile ( const KNgcObj *self, struct KFile * dst )
398 {
399     rc_t rc = 0;
400     if ( self == NULL )
401         rc = RC ( rcKFG, rcFile, rcWriting, rcSelf, rcNull );
402     else if ( dst == NULL )
403         rc = RC ( rcKFG, rcFile, rcWriting, rcParam, rcNull );
404     else
405     {
406         size_t written_to_hdr;
407         char hdr [ 10 ];
408         rc = string_printf( hdr, sizeof hdr, &written_to_hdr, "ncbi_gap" );
409         if ( rc == 0 )
410         {
411             size_t written_to_file;
412             rc = KFileWriteAll ( dst, 0, hdr, written_to_hdr, &written_to_file );
413             if ( rc == 0 && written_to_hdr == written_to_file )
414             {
415                 struct KFile * sub;
416                 rc = KFileMakeSubUpdate ( &sub, dst, written_to_file, 4096 );
417                 if ( rc == 0 )
418                 {
419                     struct KFile * gzip;
420                     rc = KFileMakeGzipForWrite ( &gzip, sub );
421                     if ( rc == 0 )
422                     {
423                         size_t written_to_buffer;
424                         char buffer[ 1024 ];
425                         rc = string_printf( buffer, sizeof buffer, &written_to_buffer,
426                                             "version %S\n%u|%S|%S|%S",
427                                             &self -> version, self -> projectId, &self -> encryptionKey,
428                                             &self -> downloadTicket, &self -> description );
429                         if ( rc == 0 )
430                         {
431                             size_t written_to_gzip;
432                             rc = KFileWriteAll ( gzip, 0, buffer, written_to_buffer, &written_to_gzip );
433                         }
434                         KFileRelease ( gzip );
435                     }
436                     KFileRelease ( sub );
437                 }
438             }
439         }
440     }
441     return rc;
442 }
443 
444 
KNgcObjWriteKeyToFile(const KNgcObj * self,struct KFile * dst)445 LIB_EXPORT rc_t CC KNgcObjWriteKeyToFile ( const KNgcObj *self, struct KFile * dst )
446 {
447     rc_t rc = 0;
448     if ( self == NULL )
449         rc = RC ( rcKFG, rcFile, rcWriting, rcSelf, rcNull );
450     else if ( dst == NULL )
451         rc = RC ( rcKFG, rcFile, rcWriting, rcParam, rcNull );
452     else if ( self -> encryptionKey . addr == NULL || self -> encryptionKey . len < 1 )
453         rc = RC ( rcKFG, rcFile, rcWriting, rcParam, rcInvalid );
454     else
455     {
456         size_t written_to_file;
457         rc = KFileWriteAll ( dst, 0, self -> encryptionKey . addr, self -> encryptionKey . len, &written_to_file );
458     }
459     return rc;
460 }
461 
462 
KNgcObjGetProjectId(const KNgcObj * self,uint32_t * id)463 LIB_EXPORT rc_t CC KNgcObjGetProjectId ( const KNgcObj *self, uint32_t * id )
464 {
465     rc_t rc;
466     if ( id == NULL )
467         rc = RC ( rcKFG, rcFile, rcFormatting, rcParam, rcNull );
468     else
469     {
470         if ( self == NULL )
471             rc = RC ( rcKFG, rcFile, rcFormatting, rcSelf, rcNull );
472         else
473         {
474             * id = self -> projectId;
475             return 0;
476         }
477 
478         * id = 0;
479     }
480     return rc;
481 }
482 
KNgcObjGetProjectName(const KNgcObj * self,char * buffer,size_t buffer_size,size_t * written)483 LIB_EXPORT rc_t CC KNgcObjGetProjectName ( const KNgcObj *self, char * buffer, size_t buffer_size, size_t * written )
484 {
485     rc_t rc = 0;
486     if ( self == NULL )
487         rc = RC ( rcKFG, rcFile, rcFormatting, rcSelf, rcNull );
488     else if ( buffer == NULL )
489         rc = RC ( rcKFG, rcFile, rcFormatting, rcParam, rcNull );
490     else
491         rc = string_printf( buffer, buffer_size, written, "dbGaP-%u", self -> projectId );
492     return rc;
493 }
494 
KNgcObjGetTicket(const KNgcObj * self,char * buffer,size_t buffer_size,size_t * written)495 LIB_EXPORT rc_t CC KNgcObjGetTicket(const KNgcObj *self,
496     char * buffer, size_t buffer_size, size_t * written)
497 {
498     rc_t rc = 0;
499 
500     if (self == NULL)
501         rc = RC(rcKFG, rcFile, rcFormatting, rcSelf, rcNull);
502     else if (buffer == NULL)
503         rc = RC(rcKFG, rcFile, rcFormatting, rcParam, rcNull);
504     else
505         rc = string_printf(buffer, buffer_size, written, "%S",
506             &self->downloadTicket);
507 
508     return rc;
509 }
510 
KNgcObjGetEncryptionKey(const KNgcObj * self,char * buffer,size_t buffer_size,size_t * written)511 rc_t KNgcObjGetEncryptionKey(const KNgcObj *self,
512     char * buffer, size_t buffer_size, size_t * written)
513 {
514     rc_t rc = 0;
515 
516     if (self == NULL)
517         rc = RC(rcKFG, rcFile, rcFormatting, rcSelf, rcNull);
518     else if (buffer == NULL)
519         rc = RC(rcKFG, rcFile, rcFormatting, rcParam, rcNull);
520     else
521         rc = string_printf(buffer, buffer_size, written, "%S",
522             &self->encryptionKey);
523 
524     return rc;
525 }
526 
KNgcObjMakeFromCmdLine(const KNgcObj ** self)527 rc_t KNgcObjMakeFromCmdLine(const KNgcObj ** self) {
528     const char * ngc_file = NULL;
529 
530     assert(self);
531 
532     *self = NULL;
533 
534     ngc_file = KConfigGetNgcFile();
535 
536     if (ngc_file == NULL)
537         return 0;
538     else {
539         KDirectory * dir = NULL;
540         const KFile * f = NULL;
541 
542         rc_t rc = KDirectoryNativeDir(&dir);
543 
544         if (rc == 0)
545             rc = KDirectoryOpenFileRead(dir, &f, "%s", ngc_file);
546 
547         if (rc == 0)
548             rc = KNgcObjMakeFromFile(self, f);
549 
550         RELEASE(KFile, f);
551 
552         RELEASE(KDirectory, dir);
553 
554         return rc;
555     }
556 }
557