1 /*
2  * copied and amended from a simple http socket program:
3  * "Simple Internet client program - by Dan Drown <abob@linux.com>"
4  * available (last I looked) at:
5  *      http://linux.omnipotent.net/article.php?article_id=5424
6  */
7 
8 #include "mhttp.h"
9 
10 
11 #ifdef DOHERROR
herror(char * str)12 void herror(char *str){
13   fprintf(stderr, "herror: %s\n", str);
14 }
15 #endif
16 
17 bool mhttp_lets_debug;        /* global debugging flag           */
18 bool mhttp_body_set_flag;     /* global body set flag            */
19 bool mhttp_host_hdr;          /* I got a Host header             */
20 int mhttp_protocol = 0;
21 bool mhttp_first_init = false;
22 
23 int  mhttp_hcnt,
24      mhttp_rcode,
25      mhttp_response_length;
26 
27 char *mhttp_body,
28      *mhttp_response,
29      *mhttp_reason,
30      mhttp_resp_headers[MAX_HDR_STR],
31      *mhttp_headers[MAX_HEADERS],
32      *mhttp_buffers[MAX_BUFFERS];
33 
34 mhttp_conn_t mhttp_connection = NULL;
35 
36 mhttp_conn_t mhttp_last_connection = NULL;
37 
38 
39 
mhttp_call(char * paction,char * purl)40 int mhttp_call(char *paction, char *purl)
41 {
42     bool found_hdrs;        /* found the end of headers flag   */
43     bool rcode_flag;        /* found return code flag          */
44     bool newcon_flag;       /* new connection flag             */
45     bool chunked;           /* transfer encoding is chunked    */
46     char *action, *url, *host, *ptr, *eomsg, *clptr;
47     char *req;
48     char *surl;
49     char *cert_str;
50     int  port,
51          returnval,
52 	 len,
53 	 curr_len,
54 	 buffer_size,
55 	 this_chunk,
56 	 i,
57 	 rem,
58 	 pos;
59     char str[MAX_STR];
60 
61 
62     newcon_flag = false;
63     if (!mhttp_first_init)
64         mhttp_init();
65     memset(mhttp_resp_headers, 0, MAX_HDR_STR);
66 
67     mhttp_connection = mhttp_new_conn();
68 
69     if ((returnval = check_action(paction, &action)) < 0)
70         return returnval;
71 
72     if ((returnval = check_url(purl, &url, &host)) < 0)
73         return returnval;
74 
75     if ((port = get_port_and_uri(purl, host, &surl)) < 0)
76         return port;
77     mhttp_connection->host = strdup(host);
78     mhttp_connection->port = port;
79 
80     mhttp_debug("action: %s host: %s port: %d url: %s", action, host, port, url);
81     mhttp_debug("purl is: #%s#", purl);
82     if (strncmp(purl, "https", 5) == 0)
83            mhttp_connection->is_ssl = true;
84 
85     mhttp_debug("This connection: %s / %d / %d",
86        mhttp_connection->host,
87        mhttp_connection->port,
88        mhttp_connection->is_ssl);
89 
90     if (mhttp_protocol == 1 && ! mhttp_host_hdr){
91         mhttp_debug("This is HTTP/1.1 and we don't have a Host header");
92         return -19;
93     }
94 
95     if (mhttp_last_connection != NULL){
96     mhttp_debug("OLD connection: %s / %d / %d",
97        mhttp_last_connection->host,
98        mhttp_last_connection->port,
99        mhttp_last_connection->is_ssl);
100      }
101 #ifdef GOTSSL
102     if (mhttp_last_connection != NULL &&
103        strcmp(mhttp_connection->host, mhttp_last_connection->host) == 0 &&
104        mhttp_last_connection->port == mhttp_connection->port &&
105        mhttp_last_connection->is_ssl == mhttp_connection->is_ssl){
106        mhttp_connection->fd = mhttp_last_connection->fd;
107        mhttp_connection->ctx = mhttp_last_connection->ctx;
108        mhttp_connection->ssl = mhttp_last_connection->ssl;
109        mhttp_connection->meth = mhttp_last_connection->meth;
110        mhttp_connection->server_cert = mhttp_last_connection->server_cert;
111 #else
112     if (mhttp_last_connection != NULL &&
113        strcmp(mhttp_connection->host, mhttp_last_connection->host) == 0 &&
114        mhttp_last_connection->port == mhttp_connection->port){
115        mhttp_connection->fd = mhttp_last_connection->fd;
116 #endif
117        mhttp_debug("using the cached connection");
118     } else {
119       if (mhttp_last_connection != NULL){
120 #ifdef GOTSSL
121         if (mhttp_last_connection->is_ssl)
122             SSL_shutdown (mhttp_last_connection->ssl);  /* send SSL/TLS close_notify */
123 #endif
124         close(mhttp_last_connection->fd);
125 #ifdef GOTSSL
126         if (mhttp_last_connection->is_ssl){
127             mhttp_debug("shuting down the ssl engine");
128             SSL_free (mhttp_last_connection->ssl);
129             SSL_CTX_free (mhttp_last_connection->ctx);
130         }
131 #endif
132         mhttp_end_conn(mhttp_last_connection);
133 	mhttp_last_connection = NULL;
134         mhttp_debug("Closed the last connection");
135       }
136       newcon_flag = true;
137       mhttp_connection->fd = -1;
138       mhttp_debug("didnt find connection - creating a new one: %s/%d - %d ", mhttp_connection->host, mhttp_connection->port, mhttp_connection->fd);
139 #ifdef GOTSSL
140        if (mhttp_connection->is_ssl){
141 	   mhttp_debug("creating an SSL connection");
142            //SSLeay_add_ssl_algorithms();
143 	   OpenSSL_add_ssl_algorithms();
144            //mhttp_connection->meth = SSLv2_client_method();
145 	   mhttp_connection->meth = SSLv3_client_method();
146            SSL_load_error_strings();
147            mhttp_connection->ctx = SSL_CTX_new (mhttp_connection->meth);
148 	   if (mhttp_connection->ctx == NULL){
149 	       mhttp_debug("SSL_CTX_new failed - abort everything");
150 	       return -11;
151 	   }
152           if( (mhttp_connection->fd = mhttp_connect_inet_addr(mhttp_connection->host, mhttp_connection->port)) < 0) {
153                mhttp_debug("could not create a new socket: %d", mhttp_connection->fd);
154                return mhttp_connection->fd;
155            }
156            // XXX set SSL here
157 
158            /* ----------------------------------------------- */
159            /* Now we have TCP conncetion. Start SSL negotiation. */
160 
161            mhttp_debug("SSL craeting the ctx");
162            mhttp_connection->ssl = SSL_new (mhttp_connection->ctx);
163 	   if (mhttp_connection->ssl == NULL){
164 	       mhttp_debug("SSL_new failed - abort everything");
165 	       return -12;
166 	   }
167 	   mhttp_debug("SSL set_verify");
168 	   SSL_CTX_set_default_verify_paths(mhttp_connection->ctx);
169 	   //SSL_CTX_load_verify_locations(mhttp_connection->ctx, "/etc/httpd/conf/ssl.crt/ca-bundle.crt",
170 	   //                                   NULL);
171 	   SSL_CTX_set_verify(mhttp_connection->ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, mhttp_verify_callback);
172 	   //SSL_CTX_set_verify(mhttp_connection->ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, NULL);
173 	   mhttp_debug("SSL set_fd");
174            SSL_set_fd (mhttp_connection->ssl, mhttp_connection->fd);
175            returnval = SSL_connect (mhttp_connection->ssl);
176 	   if (returnval == -1){
177 	       mhttp_debug("SSL_connect failed - abort everything");
178 	       ERR_print_errors_fp(stderr);
179 	       return -13;
180 	   }
181 
182            /* Get the cipher - opt */
183            mhttp_debug ("SSL connection using %s\n", SSL_get_cipher (mhttp_connection->ssl));
184 
185            /* Get server's certificate (note: beware of dynamic allocation) - opt */
186            mhttp_connection->server_cert = SSL_get_peer_certificate (mhttp_connection->ssl);
187 	   if (mhttp_connection->server_cert == NULL){
188 	       mhttp_debug("SSL_get_peer_certificate failed - abort everything");
189 	       return -14;
190 	   }
191 
192            cert_str = X509_NAME_oneline (X509_get_subject_name (mhttp_connection->server_cert),0,0);
193 	   if (cert_str == NULL){
194 	       mhttp_debug("X509_get_subject_name failed - abort everything");
195 	       return -15;
196 	   }
197            mhttp_debug ("Certificate subject: %s\n", cert_str);
198            free (cert_str);
199 
200            cert_str = X509_NAME_oneline (X509_get_issuer_name  (mhttp_connection->server_cert),0,0);
201 	   if (cert_str == NULL){
202 	       mhttp_debug("X509_get_issuer_name failed - abort everything");
203 	       return -16;
204 	   }
205            mhttp_debug ("Certificate issuer: %s\n", cert_str);
206            free (cert_str);
207 
208            /* We could do all sorts of certificate verification stuff here before
209               deallocating the certificate. */
210 
211            if (SSL_get_verify_result(mhttp_connection->ssl) == X509_V_OK){
212            /* The client sent a certificate which verified OK */
213 	       mhttp_debug("certificate OK");
214            } else {
215 	       mhttp_debug("Certificate error: %s", X509_verify_cert_error_string(SSL_get_verify_result(mhttp_connection->ssl)));
216 	   }
217 
218            X509_free (mhttp_connection->server_cert);
219 
220       } else {
221           if( (mhttp_connection->fd = mhttp_connect_inet_addr(mhttp_connection->host, mhttp_connection->port)) < 0) {
222                mhttp_debug("could not create a new socket: %d", mhttp_connection->fd);
223                return mhttp_connection->fd;
224            }
225       }
226 #else
227       if( (mhttp_connection->fd = mhttp_connect_inet_addr(mhttp_connection->host, mhttp_connection->port)) < 0) {
228            mhttp_debug("could not create a new socket: %d", mhttp_connection->fd);
229            return mhttp_connection->fd;
230        }
231 #endif
232   }
233 
234   mhttp_debug("socket descriptor is: %d ", mhttp_connection->fd);
235 
236   len = 0;
237 
238   // construct the query string
239   memset(str, 0, sizeof(str));
240 
241   if ((req = construct_request(action, surl)) == NULL)
242   	return -2;
243 
244   returnval = write_socket(mhttp_connection, req, strlen(req));
245   if(returnval < 0)
246   {
247     /* write returns -1 on error */
248     perror("write(query string) error");
249     return -5;
250   }
251 
252   if(returnval < strlen(req))
253   {
254     /* I'm not dealing with this error, regular programs should. */
255     perror("the query string write was short\n");
256     return -6;
257   }
258   free(req);
259   free(surl);
260   free(url);
261   free(host);
262 
263   // if this is a PUT or POST - write the body
264   if (mhttp_body_set_flag)
265   {
266       // XXX we probably have a problem where 1024 is exceeded and need to rewrite
267       mhttp_debug("this is a %s.... writing the body...", action);
268       mhttp_debug("writing data: %s", mhttp_body);
269       returnval = write_socket(mhttp_connection, mhttp_body, strlen(mhttp_body));
270       if(returnval < strlen(mhttp_body))
271       {
272           /* I'm not dealing with this error, regular programs should. */
273           fprintf(stderr, "the write of %s data was short\n", action);
274           return -6;
275       }
276       returnval = write_socket(mhttp_connection, "\r\n", 2);
277       if(returnval < 2)
278       {
279           /* I'm not dealing with this error, regular programs should. */
280           fprintf(stderr, "the write of %s data - last line failed\n", action);
281           return -7;
282       }
283   }
284   free(action);
285 
286 
287   /* read off the response and split out headers and content */
288   mhttp_debug("starting output:");
289   found_hdrs = false;
290   rcode_flag = false;
291   len = 0;
292   curr_len = 0;
293   chunked = false;
294 
295   if ((len = read_headers(mhttp_connection, str)) < 0){
296     // we have no headers
297     mhttp_debug("we have no headers ");
298     exit(1);
299   }
300   if (mhttp_response_length > 0){
301       buffer_size = mhttp_response_length;
302   } else {
303       buffer_size = MAX_STR;
304   }
305 
306 
307   mhttp_debug("initial len is: %d", len);
308   while((returnval = read_socket(mhttp_connection, str)) > 0)
309   {
310        *(str+returnval) = '\0';
311        // we have the headers - this is the body
312        if (mhttp_response_length > 0){
313 	      // we dont know how big it should be so compensate
314 	      if (mhttp_connection->is_chunked){
315 	          if (len + returnval >= buffer_size - 2){
316                      memcpy(mhttp_response+len, str, (buffer_size - 2) - len);
317 		     pos = (buffer_size - 2) - len;
318 		     len += pos;
319 		     mhttp_debug("len at end of chunk is: %d", len);
320 
321 		     // find the next chunk
322 		     mhttp_debug("looking for the next chunk");
323 		     ptr = str+pos;
324 		     rem = returnval - pos;
325 
326 	             if ((this_chunk = find_chunk(mhttp_connection, &ptr, &rem)) > 0){
327 			 // resize and paste on remainder
328 			 mhttp_debug("resize and paste on remainder for next chunk processing");
329 	                 mhttp_response = realloc(mhttp_response, buffer_size + this_chunk);
330 		         buffer_size += this_chunk;
331                          memcpy(mhttp_response+len, ptr, rem);
332 			 returnval = rem;
333 	             } else if (this_chunk == 0){
334 	               // no more to come
335 		       mhttp_debug("we found the final chunk");
336 		       break;
337 	             }
338 	          } else {
339 		    // not end of chunk yet
340                     memcpy(mhttp_response+len, str, returnval);
341 		  }
342 	     } else {
343                // make sure that it does not overflow the buffer
344                if (mhttp_response_length >= (len + returnval)){
345                    memcpy(mhttp_response+len, str, returnval);
346                }
347            }
348        } else {
349            // we dont know how big it should be so compensate
350            // else this is not a chunked read - so realloc when necessary
351            if (len + returnval > buffer_size){
352                mhttp_response = realloc(mhttp_response, buffer_size + MAX_STR);
353                buffer_size += MAX_STR;
354            }
355            memcpy(mhttp_response+len, str, returnval);
356        }
357        len += returnval;
358 
359 
360       // lets get out of here if we have read enough
361       if (mhttp_response_length > 0 && len >= mhttp_response_length )
362           break;
363   }
364   mhttp_debug("content length actually copied: %d (may include \\r\\n)", len);
365   mhttp_response_length = len;
366 
367   /* it will be closed anyway when we exit */
368   if (mhttp_protocol == 0 ||
369       (clptr = strstr(mhttp_resp_headers, "Connection: close")) ||
370       (clptr = strstr(mhttp_resp_headers, "Connection: Close")) ){
371 #ifdef GOTSSL
372       if (mhttp_connection->is_ssl)
373           SSL_shutdown (mhttp_connection->ssl);  /* send SSL/TLS close_notify */
374 #endif
375       /* Clean up. */
376       close(mhttp_connection->fd);
377 #ifdef GOTSSL
378       if (mhttp_connection->is_ssl){
379           mhttp_debug("shuting down the ssl engine");
380           SSL_free (mhttp_connection->ssl);
381           SSL_CTX_free (mhttp_connection->ctx);
382       }
383 #endif
384       if (mhttp_last_connection != NULL){
385           mhttp_end_conn(mhttp_last_connection);
386 	  mhttp_last_connection = NULL;
387           mhttp_debug("removed socket name");
388       }
389       mhttp_debug("Closed the connection");
390   } else {
391       if (newcon_flag){
392           if (mhttp_last_connection != NULL){
393               mhttp_end_conn(mhttp_last_connection);
394 	      mhttp_last_connection = NULL;
395 	  }
396 	  mhttp_last_connection = mhttp_new_conn();
397           mhttp_last_connection->host = strdup(mhttp_connection->host);
398           mhttp_last_connection->port = mhttp_connection->port;
399           mhttp_last_connection->fd = mhttp_connection->fd;
400 #ifdef GOTSSL
401           if (mhttp_connection->is_ssl){
402 	      mhttp_debug("saving SSL stuff");
403               mhttp_last_connection->is_ssl = mhttp_connection->is_ssl;
404               mhttp_last_connection->ssl = mhttp_connection->ssl;
405               mhttp_last_connection->ctx = mhttp_connection->ctx;
406 	  }
407 #endif
408           mhttp_debug("Caching the connection");
409       } else {
410           mhttp_debug("connection allready cached");
411       }
412   }
413   mhttp_end_conn(mhttp_connection);
414   mhttp_connection = NULL;
415   mhttp_debug("all done");
416   return 1;
417 }
418 
419 
420 int find_content_length(void){
421     char *ptr;
422     int rem = 0;
423 
424     // determine the Content-Length header
425     if ((ptr = strstr(mhttp_resp_headers, "Content-Length:")) ||
426         (ptr = strstr(mhttp_resp_headers, "Content-length:"))){
427         mhttp_debug("found content-length");
428         ptr += 16;
429         mhttp_response_length = atoi(ptr);
430         mhttp_debug("content length: %d", mhttp_response_length);
431         mhttp_response = malloc(mhttp_response_length + 2);
432 	return mhttp_response_length;
433     }
434     return 0;
435 
436 }
437 
438 
439 bool find_transfer_encoding(void){
440     char *clptr;
441     int this_chunk;
442 
443     /* look for Transfer-Encoding: chunked */
444     // clptr the chunk pointer
445     // ptr the beginning of the body
446     if ((clptr = strstr(mhttp_resp_headers, "Transfer-Encoding:")) ||
447         (clptr = strstr(mhttp_resp_headers, "Transfer-encoding:"))){
448         clptr += 19;
449         if (strncmp(clptr, "chunked",7) == 0){
450             mhttp_debug("found Transfer-Encoding: chunked");
451 	    return true;
452 	 }
453     }
454     return false;
455 
456 }
457 
458 
459 int find_chunk(mhttp_conn_t conn, char **ptr, int *rem){
460     char *clptr;
461     char *myptr;
462     int myrem;
463     int diff;
464     int this_chunk;
465     int returnval;
466 
467     /* look for Transfer-Encoding: chunked */
468     // clptr the chunk pointer
469     // ptr the beginning of the body
470 
471     myptr = *ptr;
472     myrem = *rem;
473 
474     // make sure that there is enuf of ptr left
475     mhttp_debug("remainder is: %d", myrem);
476     if (myrem <= 2 || !(clptr = strstr(myptr, "\r\n"))){
477         mhttp_debug("getting another line");
478         if ((returnval = read_socket(conn, myptr+myrem)) > 0){
479             myrem += returnval;
480             *(myptr+myrem) = '\0';
481 	    mhttp_debug("got another line: %d - #%s#", returnval, myptr);
482 	} else {
483             mhttp_debug("cant get another line - aborting");
484 	    return -17;
485 	}
486     }
487 
488     // must find the end of the chunked length line
489     // in the remainding buffer
490     if (clptr = strstr(myptr, "\r\n")){
491         mhttp_debug("looking for chunk in: %s#", myptr);
492         if (sscanf(myptr, "%x\r\n", &this_chunk) != 1){
493              mhttp_debug("count not the chunked amount - something ify");
494             if ((returnval = read_socket(conn, myptr+myrem)) > 0){
495                 myrem += returnval;
496                 *(myptr+myrem) = '\0';
497 	        mhttp_debug("got another line: %d - #%s#", returnval, myptr);
498 		if (strncmp(myptr, "\r\n", 2) == 0){
499 		   myptr+=2;
500 		   myrem-=2;
501 		}
502                 mhttp_debug("looking for chunk in: #%s#", myptr);
503                 if (sscanf(myptr, "%x\r\n", &this_chunk) != 1){
504                      mhttp_debug("count not the chunked amount - something broken");
505 	             return -17;
506                 }
507 	     }
508              return -17;
509 	 }
510 	 // shift past the chunk size
511 	 *clptr = '\0';
512 	 myrem -= strlen(myptr) + 2;
513 	 clptr += 2;
514          mhttp_debug("Transfer-Encoding: chunked buffer is %d - %d bytes left: %s", this_chunk, myrem, clptr+myrem);
515 
516 	 *ptr = clptr;
517 	 *rem = myrem;
518          return this_chunk;
519     }
520     return -17;
521 
522 }
523 
524 
525 
526 int read_headers(mhttp_conn_t conn, char *str){
527   int returnval;
528   int curr_len;
529   int rem;
530   int this_chunk;
531   char *ptr, *eomsg;
532   bool rcode_flag;
533 
534 
535   rcode_flag = false;
536   curr_len = 0;
537   while((returnval = read_socket(conn, str)) > 0)
538   {
539        *(str+returnval) = '\0';
540        mhttp_debug("Header line %d: %s", returnval, str);
541 
542        if (strlen(mhttp_resp_headers) + returnval > MAX_HDR_STR){
543            mhttp_debug("have not found the headers within MAX_HDR_STR: %d", MAX_HDR_STR);
544            return -18;
545        }
546 
547        /* detect the end of the headers */
548        sprintf(mhttp_resp_headers+strlen(mhttp_resp_headers), "%s", str);
549 
550        /* find the return code        */
551        if (!rcode_flag &&
552            strncmp(str, "HTTP/",5) == 0 &&
553            (strncmp(str+5, "0.9 ",4) == 0 ||
554  	    strncmp(str+5, "1.0 ",4) == 0 ||
555  	    strncmp(str+5, "1.1 ",4) == 0 ) ){
556            ptr = str+9;
557 	   *(ptr+3) = '\0';
558            mhttp_rcode = atoi(ptr);
559 	   rcode_flag = true;
560 	   ptr+=4;
561 	   /* find the status reason */
562            if ((eomsg = strstr(ptr, "\r\n")) || (eomsg = strstr(ptr, "\n"))){
563 	       *eomsg = '\0';
564 	       mhttp_reason = strdup(ptr);
565 	   }
566            mhttp_debug("detected return code: %d - %s", mhttp_rcode, mhttp_reason);
567        }
568 
569        if ((ptr = strstr(mhttp_resp_headers, "\r\n\r\n")) ||
570            (ptr = strstr(mhttp_resp_headers, "\n\n"))){
571           *ptr = '\0';
572           mhttp_debug("found end of headers at: %d", strlen(mhttp_resp_headers));
573           mhttp_debug("headers are: %s", mhttp_resp_headers);
574           if (strncmp(ptr,"\0\n\r\n",4) == 0){
575               /* how far along the current buffer is the eoh marker */
576               curr_len = (strlen(mhttp_resp_headers) + 4) - curr_len;
577               ptr+=4;
578           } else {
579               curr_len = (strlen(mhttp_resp_headers) + 2) - curr_len;
580               ptr+=2;
581           }
582 
583           /* tidy up the first bit of the body XXX */
584 	  mhttp_debug("returnval: %d - curr_len: %d", returnval, curr_len);
585 	  rem = returnval - curr_len;
586 	  mhttp_debug("the remainder is: %d", rem);
587 
588           // find the Content-Length header
589           if ( find_content_length() > 0 ){
590               if (mhttp_response_length >= rem){
591                   mhttp_debug("copying the initial part of the body: %s", ptr);
592                   memcpy(mhttp_response, ptr, rem);
593                   return rem;
594               } else {
595                   // serious error - cant determine length properly
596                   mhttp_debug("serious error - cant determine length properly");
597                   return -8;
598               }
599            // or find the Tranfer-Enconding: chunked header
600            } else if (find_transfer_encoding()){
601 	       // find the chunk value
602 	       conn->is_chunked = true;
603 	       if ((this_chunk = find_chunk(conn, &ptr, &rem)) > 0){
604                    mhttp_response = malloc(this_chunk + 2);
605                    memcpy(mhttp_response, ptr, rem);
606                    mhttp_response_length = this_chunk + 2;
607 		   return rem;
608 	       } else if (this_chunk == 0){
609 	         // an empty body
610 		 return 0;
611 	       } else {
612 	          // failed to find the next chunk value
613 		  mhttp_debug("cannot find \\r\\n after first chunked marker - time to give up");
614 		  return -17;
615 	       }
616            } else {
617 	      mhttp_debug("didnt find content-length - must use realloc: %d", rem);
618               mhttp_response_length = 0;
619               mhttp_response = malloc(MAX_STR);
620               memcpy(mhttp_response, ptr, rem);
621 	      return rem;
622 	   }
623            // or determine that it is HTTP/1.0
624            // or find the Connection: close
625           return curr_len;
626       }
627       curr_len += returnval;
628   }
629 
630   /* must have hit an error */
631   return returnval;
632 
633 }
634 
635 
636 int check_url(char *purl, char **url, char **host){
637 
638     char *ptrhost;
639 
640     if(strlen(purl) == 0)
641     {
642         mhttp_debug("must supply a url");
643         return -3;
644     }
645 
646     if (strncmp(purl, "http://", 7) == 0){
647         ptrhost = purl+7;
648 #ifdef GOTSSL
649     } else if (strncmp(purl, "https://", 8) == 0){
650         ptrhost = purl+8;
651 	mhttp_debug("setting the ssl flag");
652 	mhttp_connection->is_ssl = true;
653 #endif
654     } else {
655         mhttp_debug("url must start with http:// - and yep we dont support https\n");
656         return -4;
657     }
658     *url = strdup(purl);
659     *host = strdup(ptrhost);
660     mhttp_debug("begin of host is: %s", *host);
661     return 0;
662 }
663 
664 
665 
666 int get_port_and_uri(char *url, char *host, char **surl){
667   char *ptr;
668   int port;
669 
670   // hunt for the beginning of the uri
671   mhttp_debug("begin looking for host at: %s", host);
672   *surl = malloc(MAX_STR);
673   ptr = strstr(host, "/");
674   if (ptr != NULL){
675       *ptr = '\0';
676       ptr++;
677       sprintf(*surl, "/%s", ptr);
678   } else {
679       sprintf(*surl, "/");
680   }
681   // hunt for the beginning of the port
682   ptr = strstr(host, ":");
683   if (ptr != NULL){
684       *ptr = '\0';
685       ptr++;
686       port = atoi(ptr);
687   } else {
688 #ifdef GOTSSL
689       if (strncmp(url ,"https", 5) == 0){
690           port = 443;
691       } else {
692           port = 80;
693       }
694 #else
695       port = 80;
696 #endif
697 
698   }
699   return port;
700 
701 }
702 
703 
704 #ifdef GOTSSL
705 static int mhttp_verify_callback(int ok, X509_STORE_CTX* ctx)
706 {
707     return 1;
708 }
709 #endif
710 
711 mhttp_conn_t mhttp_new_conn(void){
712     mhttp_conn_t new_conn;
713 
714     new_conn = (mhttp_conn_t) malloc(sizeof(struct mhttp_conn_st));
715     memset(new_conn, 0, sizeof(struct mhttp_conn_st));
716 
717     new_conn->host = NULL;
718     new_conn->port = 0;
719     new_conn->is_ssl = false;
720     new_conn->is_chunked = false;
721     return new_conn;
722 }
723 
724 
725 void mhttp_end_conn(mhttp_conn_t conn){
726 
727     mhttp_debug("resetting conn");
728     free(conn->host);
729     free(conn);
730 
731 }
732 
733 
734 char *construct_request(char *action, char *url){
735   char *str;
736   int i;
737 
738   str = malloc(MAX_HDR_STR);
739 
740   strcpy(str, action);
741   strcpy(str+strlen(str), " ");
742   strcpy(str+strlen(str), url);
743   sprintf(str+strlen(str), " HTTP/1.%d\r\n", mhttp_protocol);
744   mhttp_debug("adding on the headers: %d", mhttp_hcnt);
745   for(i = 0; i < mhttp_hcnt; i++)
746   {
747       // make sure that we dont exceed the buffer
748       if ((strlen(str) + strlen(mhttp_headers[i])) > MAX_BUFFERS - 1)
749           break;
750       mhttp_debug("adding header: %s", mhttp_headers[i]);
751       sprintf(str+strlen(str), "%s\r\n", mhttp_headers[i]);
752   }
753   // if this is a post - add the Content-Length header
754   if (mhttp_body_set_flag)
755   {
756       sprintf(str+strlen(str),
757               "Content-Length: %d\r\n\r\n", strlen(mhttp_body));
758   } else {
759       strcpy(str+strlen(str), "\r\n\r\n");
760   }
761   mhttp_debug("query string + headers are: %s", str);
762 
763   return str;
764 
765 }
766 
767 
768 int check_action(char *paction, char **action){
769     if(strlen(paction) == 0)
770     {
771         mhttp_debug("must supply an action");
772         return -2;
773     }
774 
775     if (strcmp(paction, "GET") != 0 &&
776         strcmp(paction, "POST") != 0 &&
777         strcmp(paction, "PUT") != 0 &&
778         strcmp(paction, "DELETE") != 0 &&
779         strcmp(paction, "HEAD") != 0)
780     {
781         mhttp_debug("must supply an action of GET, PUT, POST, DELETE, or HEAD");
782         return -1;
783     }
784     *action = strdup(paction);
785     mhttp_debug("The action is: %s", *action);
786     return 0;
787 }
788 
789 
790 void mhttp_switch_debug(int set)
791 {
792     if (!mhttp_first_init)
793         mhttp_init();
794 
795     if (set > 0)
796     {
797         mhttp_lets_debug = true;
798 #ifdef GOTSSL
799         mhttp_debug("%s", "switched on debugging(SSL Support running)...");
800 #else
801         mhttp_debug("%s", "switched on debugging...");
802 #endif
803     } else {
804         mhttp_lets_debug = false;
805     }
806 
807 }
808 
809 
810 void mhttp_set_protocol(int proto)
811 {
812 
813     if (!mhttp_first_init)
814         mhttp_init();
815      mhttp_protocol = proto;
816 
817 }
818 
819 
820 int mhttp_get_status_code(void)
821 {
822 
823     return mhttp_rcode;
824 
825 }
826 
827 
828 int mhttp_get_response_length(void)
829 {
830 
831     return mhttp_response_length;
832 
833 }
834 
835 
836 char *mhttp_get_reason(void)
837 {
838 
839   if (mhttp_reason != NULL){
840     mhttp_debug("the reason is: %s", mhttp_reason);
841     return strdup(mhttp_reason);
842   } else {
843     return NULL;
844   }
845 
846 }
847 
848 
849 char *mhttp_get_response(void)
850 {
851 
852     return mhttp_response;
853 
854 }
855 
856 
857 char *mhttp_get_response_headers(void)
858 {
859 
860     return strdup(mhttp_resp_headers);
861 
862 }
863 
864 
865 void mhttp_reset(void)
866 {
867 
868   int i;
869 
870     if (!mhttp_first_init)
871         mhttp_init();
872     if (mhttp_response != NULL){
873         free(mhttp_response);
874         mhttp_response = NULL;
875         mhttp_debug("reset the response");
876     }
877     mhttp_response_length = 0;
878     if (mhttp_reason != NULL){
879         free(mhttp_reason);
880         mhttp_reason = NULL;
881         mhttp_debug("reset the reason");
882     }
883     if (mhttp_body_set_flag)
884         free(mhttp_body);
885     mhttp_body_set_flag = false;
886     mhttp_rcode = 0;
887 
888     mhttp_debug("finished reset");
889 
890 }
891 
892 
893 void mhttp_init(void)
894 {
895 
896   int i;
897 
898   mhttp_first_init = true;
899   for (i = 0; i < mhttp_hcnt; i++)
900   {
901       free(mhttp_headers[i]);
902       mhttp_debug("freeing header");
903       mhttp_headers[i] = NULL;
904   }
905   mhttp_hcnt = 0;
906   mhttp_lets_debug = false;
907   mhttp_protocol = 0;
908   mhttp_host_hdr = false;
909   mhttp_reset();
910   mhttp_debug("finished init");
911 }
912 
913 
914 void mhttp_add_header(char *hdr)
915 {
916 
917     if (!mhttp_first_init)
918         mhttp_init();
919 
920     /* Do we have a Host Header?                          */
921     if (! mhttp_host_hdr && strncmp("Host:", hdr, 5) == 0)
922         mhttp_host_hdr = true;
923 
924     mhttp_headers[mhttp_hcnt++] = strdup(hdr);
925     mhttp_debug("request header %s", mhttp_headers[mhttp_hcnt - 1]);
926     mhttp_headers[mhttp_hcnt] = NULL;
927 
928 }
929 
930 
931 void mhttp_set_body(char *bdy)
932 {
933 
934     // assumes body is a string XXX
935     if (!mhttp_first_init)
936         mhttp_init();
937     mhttp_body = strdup(bdy);
938     mhttp_debug("setting body: %s", mhttp_body);
939     mhttp_body_set_flag = true;
940 
941 }
942 
943 
944 int read_socket(mhttp_conn_t conn, void *buf){
945   int returnval;
946 
947 #ifdef GOTSSL
948   if (conn->is_ssl){
949       returnval = SSL_read (conn->ssl, buf, READ_BUF);
950       if (returnval == -1){
951           mhttp_debug("SSL_read failed - abort everything");
952           ERR_print_errors_fp(stderr);
953           return -16;
954       }
955   } else {
956       returnval = read(conn->fd, buf, READ_BUF);
957   }
958 #else
959   returnval = read(conn->fd, buf, READ_BUF);
960 #endif
961   return returnval;
962 
963 }
964 
965 
966 
967 int write_socket(mhttp_conn_t conn, const void *buf, size_t count){
968   int returnval;
969 
970 #ifdef GOTSSL
971   if (conn->is_ssl){
972       //mhttp_debug("writing to ssl connection");
973       returnval = SSL_write (conn->ssl, buf, count);
974       if (returnval == -1){
975           mhttp_debug("SSL_write failed - abort everything");
976           ERR_print_errors_fp(stderr);
977           return -17;
978       }
979   } else {
980       returnval = write(conn->fd, buf, count);
981   }
982 #else
983   returnval = write(conn->fd, buf, count);
984 #endif
985   return returnval;
986 
987 }
988 
989 
990 int mhttp_connect_inet_addr(const char *hostname, unsigned short int port)
991 {
992   int inet_socket; /* socket descriptor */
993   struct sockaddr_in inet_address; /* IP/port of the remote host to connect to */
994 
995   if ( mhttp_build_inet_addr(&inet_address, hostname, port) < 0 )
996       return -1;
997 
998   /* socket(domain, type, protocol) */
999   inet_socket = socket(PF_INET, SOCK_STREAM, 0);
1000 
1001   mhttp_debug("socket no: %d", inet_socket);
1002   /* domain is PF_INET(internet/IPv4 domain) *
1003    * type is SOCK_STREAM(tcp) *
1004    * protocol is 0(only one SOCK_STREAM type in the PF_INET domain
1005    */
1006 
1007   if (inet_socket < 0)
1008   {
1009     /* socket returns -1 on error */
1010     perror("socket(PF_INET, SOCK_STREAM, 0) error");
1011     mhttp_debug("socket(PF_INET, SOCK_STREAM, 0) error");
1012     return -2;
1013   }
1014 
1015   /* connect(sockfd, serv_addr, addrlen) */
1016   if(connect(inet_socket, (struct sockaddr *)&inet_address, sizeof(struct sockaddr_in)) < 0)
1017   {
1018     /* connect returns -1 on error */
1019     perror("connect(...) error");
1020     mhttp_debug("connect(...) error");
1021     return -3;
1022   }
1023 
1024   return inet_socket;
1025 }
1026 
1027 
1028 int mhttp_build_inet_addr(struct sockaddr_in *addr, const char *hostname, unsigned short int port)
1029 {
1030   struct hostent *host_entry;
1031 
1032   /* gethostbyname(name) */
1033   host_entry = gethostbyname(hostname);
1034 
1035   if(host_entry == NULL)
1036   {
1037     /* gethostbyname returns NULL on error */
1038     herror("gethostbyname failed");
1039     mhttp_debug("gethostbyname failed");
1040     return -1;
1041   }
1042 
1043   /* memcpy(dest, src, length) */
1044   memcpy(&addr->sin_addr.s_addr, host_entry->h_addr_list[0], host_entry->h_length);
1045   /* copy the address to the sockaddr_in struct. */
1046 
1047   /* set the family type (PF_INET) */
1048   addr->sin_family = host_entry->h_addrtype;
1049 
1050   /* addr->sin_port = port won't work because they are different byte
1051    * orders
1052    */
1053   addr->sin_port = htons(port);
1054 
1055   /* just to be pedantic... */
1056   return 1;
1057 }
1058 
1059 /* debug logging */
1060 void mhttp_debug(const char *msgfmt, ...)
1061 {
1062     va_list ap;
1063     char *pos, message[MAX_STR];
1064     int sz;
1065     time_t t;
1066 
1067     if (!mhttp_lets_debug)
1068         return;
1069 
1070     /* timestamp */
1071     t = time(NULL);
1072     pos = ctime(&t);
1073     sz = strlen(pos);
1074     /* chop off the \n */
1075     pos[sz-1]='\0';
1076 
1077     /* insert the header */
1078 
1079     snprintf(message, MAX_STR, "mhttp debug:%s: ", pos);
1080 
1081     /* find the end and attach the rest of the msg */
1082     for (pos = message; *pos != '\0'; pos++); //empty statement
1083     sz = pos - message;
1084     va_start(ap, msgfmt);
1085     vsnprintf(pos, MAX_STR - sz, msgfmt, ap);
1086     fprintf(stderr,"%s", message);
1087     fprintf(stderr, "\n");
1088     fflush(stderr);
1089 }
1090 
1091