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 #include "path-priv.h" /* VPathMakeFmt */
27 #include "resolver-priv.h" /* rcResolver */
28 
29 #include <klib/log.h> /* klogInt */
30 #include <klib/rc.h> /* RC */
31 #include <klib/text.h> /* string_chr */
32 
33 #include <vfs/path.h> /* VPathMakeFmt */
34 
_GetAny(const rc_t * rc,const char ** start,const char * end,String * s,bool last)35 static rc_t _GetAny(const rc_t *rc, const char **start,
36     const char *end, String *s, bool last)
37 {
38     assert(rc && start);
39 
40     if (*rc != 0) {
41         return *rc;
42     }
43 
44     if (!last) {
45         const char *sep = string_chr ( *start, end - *start, '|' );
46         if ( sep == NULL ) {
47             return RC ( rcVFS, rcResolver, rcResolving, rcName, rcNotFound );
48         }
49 
50         StringInit(s, *start, sep - *start, ( uint32_t ) ( sep - *start ));
51 
52         *start = sep + 1;
53     }
54     else {
55         const char *sep = NULL;
56         for ( sep = end; sep > *start; -- sep ) {
57             switch ( sep [ -1 ] ) {
58                 case '\n':
59                 case '\r':
60                     continue;
61                 default:
62                     break;
63             }
64             break;
65         }
66 
67         StringInit ( s, *start, sep - *start, ( uint32_t ) ( sep - *start ) );
68     }
69 
70     return 0;
71 }
72 
_Get(const rc_t * rc,const char ** start,const char * end,String * s)73 static rc_t _Get(const rc_t *rc, const char **start,
74     const char *end, String *s)
75 {
76     return _GetAny(rc, start, end, s, false);
77 }
78 
_GetLast(const rc_t * rc,const char ** start,const char * end,String * s)79 static rc_t _GetLast(const rc_t *rc, const char **start,
80     const char *end, String *s)
81 {
82     return _GetAny(rc, start, end, s, true);
83 }
84 
85 typedef enum {
86     eBadObjectType,
87     eDbgap,
88     eProvisional,
89     eSrapub, /* eSra_run, */
90     eSrapub_files,
91     eSragap, /* eSra_run, */
92     eSra_source,
93     eSra_addon,
94     eRefseq,
95     eWgs,
96     eNa,
97 } EObjectType;
_StringToObjectType(const String * self)98 EObjectType _StringToObjectType(const String *self) {
99     if (self->size == 0) {
100         return eBadObjectType;
101     }
102     else {
103         String dbgap;
104         String provisional;
105         String srapub;
106         String srapub_files;
107         String sragap;
108         String sra_source;
109         String sra_addon;
110         String refseq;
111         String wgs;
112         String na;
113         CONST_STRING(&dbgap, "dbgap");
114         CONST_STRING(&provisional, "provisional");
115         CONST_STRING(&srapub, "srapub");
116         CONST_STRING(&srapub_files, "srapub_files");
117         CONST_STRING(&sragap, "sragap");
118         CONST_STRING(&sra_source, "sra-source");
119         CONST_STRING(&sra_addon, "sra-addon");
120         CONST_STRING(&refseq, "refseq");
121         CONST_STRING(&wgs, "wgs");
122         CONST_STRING(&na, "na");
123         if (StringEqual(self, &dbgap)) {
124             return eDbgap;
125         }
126         else if (StringEqual(self, &provisional)) {
127             return eProvisional;
128         }
129         else if (StringEqual(self, &srapub)) {
130             return eSrapub;
131         }
132         else if (StringEqual(self, &srapub_files)) {
133             return eSrapub_files;
134         }
135         else if (StringEqual(self, &sragap)) {
136             return eSragap;
137         }
138         else if (StringEqual(self, &sra_source)) {
139             return eSra_source;
140         }
141         else if (StringEqual(self, &sra_addon)) {
142             return eSra_addon;
143         }
144         else if (StringEqual(self, &refseq)) {
145             return eRefseq;
146         }
147         else if (StringEqual(self, &wgs)) {
148             return eWgs;
149         }
150         else if (StringEqual(self, &na)) {
151             return eNa;
152         }
153         else {
154             return eBadObjectType;
155         }
156     }
157 }
158 
_ProcessCode(EObjectType objectType,const String * object_id,const String * code,const String * download_ticket,const String * url,const String * message,const VPath ** path,const VPath ** mapping,const String * acc,const String * ticket)159 static rc_t _ProcessCode(EObjectType objectType, const String *object_id,
160     const String *code, const String *download_ticket,
161     const String *url, const String *message, const VPath **path,
162     const VPath **mapping, const String *acc, const String *ticket)
163 {
164     rc_t rc = 0;
165     KLogLevel lvl = 0;
166     uint32_t result_code = 0;
167     char *rslt_end = NULL;
168     assert(objectType && object_id && code && download_ticket && url);
169     if (code->size == 0)
170         return RC ( rcVFS, rcResolver, rcResolving, rcMessage, rcCorrupt );
171     result_code = strtoul ( code -> addr, & rslt_end, 10 );
172     if ( ( const char* ) rslt_end - code -> addr != code -> size )
173         return RC ( rcVFS, rcResolver, rcResolving, rcMessage, rcCorrupt );
174     switch ( result_code / 100 ) {
175         case 2: /* successful response but can only handle 200 */
176             if ( result_code == 200 ) { /* normal public response */
177                 if (download_ticket->size == 0) {
178                     rc = VPathMakeFmt ( ( VPath** ) path, "%S", url );
179                 }
180                 else { /* protected response */
181                     rc = VPathMakeFmt
182                         ((VPath**) path, "%S?tic=%S", url, download_ticket);
183                 }
184                 if ( rc == 0 ) {
185                     rc = VPathCheckFromNamesCGI ( * path, ticket, -1, mapping );
186                     if ( rc == 0 ) {
187                         if ( mapping == NULL )
188                             return 0;
189                         if (download_ticket->size != 0) {
190                             if (object_id->size != 0 && objectType == eSragap) {
191                                 rc = VPathMakeFmt ( ( VPath** ) mapping,
192                                     "ncbi-acc:%S?tic=%S",
193                                     object_id, download_ticket);
194                             }
195                             else {
196                                 if (object_id->size == 0)
197                                     return 0;
198                                 else
199                                     rc = VPathMakeFmt ( ( VPath** ) mapping,
200                                         "ncbi-file:%S?tic=%S",
201                                         object_id, download_ticket);
202                             }
203                         }
204                         else if (object_id->size != 0 && objectType == eSrapub)
205                         {
206                             rc = VPathMakeFmt ( ( VPath** ) mapping,
207                                 "ncbi-acc:%S", object_id );
208                         }
209                         else {
210                             if (object_id->size == 0)
211                                 return 0;
212                             else
213                                 rc = VPathMakeFmt ( ( VPath** ) mapping,
214                                     "ncbi-file:%S", object_id );
215                         }
216                         if ( rc == 0 )
217                             return 0;
218                     }
219                     VPathRelease ( * path );
220                     * path = NULL;
221                 }
222                 return rc;
223             }
224             lvl = klogInt;
225             rc = RC ( rcVFS, rcResolver, rcResolving, rcError, rcUnexpected );
226             break;
227         case 4: /* client error */
228             lvl = klogErr;
229             switch (result_code) {
230                 case 400:
231                     rc = RC(rcVFS, rcResolver, rcResolving,
232                         rcMessage, rcInvalid);
233                     break;
234                 case 401:
235                 case 403:
236                     rc = RC( rcVFS, rcResolver, rcResolving,
237                         rcQuery, rcUnauthorized);
238                     break;
239                 case 404: /* 404|no data :
240            If it is a real response then this assession is not found.
241            What if it is a DB failure? Will be retried if configured to do so?*/
242                 case 410:
243                     rc = RC( rcVFS, rcResolver, rcResolving,
244                         rcName, rcNotFound );
245                     break;
246                 default:
247                     rc = RC(rcVFS, rcResolver, rcResolving,
248                         rcError, rcUnexpected);
249             }
250             break;
251         case 5: /* server error */
252             lvl = klogSys;
253             switch (result_code) {
254                 case 503:
255                     rc = RC(rcVFS, rcResolver, rcResolving,
256                         rcDatabase, rcNotAvailable);
257                     break;
258                 case 504:
259                     rc = RC(rcVFS, rcResolver, rcResolving,
260                         rcTimeout, rcExhausted);
261                     break;
262                 default:
263                     rc = RC(rcVFS, rcResolver, rcResolving,
264                         rcError, rcUnexpected);
265             }
266             break;
267         case 1: /* informational response not much we can do here */
268         case 3: /* redirection:
269                         currently this is being handled by our request object */
270         default:
271             lvl = klogInt;
272             rc = RC ( rcVFS, rcResolver, rcResolving, rcError, rcUnexpected );
273             break;
274     }
275     /* log message to user */
276     PLOGERR(lvl, (lvl, rc,
277         "failed to resolve accession '$(acc)' - $(msg) ( $(code) )",
278         "acc=%S,msg=%S,code=%u", acc, message, result_code));
279     return rc;
280 }
281 
VResolverAlgParseResolverCGIResponse_3_0(const char * astart,size_t asize,const VPath ** path,const VPath ** mapping,const String * acc,const String * ticket)282 rc_t VResolverAlgParseResolverCGIResponse_3_0(const char *astart,
283     size_t asize, const VPath **path, const VPath **mapping,
284     const String *acc, const String *ticket)
285 {
286     const char *start = astart;
287     const char *end = start + asize;
288     String object_type, object_id, size, date,
289         md5, download_ticket, url, code, message;
290     EObjectType objectType = eBadObjectType;
291 
292     rc_t rc = 0;
293     rc = _Get    (&rc, &start, end, &object_type);
294     rc = _Get    (&rc, &start, end, &object_id);
295     rc = _Get    (&rc, &start, end, &size);
296     rc = _Get    (&rc, &start, end, &date);
297     rc = _Get    (&rc, &start, end, &md5);
298     rc = _Get    (&rc, &start, end, &download_ticket);
299     rc = _Get    (&rc, &start, end, &url);
300     rc = _Get    (&rc, &start, end, &code);
301     rc = _GetLast(&rc, &start, end, &message);
302     if (rc != 0) {
303         return rc;
304     }
305 
306     objectType = _StringToObjectType(&object_type);
307     if (objectType == eBadObjectType) {
308         return RC ( rcVFS, rcResolver, rcResolving, rcMessage, rcCorrupt );
309     }
310 
311     /* compare acc to accession or obj_id */
312     assert(acc);
313     if ( ! StringEqual ( & object_id, acc ) && objectType != eDbgap )
314         return RC ( rcVFS, rcResolver, rcResolving, rcMessage, rcCorrupt );
315 
316     /* compare ticket
317        currently this makes sense with 1 request from a known workspace */
318     if (download_ticket.size != 0) {
319         if ( ticket == NULL || ! StringEqual ( & download_ticket, ticket ) )
320             return RC ( rcVFS, rcResolver, rcResolving, rcMessage, rcCorrupt );
321     }
322 
323     /* get the result code */
324     return _ProcessCode(objectType, &object_id, &code,
325         &download_ticket, &url, &message, path, mapping, acc, ticket);
326 }
327