5
* (C) 1999 Craig Knudsen, cknudsen@cknudsen.com
6
* See accompanying file "COPYING".
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.
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.
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
24
* Helps you keep track of time spent on different tasks.
27
* Craig Knudsen, cknudsen@cknudsen.com, http://www.cknudsen.com
30
* http://www.cknudsen.com/gtimer/
33
* 19-May-1999 Created (based on an 1995 app I wrote)
35
****************************************************************************/
44
#include <sys/types.h>
47
#include <sys/select.h>
49
#include <sys/ioctl.h>
51
#include <winsock.h> /* winsock API */
54
#include <netdb.h> /* for gethostbyaddr() */
55
#include <sys/socket.h>
56
#include <netinet/in.h>
68
#ifdef GTIMER_MEMDEBUG
69
#include "memdebug/memdebug.h"
73
static char *my_strtok (
81
** Max number of bytes to read at once.
83
#define MAX_READ_SIZE 1024
86
** Structure for states.
88
typedef struct _httpRequest {
89
sockfd connection; /* connection to server */
90
char *request; /* text message to send */
91
httpError (*read_function)(); /* read function */
93
void (*callback)(); /* callback when results received */
95
void (*callback)(char *); /* callback when results received */
98
void (*gen_callback)(); /* callback when results received */
100
void (*gen_callback)(char *,int); /* callback when results received */
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 */
109
#define MAX_REQUESTS_QUEUED 2048
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;
119
static char *user_agent () {
120
static char ret[100];
122
sprintf ( ret, "GTimer/%s", GTIMER_VERSION );
129
static char *httpTruncateHeader (
135
static httpError httpReadGet (
140
static httpError httpReadGetArticles (
145
static httpError httpReadGetAds (
150
static httpError httpReadList (
155
static httpError httpReadGetUserId (
160
static httpError httpReadGetSessionId (
165
static httpError httpSendRequest (
170
static char *encode_for_use_in_url (
181
** URL-encode a string
182
** See RFC 1738 (http://andrew2.andrew.cmu.edu/rfc/rfc1738.html)
183
** Caller should free result.
185
static char *encode_for_use_in_url ( str )
188
char *ret, *ptr1, *ptr2, *ptr3;
189
static char unsafe_chars[] = {
190
' ', '{', '}', '|', '/', '\\', '^', '~', '[', ']', '`', '%', '<', '>', '"', '#', ')', '(', '\0',
194
/* just to be safe... */
195
ret = (char *) malloc ( strlen ( str ) * 3 + 1 );
197
for ( ptr1 = str, ptr2 = ret; *ptr1 != '\0'; ptr1++ ) {
200
for ( ptr3 = unsafe_chars; *ptr3 != '\0'; ptr3++ ) {
201
if ( *ptr3 == *ptr1 ) {
208
if ( ( (unsigned int)*ptr1 < 0x80 || (unsigned int)*ptr1 > 0xFF ) ||
209
( (unsigned int)*ptr1 >= 0x00 && (unsigned int)*ptr1 <= 0x1F ) ||
210
( (unsigned int)*ptr1 == 0x7F ) )
215
sprintf ( ptr2, "%%%02X", (unsigned int)*ptr1 );
230
void httpEnableProxy ( server, port )
236
http_proxy = (char *) malloc ( strlen ( server ) + 1 );
237
strcpy ( http_proxy, server );
238
http_proxy_port = port;
242
void httpDisableProxy ()
246
http_proxy_string[0] = '\0';
252
** Put a request onto the end of the queue.
253
** Current request is always reqeusts[0].
255
httpError httpEnqueueRequest ( connection, msg, read_function, callback,
260
httpError (*read_function)();
262
void (*gen_callback)();
264
httpError (*read_function)(sockfd);
265
void (*callback)(char *);
266
void (*gen_callback)(char *,int);
269
httpRequest *request;
271
if ( num_requests >= MAX_REQUESTS_QUEUED - 1 ) {
272
fprintf ( stderr, "Too many errors...\n" );
273
return ( HTTP_TOO_MANY_REQUESTS );
276
/* put request at end of queue. */
277
request = (httpRequest *) malloc ( sizeof ( httpRequest ) );
278
memset ( request, '\0', sizeof ( httpRequest ) );
280
request->request = (char *) malloc ( strlen ( msg ) + 1 );
281
strcpy ( request->request, msg );
282
request->connection = connection;
285
/* NULL msg indicates no message to send */
286
request->request = NULL;
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;
296
/* now send the request if this is the only request */
297
if ( num_requests == 1 ) {
298
httpSendRequest ( requests[0] );
301
return ( HTTP_NO_ERROR );
307
** Send a request to the server.
308
** This should not be called directly, only from httpDequeueRequest and
309
** httpEnqueueRequest.
311
static httpError httpSendRequest ( request )
312
httpRequest *request;
316
if ( ! request->sent ) {
317
rval = send ( request->connection, request->request,
318
strlen ( request->request ), 0 );
323
return ( HTTP_NO_ERROR );
325
return ( HTTP_SOCKET_ERROR );
328
return ( HTTP_NO_ERROR );
334
** Remove the most recent request from the queue.
335
** It is not an error to call this function with nothing in the queue.
337
httpError httpDequeueRequest ()
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];
354
httpSendRequest ( requests[0] );
356
return ( HTTP_NO_ERROR );
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.
366
httpError httpProcessRead ( connection )
372
for ( loop = 0; loop < num_requests; loop++ ) {
373
if ( requests[loop]->connection == connection ) {
380
return ( HTTP_NO_REQUESTS );
382
return ( requests[loop]->read_function ( requests[loop] ) );
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.
393
httpError httpOpenConnection ( http_host, port, connection )
399
static struct sockaddr_in server;
400
struct hostent *hp, *gethostbyname ();
403
memset ( &server, '\0', sizeof ( struct sockaddr_in ) );
405
/* Verify all necessary data is available */
406
if ( ! http_host || ! strlen ( http_host ) )
407
return ( HTTP_INVALID_HOST );
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;
417
hp = gethostbyname ( http_host );
420
return ( HTTP_HOST_LOOKUP_FAILED );
423
/* Init windows winsock DLL */
425
return ( HTTP_SOCKET_ERROR );
428
sock = socket ( AF_INET, SOCK_STREAM, 0 );
431
return ( HTTP_SOCKET_ERROR );
434
server.sin_family = AF_INET;
435
memcpy ( (char *)&server.sin_addr, (char*)hp->h_addr, hp->h_length );
437
/* Get HTTP port (80) */
439
server.sin_port = htons ( port );
441
server.sin_port = htons ( HTTP_PORT );
443
if ( ( ret = tcptConnect ( sock, (struct sockaddr *)&server,
444
sizeof ( server ) ) ) ) {
445
strcpy ( http_other_error, tcptErrorString ( ret ) );
446
return ( HTTP_OTHER_ERROR );
449
/* success. return socket */
452
/* Set to non-blocking */
453
/*ioctl ( *connection, FIONBIO, 1 );*/
455
return ( HTTP_NO_ERROR );
463
** Close the connection and remove all requests from the queue.
465
httpError httpKillConnection ( connection )
471
** Remove all requests in the queue that reference this connection.
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;
484
requests[newnum] = requests[loop];
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;
496
num_requests = newnum;
498
closesocket ( connection );
499
return ( HTTP_NO_ERROR );
505
** Do a generic get request.
507
httpError httpGet ( connection, virthost, path, qs_names, qs_values,
518
void (*callback)(char *,int);
521
char *command, *temp, temp2[256], *ret1, *ret2;
524
temp = (char *) malloc ( strlen ( path ) + strlen ( http_proxy_string ) +
526
sprintf ( temp, "GET %s%s", http_proxy_string, path );
527
command = (char *) malloc ( strlen ( temp ) + 2 );
528
strcpy ( command, temp );
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 );
538
strcat ( command, "&" );
539
strcat ( command, ret1 );
540
strcat ( command, "=" );
541
strcat ( command, ret2 );
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" );
555
strcat ( temp2, "\r\n" );
556
command = (char *) realloc ( command, strlen ( command ) +
557
strlen ( temp2 ) + 1 );
558
strcat ( command, temp2 );
560
httpEnqueueRequest ( connection, command, httpReadGet, NULL, callback );
562
return ( HTTP_NO_ERROR );
565
static int my_strncasecmp ( str1, str2, len )
572
a1 = (char *) malloc ( len + 1 );
573
strncpy ( a1, str1, len );
575
a2 = (char *) malloc ( len + 1 );
576
strncpy ( a2, str2, len );
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 );
583
ret = strncmp ( a1, a2, len );
590
static httpError httpReadGet ( request )
591
httpRequest *request;
594
char data[MAX_READ_SIZE], *alldata, *ptr;
597
memset ( data, '\0', MAX_READ_SIZE );
598
rval = recv ( request->connection, data, MAX_READ_SIZE, 0 );
600
return ( HTTP_SOCKET_ERROR );
601
else if ( rval == 0 ) {
603
request->gen_callback ( request->data_read, request->data_len );
604
request->gen_callback ( NULL, 0 );
605
httpDequeueRequest ();
606
return ( HTTP_NO_ERROR );
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;
615
alldata = (char *) malloc ( rval );
616
memcpy ( alldata, data, rval );
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" );
629
if ( strncmp ( ptr, "HTTP/1.0 200", 12 ) == 0 ||
630
strncmp ( ptr, "HTTP/1.1 200", 12 ) == 0 ) {
631
/* ok status. ignore */
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 ();
639
return ( HTTP_HTTP_ERROR );
641
else if ( my_strncasecmp ( ptr, "Content-Length:", 15 ) == 0 ) {
642
/* specifies length of content */
643
request->content_length = atoi ( ptr + 15 );
647
/* ignore all others.... */
649
ptr = strtok ( NULL, "\r\n" );
654
if ( request->content_length == len ) {
656
request->gen_callback ( alldata, len );
657
request->gen_callback ( NULL, 0 );
658
httpDequeueRequest ();
660
return ( HTTP_HTTP_ERROR );
665
return ( HTTP_NO_ERROR );
673
char *httpErrorString ( num )
676
static char msg[200];
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 );
686
case HTTP_SOCKET_ERROR:
687
sprintf ( msg, "System error (%d)", errno );
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:
701
return ( "Unknown error." );
714
** Take the given text and truncate after the http header
716
static char *httpTruncateHeader ( text, len )
722
char *end_header1 = "\n\n";
723
char *end_header2 = "\r\n\r\n";
727
/* check to see it we've reached the end. */
728
if ( strlen ( text ) < 10 )
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 */
736
ptr += strlen ( end_header1 );
737
newlen = *len - strlen ( text ) - strlen ( end_header1 );
738
ret = (char *) malloc ( newlen );
739
memcpy ( ret, ptr, newlen );
743
else if ( strncmp ( ptr, end_header2, strlen ( end_header2 ) ) == 0 ) {
744
/* end of http header found */
746
ptr += strlen ( end_header2 );
747
newlen = *len - strlen ( text ) - strlen ( end_header2 );
748
ret = (char *) malloc ( newlen );
749
memcpy ( ret, ptr, newlen );
761
static char *my_strtok ( ptr1, tok )
776
for ( p = ptr1; *p != '\0'; p++ ) {
777
if ( strncmp ( p, tok, strlen ( tok ) ) == 0 ) {
779
last = p + strlen ( tok );