1 /* $Id: urlquery.c,v 6.69 2016/10/20 16:25:39 lavr Exp $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *            National Center for Biotechnology Information (NCBI)
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government do not place any restriction on its use or reproduction.
13  *  We would, however, appreciate having the NCBI and the author cited in
14  *  any work or product based on this material
15  *
16  *  Although all reasonable efforts have been taken to ensure the accuracy
17  *  and reliability of the software and data, the NLM and the U.S.
18  *  Government do not and cannot warrant the performance or results that
19  *  may be obtained by using this software or data. The NLM and the U.S.
20  *  Government disclaim all warranties, express or implied, including
21  *  warranties of performance, merchantability or fitness for any particular
22  *  purpose.
23  *
24  * ===========================================================================
25  *
26  * File Name:  urlquery.c
27  *
28  * Author:  Jonathan Kans
29  *
30  * Version Creation Date:   4/16/98
31  *
32  * $Revision: 6.69 $
33  *
34  * File Description:
35  *
36  * Modifications:
37  * --------------------------------------------------------------------------
38  *
39  * ==========================================================================
40  */
41 
42 #include "asnbuild.h"
43 #include <urlquery.h>
44 
45 
46 #ifdef OS_MAC
47 #include <Events.h>
48 
QUERY_WaitForNextMacEvent(void)49 NLM_EXTERN void QUERY_WaitForNextMacEvent (void)
50 {
51   EventRecord  currEvent;
52 
53   WaitNextEvent (0, &currEvent, 0, NULL);
54 }
55 #endif
56 
57 
58 /* Set HTTP user header */
x_SetupUserHeader(SConnNetInfo * net_info,const char * appName,EMIME_Type type,EMIME_SubType subtype,EMIME_Encoding encoding)59 static void x_SetupUserHeader (
60   SConnNetInfo*  net_info,
61   const char*    appName,
62   EMIME_Type     type,
63   EMIME_SubType  subtype,
64   EMIME_Encoding encoding
65 )
66 {
67   const char* userAgentName = NULL;
68   char        user_header [MAX_CONTENT_TYPE_LEN + 80];
69 
70   /* content-type if specified */
71   if ( MIME_ComposeContentTypeEx (type, subtype, encoding,
72                                   user_header, MAX_CONTENT_TYPE_LEN) ) {
73     ConnNetInfo_OverrideUserHeader (net_info, user_header);
74   }
75 
76   /* allow the user to specify a prog. name, otherwise get it from elsewhere */
77   if (StringHasNoText (appName)) {
78     const char* progName = GetProgramName ();
79     if (StringHasNoText (progName)) {
80       char path [PATH_MAX];
81       Nlm_ProgramPath (path, sizeof (path));
82       userAgentName = StringRChr (path, DIRDELIMCHR);
83       if (userAgentName)
84         ++userAgentName;
85     } else
86       userAgentName = progName;
87   } else
88     userAgentName = appName;
89   if (StringDoesHaveText (userAgentName)) {
90     sprintf (user_header, "User-Agent: %.80s\r\n", userAgentName);
91     ConnNetInfo_ExtendUserHeader (net_info, user_header);
92   }
93 }
94 
95 
QUERY_OpenUrlQuery(const char * host_machine,Nlm_Uint2 host_port,const char * host_path,const char * arguments,const char * appName,Nlm_Uint4 timeoutsec,EMIME_Type type,EMIME_SubType subtype,EMIME_Encoding encoding,THTTP_Flags flags)96 NLM_EXTERN CONN QUERY_OpenUrlQuery (
97   const char*     host_machine,
98   Nlm_Uint2       host_port,
99   const char*     host_path,
100   const char*     arguments,
101   const char*     appName,
102   Nlm_Uint4       timeoutsec,
103   EMIME_Type      type,
104   EMIME_SubType   subtype,
105   EMIME_Encoding  encoding,
106   THTTP_Flags     flags
107 )
108 {
109   CONN           conn;
110   CONNECTOR      connector;
111   SConnNetInfo*  net_info;
112   EIO_Status     status;
113 
114   if (StringHasNoText (host_path))
115     return NULL;
116 
117   /* fill in connection info fields and create the connection */
118   net_info = ConnNetInfo_Create (0);
119   ASSERT ( net_info );
120 
121   /* explicit setting to https is needed for CCC-23 */
122   net_info->scheme = eURL_Https;
123 
124   x_SetupUserHeader (net_info, appName, type, subtype, encoding);
125 
126   if (StringDoesHaveText (host_machine)) {
127     StringNCpy_0 (net_info->host, host_machine, sizeof (net_info->host));
128   }
129   if ( host_port ) {
130     net_info->port = host_port;
131   }
132   StringNCpy_0 (net_info->path, host_path, sizeof (net_info->path));
133   if (StringDoesHaveText (arguments)) {
134     StringNCpy_0 (net_info->args, arguments, sizeof (net_info->args));
135   }
136 
137   if (timeoutsec == (Nlm_Uint4)(-1L)) {
138     net_info->timeout  = kInfiniteTimeout;
139   } else if ( timeoutsec ) {
140     net_info->tmo.sec  = timeoutsec;
141     net_info->tmo.usec = 0;
142     net_info->timeout  = &net_info->tmo;
143   }
144 
145   connector = HTTP_CreateConnector (net_info, NULL, flags);
146 
147   ConnNetInfo_Destroy (net_info);
148 
149   if (connector == NULL) {
150     ErrPostEx (SEV_ERROR, 0, 0, "QUERY_OpenUrlQuery failed in HTTP_CreateConnector");
151     conn = NULL;
152   } else if ((status = CONN_Create (connector, &conn)) != eIO_Success) {
153     ErrPostEx (SEV_ERROR, 0, 0, "QUERY_OpenUrlQuery failed in CONN_Create: %s",
154               IO_StatusStr (status));
155     ASSERT (conn == NULL);
156   }
157 
158   return conn;
159 }
160 
161 
QUERY_OpenServiceQueryEx(const char * service,const char * parameters,Nlm_Uint4 timeoutsec,const char * arguments)162 NLM_EXTERN CONN QUERY_OpenServiceQueryEx (
163   const char* service,
164   const char* parameters,
165   Nlm_Uint4   timeoutsec,
166   const char* arguments
167 )
168 {
169   CONN           conn;
170   CONNECTOR      connector;
171   SConnNetInfo*  net_info;
172   size_t         n_written;
173   EIO_Status     status;
174 
175   /* fill in connection info fields and create the connection */
176   net_info = ConnNetInfo_Create (service);
177   ASSERT ( net_info );
178 
179   /* let the user agent be set with a program name */
180   x_SetupUserHeader (net_info,
181                      NULL, eMIME_T_Undefined, eMIME_Undefined, eENCOD_None);
182 
183   if (timeoutsec == (Nlm_Uint4)(-1L)) {
184     net_info->timeout  = kInfiniteTimeout;
185   } else if ( timeoutsec ) {
186     net_info->tmo.sec  = timeoutsec;
187     net_info->tmo.usec = 0;
188     net_info->timeout  = &net_info->tmo;
189   }
190 
191   ConnNetInfo_PostOverrideArg (net_info, arguments, 0);
192 
193   connector = SERVICE_CreateConnectorEx (service, fSERV_Any, net_info, 0);
194 
195   ConnNetInfo_Destroy (net_info);
196 
197   if (connector == NULL) {
198     ErrPostEx (SEV_ERROR, 0, 0, "QUERY_OpenServiceQuery failed in SERVICE_CreateConnectorEx");
199     conn = NULL;
200   } else if ((status = CONN_Create (connector, &conn)) != eIO_Success) {
201     ErrPostEx (SEV_ERROR, 0, 0, "QUERY_OpenServiceQuery failed in CONN_Create:"
202                " %s", IO_StatusStr (status));
203     ASSERT (conn == NULL);
204   } else if (StringDoesHaveText (parameters)) {
205     status = CONN_Write (conn, parameters, StringLen (parameters),
206                          &n_written, eIO_WritePersist);
207     if (status != eIO_Success) {
208       ErrPostEx (SEV_ERROR, 0, 0, "QUERY_OpenServiceQuery failed to write service parameters in CONN_Write: %s", IO_StatusStr (status));
209       CONN_Close (conn);
210       conn = NULL;
211     }
212   }
213 
214   return conn;
215 }
216 
217 
QUERY_OpenServiceQuery(const char * service,const char * parameters,Nlm_Uint4 timeoutsec)218 NLM_EXTERN CONN QUERY_OpenServiceQuery (
219   const char* service,
220   const char* parameters,
221   Nlm_Uint4   timeoutsec
222 )
223 {
224   return QUERY_OpenServiceQueryEx (service, parameters, timeoutsec, 0);
225 }
226 
227 
QUERY_SendQuery(CONN conn)228 NLM_EXTERN EIO_Status QUERY_SendQuery (
229   CONN conn
230 )
231 
232 {
233   static const STimeout kPollTimeout = { 0 };
234   EIO_Status            status;
235 
236   if (conn == NULL) return eIO_Closed;
237 
238   /* flush buffer, sending query, without waiting for response */
239   status = CONN_Wait (conn, eIO_Read, &kPollTimeout);
240   return status == eIO_Timeout ? eIO_Success : status;
241 }
242 
243 
244 #define URL_QUERY_BUFLEN  4096
245 
QUERY_CopyFileToQuery(CONN conn,FILE * fp)246 NLM_EXTERN void QUERY_CopyFileToQuery (
247   CONN conn,
248   FILE *fp
249 )
250 {
251   Char         buffer [URL_QUERY_BUFLEN + 4];
252   size_t       ct;
253   size_t       n_written;
254   EIO_Status   status;
255 
256   if (conn == NULL || fp == NULL) return;
257 
258   while ((ct = FileRead (buffer, 1, URL_QUERY_BUFLEN, fp)) > 0) {
259     status = CONN_Write (conn, (const void *) buffer, ct,
260                          &n_written, eIO_WritePersist);
261     if (status != eIO_Success) break;
262   }
263 }
264 
265 
QUERY_CopyResultsToFile(CONN conn,FILE * fp)266 NLM_EXTERN void QUERY_CopyResultsToFile (
267   CONN conn,
268   FILE *fp
269 )
270 {
271   Char         buffer [URL_QUERY_BUFLEN + 4];
272   size_t       n_read;
273   EIO_Status   status;
274 
275   if (conn == NULL || fp == NULL) return;
276 
277   do {
278     status = CONN_Read (conn, buffer, URL_QUERY_BUFLEN, &n_read, eIO_ReadPlain);
279     if ( n_read ) {
280       FileWrite (buffer, 1, n_read, fp);
281     }
282   } while (status == eIO_Success);
283 }
284 
285 
QUERY_CopyResultsToString(CONN conn)286 NLM_EXTERN CharPtr QUERY_CopyResultsToString (
287   CONN conn
288 )
289 
290 {
291   Char        buffer [URL_QUERY_BUFLEN + 4];
292   ValNodePtr  head = NULL, last = NULL, vnp;
293   size_t      n_read;
294   EIO_Status  status;
295   CharPtr     str;
296 
297   if (conn == NULL) return NULL;
298 
299   do {
300     status = CONN_Read (conn, buffer, URL_QUERY_BUFLEN, &n_read, eIO_ReadPlain);
301     if ( n_read ) {
302       buffer [n_read] = '\0';
303       vnp = ValNodeCopyStr (&last, 0, buffer);
304       if (head == NULL) {
305         head = vnp;
306       }
307       last = vnp;
308     }
309   } while (status == eIO_Success);
310 
311   if (head == NULL) return NULL;
312 
313   str = ValNodeMergeStrs (head);
314   ValNodeFreeData (head);
315 
316   return str;
317 }
318 
319 
AsnIoConnWrite(Pointer ptr,CharPtr buf,Nlm_Uint2 count)320 static Nlm_Int2 LIBCALL AsnIoConnWrite (
321   Pointer ptr,
322   CharPtr buf,
323   Nlm_Uint2 count
324 )
325 {
326   size_t        bytes;
327   AsnIoConnPtr  aicp;
328 
329   aicp = (AsnIoConnPtr) ptr;
330   if (aicp == NULL || aicp->conn == NULL) return 0;
331   CONN_Write (aicp->conn, buf, (size_t) count, &bytes, eIO_WritePersist);
332   return (Nlm_Int2) bytes;
333 }
334 
335 
AsnIoConnRead(Pointer ptr,CharPtr buf,Nlm_Uint2 count)336 static Nlm_Int2 LIBCALL AsnIoConnRead (
337   Pointer ptr,
338   CharPtr buf,
339   Nlm_Uint2 count
340 )
341 {
342   size_t        bytes;
343   AsnIoConnPtr  aicp;
344 
345   aicp = (AsnIoConnPtr) ptr;
346   if (aicp == NULL || aicp->conn == NULL) return 0;
347   CONN_Read (aicp->conn, buf, (size_t) count, &bytes, eIO_ReadPlain);
348   return (Nlm_Int2) bytes;
349 }
350 
351 
QUERY_AsnIoConnOpen(const char * mode,CONN conn)352 NLM_EXTERN AsnIoConnPtr QUERY_AsnIoConnOpen (
353   const char* mode,
354   CONN conn
355 )
356 {
357   Int1          type;
358   AsnIoConnPtr  aicp;
359 
360   if (strcmp(mode, "r") == 0)
361     type = (ASNIO_IN | ASNIO_TEXT);
362   else if (strcmp(mode, "rb") == 0)
363     type = (ASNIO_IN | ASNIO_BIN);
364   else if (strcmp(mode, "w") == 0)
365     type = (ASNIO_OUT | ASNIO_TEXT);
366   else if (strcmp(mode, "wb") == 0)
367     type = (ASNIO_OUT | ASNIO_BIN);
368   else {
369     AsnIoErrorMsg (NULL, 81, mode);
370     return NULL;
371   }
372 
373   aicp = (AsnIoConnPtr) MemNew (sizeof (AsnIoConn));
374   aicp->aip = AsnIoNew (type, NULL, (Pointer) aicp, AsnIoConnRead, AsnIoConnWrite);
375   aicp->conn = conn;
376   return aicp;
377 }
378 
379 
QUERY_AsnIoConnClose(AsnIoConnPtr aicp)380 NLM_EXTERN AsnIoConnPtr QUERY_AsnIoConnClose (
381   AsnIoConnPtr aicp
382 )
383 {
384   if (aicp == NULL) return NULL;
385   AsnIoClose (aicp->aip);
386   aicp->conn = NULL;
387   return (AsnIoConnPtr) MemFree (aicp);
388 }
389 
390 
391 typedef struct SQueueTag {
392   CONN                conn;
393   QueryResultProc     resultproc;
394   Nlm_VoidPtr         userdata;
395   Nlm_Boolean         closeConn;
396   Nlm_Boolean         protect;
397   struct SQueueTag*   next;
398 } SConnQueue, PNTR QueuePtr;
399 
400 
QUERY_AddToQueue(QUEUE * queue,CONN conn,QueryResultProc resultproc,Nlm_VoidPtr userdata,Nlm_Boolean closeConn)401 NLM_EXTERN void QUERY_AddToQueue (
402   QUEUE* queue,
403   CONN conn,
404   QueryResultProc resultproc,
405   Nlm_VoidPtr userdata,
406   Nlm_Boolean closeConn
407 )
408 {
409   QueuePtr       cqp;
410   QueuePtr PNTR  qptr;
411 
412   ASSERT (queue != NULL);
413 
414   if (conn == NULL || resultproc == NULL) return;
415 
416   /* allocate queue element */
417 
418   cqp = (QueuePtr) MemNew (sizeof (SConnQueue));
419   if (cqp == NULL) return;
420 
421   cqp->conn = conn;
422   cqp->resultproc = resultproc;
423   cqp->userdata = userdata;
424   cqp->closeConn = closeConn;
425   cqp->protect = FALSE;
426 
427   /* add to polling queue */
428 
429   for (qptr = (QueuePtr PNTR) queue;  *qptr != NULL;  qptr = &(*qptr)->next);
430   *qptr = cqp;
431 }
432 
433 
QUERY_RemoveFromQueue(QUEUE * queue,CONN conn)434 static void QUERY_RemoveFromQueue (
435   QUEUE* queue,
436   CONN conn
437 )
438 {
439   QueuePtr       curr;
440   QueuePtr       next;
441   QueuePtr PNTR  prev;
442   QueuePtr PNTR  qptr;
443 
444   qptr = (QueuePtr PNTR) queue;
445   if (qptr == NULL || *qptr == NULL || conn == NULL) return;
446 
447   prev = qptr;
448   curr = *qptr;
449 
450   while (curr != NULL) {
451     next = curr->next;
452     if (curr->conn == conn) {
453       *prev = next;
454       curr->next = NULL;
455       MemFree (curr);
456     } else {
457       prev = &(curr->next);
458     }
459     curr = next;
460   }
461 }
462 
463 
QUERY_CheckQueue(QUEUE * queue)464 NLM_EXTERN Nlm_Int4 QUERY_CheckQueue (
465   QUEUE* queue
466 )
467 {
468   static const STimeout kPollTimeout = { 0 };
469   Nlm_Int4       count = 0;
470   QueuePtr       curr;
471   QueuePtr       next;
472   QueuePtr PNTR  qptr;
473   EIO_Status     status;
474 
475   qptr = (QueuePtr PNTR) queue;
476   if (qptr == NULL || *qptr == NULL) return 0;
477 
478   curr = *qptr;
479 
480   while (curr != NULL) {
481     next = curr->next;
482 
483     if (curr->conn != NULL && (! curr->protect)) {
484       status = CONN_Wait (curr->conn, eIO_Read, &kPollTimeout);
485 
486       if (status == eIO_Success || status == eIO_Closed) {
487         /* protect against reentrant calls if resultproc is GUI and processes timer */
488         curr->protect = TRUE;
489         if (curr->resultproc != NULL) {
490           /* result could eventually be used to reconnect on timeout */
491           curr->resultproc (curr->conn, curr->userdata, status);
492         }
493         if (curr->closeConn) {
494           CONN_Close (curr->conn);
495         }
496         QUERY_RemoveFromQueue (queue, curr->conn);
497       } else {
498         count++;
499       }
500     }
501 
502     curr = next;
503   }
504 
505   return count;
506 }
507 
508 
QUERY_QueueSize(QUEUE queue)509 NLM_EXTERN Nlm_Int4 QUERY_QueueSize (
510   QUEUE queue
511 )
512 {
513   Nlm_Int4  count = 0;
514   QueuePtr  qptr;
515 
516   for (qptr = (QueuePtr) queue;  qptr;  qptr = qptr->next) {
517     count++;
518   }
519 
520   return count;
521 }
522 
523 
QUERY_CloseQueue(QUEUE * queue)524 NLM_EXTERN void QUERY_CloseQueue (
525   QUEUE* queue
526 )
527 {
528   QueuePtr       curr;
529   QueuePtr       next;
530   QueuePtr PNTR  qptr;
531 
532   qptr = (QueuePtr PNTR) queue;
533   if (qptr == NULL || *qptr == NULL) return;
534 
535   curr = *qptr;
536 
537   while (curr != NULL) {
538     next = curr->next;
539 
540     if (curr->conn != NULL) {
541       CONN_Close (curr->conn);
542       QUERY_RemoveFromQueue (queue, curr->conn);
543     }
544 
545     curr = next;
546   }
547 }
548 
QUERY_SendSimpleUrlQuery(CharPtr machine,Uint2 port,CharPtr path,CharPtr prefix,CharPtr arguments,CharPtr suffix,CharPtr post)549 static CONN QUERY_SendSimpleUrlQuery (
550   CharPtr machine,
551   Uint2 port,
552   CharPtr path,
553   CharPtr prefix,
554   CharPtr arguments,
555   CharPtr suffix,
556   CharPtr post
557 )
558 
559 {
560   Char     ch;
561   CONN     conn = NULL;
562   size_t   len;
563   size_t   n_written;
564   CharPtr  ptr;
565   CharPtr  query = NULL;
566 
567   len = StringLen (prefix) + StringLen (arguments) + StringLen (suffix);
568   if (len > 0) {
569     query = (CharPtr) MemNew (len + 2);
570     if (query != NULL) {
571       StringCpy (query, prefix);
572       StringCat (query, arguments);
573       StringCat (query, suffix);
574       ptr = query;
575       ch = *ptr;
576       while (ch != '\0') {
577         if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
578           *ptr = '+';
579         }
580         ptr++;
581         ch = *ptr;
582       }
583     }
584   }
585 
586   if (StringHasNoText (post)) {
587     post = NULL;
588   }
589 
590   if (post != NULL) {
591     conn = QUERY_OpenUrlQuery (machine, port, path, query, NULL, 30,
592                                eMIME_T_Application, eMIME_WwwForm, eENCOD_Url,
593                                fHTTP_Flushable);
594   } else {
595     conn = QUERY_OpenUrlQuery (machine, port, path, query, NULL, 30,
596                                eMIME_T_Undefined, eMIME_Undefined, eENCOD_None,
597                                fHTTP_Flushable);
598   }
599 
600   MemFree (query);
601 
602   if (conn == NULL) return NULL;
603 
604   if (post != NULL) {
605     CONN_Write (conn, (const void *) post, StringLen (post), &n_written, eIO_WritePersist);
606   }
607 
608   QUERY_SendQuery (conn);
609 
610   return conn;
611 }
612 
QUERY_UrlSynchronousQuery(CharPtr machine,Uint2 port,CharPtr path,CharPtr prefix,CharPtr arguments,CharPtr suffix,CharPtr post)613 NLM_EXTERN CharPtr QUERY_UrlSynchronousQuery (
614   CharPtr machine,
615   Uint2 port,
616   CharPtr path,
617   CharPtr prefix,
618   CharPtr arguments,
619   CharPtr suffix,
620   CharPtr post
621 )
622 
623 {
624   CONN        conn;
625   time_t      currtime, starttime, max = 0;
626   EIO_Status  status;
627   CharPtr     str = NULL;
628   STimeout    timeout;
629 
630   conn = QUERY_SendSimpleUrlQuery (machine, port, path, prefix, arguments, suffix, post);
631 
632   if (conn == NULL) return NULL;
633 
634 #ifdef OS_MAC
635   timeout.sec = 0;
636   timeout.usec = 1000;
637 #else
638   timeout.sec = 100;
639   timeout.usec = 0;
640 #endif
641 
642   starttime = GetSecs ();
643   while (max < 300 && (status = CONN_Wait (conn, eIO_Read, &timeout)) == eIO_Timeout) {
644     currtime = GetSecs ();
645     max = currtime - starttime;
646   }
647 
648   if (status == eIO_Success) {
649     str = QUERY_CopyResultsToString (conn);
650   }
651 
652   CONN_Close (conn);
653 
654   return str;
655 }
656 
QUERY_UrlAsynchronousQuery(CharPtr machine,Uint2 port,CharPtr path,CharPtr prefix,CharPtr arguments,CharPtr suffix,CharPtr post,QUEUE * queue,QueryResultProc resultproc,VoidPtr userdata)657 NLM_EXTERN Boolean QUERY_UrlAsynchronousQuery (
658   CharPtr machine,
659   Uint2 port,
660   CharPtr path,
661   CharPtr prefix,
662   CharPtr arguments,
663   CharPtr suffix,
664   CharPtr post,
665   QUEUE* queue,
666   QueryResultProc resultproc,
667   VoidPtr userdata
668 )
669 
670 {
671   CONN  conn;
672 
673   conn = QUERY_SendSimpleUrlQuery (machine, port, path, prefix, arguments, suffix, post);
674 
675   if (conn == NULL) return FALSE;
676 
677   QUERY_AddToQueue (queue, conn, resultproc, userdata, TRUE);
678 
679   return TRUE;
680 }
681 
682