Subversion Repositories SvarDOS

Rev

Rev 342 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 342 Rev 344
1
/*
1
/*
2
 * pkgnet - pulls SvarDOS packages from the project's online repository
2
 * pkgnet - pulls SvarDOS packages from the project's online repository
3
 * Copyright (C) 2021 Mateusz Viste
3
 * Copyright (C) 2021 Mateusz Viste
4
 *
4
 *
5
 * PUBLISHED UNDER THE TERMS OF THE MIT LICENSE
5
 * PUBLISHED UNDER THE TERMS OF THE MIT LICENSE
6
 *
6
 *
7
 * COPYRIGHT (C) 2016-2021 MATEUSZ VISTE, ALL RIGHTS RESERVED.
7
 * COPYRIGHT (C) 2016-2021 MATEUSZ VISTE, ALL RIGHTS RESERVED.
8
 *
8
 *
9
 * Permission is hereby granted, free of charge, to any person obtaining a
9
 * Permission is hereby granted, free of charge, to any person obtaining a
10
 * copy of this software and associated documentation files (the "Software"),
10
 * copy of this software and associated documentation files (the "Software"),
11
 * to deal in the Software without restriction, including without limitation
11
 * to deal in the Software without restriction, including without limitation
12
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13
 * and/or sell copies of the Software, and to permit persons to whom the
13
 * and/or sell copies of the Software, and to permit persons to whom the
14
 * Software is furnished to do so, subject to the following conditions:
14
 * Software is furnished to do so, subject to the following conditions:
15
 *
15
 *
16
 * The above copyright notice and this permission notice shall be included in
16
 * The above copyright notice and this permission notice shall be included in
17
 * all copies or substantial portions of the Software.
17
 * all copies or substantial portions of the Software.
18
 *
18
 *
19
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25
 * DEALINGS IN THE SOFTWARE.
25
 * DEALINGS IN THE SOFTWARE.
26
 *
26
 *
27
 * http://svardos.osdn.io
27
 * http://svardos.osdn.io
28
 */
28
 */
29
 
29
 
-
 
30
#include <direct.h> /* opendir() and friends */
30
#include <stdio.h>
31
#include <stdio.h>
31
#include <stdlib.h>
32
#include <stdlib.h>
32
#include <string.h>
33
#include <string.h>
33
#include <time.h>
34
#include <time.h>
34
 
35
 
35
#include "net.h"
36
#include "net.h"
36
#include "unchunk.h"
37
#include "unchunk.h"
37
 
38
 
-
 
39
#include "../pkg/lsm.h"
-
 
40
 
-
 
41
 
38
#define PVER "20210903"
42
#define PVER "20210903"
39
#define PDATE "2021"
43
#define PDATE "2021"
40
 
44
 
41
#define HOSTADDR "svardos.osdn.io"
45
#define HOSTADDR "svardos.osdn.io"
42
 
46
 
43
 
47
 
44
/* returns length of all http headers, or 0 if uncomplete yet */
48
/* returns length of all http headers, or 0 if uncomplete yet */
45
static unsigned short detecthttpheadersend(const unsigned char *buff) {
49
static unsigned short detecthttpheadersend(const unsigned char *buff) {
46
  char lastbyteislf = 0;
50
  char lastbyteislf = 0;
47
  unsigned short i;
51
  unsigned short i;
48
  for (i = 0; buff[i] != 0; i++) {
52
  for (i = 0; buff[i] != 0; i++) {
49
    if (buff[i] == '\r') continue; /* ignore CR characters */
53
    if (buff[i] == '\r') continue; /* ignore CR characters */
50
    if (buff[i] != '\n') {
54
    if (buff[i] != '\n') {
51
      lastbyteislf = 0;
55
      lastbyteislf = 0;
52
      continue;
56
      continue;
53
    }
57
    }
54
    /* cur byte is LF -> if last one was also LF then this is an empty line, meaning headers are over */
58
    /* cur byte is LF -> if last one was also LF then this is an empty line, meaning headers are over */
55
    if (lastbyteislf == 0) {
59
    if (lastbyteislf == 0) {
56
      lastbyteislf = 1;
60
      lastbyteislf = 1;
57
      continue;
61
      continue;
58
    }
62
    }
59
    /* end of headers! return length of headers */
63
    /* end of headers! return length of headers */
60
    return(i + 1); /* add 1 to skip the current \n character */
64
    return(i + 1); /* add 1 to skip the current \n character */
61
  }
65
  }
62
  return(0);
66
  return(0);
63
}
67
}
64
 
