1// This file is part of SmallBASIC
2//
3// BSD sockets driver (byte-stream client)
4//
5// This program is distributed under the terms of the GPL v2.0 or later
6// Download the GNU Public License (GPL) from www.gnu.org
7//
8// Copyright(C) 2000 Nicholas Christopoulos
9
10#include "common/inet.h"
11#include "common/device.h"
12#include "common/fs_socket_client.h"
13#include "common/sberr.h"
14#include <time.h>
15
16int sockcl_open(dev_file_t *f) {
17 // open "SOCL:smallbasic.sf.net:80" as #1
18 // open "SOCL:80" as #2
19 f->drv_dw[0] = 1;
20 char *p = strchr(f->name + 5, ':');
21 if (!p) {
22 int port = xstrtol(f->name + 5);
23 f->handle = (int) net_listen(port);
24 } else {
25 *p = '\0';
26 char server[255];
27 strlcpy(server, f->name + 5, sizeof(server));
28 *p = ':';
29 int port = xstrtol(p + 1);
30 f->handle = (int) net_connect(server, port);
31 }
32
33 if (f->handle <= 0) {
34 f->handle = -1;
35 f->drv_dw[0] = 0;
36 return 0;
37 }
38
39 return 1;
40}
41
42// open a web server connection
43int http_open(dev_file_t *f) {
44 char host[250];
45 char txbuf[1024];
46 f->port = 0;
47
48 // check for http://
49 if (0 != strncasecmp(f->name, "http://", 7)) {
50 rt_raise("HTTP: INVALID URL");
51 return 0;
52 }
53
54 // check for end of host delimeter
55 char *colon = strchr(f->name + 7, ':');
56 char *slash = strchr(f->name + 7, '/');
57 char *lastSlash;
58
59 // saves the length of the path component in f->drv_dw[1]
60 if (colon) {
61 // http://host:port/resource or http://host:port
62 if (slash) {
63 *slash = 0;
64 f->port = xstrtol(colon + 1);
65 *slash = '/';
66 lastSlash = strrchr(slash, '/');
67 f->drv_dw[1] = lastSlash ? lastSlash - f->name : slash - f->name;
68 } else {
69 f->port = xstrtol(colon + 1);
70 f->drv_dw[1] = strlen(f->name);
71 }
72 *colon = 0;
73 strcpy(host, f->name + 7);
74 *colon = ':';
75 } else if (slash) {
76 // http://host/resource or http://host/
77 *slash = 0;
78 strcpy(host, f->name + 7);
79 *slash = '/';
80 lastSlash = strrchr(slash, '/');
81 f->drv_dw[1] = lastSlash ? lastSlash - f->name : slash - f->name;
82 } else {
83 // http://host
84 strlcpy(host, f->name + 7, sizeof(host));
85 f->drv_dw[1] = strlen(f->name);
86 }
87
88 f->drv_dw[0] = 1;
89 if (f->port == 0) {
90 f->port = 80;
91 }
92
93 socket_t s = net_connect(host, f->port);
94 f->handle = (socket_t) s;
95
96 if (f->handle <= 0) {
97 f->handle = -1;
98 f->drv_dw[0] = 0;
99 f->port = 0;
100 return 0;
101 }
102
103 sprintf(txbuf, "GET %s HTTP/1.0\r\n"
104 "Host: %s\r\n"
105 "Accept: */*\r\n"
106 "Accept-Language: en-au\r\n"
107 "User-Agent: SmallBASIC\r\n", slash ? slash : "/", host);
108 if (f->drv_dw[2]) {
109 // If-Modified-Since: Sun, 03 Apr 2005 04:45:47 GMT
110 strcat(txbuf, "If-Modified-Since: ");
111 strftime(txbuf + strlen(txbuf), 60, "%a, %d %b %Y %H:%M:%S %Z\r\n",
112 localtime((time_t *) &f->drv_dw[2]));
113 }
114 strcat(txbuf, "\r\n");
115 net_print(s, txbuf);
116 return 1;
117}
118
119// read from a web server connection
120int http_read(dev_file_t *f, var_t *var_p) {
121 char rxbuff[1024];
122 int inHeader = 1;
123 int httpOK = 0;
124
125 v_free(var_p);
126 var_p->type = V_STR;
127 var_p->v.p.ptr = 0;
128 var_p->v.p.length = 0;
129
130 while (1) {
131 int bytes = net_read(f->handle, (char *) rxbuff, sizeof(rxbuff));
132 if (bytes == -1) {
133 httpOK = 0;
134 break;
135 } else if (bytes == 0) {
136 break; // no more data
137 }
138 // assumes http header < 1024 bytes
139 if (inHeader) {
140 int i = 0;
141 while (1) {
142 int iattr = i;
143 while (rxbuff[i] != 0 && rxbuff[i] != '\n') {
144 i++;
145 }
146 if (rxbuff[i] == 0) {
147 inHeader = 0;
148 break; // no end delimiter
149 }
150 if (rxbuff[i + 2] == '\n') {
151 var_p->v.p.length = bytes - i - 3;
152 var_p->v.p.ptr = malloc(var_p->v.p.length + 1);
153 var_p->v.p.owner = 1;
154 memcpy(var_p->v.p.ptr, rxbuff + i + 3, var_p->v.p.length);
155 var_p->v.p.ptr[var_p->v.p.length] = 0;
156 inHeader = 0;
157 break; // found start of content
158 }
159 // null terminate attribute (in \r)
160 rxbuff[i - 1] = 0;
161 i++;
162 if (strstr(rxbuff + iattr, "200 OK") != 0) {
163 httpOK = 1;
164 }
165 if (strncmp(rxbuff + iattr, "Location: ", 10) == 0) {
166 // handle redirection
167 sockcl_close(f);
168 strlcpy(f->name, rxbuff + iattr + 10, sizeof(f->name));
169 if (http_open(f) == 0) {
170 return 0;
171 }
172 break; // scan next header
173 }
174 }
175 } else {
176 var_p->v.p.ptr = realloc(var_p->v.p.ptr, var_p->v.p.length + bytes + 1);
177 memcpy(var_p->v.p.ptr + var_p->v.p.length, rxbuff, bytes);
178 var_p->v.p.length += bytes;
179 var_p->v.p.ptr[var_p->v.p.length] = 0;
180 }
181 }
182
183 return httpOK;
184}
185
186int sockcl_close(dev_file_t *f) {
187 net_disconnect((socket_t) (long) f->handle);
188 f->drv_dw[0] = 0;
189 f->handle = -1;
190 return 1;
191}
192
193/*
194 * write to a socket
195 */
196int sockcl_write(dev_file_t *f, byte *data, uint32_t size) {
197 net_send((socket_t) (long) f->handle, (char *)data, size);
198 return size;
199}
200
201/*
202 * read from a socket
203 */
204int sockcl_read(dev_file_t *f, byte *data, uint32_t size) {
205 int result;
206 if (f->handle != -1) {
207 f->drv_dw[0] = (uint32_t) net_input((socket_t) (long) f->handle, (char *)data, size, NULL);
208 result = (((long) f->drv_dw[0]) <= 0) ? 0 : (long) f->drv_dw[0];
209 } else {
210 err_network();
211 data[0] = 0;
212 result = 0;
213 }
214 return result;
215}
216
217/*
218 * Returns true (EOF) if the connection is broken
219 */
220int sockcl_eof(dev_file_t *f) {
221 return (((long) f->drv_dw[0]) <= 0) ? 1 : 0;
222}
223
224/*
225 * returns the size of the data which are waiting in stream's queue
226 */
227int sockcl_length(dev_file_t *f) {
228 return net_peek((socket_t) (long) f->handle);
229}
230