Incremental work.
[doldaconnect.git] / common / http.c
1 /*
2  *  Dolda Connect - Modular multiuser Direct Connect-style client
3  *  Copyright (C) 2007 Fredrik Tolf (fredrik@dolda2000.com)
4  *  
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *  
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *  
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <sys/socket.h>
25 #include <sys/poll.h>
26
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 #include <utils.h>
31 #include <http.h>
32
33 #define STATE_SYN 0
34 #define STATE_TXREQ 1
35
36 void freeurl(struct hturlinfo *ui)
37 {
38     free(ui->host);
39     free(ui->path);
40     free(ui->query);
41     free(ui);
42 }
43
44 struct hturlinfo *parseurl(char *url)
45 {
46     char *p, *p2, *p3;
47     struct hturlinfo *ui;
48     
49     if(strncmp(url, "http://", 7))
50         return(NULL);
51     ui = memset(smalloc(sizeof(*ui)), 0, sizeof(*ui));
52     p = url + 7;
53     if((p2 = strchr(p, '/')) != NULL)
54         *(p2++) = 0;
55     if((p3 = strrchr(p, ':')) != NULL) {
56         *(p3++) = 0;
57         ui->port = atoi(p3);
58     } else {
59         ui->port = 80;
60     }
61     ui->host = sstrdup(p);
62     if(p2 == NULL) {
63         ui->path = sstrdup("/");
64     } else {
65         p = p2;
66         if((p2 = strchr(p, '?')) != NULL)
67             *(p2++) = 0;
68         ui->path = sstrdup(p);
69     }
70     if(p2 == NULL) {
71         ui->query = sstrdup("");
72     } else {
73         ui->query = sstrdup(p2);
74     }
75     return(ui);
76 }
77
78 static struct hturlinfo *dupurl(struct hturlinfo *ui)
79 {
80     struct hturlinfo *new;
81     
82     new = memset(smalloc(sizeof(*new)), 0, sizeof(*new));
83     new->host = sstrdup(ui->host);
84     new->port = ui->port;
85     new->path = sstrdup(ui->path);
86     new->query = sstrdup(ui->query);
87     return(new);
88 }
89
90 static struct addrinfo *resolvtcp(char *name, int port)
91 {
92     struct addrinfo hint, *ret;
93     char tmp[32];
94     
95     memset(&hint, 0, sizeof(hint));
96     hint.ai_socktype = SOCK_STREAM;
97     hint.ai_flags = AI_NUMERICSERV | AI_CANONNAME;
98     snprintf(tmp, sizeof(tmp), "%i", port);
99     if(!getaddrinfo(name, tmp, &hint, &ret))
100         return(ret);
101     return(NULL);
102 }
103
104 void freehtconn(struct htconn *cn)
105 {
106     if(cn->outbuf != NULL)
107         free(cn->outbuf);
108     if(cn->inbuf != NULL)
109         free(cn->inbuf);
110     freeurl(cn->url);
111     freeaddrinfo(cn->ailist);
112     if(cn->fd != -1)
113         close(cn->fd);
114     free(cn);
115 }
116
117 struct htconn *htconnect(struct hturlinfo *ui)
118 {
119     struct htconn *cn;
120     
121     cn = memset(smalloc(sizeof(*cn)), 0, sizeof(*cn));
122     cn->fd = -1;
123     cn->url = dupurl(ui);
124     cn->ailist = resolvtcp(ui->host, ui->port);
125     return(cn);
126 }
127
128 int htpollflags(struct htconn *hc)
129 {
130     int ret;
131     
132     ret = POLLIN;
133     if(hc->outbufdata > 0)
134         ret |= POLLOUT;
135     return(ret);
136 }
137
138 int htprocess(struct htconn *hc)
139 {
140     int ret;
141     socklen_t optlen;
142     
143     if(hc->state == STATE_SYN) {
144         if(hc->fd != -1) {
145             optlen = sizeof(ret);
146             getsockopt(hc->fd, SOL_SOCKET, SO_ERROR, &ret, &optlen);
147             if(ret) {
148                 hc->fd = -1;
149             } else {
150                 hc->state = STATE_TXREQ;
151             }
152         }
153         if(hc->fd == -1) {
154             if(hc->curai == NULL)
155                 hc->curai = hc->ailist;
156             else
157                 hc->curai = hc->curai->ai_next;
158             if(hc->curai == NULL) {
159                 /* Bleh! Linux and BSD don't share any good
160                  * errno for this. */
161                 errno = ENOENT;
162                 return(-1);
163             }
164         }
165     }
166     return(0);
167 }