68
 
65
 
69
 
66
static void help(void) {
70
static void help(void) {
67
  puts("pkgnet ver " PVER " -- Copyright (C) " PDATE " Mateusz Viste");
71
  puts("pkgnet ver " PVER " -- Copyright (C) " PDATE " Mateusz Viste");
68
  puts("");
72
  puts("");
69
  puts("pkgnet is the SvarDOS package downloader.");
73
  puts("pkgnet is the SvarDOS package downloader.");
70
  puts("");
74
  puts("");
71
  puts("usage:  pkgnet search <term>");
75
  puts("usage:  pkgnet search <term>");
72
  puts("        pkgnet pull <package>");
76
  puts("        pkgnet pull <package>");
73
  puts("        pkgnet checkup");
77
  puts("        pkgnet checkup");
74
  puts("");
78
  puts("");
75
  puts("actions:");
79
  puts("actions:");
76
  puts(" search   - asks remote repository for the list of matching packages");
80
  puts(" search   - asks remote repository for the list of matching packages");
77
  puts(" pull     - downloads package into current directory");
81
  puts(" pull     - downloads package into current directory");
78
  puts(" checkup  - lists updates available for your system");
82
  puts(" checkup  - lists updates available for your system");
79
  puts("");
83
  puts("");
80
  printf("Watt32 kernel: %s", net_engine());
84
  printf("Watt32 kernel: %s", net_engine());
81
  puts("");
85
  puts("");
82
  puts("");
86
  puts("");
83
}
87
}
84
 
88
 
85
 
89
 
86
/* parses command line arguments and fills outfname and url accordingly
90
/* parses command line arguments and fills outfname and url accordingly
87
 * returns 0 on success, non-zero otherwise */
91
 * returns 0 on success, non-zero otherwise */
88
static int parseargv(int argc, char * const *argv, char *outfname, char *url, int *ispost) {
92
static int parseargv(int argc, char * const *argv, char *outfname, char *url, int *ispost) {
89
  *outfname = 0;
93
  *outfname = 0;
90
  *url = 0;
94
  *url = 0;
91
  *ispost = 0;
95
  *ispost = 0;
92
  if ((argc == 3) && (strcasecmp(argv[1], "search") == 0)) {
96
  if ((argc == 3) && (strcasecmp(argv[1], "search") == 0)) {
93
    sprintf(url, "/repo/?a=search&p=%s", argv[2]);
97
    sprintf(url, "/repo/?a=search&p=%s", argv[2]);
94
  } else if ((argc == 3) && (strcasecmp(argv[1], "pull") == 0)) {
98
  } else if ((argc == 3) && (strcasecmp(argv[1], "pull") == 0)) {
95
    if ((strlen(argv[2]) > 8) || (argv[2][0] == 0)) {
99
    if ((strlen(argv[2]) > 8) || (argv[2][0] == 0)) {
96
      puts("ERROR: package name must be 8 characters maximum");
100
      puts("ERROR: package name must be 8 characters maximum");
97
      return(-1);
101
      return(-1);
98
    }
102
    }
99
    sprintf(url, "/repo/?a=pull&p=%s", argv[2]);
103
    sprintf(url, "/repo/?a=pull&p=%s", argv[2]);
100
    sprintf(outfname, "%s.zip", argv[2]);
104
    sprintf(outfname, "%s.zip", argv[2]);
101
  } else if ((argc == 2) && (strcasecmp(argv[1], "checkup") == 0)) {
105
  } else if ((argc == 2) && (strcasecmp(argv[1], "checkup") == 0)) {
102
    sprintf(url, "/repo/?a=checkup");
106
    sprintf(url, "/repo/?a=checkup");
103
    *ispost = 1;
107
    *ispost = 1;
104
  } else {
108
  } else {
105
    help();
109
    help();
106
    return(-1);
110
    return(-1);
107
  }
111
  }
108
  return(0);
112
  return(0);
109
}
113
}
110
 
114
 
111
 
115
 
