1 /*
2 * GTimer
3 *
4 * Copyright:
5 * (C) 1999 Craig Knudsen, cknudsen@cknudsen.com
6 * See accompanying file "COPYING".
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place,
21 * Suite 330, Boston, MA 02111-1307, USA
22 *
23 * Description:
24 * Helps you keep track of time spent on different tasks.
25 *
26 * Author:
27 * Craig Knudsen, cknudsen@cknudsen.com, http://www.cknudsen.com
28 *
29 * Home Page:
30 * http://www.cknudsen.com/gtimer/
31 *
32 * History:
33 * 19-May-1999 Created (based on an 1995 app I wrote)
34 *
35 ****************************************************************************/
36
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <ctype.h>
42 #include <errno.h>
43 #include <sys/time.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #ifdef AIX
47 #include <sys/select.h>
48 #endif
49 #include <sys/ioctl.h>
50 #ifdef WIN32
51 #include <winsock.h> /* winsock API */
52 #else
53 #include <unistd.h>
54 #include <netdb.h> /* for gethostbyaddr() */
55 #include <sys/socket.h>
56 #include <netinet/in.h>
57 #endif
58 #include <fcntl.h>
59 #include <time.h>
60
61 #include "project.h"
62 #include "task.h"
63 #include "gtimer.h"
64
65 #include "tcpt.h"
66 #include "http.h"
67
68 #ifdef GTIMER_MEMDEBUG
69 #include "memdebug/memdebug.h"
70 #endif
71
72
73 static char *my_strtok (
74 #ifndef _NO_PROTO
75 char *ptr1, char *tok
76 #endif
77 );
78
79
80 /*
81 ** Max number of bytes to read at once.
82 */
83 #define MAX_READ_SIZE 1024
84
85 /*
86 ** Structure for states.
87 */
88 typedef struct _httpRequest {
89 sockfd connection; /* connection to server */
90 char *request; /* text message to send */
91 httpError (*read_function)(); /* read function */
92 #ifdef _NO_PROTO
93 void (*callback)(); /* callback when results received */
94 #else
95 void (*callback)(char *); /* callback when results received */
96 #endif
97 #ifdef _NO_PROTO
98 void (*gen_callback)(); /* callback when results received */
99 #else
100 void (*gen_callback)(char *,int); /* callback when results received */
101 #endif
102 int sent; /* has request been sent */
103 char *data_read; /* data that was read */
104 int data_len; /* size of above data */
105 int content_length; /* length according to header */
106 int pass; /* no. reads done */
107 } httpRequest;
108
109 #define MAX_REQUESTS_QUEUED 2048
110
111 static char http_other_error[256];
112 static httpRequest *requests[MAX_REQUESTS_QUEUED];
113 static int num_requests = 0;
114 static char *http_proxy = NULL;
115 static char http_proxy_string[1024];
116 static int http_proxy_port;
117
118
user_agent()119 static char *user_agent () {
120 static char ret[100];
121
122 sprintf ( ret, "GTimer/%s", GTIMER_VERSION );
123 return ( ret );
124 }
125
126 /*
127 ** Local functions.
128 */
129 static char *httpTruncateHeader (
130 #ifndef _NO_PROTO
131 char *text,
132 int *len
133 #endif
134 );
135 static httpError httpReadGet (
136 #ifndef _NO_PROTO
137 httpRequest *request
138 #endif
139 );
140 static httpError httpReadGetArticles (
141 #ifndef _NO_PROTO
142 httpRequest *request
143 #endif
144 );
145 static httpError httpReadGetAds (
146 #ifndef _NO_PROTO
147 httpRequest *request
148 #endif
149 );
150 static httpError httpReadList (
151 #ifndef _NO_PROTO
152 httpRequest *request
153 #endif
154 );
155 static httpError httpReadGetUserId (
156 #ifndef _NO_PROTO
157 httpRequest *request
158 #endif
159 );
160 static httpError httpReadGetSessionId (
161 #ifndef _NO_PROTO
162 httpRequest *request
163 #endif
164 );
165 static httpError httpSendRequest (
166 #ifndef _NO_PROTO
167 httpRequest *request
168 #endif
169 );
170 static char *encode_for_use_in_url (
171 #ifndef _NO_PROTO
172 char *str
173 #endif
174 );
175
176
177
178
179
180 /*
181 ** URL-encode a string
182 ** See RFC 1738 (http://andrew2.andrew.cmu.edu/rfc/rfc1738.html)
183 ** Caller should free result.
184 */
encode_for_use_in_url(str)185 static char *encode_for_use_in_url ( str )
186 char *str;
187 {
188 char *ret, *ptr1, *ptr2, *ptr3;
189 static char unsafe_chars[] = {
190 ' ', '{', '}', '|', '/', '\\', '^', '~', '[', ']', '`', '%', '<', '>', '"', '#', ')', '(', '\0',
191 };
192 int unsafe;
193
194 /* just to be safe... */
195 ret = (char *) malloc ( strlen ( str ) * 3 + 1 );
196
197 for ( ptr1 = str, ptr2 = ret; *ptr1 != '\0'; ptr1++ ) {
198 unsafe = 0;
199 /* is unsafe??? */
200 for ( ptr3 = unsafe_chars; *ptr3 != '\0'; ptr3++ ) {
201 if ( *ptr3 == *ptr1 ) {
202 unsafe = 1;
203 break;
204 }
205 }
206 /*
207 if ( ! unsafe ) {
208 if ( ( (unsigned int)*ptr1 < 0x80 || (unsigned int)*ptr1 > 0xFF ) ||
209 ( (unsigned int)*ptr1 >= 0x00 && (unsigned int)*ptr1 <= 0x1F ) ||
210 ( (unsigned int)*ptr1 == 0x7F ) )
211 unsafe = 1;
212 }
213 */
214 if ( unsafe ) {
215 sprintf ( ptr2, "%%%02X", (unsigned int)*ptr1 );
216 ptr2 += 3;
217 }
218 else {
219 *ptr2 = *ptr1;
220 ptr2++;
221 }
222 }
223 *ptr2 = '\0';
224
225 return ( ret );
226 }
227
228
229
httpEnableProxy(server,port)230 void httpEnableProxy ( server, port )
231 char *server;
232 int port;
233 {
234 if ( http_proxy )
235 free ( http_proxy );
236 http_proxy = (char *) malloc ( strlen ( server ) + 1 );
237 strcpy ( http_proxy, server );
238 http_proxy_port = port;
239 }
240
241
httpDisableProxy()242 void httpDisableProxy ()
243 {
244 if ( http_proxy )
245 free ( http_proxy );
246 http_proxy_string[0] = '\0';
247 http_proxy = NULL;
248 }
249
250
251 /*
252 ** Put a request onto the end of the queue.
253 ** Current request is always reqeusts[0].
254 */
httpEnqueueRequest(connection,msg,read_function,callback,gen_callback)255 httpError httpEnqueueRequest ( connection, msg, read_function, callback,
256 gen_callback )
257 sockfd connection;
258 char *msg;
259 #ifdef _NO_PROTO
260 httpError (*read_function)();
261 void (*callback)();
262 void (*gen_callback)();
263 #else
264 httpError (*read_function)(sockfd);
265 void (*callback)(char *);
266 void (*gen_callback)(char *,int);
267 #endif
268 {
269 httpRequest *request;
270
271 if ( num_requests >= MAX_REQUESTS_QUEUED - 1 ) {
272 fprintf ( stderr, "Too many errors...\n" );
273 return ( HTTP_TOO_MANY_REQUESTS );
274 }
275
276 /* put request at end of queue. */
277 request = (httpRequest *) malloc ( sizeof ( httpRequest ) );
278 memset ( request, '\0', sizeof ( httpRequest ) );
279 if ( msg ) {
280 request->request = (char *) malloc ( strlen ( msg ) + 1 );
281 strcpy ( request->request, msg );
282 request->connection = connection;
283 }
284 else {
285 /* NULL msg indicates no message to send */
286 request->request = NULL;
287 request->sent = 1;
288 }
289 request->connection = connection;
290 request->read_function = read_function;
291 request->callback = callback;
292 request->gen_callback = gen_callback;
293 request->data_read = NULL;
294 requests[num_requests++] = request;
295
296 /* now send the request if this is the only request */
297 if ( num_requests == 1 ) {
298 httpSendRequest ( requests[0] );
299 }
300
301 return ( HTTP_NO_ERROR );
302 }
303
304
305
306 /*
307 ** Send a request to the server.
308 ** This should not be called directly, only from httpDequeueRequest and
309 ** httpEnqueueRequest.
310 */
httpSendRequest(request)311 static httpError httpSendRequest ( request )
312 httpRequest *request;
313 {
314 int rval;
315
316 if ( ! request->sent ) {
317 rval = send ( request->connection, request->request,
318 strlen ( request->request ), 0 );
319
320 request->sent = 1;
321
322 if ( rval >= 0 )
323 return ( HTTP_NO_ERROR );
324 else
325 return ( HTTP_SOCKET_ERROR );
326 }
327 else
328 return ( HTTP_NO_ERROR );
329 }
330
331
332
333 /*
334 ** Remove the most recent request from the queue.
335 ** It is not an error to call this function with nothing in the queue.
336 */
httpDequeueRequest()337 httpError httpDequeueRequest ()
338 {
339 int loop;
340
341 if ( num_requests ) {
342 if ( requests[0]->data_read )
343 free ( requests[0]->data_read );
344 if ( requests[0]->request )
345 free ( requests[0]->request );
346 free ( requests[0] );
347 /* move each request up one slot */
348 for ( loop = 1; loop < num_requests; loop++ )
349 requests[loop-1] = requests[loop];
350 num_requests--;
351 }
352
353 if ( num_requests )
354 httpSendRequest ( requests[0] );
355
356 return ( HTTP_NO_ERROR );
357 }
358
359
360
361 /*
362 ** Calling app calls this when select() indicates data is ready to be
363 ** read on socket. This will figure out where we left off and
364 ** continue from there.
365 */
httpProcessRead(connection)366 httpError httpProcessRead ( connection )
367 sockfd connection;
368 {
369 int loop;
370 int num = -1;
371
372 for ( loop = 0; loop < num_requests; loop++ ) {
373 if ( requests[loop]->connection == connection ) {
374 num = loop;
375 break;
376 }
377 }
378
379 if ( num < 0 )
380 return ( HTTP_NO_REQUESTS );
381
382 return ( requests[loop]->read_function ( requests[loop] ) );
383 }
384
385
386
387 /*
388 ** Connect to an HTTP server.
389 ** Returns socket file descriptor.
390 ** Note that this is the one place where we must wait for a response
391 ** that could cause things to hang a bit.
392 */
httpOpenConnection(http_host,port,connection)393 httpError httpOpenConnection ( http_host, port, connection )
394 char *http_host;
395 int port;
396 sockfd *connection;
397 {
398 sockfd sock;
399 static struct sockaddr_in server;
400 struct hostent *hp, *gethostbyname ();
401 tcptError ret;
402
403 memset ( &server, '\0', sizeof ( struct sockaddr_in ) );
404
405 /* Verify all necessary data is available */
406 if ( ! http_host || ! strlen ( http_host ) )
407 return ( HTTP_INVALID_HOST );
408
409 if ( http_proxy ) {
410 /* save info for next request */
411 sprintf ( http_proxy_string, "http://%s:%d", http_host, port );
412 /* now change to http proxy */
413 http_host = http_proxy;
414 port = http_proxy_port;
415 }
416
417 hp = gethostbyname ( http_host );
418
419 if ( ! hp ) {
420 return ( HTTP_HOST_LOOKUP_FAILED );
421 }
422
423 /* Init windows winsock DLL */
424 if ( tcptInit () ) {
425 return ( HTTP_SOCKET_ERROR );
426 }
427
428 sock = socket ( AF_INET, SOCK_STREAM, 0 );
429 if ( sock < 0 ) {
430 tcptCleanup ();
431 return ( HTTP_SOCKET_ERROR );
432 }
433
434 server.sin_family = AF_INET;
435 memcpy ( (char *)&server.sin_addr, (char*)hp->h_addr, hp->h_length );
436
437 /* Get HTTP port (80) */
438 if ( port )
439 server.sin_port = htons ( port );
440 else
441 server.sin_port = htons ( HTTP_PORT );
442
443 if ( ( ret = tcptConnect ( sock, (struct sockaddr *)&server,
444 sizeof ( server ) ) ) ) {
445 strcpy ( http_other_error, tcptErrorString ( ret ) );
446 return ( HTTP_OTHER_ERROR );
447 }
448
449 /* success. return socket */
450 *connection = sock;
451
452 /* Set to non-blocking */
453 /*ioctl ( *connection, FIONBIO, 1 );*/
454
455 return ( HTTP_NO_ERROR );
456 }
457
458
459
460
461
462 /*
463 ** Close the connection and remove all requests from the queue.
464 */
httpKillConnection(connection)465 httpError httpKillConnection ( connection )
466 sockfd connection;
467 {
468 int loop, newnum;
469
470 /*
471 ** Remove all requests in the queue that reference this connection.
472 */
473 for ( loop = 0, newnum = 0; loop < num_requests; loop++ ) {
474 if ( requests[loop]->connection == connection ) {
475 /* NULL out requests we are killing */
476 if ( requests[loop]->data_read )
477 free ( requests[loop]->data_read );
478 if ( requests[loop]->request )
479 free ( requests[loop]->request );
480 free ( requests[loop] );
481 requests[loop] = NULL;
482 }
483 else {
484 requests[newnum] = requests[loop];
485 }
486 }
487
488 for ( loop = 0, newnum = 0; loop < num_requests; loop++ ) {
489 if ( requests[loop] && loop != newnum ) {
490 requests[newnum] = requests[loop];
491 requests[loop] = NULL;
492 newnum++;
493 }
494 }
495
496 num_requests = newnum;
497
498 closesocket ( connection );
499 return ( HTTP_NO_ERROR );
500 }
501
502
503
504 /*
505 ** Do a generic get request.
506 */
httpGet(connection,virthost,path,qs_names,qs_values,num,callback)507 httpError httpGet ( connection, virthost, path, qs_names, qs_values,
508 num, callback )
509 sockfd connection;
510 char *virthost;
511 char *path;
512 char *qs_names[];
513 char *qs_values[];
514 int num;
515 #ifdef _NO_PROTO
516 void (*callback)();
517 #else
518 void (*callback)(char *,int);
519 #endif
520 {
521 char *command, *temp, temp2[256], *ret1, *ret2;
522 int loop;
523
524 temp = (char *) malloc ( strlen ( path ) + strlen ( http_proxy_string ) +
525 5 );
526 sprintf ( temp, "GET %s%s", http_proxy_string, path );
527 command = (char *) malloc ( strlen ( temp ) + 2 );
528 strcpy ( command, temp );
529 free ( temp );
530 if ( num ) {
531 strcat ( command, "?" );
532 for ( loop = 0; loop < num; loop++ ) {
533 ret1 = encode_for_use_in_url ( qs_names[loop] );
534 ret2 = encode_for_use_in_url ( qs_values[loop] );
535 command = (char *) realloc ( command, strlen ( command ) +
536 strlen ( ret1 ) + strlen ( ret2 ) + 3 );
537 if ( loop )
538 strcat ( command, "&" );
539 strcat ( command, ret1 );
540 strcat ( command, "=" );
541 strcat ( command, ret2 );
542 free ( ret1 );
543 free ( ret2 );
544 }
545 }
546 strcpy ( temp2, " HTTP/1.0\r\n" );
547 strcat ( temp2, "User-Agent: " );
548 strcat ( temp2, user_agent() );
549 strcat ( temp2, "\r\n" );
550 if ( virthost != NULL ) {
551 strcat ( temp2, "Host: " );
552 strcat ( temp2, virthost );
553 strcat ( temp2, "\r\n" );
554 }
555 strcat ( temp2, "\r\n" );
556 command = (char *) realloc ( command, strlen ( command ) +
557 strlen ( temp2 ) + 1 );
558 strcat ( command, temp2 );
559
560 httpEnqueueRequest ( connection, command, httpReadGet, NULL, callback );
561
562 return ( HTTP_NO_ERROR );
563 }
564
my_strncasecmp(str1,str2,len)565 static int my_strncasecmp ( str1, str2, len )
566 char *str1, *str2;
567 int len;
568 {
569 char *a1, *a2, *ptr;
570 int ret, loop;
571
572 a1 = (char *) malloc ( len + 1 );
573 strncpy ( a1, str1, len );
574 a1[len] = '\0';
575 a2 = (char *) malloc ( len + 1 );
576 strncpy ( a2, str2, len );
577 a2[len] = '\0';
578 for ( ptr = a1, loop = 0; *ptr != '\0' && loop < len; loop++, ptr++ )
579 *ptr = toupper ( *ptr );
580 for ( ptr = a2, loop = 0; *ptr != '\0' && loop < len; loop++, ptr++ )
581 *ptr = toupper ( *ptr );
582
583 ret = strncmp ( a1, a2, len );
584 free ( a1 );
585 free ( a2 );
586
587 return ( ret );
588 }
589
httpReadGet(request)590 static httpError httpReadGet ( request )
591 httpRequest *request;
592 {
593 int rval;
594 char data[MAX_READ_SIZE], *alldata, *ptr;
595 int len;
596
597 memset ( data, '\0', MAX_READ_SIZE );
598 rval = recv ( request->connection, data, MAX_READ_SIZE, 0 );
599 if ( rval < 0 )
600 return ( HTTP_SOCKET_ERROR );
601 else if ( rval == 0 ) {
602 /* we are done. */
603 request->gen_callback ( request->data_read, request->data_len );
604 request->gen_callback ( NULL, 0 );
605 httpDequeueRequest ();
606 return ( HTTP_NO_ERROR );
607 }
608 if ( request->data_read ) {
609 alldata = (char *) malloc ( request->data_len + rval );
610 memcpy ( alldata, request->data_read, request->data_len );
611 memcpy ( alldata + request->data_len, data, rval );
612 len = request->data_len + rval;
613 }
614 else {
615 alldata = (char *) malloc ( rval );
616 memcpy ( alldata, data, rval );
617 len = rval;
618 }
619 if ( request->data_read )
620 free ( request->data_read );
621 if ( ! request->content_length ) {
622 request->data_read = httpTruncateHeader ( alldata, &len );
623 if ( request->data_read )
624 request->data_len = len;
625 if ( request->data_read ) {
626 /* complete header found (starts in alldata) */
627 ptr = strtok ( alldata, "\r\n" );
628 while ( ptr ) {
629 if ( strncmp ( ptr, "HTTP/1.0 200", 12 ) == 0 ||
630 strncmp ( ptr, "HTTP/1.1 200", 12 ) == 0 ) {
631 /* ok status. ignore */
632 }
633 else if ( strncmp ( ptr, "HTTP/1.", 7 ) == 0 ) {
634 /* some other status -- error */
635 request->gen_callback ( ptr, strlen ( ptr ) ); /* indicates error */
636 request->gen_callback ( NULL, 0 ); /* indicates error */
637 httpDequeueRequest ();
638 free ( alldata );
639 return ( HTTP_HTTP_ERROR );
640 }
641 else if ( my_strncasecmp ( ptr, "Content-Length:", 15 ) == 0 ) {
642 /* specifies length of content */
643 request->content_length = atoi ( ptr + 15 );
644 break;
645 }
646 else {
647 /* ignore all others.... */
648 }
649 ptr = strtok ( NULL, "\r\n" );
650 }
651 }
652 }
653 else {
654 if ( request->content_length == len ) {
655 /* we're done */
656 request->gen_callback ( alldata, len );
657 request->gen_callback ( NULL, 0 );
658 httpDequeueRequest ();
659 free ( alldata );
660 return ( HTTP_HTTP_ERROR );
661 }
662 }
663
664 free ( alldata );
665 return ( HTTP_NO_ERROR );
666 }
667
668
669
670
671
672
httpErrorString(num)673 char *httpErrorString ( num )
674 httpError num;
675 {
676 static char msg[200];
677
678 switch ( num ) {
679 case HTTP_NO_ERROR:
680 return ( "No error." );
681 case HTTP_INVALID_HOST:
682 return ( "Unable to resolve server name." );
683 case HTTP_SYSTEM_ERROR:
684 sprintf ( msg, "System error (%d)", errno );
685 return ( msg );
686 case HTTP_SOCKET_ERROR:
687 sprintf ( msg, "System error (%d)", errno );
688 return ( msg );
689 case HTTP_HTTP_ERROR:
690 return ( "HTTP protocol error." );
691 case HTTP_NO_REQUESTS:
692 return ( "No requests on queue." );
693 case HTTP_TOO_MANY_REQUESTS:
694 return ( "Too many requests on queue." );
695 case HTTP_HOST_LOOKUP_FAILED:
696 return ( "Unable to resolve server hostname" );
697 case HTTP_OTHER_ERROR:
698 return ( http_other_error );
699 case HTTP_UNKNOWN_ERROR:
700 default:
701 return ( "Unknown error." );
702 }
703 }
704
705
706
707
708
709
710
711
712
713 /*
714 ** Take the given text and truncate after the http header
715 */
httpTruncateHeader(text,len)716 static char *httpTruncateHeader ( text, len )
717 char *text;
718 int *len;
719 {
720 char *ptr;
721 char *ret;
722 char *end_header1 = "\n\n";
723 char *end_header2 = "\r\n\r\n";
724 int loop;
725 int newlen;
726
727 /* check to see it we've reached the end. */
728 if ( strlen ( text ) < 10 )
729 return ( NULL );
730
731 /* back up to the last separator */
732 for ( loop = 0, ptr = text; loop < *len; loop++, ptr++ ) {
733 if ( strncmp ( ptr, end_header1, strlen ( end_header1 ) ) == 0 ) {
734 /* end of http header found */
735 *ptr = '\0';
736 ptr += strlen ( end_header1 );
737 newlen = *len - strlen ( text ) - strlen ( end_header1 );
738 ret = (char *) malloc ( newlen );
739 memcpy ( ret, ptr, newlen );
740 *len = newlen;
741 return ( ret );
742 }
743 else if ( strncmp ( ptr, end_header2, strlen ( end_header2 ) ) == 0 ) {
744 /* end of http header found */
745 *ptr = '\0';
746 ptr += strlen ( end_header2 );
747 newlen = *len - strlen ( text ) - strlen ( end_header2 );
748 ret = (char *) malloc ( newlen );
749 memcpy ( ret, ptr, newlen );
750 *len = newlen;
751 return ( ret );
752 }
753 }
754
755 return ( NULL );
756 }
757
758
759
760
my_strtok(ptr1,tok)761 static char *my_strtok ( ptr1, tok )
762 char *ptr1;
763 char *tok;
764 {
765 static char *last;
766 char *p;
767
768 if ( ! ptr1 )
769 ptr1 = last;
770 else
771 last = ptr1;
772
773 if ( ! ptr1 )
774 return ( NULL );
775
776 for ( p = ptr1; *p != '\0'; p++ ) {
777 if ( strncmp ( p, tok, strlen ( tok ) ) == 0 ) {
778 *p = '\0';
779 last = p + strlen ( tok );
780 return ( ptr1 );
781 }
782 }
783
784 last = NULL;
785 return ( ptr1 );
786 }
787
788