Subversion Repositories SvarDOS

Rev

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

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