112
static int htget_headers(unsigned char *buffer, size_t buffersz, struct net_tcpsocket *sock, int *httpcode, int *ischunked)  {
116
static int htget_headers(unsigned char *buffer, size_t buffersz, struct net_tcpsocket *sock, int *httpcode, int *ischunked)  {
113
  unsigned char *buffptr = buffer;
117
  unsigned char *buffptr = buffer;
114
  unsigned short bufflen = 0;
118
  unsigned short bufflen = 0;
115
  int byteread;
119
  int byteread;
116
  time_t starttime = time(NULL);
120
  time_t starttime = time(NULL);
117
  for (;;) {
121
  for (;;) {
118
    byteread = net_recv(sock, buffptr, buffersz - (bufflen + 1)); /* -1 because I will append a NULL terminator */
122
    byteread = net_recv(sock, buffptr, buffersz - (bufflen + 1)); /* -1 because I will append a NULL terminator */
119
 
123
 
120
    if (byteread > 0) { /* got data */
124
    if (byteread > 0) { /* got data */
121
      int hdlen;
125
      int hdlen;
122
      bufflen += byteread;
126
      bufflen += byteread;
123
      buffptr += byteread;
127
      buffptr += byteread;
124
      buffer[bufflen] = 0;
128
      buffer[bufflen] = 0;
125
      hdlen = detecthttpheadersend(buffer);
129
      hdlen = detecthttpheadersend(buffer);
126
      if (hdlen > 0) { /* full headers - parse http code and continue processing */
130
      if (hdlen > 0) { /* full headers - parse http code and continue processing */
127
        int i;
131
        int i;
128
        buffer[hdlen - 1] = 0;
132
        buffer[hdlen - 1] = 0;
129
        /* find the first space (HTTP/1.1 200 OK) */
133
        /* find the first space (HTTP/1.1 200 OK) */
130
        for (i = 0; i < 16; i++) {
134
        for (i = 0; i < 16; i++) {
131
          if ((buffer[i] == ' ') || (buffer[i] == 0)) break;
135
          if ((buffer[i] == ' ') || (buffer[i] == 0)) break;
132
        }
136
        }
133
        if (buffer[i] != ' ') return(-1);
137
        if (buffer[i] != ' ') return(-1);
134
        *httpcode = atoi((char *)(buffer + i + 1));
138
        *httpcode = atoi((char *)(buffer + i + 1));
135
        /* switch all headers to low-case so it is easier to parse them */
139
        /* switch all headers to low-case so it is easier to parse them */
136
        for (i = 0; i < hdlen; i++) if ((buffer[i] >= 'A') && (buffer[i] <= 'Z')) buffer[i] += ('a' - 'A');
140
        for (i = 0; i < hdlen; i++) if ((buffer[i] >= 'A') && (buffer[i] <= 'Z')) buffer[i] += ('a' - 'A');
137
        /* look out for chunked transfer encoding */
141
        /* look out for chunked transfer encoding */
138
        {
142
        {
139
        char *lineptr = strstr((char *)buffer, "\ntransfer-encoding:");
143
        char *lineptr = strstr((char *)buffer, "\ntransfer-encoding:");
140
        if (lineptr != NULL) {
144
        if (lineptr != NULL) {
141
          lineptr += 19;
145
          lineptr += 19;
142
          /* replace nearest \r, \n or 0 by 0 */
146
          /* replace nearest \r, \n or 0 by 0 */
143
          for (i = 0; ; i++) {
147
          for (i = 0; ; i++) {
144
            if ((lineptr[i] == '\r') || (lineptr[i] == '\n') || (lineptr[i] == 0)) {
148
            if ((lineptr[i] == '\r') || (lineptr[i] == '\n') || (lineptr[i] == 0)) {
145
              lineptr[i] = 0;
149
              lineptr[i] = 0;
146
              break;
150
              break;
147
            }
151
            }
148
          }
152
          }
149
          /* do I see the 'chunked' word? */
153
          /* do I see the 'chunked' word? */
150
          if (strstr((char *)lineptr, "chunked") != NULL) *ischunked = 1;
154
          if (strstr((char *)lineptr, "chunked") != NULL) *ischunked = 1;
151
        }
155
        }
152
        }
156
        }
153
        /* rewind the buffer */
157
        /* rewind the buffer */
154
        bufflen -= hdlen;
158
        bufflen -= hdlen;
155
        memmove(buffer, buffer + hdlen, bufflen);
159
        memmove(buffer, buffer + hdlen, bufflen);
156
        return(bufflen); /* move to body processing now */
160
        return(bufflen); /* move to body processing now */
157
      }
161
      }
158
 
162
 
159
    } else if (byteread < 0) { /* abort on error */
163
    } else if (byteread < 0) { /* abort on error */
160
      return(-2); /* unexpected end of connection (while waiting for all http headers) */
164
      return(-2); /* unexpected end of connection (while waiting for all http headers) */
161
 
165
 
162
    } else { /* else no data received - look for timeout and release a cpu cycle */
166
    } else { /* else no data received - look for timeout and release a cpu cycle */
163
      if (time(NULL) - starttime > 20) return(-3); /* TIMEOUT! */
167
      if (time(NULL) - starttime > 20) return(-3); /* TIMEOUT! */
164
      _asm int 28h; /* release a CPU cycle */
168
      _asm int 28h; /* release a CPU cycle */
165
    }
169
    }
166
  }
170
  }
167
}
171
}
168
 
