curl_multi_fdset: correct fdset with FTP PORT use
[platform/upstream/curl.git] / lib / gopher.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  ***************************************************************************/
22
23 #include "setup.h"
24
25 #ifndef CURL_DISABLE_GOPHER
26
27 #ifdef HAVE_SYS_SOCKET_H
28 #include <sys/socket.h>
29 #endif
30 #ifdef HAVE_NETINET_IN_H
31 #include <netinet/in.h>
32 #endif
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36 #ifdef HAVE_NETDB_H
37 #include <netdb.h>
38 #endif
39 #ifdef HAVE_ARPA_INET_H
40 #include <arpa/inet.h>
41 #endif
42 #ifdef HAVE_NET_IF_H
43 #include <net/if.h>
44 #endif
45 #ifdef HAVE_SYS_IOCTL_H
46 #include <sys/ioctl.h>
47 #endif
48
49 #ifdef HAVE_SYS_PARAM_H
50 #include <sys/param.h>
51 #endif
52
53 #ifdef HAVE_SYS_SELECT_H
54 #include <sys/select.h>
55 #endif
56
57 #include "urldata.h"
58 #include <curl/curl.h>
59 #include "transfer.h"
60 #include "sendf.h"
61
62 #include "progress.h"
63 #include "strequal.h"
64 #include "gopher.h"
65 #include "rawstr.h"
66 #include "select.h"
67 #include "url.h"
68 #include "warnless.h"
69
70 #define _MPRINTF_REPLACE /* use our functions only */
71 #include <curl/mprintf.h>
72
73 /* The last #include file should be: */
74 #include "memdebug.h"
75
76
77 /*
78  * Forward declarations.
79  */
80
81 static CURLcode gopher_do(struct connectdata *conn, bool *done);
82
83 /*
84  * Gopher protocol handler.
85  * This is also a nice simple template to build off for simple
86  * connect-command-download protocols.
87  */
88
89 const struct Curl_handler Curl_handler_gopher = {
90   "GOPHER",                             /* scheme */
91   ZERO_NULL,                            /* setup_connection */
92   gopher_do,                            /* do_it */
93   ZERO_NULL,                            /* done */
94   ZERO_NULL,                            /* do_more */
95   ZERO_NULL,                            /* connect_it */
96   ZERO_NULL,                            /* connecting */
97   ZERO_NULL,                            /* doing */
98   ZERO_NULL,                            /* proto_getsock */
99   ZERO_NULL,                            /* doing_getsock */
100   ZERO_NULL,                            /* domore_getsock */
101   ZERO_NULL,                            /* perform_getsock */
102   ZERO_NULL,                            /* disconnect */
103   ZERO_NULL,                            /* readwrite */
104   PORT_GOPHER,                          /* defport */
105   CURLPROTO_GOPHER,                     /* protocol */
106   PROTOPT_NONE                          /* flags */
107 };
108
109 static CURLcode gopher_do(struct connectdata *conn, bool *done)
110 {
111   CURLcode result=CURLE_OK;
112   struct SessionHandle *data=conn->data;
113   curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
114
115   curl_off_t *bytecount = &data->req.bytecount;
116   char *path = data->state.path;
117   char *sel;
118   char *sel_org = NULL;
119   ssize_t amount, k;
120
121   *done = TRUE; /* unconditionally */
122
123   /* Create selector. Degenerate cases: / and /1 => convert to "" */
124   if(strlen(path) <= 2)
125     sel = (char *)"";
126   else {
127     char *newp;
128     size_t j, i;
129     int len;
130
131     /* Otherwise, drop / and the first character (i.e., item type) ... */
132     newp = path;
133     newp+=2;
134
135     /* ... then turn ? into TAB for search servers, Veronica, etc. ... */
136     j = strlen(newp);
137     for(i=0; i<j; i++)
138       if(newp[i] == '?')
139         newp[i] = '\x09';
140
141     /* ... and finally unescape */
142     sel = curl_easy_unescape(data, newp, 0, &len);
143     if(!sel)
144       return CURLE_OUT_OF_MEMORY;
145     sel_org = sel;
146   }
147
148   /* We use Curl_write instead of Curl_sendf to make sure the entire buffer is
149      sent, which could be sizeable with long selectors. */
150   k = curlx_uztosz(strlen(sel));
151
152   for(;;) {
153     result = Curl_write(conn, sockfd, sel, k, &amount);
154     if(CURLE_OK == result) { /* Which may not have written it all! */
155       result = Curl_client_write(conn, CLIENTWRITE_HEADER, sel, amount);
156       if(result) {
157         Curl_safefree(sel_org);
158         return result;
159       }
160       k -= amount;
161       sel += amount;
162       if(k < 1)
163         break; /* but it did write it all */
164     }
165     else {
166       failf(data, "Failed sending Gopher request");
167       Curl_safefree(sel_org);
168       return result;
169     }
170     /* Don't busyloop. The entire loop thing is a work-around as it causes a
171        BLOCKING behavior which is a NO-NO. This function should rather be
172        split up in a do and a doing piece where the pieces that aren't
173        possible to send now will be sent in the doing function repeatedly
174        until the entire request is sent.
175
176        Wait a while for the socket to be writable. Note that this doesn't
177        acknowledge the timeout.
178     */
179     Curl_socket_ready(CURL_SOCKET_BAD, sockfd, 100);
180   }
181
182   Curl_safefree(sel_org);
183
184   /* We can use Curl_sendf to send the terminal \r\n relatively safely and
185      save allocing another string/doing another _write loop. */
186   result = Curl_sendf(sockfd, conn, "\r\n");
187   if(result != CURLE_OK) {
188     failf(data, "Failed sending Gopher request");
189     return result;
190   }
191   result = Curl_client_write(conn, CLIENTWRITE_HEADER, (char *)"\r\n", 2);
192   if(result)
193     return result;
194
195   Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount,
196                       -1, NULL); /* no upload */
197   return CURLE_OK;
198 }
199 #endif /*CURL_DISABLE_GOPHER*/