172
 
169
 
173
 
170
/* provides body data of the POST query for checkup actions
174
/* provides body data of the POST query for checkup actions
171
   fills buff with data and returns data length.
175
   fills buff with data and returns data length.
172
   must be called repeateadly until zero-lengh is returned */
176
   must be called repeateadly until zero-lengh is returned */
173
static unsigned short checkupdata(char *buff) {
177
static unsigned short checkupdata(char *buff) {
-
 
178
  static char *dosdir = NULL;
-
 
179
  static DIR *dp;
-
 
180
  static struct dirent *ep;
-
 
181
 
-
 
182
  /* make sure I know %DOSDIR% */
-
 
183
  if (dosdir == NULL) {
-
 
184
    dosdir = getenv("DOSDIR");
-
 
185
    if ((dosdir == NULL) || (dosdir[0] == 0)) {
-
 
186
      puts("ERROR: %DOSDIR% not set");
174
  buff[0] = 0;
187
      return(0);
-
 
188
    }
-
 
189
  }
-
 
190
 
-
 
191
  /* if first call, open the package directory */
-
 
192
  if (dp == NULL) {
-
 
193
    sprintf(buff, "%s\\packages", dosdir);
-
 
194
    dp = opendir(buff);
-
 
195
    if (dp == NULL) {
-
 
196
      puts("ERROR: Could not access %DOSDIR%\\packages directory");
-
 
197
      return(0);
-
 
198
    }
-
 
199
  }
-
 
200
 
-
 
201
  for (;;) {
-
 
202
    int tlen;
-
 
203
    char ver[16];
-
 
204
    ep = readdir(dp);   /* readdir() result must never be freed (statically allocated) */
-
 
205
    if (ep == NULL) {   /* no more entries */
-
 
206
      closedir(dp);
175
  return(0);
207
      return(0);
-
 
208
    }
-
 
209
 
-
 
210
    tlen = strlen(ep->d_name);
-
 
211
    if (tlen < 4) continue; /* files must be at least 5 bytes long ("x.lst") */
-
 
212
    if (strcasecmp(ep->d_name + tlen - 4, ".lst") != 0) continue;  /* if not an .lst file, skip it silently */
-
 
213
    ep->d_name[tlen - 4] = 0; /* trim out the ".lst" suffix */
-
 
214
 
-
 
215
    /* load the metadata from %DOSDIR\APPINFO\*.lsm */
-
 
216
    sprintf(buff, "%s\\appinfo\\%s.lsm", dosdir, ep->d_name);
-
 
217
    readlsm(buff, ver, sizeof(ver));
-
 
218
 
-
 
219
    return(sprintf(buff, "%s %s\n", ep->d_name, ver));
-
 
220
  }
176
}
221
}
177
 
222
 
178
 
223
 
179
/* fetch http data from ipaddr using url
224
/* fetch http data from ipaddr using url
180
 * write result to file outfname if not null, or print to stdout otherwise
225
 * write result to file outfname if not null, or print to stdout otherwise
181
 * fills bsum with the BSD sum of the data
226
 * fills bsum with the BSD sum of the data
182
 * is ispost is non-zero, then the request is a POST and its body data is
227
 * is ispost is non-zero, then the request is a POST and its body data is
183
 * obtained through repeated calls to checkupdata()
228
 * obtained through repeated calls to checkupdata()
184
 * returns the length of data obtained, or neg value on error */
229
 * returns the length of data obtained, or neg value on error */
185
static long htget(const char *ipaddr, const char *url, const char *outfname, unsigned short *bsum, int ispost, unsigned char *buffer, size_t buffersz) {
230
static long htget(const char *ipaddr, const char *url, const char *outfname, unsigned short *bsum, int ispost, unsigned char *buffer, size_t buffersz) {
186
  struct net_tcpsocket *sock;
231
  struct net_tcpsocket *sock;
187
  time_t lastactivity, lastprogressoutput = 0;
232
  time_t lastactivity, lastprogressoutput = 0;
188
  int httpcode = -1, ischunked = 0;
233
  int httpcode = -1, ischunked = 0;
189
  int byteread;
234
  int byteread;
190
  long flen = 0, lastflen = 0;
235
  long flen = 0, lastflen = 0;
191
  FILE *fd = NULL;
236
  FILE *fd = NULL;
192
 
237
 
193
  /* unchunk state variable is using a little part of the supplied buffer */
238
  /* unchunk state variable is using a little part of the supplied buffer */
194
  struct unchunk_state *unchstate = (void *)buffer;
239
  struct unchunk_state *unchstate = (void *)buffer;
195
  buffer += sizeof(*unchstate);
240
  buffer += sizeof(*unchstate);
196
  buffersz -= sizeof(*unchstate);
241
  buffersz -= sizeof(*unchstate);
197
  memset(unchstate, 0, sizeof(*unchstate));
242
  memset(unchstate, 0, sizeof(*unchstate));
198
 
243
 
199
  sock = net_connect(ipaddr, 80);
244
  sock = net_connect(ipaddr, 80);
200
  if (sock == NULL) {
245
  if (sock == NULL) {
201
    puts("ERROR: failed to connect to " HOSTADDR);
246
    puts("ERROR: failed to connect to " HOSTADDR);
202
    goto SHITQUIT;
247
    goto SHITQUIT;
203
  }
248
  }
204
 
249
 
205
  /* wait for net_connect() to actually connect */
250
  /* wait for net_connect() to actually connect */
206
  for (;;) {
251
  for (;;) {
207
    int connstate = net_isconnected(sock);
252
    int connstate = net_isconnected(sock);
208
    if (connstate > 0) break;
253
    if (connstate > 0) break;
209
    if (connstate < 0) {
254
    if (connstate < 0) {
210
      puts("ERROR: connection error");
255
      puts("ERROR: connection error");
211
      goto SHITQUIT;
256
      goto SHITQUIT;
212
    }
257
    }
213
    _asm int 28h;  /* DOS idle */
258
    _asm int 28h;  /* DOS idle */
214
  }
259
  }
215
 
260
 
216
  /* socket is connected - send the http request (MUST be HTTP/1.0 because I do not support chunked transfers!) */
261
  /* socket is connected - send the http request (MUST be HTTP/1.0 because I do not support chunked transfers!) */
217
  if (ispost) {
262
  if (ispost) {
218
    snprintf((char *)buffer, buffersz, "POST %s HTTP/1.1\r\nHOST: " HOSTADDR "\r\nUSER-AGENT: pkgnet/" PVER "\r\nTransfer-Encoding: chunked\r\nConnection: close\r\n\r\n", url);
263
    snprintf((char *)buffer, buffersz, "POST %s HTTP/1.1\r\nHOST: " HOSTADDR "\r\nUSER-AGENT: pkgnet/" PVER "\r\nTransfer-Encoding: chunked\r\nConnection: close\r\n\r\n", url);
219
  } else {
264
  } else {
220
    snprintf((char *)buffer, buffersz, "GET %s HTTP/1.1\r\nHOST: " HOSTADDR "\r\nUSER-AGENT: pkgnet/" PVER "\r\nConnection: close\r\n\r\n", url);
265
    snprintf((char *)buffer, buffersz, "GET %s HTTP/1.1\r\nHOST: " HOSTADDR "\r\nUSER-AGENT: pkgnet/" PVER "\r\nConnection: close\r\n\r\n", url);
221
  }
266
  }
222
 
267
 
223
  if (net_send(sock, buffer, strlen((char *)buffer)) != (int)strlen((char *)buffer)) {
268
  if (net_send(sock, buffer, strlen((char *)buffer)) != (int)strlen((char *)buffer)) {
224
    puts("ERROR: failed to send HTTP query to remote server");
269
    puts("ERROR: failed to send HTTP query to remote server");
225
    goto SHITQUIT;
270
    goto SHITQUIT;
226
  }
271
  }
227
 
272
 
228
  /* send chunked data for POST queries */
273
  /* send chunked data for POST queries */
229
  if (ispost) {
274
  if (ispost) {
230
    unsigned short blen;
275
    unsigned short blen;
231
    int hlen;
276
    int hlen;
232
    char *hbuf = (char *)buffer + buffersz - 16;
277
    char *hbuf = (char *)buffer + buffersz - 16;
233
    for (;;) {
278
    do {
234
      blen = checkupdata((char *)buffer);
279
      blen = checkupdata((char *)buffer);
235
      if (blen == 0) { /* last item contains the message trailer */
280
      if (blen == 0) { /* last item contains the message trailer */
236
        hlen = sprintf(hbuf, "0\r\n\r\n");
281
        hlen = sprintf(hbuf, "0\r\n");
237
      } else {
282
      } else {
238
        hlen = sprintf(hbuf, "%X\r\n", blen);
283
        hlen = sprintf(hbuf, "%X\r\n", blen);
239
      }
284
      }
240
      if (net_send(sock, hbuf, hlen) != hlen) {
285
      if (net_send(sock, hbuf, hlen) != hlen) {
241
        puts("ERROR: failed to send POST data to remote server");
286
        puts("ERROR: failed to send POST data to remote server");
242
        goto SHITQUIT;
287
        goto SHITQUIT;
243
      }
288
      }
-
 
289
      /* add trailing CR/LF to buffer as required by chunked mode */
-
 
290
      buffer[blen++] = '\r';
244
      if (blen == 0) break;
291
      buffer[blen++] = '\n';
245
      if (net_send(sock, buffer, blen) != blen) {
292
      if (net_send(sock, buffer, blen) != blen) {
246
        puts("ERROR: failed to send POST data to remote server");
293
        puts("ERROR: failed to send POST data to remote server");
247
        goto SHITQUIT;
294
        goto SHITQUIT;
248
      }
295
      }
249
    }
296
    } while (blen != 2);
250
  }
297
  }
251
 
298
 
252
  /* receive and process HTTP headers */
299
  /* receive and process HTTP headers */
253
  byteread = htget_headers(buffer, buffersz, sock, &httpcode, &ischunked);
300
  byteread = htget_headers(buffer, buffersz, sock, &httpcode, &ischunked);
254
 
301
 
255
  /* transmission error? */
302
  /* transmission error? */
256
  if (byteread < 0) {
303
  if (byteread < 0) {
257
    printf("ERROR: communication error (%d)", byteread);
304
    printf("ERROR: communication error (%d)", byteread);
258
    puts("");
305
    puts("");
259
    goto SHITQUIT;
306
    goto SHITQUIT;
260
  }
307
  }
261
 
308
 
262
  /* open destination file if required and if no server-side error occured */
309
  /* open destination file if required and if no server-side error occured */
263
  if ((httpcode >= 200) && (httpcode <= 299) && (*outfname != 0)) {
310
  if ((httpcode >= 200) && (httpcode <= 299) && (*outfname != 0)) {
264
    fd = fopen(outfname, "wb");
311
    fd = fopen(outfname, "wb");
265
    if (fd == NULL) {
312
    if (fd == NULL) {
266
      printf("ERROR: failed to create file %s", outfname);
313
      printf("ERROR: failed to create file %s", outfname);
267
      puts("");
314
      puts("");
268
      goto SHITQUIT;
315
      goto SHITQUIT;
269
    }
316
    }
270
  }
317
  }
271
 
318
 
272
  /* read body of the answer (and chunk-decode it if needed) */
319
  /* read body of the answer (and chunk-decode it if needed) */
273
  lastactivity = time(NULL);
320
  lastactivity = time(NULL);
274
  for (;; byteread = net_recv(sock, buffer, buffersz - 1)) { /* read 1 byte less because I need to append a NULL terminator when printf'ing the content */
321
  for (;; byteread = net_recv(sock, buffer, buffersz - 1)) { /* read 1 byte less because I need to append a NULL terminator when printf'ing the content */
275
 
322
 
276
    if (byteread > 0) { /* got data */
323
    if (byteread > 0) { /* got data */
277
      lastactivity = time(NULL);
324
      lastactivity = time(NULL);
278
 
325
 
279
      /* unpack data if server transmits in chunked mode */
326
      /* unpack data if server transmits in chunked mode */
280
      if (ischunked) byteread = unchunk(buffer, byteread, unchstate);
327
      if (ischunked) byteread = unchunk(buffer, byteread, unchstate);
281
 
328
 
282
      /* if downloading to file, write stuff to disk */
329
      /* if downloading to file, write stuff to disk */
283
      if (fd != NULL) {
330
      if (fd != NULL) {
284
        int i;
331
        int i;
285
        if (fwrite(buffer, 1, byteread, fd) != byteread) {
332
        if (fwrite(buffer, 1, byteread, fd) != byteread) {
286
          printf("ERROR: failed to write data to file %s after %ld bytes", outfname, flen);
333
          printf("ERROR: failed to write data to file %s after %ld bytes", outfname, flen);
287
          puts("");
334
          puts("");
288
          break;
335
          break;
289
        }
336
        }
290
        flen += byteread;
337
        flen += byteread;
291
        /* update progress once a sec */
338
        /* update progress once a sec */
292
        if (lastprogressoutput != lastactivity) {
339
        if (lastprogressoutput != lastactivity) {
293
          lastprogressoutput = lastactivity;
340
          lastprogressoutput = lastactivity;
294
          printf("%ld KiB (%ld KiB/s)     \r", flen >> 10, (flen >> 10) - (lastflen >> 10)); /* trailing spaces are meant to avoid leaving garbage on screen if speed goes from, say, 1000 KiB/s to 9 KiB/s */
341
          printf("%ld KiB (%ld KiB/s)     \r", flen >> 10, (flen >> 10) - (lastflen >> 10)); /* trailing spaces are meant to avoid leaving garbage on screen if speed goes from, say, 1000 KiB/s to 9 KiB/s */
295
          lastflen = flen;
342
          lastflen = flen;
296
          fflush(stdout); /* avoid console buffering */
343
          fflush(stdout); /* avoid console buffering */
297
        }
344
        }
298
        /* update the bsd sum */
345
        /* update the bsd sum */
299
        for (i = 0; i < byteread; i++) {
346
        for (i = 0; i < byteread; i++) {
300
          /* rotr16 */
347
          /* rotr16 */
301
          unsigned short bsumlsb = *bsum & 1;
348
          unsigned short bsumlsb = *bsum & 1;
302
          *bsum >>= 1;
349
          *bsum >>= 1;
303
          *bsum |= (bsumlsb << 15);
350
          *bsum |= (bsumlsb << 15);
304
          *bsum += buffer[i];
351
          *bsum += buffer[i];
305
        }
352
        }
306
      } else { /* otherwise dump to screen */
353
      } else { /* otherwise dump to screen */
307
        buffer[byteread] = 0;
354
        buffer[byteread] = 0;
308
        printf("%s", buffer);
355
        printf("%s", buffer);
309
      }
356
      }
310
 
357
 
311
    } else if (byteread < 0) { /* end of connection */
358
    } else if (byteread < 0) { /* end of connection */
312
      break;
359
      break;
313
 
360
 
314
    } else { /* check for timeout (byteread == 0) */
361
    } else { /* check for timeout (byteread == 0) */
315
      if (time(NULL) - lastactivity > 20) { /* TIMEOUT! */
362
      if (time(NULL) - lastactivity > 20) { /* TIMEOUT! */
316
        puts("ERROR: Timeout while waiting for data");
363
        puts("ERROR: Timeout while waiting for data");
317
        goto SHITQUIT;
364
        goto SHITQUIT;
318
      }
365
      }
319
      /* waiting for packets - release a CPU cycle in the meantime */
366
      /* waiting for packets - release a CPU cycle in the meantime */
320
      _asm int 28h;
367
      _asm int 28h;
321
    }
368
    }
322
  }
369
  }
323
 
370
 
324
  goto ALLGOOD;
371
  goto ALLGOOD;
325
 
372
 
326
  SHITQUIT:
373
  SHITQUIT:
327
  flen = -1;
374
  flen = -1;
328
 
375
 
329
  ALLGOOD:
376
  ALLGOOD:
330
  if (fd != NULL) fclose(fd);
377
  if (fd != NULL) fclose(fd);
331
  net_close(sock);
378
  net_close(sock);
332
  return(flen);
379
  return(flen);
333
}
380
}
334
 
381
 
335
 
382
 
336
/* checks if file exists, returns 0 if not, non-zero otherwise */
383
/* checks if file exists, returns 0 if not, non-zero otherwise */
337
static int fexists(const char *fname) {
384
static int fexists(const char *fname) {
338
  FILE *fd = fopen(fname, "rb");
385
  FILE *fd = fopen(fname, "rb");
339
  if (fd == NULL) return(0);
386
  if (fd == NULL) return(0);
340
  fclose(fd);
387
  fclose(fd);
341
  return(1);
388
  return(1);
342
}
389
}
343
 
390
 
344
 
391
 
345
int main(int argc, char **argv) {
392
int main(int argc, char **argv) {
346
  unsigned short bsum = 0;
393
  unsigned short bsum = 0;
347
  long flen;
394
  long flen;
348
  int ispost; /* is the request a POST? */
395
  int ispost; /* is the request a POST? */
349
 
396
 
350
  struct {
397
  struct {
351
    unsigned char buffer[5000];
398
    unsigned char buffer[5000];
352
    char ipaddr[64];
399
    char ipaddr[64];
353
    char url[64];
400
    char url[64];
354
    char outfname[16];
401
    char outfname[16];
355
  } *mem;
402
  } *mem;
356
 
403
 
357
  /* allocate memory */
404
  /* allocate memory */
358
  mem = malloc(sizeof(*mem));
405
  mem = malloc(sizeof(*mem));
359
  if (mem == NULL) {
406
  if (mem == NULL) {
360
    puts("ERROR: out of memory");
407
    puts("ERROR: out of memory");
361
    return(1);
408
    return(1);
362
  }
409
  }
363
 
410
 
364
  /* parse command line arguments */
411
  /* parse command line arguments */
365
  if (parseargv(argc, argv, mem->outfname, mem->url, &ispost) != 0) return(1);
412
  if (parseargv(argc, argv, mem->outfname, mem->url, &ispost) != 0) return(1);
366
 
413
 
367
  /* if outfname requested, make sure that file does not exist yet */
414
  /* if outfname requested, make sure that file does not exist yet */
368
  if ((mem->outfname[0] != 0) && (fexists(mem->outfname))) {
415
  if ((mem->outfname[0] != 0) && (fexists(mem->outfname))) {
369
    printf("ERROR: file %s already exists", mem->outfname);
416
    printf("ERROR: file %s already exists", mem->outfname);
370
    puts("");
417
    puts("");
371
    return(1);
418
    return(1);
372
  }
419
  }
373
 
420
 
374
  /* init network stack */
421
  /* init network stack */
375
  if (net_init() != 0) {
422
  if (net_init() != 0) {
376
    puts("ERROR: Network subsystem initialization failed");
423
    puts("ERROR: Network subsystem initialization failed");
377
    return(1);
424
    return(1);
378
  }
425
  }
379
 
426
 
380
  puts(""); /* required because watt-32 likes to print out garbage sometimes ("configuring through DHCP...") */
427
  puts(""); /* required because watt-32 likes to print out garbage sometimes ("configuring through DHCP...") */
381
 
428
 
382
  if (net_dnsresolve(mem->ipaddr, HOSTADDR) != 0) {
429
  if (net_dnsresolve(mem->ipaddr, HOSTADDR) != 0) {
383
    puts("ERROR: DNS resolution failed");
430
    puts("ERROR: DNS resolution failed");
384
    return(1);
431
    return(1);
385
  }
432
  }
386
 
433
 
387
  flen = htget(mem->ipaddr, mem->url, mem->outfname, &bsum, ispost, mem->buffer, sizeof(mem->buffer));
434
  flen = htget(mem->ipaddr, mem->url, mem->outfname, &bsum, ispost, mem->buffer, sizeof(mem->buffer));
388
  if (flen < 1) return(1);
435
  if (flen < 1) return(1);
389
 
436
 
390
  if (mem->outfname[0] != 0) {
437
  if (mem->outfname[0] != 0) {
391
    /* print bsum, size, filename */
438
    /* print bsum, size, filename */
392
    printf("Downloaded %ld KiB into %s (BSUM: %04X)", flen >> 10, mem->outfname, bsum);
439
    printf("Downloaded %ld KiB into %s (BSUM: %04X)", flen >> 10, mem->outfname, bsum);
393
    puts("");
440
    puts("");
394
  }
441
  }
395
 
442
 
396
  return(0);
443
  return(0);
397
}
444
}
398
 
445