mirror of
https://github.com/sheumann/DiskBrowser.git
synced 2024-11-25 02:32:44 +00:00
Add utility code for HTTP connections, URLs, and networking.
This is adapted from NetDisk with minor changes.
This commit is contained in:
parent
eaff2fdc42
commit
c237bd661f
2
Makefile
2
Makefile
@ -4,7 +4,7 @@ CFLAGS = -w-1 -O-1
|
|||||||
TEST_OBJS = test.a json.a jsonutil.a
|
TEST_OBJS = test.a json.a jsonutil.a
|
||||||
TEST_PROG = test
|
TEST_PROG = test
|
||||||
|
|
||||||
DISKBROWSER_OBJS = diskbrowser.a
|
DISKBROWSER_OBJS = diskbrowser.a hostname.a http.a json.a jsonutil.a readtcp.a seturl.a strcasecmp.a tcpconnection.a urlparser.a
|
||||||
DISKBROWSER_RSRC = diskbrowser.rez
|
DISKBROWSER_RSRC = diskbrowser.rez
|
||||||
DISKBROWSER_PROG = DiskBrowser
|
DISKBROWSER_PROG = DiskBrowser
|
||||||
|
|
||||||
|
40
hostname.c
Normal file
40
hostname.c
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#pragma noroot
|
||||||
|
|
||||||
|
#include <tcpip.h>
|
||||||
|
#include <misctool.h>
|
||||||
|
#include <orca.h>
|
||||||
|
#include "hostname.h"
|
||||||
|
|
||||||
|
#define DNR_WAIT_TIME 15 /*seconds*/
|
||||||
|
|
||||||
|
Boolean DoLookupName(Session *sess) {
|
||||||
|
dnrBuffer dnrInfo;
|
||||||
|
|
||||||
|
if (TCPIPValidateIPString(sess->hostName)) {
|
||||||
|
cvtRec cvtInfo;
|
||||||
|
TCPIPConvertIPToHex(&cvtInfo, sess->hostName);
|
||||||
|
sess->ipAddr = cvtInfo.cvtIPAddress;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
TCPIPDNRNameToIP(sess->hostName, &dnrInfo);
|
||||||
|
if (toolerror())
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
sess->dnsTime = GetTick();
|
||||||
|
while (dnrInfo.DNRstatus == DNR_Pending) {
|
||||||
|
if (GetTick() - sess->dnsTime >= DNR_WAIT_TIME * 60)
|
||||||
|
break;
|
||||||
|
TCPIPPoll();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dnrInfo.DNRstatus == DNR_OK) {
|
||||||
|
sess->ipAddr = dnrInfo.DNRIPaddress;
|
||||||
|
return TRUE;
|
||||||
|
} else {
|
||||||
|
if (dnrInfo.DNRstatus == DNR_Pending) {
|
||||||
|
TCPIPCancelDNR(&dnrInfo);
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
9
hostname.h
Normal file
9
hostname.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#ifndef HOSTNAME_H
|
||||||
|
#define HOSTNAME_H
|
||||||
|
|
||||||
|
#include <types.h>
|
||||||
|
#include "session.h"
|
||||||
|
|
||||||
|
Boolean DoLookupName(Session *sess);
|
||||||
|
|
||||||
|
#endif
|
358
http.c
Normal file
358
http.c
Normal file
@ -0,0 +1,358 @@
|
|||||||
|
#pragma lint -1
|
||||||
|
#pragma noroot
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <types.h>
|
||||||
|
#include <tcpip.h>
|
||||||
|
#include <misctool.h>
|
||||||
|
#include <memory.h>
|
||||||
|
#include <orca.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "session.h"
|
||||||
|
#include "http.h"
|
||||||
|
#include "tcpconnection.h"
|
||||||
|
#include "strcasecmp.h"
|
||||||
|
#include "seturl.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
#define buffTypePointer 0x0000 /* For TCPIPReadTCP() */
|
||||||
|
#define buffTypeHandle 0x0001
|
||||||
|
#define buffTypeNewHandle 0x0002
|
||||||
|
|
||||||
|
#define HTTP_RESPONSE_TIMEOUT 15 /* seconds to wait for response headers */
|
||||||
|
|
||||||
|
#define MAX_REDIRECTS 5
|
||||||
|
|
||||||
|
enum ResponseHeader {
|
||||||
|
UNKNOWN_HEADER = 0,
|
||||||
|
CONTENT_RANGE,
|
||||||
|
CONTENT_LENGTH,
|
||||||
|
TRANSFER_ENCODING,
|
||||||
|
CONTENT_ENCODING,
|
||||||
|
LOCATION,
|
||||||
|
};
|
||||||
|
|
||||||
|
Boolean BuildHTTPRequest(Session *sess, char *resourceStr) {
|
||||||
|
long sizeNeeded;
|
||||||
|
int round = 0;
|
||||||
|
|
||||||
|
char *escapedStr = NULL;
|
||||||
|
if (strchr(resourceStr, ' ') != NULL) {
|
||||||
|
sizeNeeded = strlen(resourceStr);
|
||||||
|
if (sizeNeeded > 10000)
|
||||||
|
return FALSE;
|
||||||
|
escapedStr = malloc(sizeNeeded*3 + 1);
|
||||||
|
if (escapedStr == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
char *s = escapedStr;
|
||||||
|
char c;
|
||||||
|
while ((c = *resourceStr++) != '\0') {
|
||||||
|
if (c == ' ') {
|
||||||
|
*s++ = '%';
|
||||||
|
*s++ = '2';
|
||||||
|
*s++ = '0';
|
||||||
|
} else {
|
||||||
|
*s++ = c;
|
||||||
|
}
|
||||||
|
*s = '\0';
|
||||||
|
}
|
||||||
|
resourceStr = escapedStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
sizeNeeded = 0;
|
||||||
|
do {
|
||||||
|
sizeNeeded = snprintf(sess->httpRequest, sizeNeeded,
|
||||||
|
"GET /%s HTTP/1.1\r\n"
|
||||||
|
"Host: %s\r\n"
|
||||||
|
"User-Agent: GS-Disk-Browser/" USER_AGENT_VERSION "\r\n"
|
||||||
|
"Accept-Encoding: identity\r\n"
|
||||||
|
//"Accept: */*\r\n" /* default, but some clients send explicitly */
|
||||||
|
//"Connection: Keep-Alive\r\n" /* same */
|
||||||
|
"\r\n",
|
||||||
|
resourceStr,
|
||||||
|
sess->hostName+1);
|
||||||
|
|
||||||
|
if (sizeNeeded <= 0) {
|
||||||
|
free(sess->httpRequest);
|
||||||
|
sess->httpRequest = NULL;
|
||||||
|
free(escapedStr);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (round == 0) {
|
||||||
|
sizeNeeded++; /* account for terminating NUL */
|
||||||
|
free(sess->httpRequest);
|
||||||
|
sess->httpRequest = malloc(sizeNeeded);
|
||||||
|
if (sess->httpRequest == NULL) {
|
||||||
|
free(escapedStr);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (round++ == 0);
|
||||||
|
|
||||||
|
free(escapedStr);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum NetDiskError
|
||||||
|
DoHTTPRequest(Session *sess, unsigned long start, unsigned long end) {
|
||||||
|
top:;
|
||||||
|
union {
|
||||||
|
srBuff srBuff;
|
||||||
|
rlrBuff rlrBuff;
|
||||||
|
} u;
|
||||||
|
Word tcpError;
|
||||||
|
Boolean wantRedirect = FALSE, gotRedirect = FALSE;
|
||||||
|
enum NetDiskError result;
|
||||||
|
|
||||||
|
sess->responseCode = 0;
|
||||||
|
|
||||||
|
/* Send out request */
|
||||||
|
result = NETWORK_ERROR;
|
||||||
|
unsigned int netErrors = 0;
|
||||||
|
|
||||||
|
TCPIPPoll();
|
||||||
|
if (sess->tcpLoggedIn) {
|
||||||
|
tcpError = TCPIPStatusTCP(sess->ipid, &u.srBuff);
|
||||||
|
if (tcpError || toolerror() || u.srBuff.srState != TCPSESTABLISHED) {
|
||||||
|
EndTCPConnection(sess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u.rlrBuff.rlrBuffHandle = NULL;
|
||||||
|
|
||||||
|
netRetry:
|
||||||
|
if (!sess->tcpLoggedIn || netErrors) {
|
||||||
|
if (StartTCPConnection(sess) != 0)
|
||||||
|
goto errorReturn;
|
||||||
|
}
|
||||||
|
tcpError = TCPIPWriteTCP(sess->ipid, sess->httpRequest,
|
||||||
|
sess->httpRequestLen, TRUE, FALSE);
|
||||||
|
if (tcpError || toolerror()) {
|
||||||
|
if (netErrors == 0) {
|
||||||
|
netErrors++;
|
||||||
|
goto netRetry;
|
||||||
|
} else {
|
||||||
|
goto errorReturn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get response status line & headers */
|
||||||
|
LongWord startTime = GetTick();
|
||||||
|
do {
|
||||||
|
TCPIPPoll();
|
||||||
|
tcpError = TCPIPReadLineTCP(sess->ipid,
|
||||||
|
(void*)((LongWord)"\p\r\n\r\n" | 0x80000000),
|
||||||
|
buffTypeNewHandle, (Ref)NULL,
|
||||||
|
0xFFFFFF, &u.rlrBuff);
|
||||||
|
if (tcpError || toolerror()) {
|
||||||
|
if (netErrors == 0) {
|
||||||
|
netErrors++;
|
||||||
|
goto netRetry;
|
||||||
|
} else {
|
||||||
|
goto errorReturn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (u.rlrBuff.rlrBuffCount == 0
|
||||||
|
&& GetTick() - startTime < HTTP_RESPONSE_TIMEOUT * 60);
|
||||||
|
|
||||||
|
result = NO_RESPONSE;
|
||||||
|
if (!u.rlrBuff.rlrIsDataFlag)
|
||||||
|
goto errorReturn;
|
||||||
|
|
||||||
|
result = INVALID_RESPONSE;
|
||||||
|
/* Response must be at least long enough for a status line & final CRLF */
|
||||||
|
if (u.rlrBuff.rlrBuffCount < 8+1+3+1+2+2)
|
||||||
|
goto errorReturn;
|
||||||
|
|
||||||
|
HLock(u.rlrBuff.rlrBuffHandle);
|
||||||
|
|
||||||
|
char *response = *u.rlrBuff.rlrBuffHandle;
|
||||||
|
char *responseEnd = response + u.rlrBuff.rlrBuffCount;
|
||||||
|
/* Make response a C-string. Specifically, it will end "CR LF NUL NUL". */
|
||||||
|
response[u.rlrBuff.rlrBuffCount - 2] = '\0';
|
||||||
|
response[u.rlrBuff.rlrBuffCount - 1] = '\0';
|
||||||
|
|
||||||
|
/* Parse status line of HTTP response */
|
||||||
|
char *endPtr;
|
||||||
|
|
||||||
|
if (strncmp(response, "HTTP/1.", 7) != 0)
|
||||||
|
goto errorReturn;
|
||||||
|
response += 7;
|
||||||
|
if (!(*response >= '1' && *response <= '9'))
|
||||||
|
goto errorReturn;
|
||||||
|
response += 1;
|
||||||
|
if (*response != ' ')
|
||||||
|
goto errorReturn;
|
||||||
|
response += 1;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
sess->responseCode = strtoul(response, &endPtr, 10);
|
||||||
|
if (errno || sess->responseCode > 999 || endPtr != response + 3)
|
||||||
|
goto errorReturn;
|
||||||
|
response += 3;
|
||||||
|
|
||||||
|
if (*response != ' ')
|
||||||
|
goto errorReturn;
|
||||||
|
response++;
|
||||||
|
|
||||||
|
while (*response != '\r')
|
||||||
|
response++;
|
||||||
|
response++;
|
||||||
|
|
||||||
|
if (*response != '\n')
|
||||||
|
goto errorReturn;
|
||||||
|
response++;
|
||||||
|
|
||||||
|
|
||||||
|
switch((unsigned int)sess->responseCode) {
|
||||||
|
/* Redirect responses */
|
||||||
|
case 300: case 301: case 302: case 307: case 308:
|
||||||
|
if (sess->redirectCount < MAX_REDIRECTS) {
|
||||||
|
sess->redirectCount++;
|
||||||
|
wantRedirect = TRUE;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
result = EXCESSIVE_REDIRECTS;
|
||||||
|
goto errorReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Full content, as desired */
|
||||||
|
case 200:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (sess->responseCode < 400 || sess->responseCode > 599) {
|
||||||
|
result = UNSUPPORTED_RESPONSE;
|
||||||
|
} else {
|
||||||
|
result = sess->responseCode;
|
||||||
|
}
|
||||||
|
goto errorReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse response headers */
|
||||||
|
sess->contentLength = 0;
|
||||||
|
|
||||||
|
result = UNSUPPORTED_HEADER_VALUE;
|
||||||
|
|
||||||
|
while (response < responseEnd - 4) {
|
||||||
|
enum ResponseHeader header = UNKNOWN_HEADER;
|
||||||
|
|
||||||
|
if (wantRedirect) {
|
||||||
|
if (strncasecmp(response, "Location:", 9) == 0) {
|
||||||
|
response += 9;
|
||||||
|
header = LOCATION;
|
||||||
|
}
|
||||||
|
} else switch (_toupper(*response)) {
|
||||||
|
case 'C':
|
||||||
|
if (strncasecmp(response, "Content-Length:", 15) == 0) {
|
||||||
|
response += 15;
|
||||||
|
header = CONTENT_LENGTH;
|
||||||
|
} else if (strncasecmp(response, "Content-Encoding:", 17) == 0) {
|
||||||
|
response += 17;
|
||||||
|
header = CONTENT_ENCODING;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'T':
|
||||||
|
if (strncasecmp(response, "Transfer-Encoding:", 18) == 0) {
|
||||||
|
response += 18;
|
||||||
|
header = TRANSFER_ENCODING;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*response == ' ' || *response == '\t')
|
||||||
|
response++;
|
||||||
|
|
||||||
|
switch (header) {
|
||||||
|
case CONTENT_LENGTH:
|
||||||
|
errno = 0;
|
||||||
|
sess->contentLength = strtoul(response, &endPtr, 10);
|
||||||
|
if (errno)
|
||||||
|
goto errorReturn;
|
||||||
|
if (sess->contentLength == 0 && !wantRedirect)
|
||||||
|
goto errorReturn;
|
||||||
|
response = endPtr;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONTENT_ENCODING:
|
||||||
|
case TRANSFER_ENCODING:
|
||||||
|
if (strcasecmp(response, "identity") == 0) {
|
||||||
|
response += 8;
|
||||||
|
} else {
|
||||||
|
goto errorReturn;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LOCATION:
|
||||||
|
endPtr = response;
|
||||||
|
char c;
|
||||||
|
while ((c = *endPtr) != '\0' && c != '\r' && c != '\n')
|
||||||
|
endPtr++;
|
||||||
|
if (c == '\0' || c == '\n')
|
||||||
|
goto errorReturn;
|
||||||
|
while (endPtr > response
|
||||||
|
&& (*(endPtr-1) == ' ' || *(endPtr-1) == '\t')) {
|
||||||
|
c = *--endPtr;
|
||||||
|
}
|
||||||
|
if (wantRedirect) {
|
||||||
|
*endPtr = '\0';
|
||||||
|
if (SetURL(sess, response, FALSE, TRUE) != OPERATION_SUCCESSFUL) {
|
||||||
|
result = REDIRECT_ERROR;
|
||||||
|
goto errorReturn;
|
||||||
|
}
|
||||||
|
*endPtr = c;
|
||||||
|
response = endPtr;
|
||||||
|
gotRedirect = TRUE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Unknown headers: ignored */
|
||||||
|
case UNKNOWN_HEADER:
|
||||||
|
default:
|
||||||
|
while (*response != '\r')
|
||||||
|
response++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((*response == ' ' || *response == '\t') && response < responseEnd)
|
||||||
|
response++;
|
||||||
|
|
||||||
|
if (*response != '\r')
|
||||||
|
goto errorReturn;
|
||||||
|
response++;
|
||||||
|
if (*response != '\n')
|
||||||
|
goto errorReturn;
|
||||||
|
response++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wanted redirect: Retry with new location if we got it. */
|
||||||
|
if (wantRedirect) {
|
||||||
|
if (gotRedirect) {
|
||||||
|
DisposeHandle(u.rlrBuff.rlrBuffHandle);
|
||||||
|
goto top;
|
||||||
|
} else {
|
||||||
|
result = UNSUPPORTED_RESPONSE;
|
||||||
|
goto errorReturn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = OPERATION_SUCCESSFUL;
|
||||||
|
DisposeHandle(u.rlrBuff.rlrBuffHandle);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
errorReturn:
|
||||||
|
if (u.rlrBuff.rlrBuffHandle != NULL) {
|
||||||
|
DisposeHandle(u.rlrBuff.rlrBuffHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Error condition on this TCP connection means it can't be reused. */
|
||||||
|
EndTCPConnection(sess);
|
||||||
|
return result;
|
||||||
|
}
|
11
http.h
Normal file
11
http.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#ifndef HTTP_H
|
||||||
|
#define HTTP_H
|
||||||
|
|
||||||
|
#include <types.h>
|
||||||
|
#include "session.h"
|
||||||
|
#include "netdiskerror.h"
|
||||||
|
|
||||||
|
Boolean BuildHTTPRequest(Session *sess, char *resourceStr);
|
||||||
|
enum NetDiskError DoHTTPRequest(Session *sess, unsigned long start, unsigned long end);
|
||||||
|
|
||||||
|
#endif
|
48
readtcp.c
Normal file
48
readtcp.c
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#pragma noroot
|
||||||
|
|
||||||
|
#include "readtcp.h"
|
||||||
|
#include "session.h"
|
||||||
|
#include <tcpip.h>
|
||||||
|
#include <misctool.h>
|
||||||
|
#include <orca.h>
|
||||||
|
|
||||||
|
#define buffTypePointer 0x0000 /* For TCPIPReadTCP() */
|
||||||
|
#define buffTypeHandle 0x0001
|
||||||
|
#define buffTypeNewHandle 0x0002
|
||||||
|
|
||||||
|
/* Time out if no new data is received for this long */
|
||||||
|
#define READ_TIMEOUT 15 /* seconds */
|
||||||
|
|
||||||
|
void InitReadTCP(Session *sess, LongWord readCount, void *readPtr) {
|
||||||
|
sess->readCount = readCount;
|
||||||
|
sess->readPtr = readPtr;
|
||||||
|
sess->lastReadTime = GetTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ReadStatus TryReadTCP(Session *sess) {
|
||||||
|
rrBuff rrBuff;
|
||||||
|
|
||||||
|
TCPIPPoll();
|
||||||
|
sess->tcperr = TCPIPReadTCP(sess->ipid, buffTypePointer, (Ref)sess->readPtr,
|
||||||
|
sess->readCount, &rrBuff);
|
||||||
|
sess->toolerr = toolerror();
|
||||||
|
if (sess->tcperr || sess->toolerr) {
|
||||||
|
return rsError;
|
||||||
|
}
|
||||||
|
|
||||||
|
sess->readCount -= rrBuff.rrBuffCount;
|
||||||
|
sess->readPtr += rrBuff.rrBuffCount;
|
||||||
|
|
||||||
|
if (sess->readCount == 0) {
|
||||||
|
return rsDone;
|
||||||
|
} else {
|
||||||
|
if (rrBuff.rrBuffCount != 0) {
|
||||||
|
sess->lastReadTime = GetTick();
|
||||||
|
} else if (GetTick() - sess->lastReadTime > READ_TIMEOUT * 60) {
|
||||||
|
return rsTimedOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rsWaiting;
|
||||||
|
}
|
||||||
|
}
|
16
readtcp.h
Normal file
16
readtcp.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef READTCP_H
|
||||||
|
#define READTCP_H
|
||||||
|
|
||||||
|
#include "session.h"
|
||||||
|
|
||||||
|
typedef enum ReadStatus {
|
||||||
|
rsDone,
|
||||||
|
rsWaiting,
|
||||||
|
rsError,
|
||||||
|
rsTimedOut
|
||||||
|
} ReadStatus;
|
||||||
|
|
||||||
|
void InitReadTCP(Session *sess, LongWord readCount, void *readPtr);
|
||||||
|
ReadStatus TryReadTCP(Session *sess);
|
||||||
|
|
||||||
|
#endif
|
47
session.h
Normal file
47
session.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#ifndef SESSION_H
|
||||||
|
#define SESSION_H
|
||||||
|
|
||||||
|
#include <types.h>
|
||||||
|
|
||||||
|
typedef struct Session {
|
||||||
|
/* Marinetti TCP connection status */
|
||||||
|
Word ipid;
|
||||||
|
Boolean tcpLoggedIn;
|
||||||
|
|
||||||
|
/* ReadTCP status */
|
||||||
|
LongWord readCount;
|
||||||
|
Byte *readPtr;
|
||||||
|
LongWord lastReadTime;
|
||||||
|
|
||||||
|
/* Marinetti error codes, both the tcperr* value and any tool error */
|
||||||
|
Word tcperr;
|
||||||
|
Word toolerr;
|
||||||
|
|
||||||
|
/* HTTP request to send (if non-NULL, points to malloc'd buffer) */
|
||||||
|
char *httpRequest;
|
||||||
|
/* Length of HTTP request */
|
||||||
|
LongWord httpRequestLen;
|
||||||
|
|
||||||
|
/* HTTP response code */
|
||||||
|
LongWord responseCode;
|
||||||
|
|
||||||
|
/* IP address and TCP port of host */
|
||||||
|
LongWord ipAddr;
|
||||||
|
Word port;
|
||||||
|
|
||||||
|
/* Domain name or IP address of host (p-string, but also null-terminated) */
|
||||||
|
char hostName[257];
|
||||||
|
/* Time (GetTick) of last DNS lookup */
|
||||||
|
LongWord dnsTime;
|
||||||
|
|
||||||
|
/* Number of redirects followed */
|
||||||
|
Word redirectCount;
|
||||||
|
|
||||||
|
/* Value reported by server in Content-Length header */
|
||||||
|
LongWord contentLength;
|
||||||
|
} Session;
|
||||||
|
|
||||||
|
|
||||||
|
void EndNetDiskSession(Session *sess);
|
||||||
|
|
||||||
|
#endif
|
99
seturl.c
Normal file
99
seturl.c
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
#pragma noroot
|
||||||
|
|
||||||
|
#include <types.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "session.h"
|
||||||
|
#include "urlparser.h"
|
||||||
|
#include "strcasecmp.h"
|
||||||
|
#include "hostname.h"
|
||||||
|
#include "http.h"
|
||||||
|
#include "tcpconnection.h"
|
||||||
|
#include "seturl.h"
|
||||||
|
|
||||||
|
#define DEFAULT_HTTP_PORT 80
|
||||||
|
|
||||||
|
/* Limit to make sure sizes stay within the range of 16-bit values */
|
||||||
|
#define MAX_URL_LENGTH 30000
|
||||||
|
|
||||||
|
|
||||||
|
enum NetDiskError
|
||||||
|
SetURL(Session *sess, char *url, Boolean permissive, Boolean partialOK) {
|
||||||
|
if (strlen(url) > MAX_URL_LENGTH) {
|
||||||
|
return URL_TOO_LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(unsigned int i = 0; url[i] != '\0'; i++) {
|
||||||
|
if ((unsigned char)url[i] < ' ') {
|
||||||
|
return INVALID_CHARACTER_IN_URL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
URLParts urlParts = ParseURL(url);
|
||||||
|
|
||||||
|
if (urlParts.errorFound)
|
||||||
|
return BAD_URL_SYNTAX;
|
||||||
|
|
||||||
|
if (urlParts.scheme == NULL) {
|
||||||
|
if (permissive) {
|
||||||
|
urlParts.scheme = "http";
|
||||||
|
} else {
|
||||||
|
return BAD_URL_SYNTAX;
|
||||||
|
}
|
||||||
|
} else if (strcasecmp(urlParts.scheme, "http") != 0) {
|
||||||
|
return UNSUPPORTED_URL_SCHEME;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (urlParts.username != NULL || urlParts.password != NULL) {
|
||||||
|
return AUTHENTICATION_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (urlParts.fragment != NULL) {
|
||||||
|
return FRAGMENT_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long portNum;
|
||||||
|
char *endPtr;
|
||||||
|
if (urlParts.port == NULL || *urlParts.port == '\0') {
|
||||||
|
portNum = DEFAULT_HTTP_PORT;
|
||||||
|
} else {
|
||||||
|
errno = 0;
|
||||||
|
portNum = strtoul(urlParts.port, &endPtr, 10);
|
||||||
|
if (errno || *endPtr != '\0' || portNum > 0xFFFF) {
|
||||||
|
return INVALID_PORT_NUMBER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sess->port = portNum;
|
||||||
|
|
||||||
|
if (urlParts.host == NULL) {
|
||||||
|
if (!partialOK || sess->hostName[0] == 0) {
|
||||||
|
return NO_HOST_SPECIFIED;
|
||||||
|
}
|
||||||
|
} else if (*urlParts.host == '\0') {
|
||||||
|
return NO_HOST_SPECIFIED;
|
||||||
|
} else if (*urlParts.host == '[') {
|
||||||
|
return IPV6_NOT_SUPPORTED;
|
||||||
|
} else {
|
||||||
|
size_t len;
|
||||||
|
if ((len = strlen(urlParts.host)) > 255) {
|
||||||
|
return HOSTNAME_TOO_LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(&sess->hostName[1], urlParts.host);
|
||||||
|
sess->hostName[0] = len;
|
||||||
|
|
||||||
|
if (!DoLookupName(sess)) {
|
||||||
|
return NAME_LOOKUP_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!BuildHTTPRequest(sess, urlParts.path)) {
|
||||||
|
return OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* End any existing TCP connection to old URL */
|
||||||
|
EndTCPConnection(sess);
|
||||||
|
|
||||||
|
return OPERATION_SUCCESSFUL;
|
||||||
|
}
|
9
seturl.h
Normal file
9
seturl.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#ifndef SETURL_H
|
||||||
|
#define SETURL_H
|
||||||
|
|
||||||
|
#include "netdiskerror.h"
|
||||||
|
|
||||||
|
enum NetDiskError
|
||||||
|
SetURL(Session *sess, char *url, Boolean permissive, Boolean partialOK);
|
||||||
|
|
||||||
|
#endif
|
31
strcasecmp.c
Normal file
31
strcasecmp.c
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# ifdef __ORCAC__
|
||||||
|
# pragma noroot
|
||||||
|
# endif
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
int strcasecmp(const char *s1, const char *s2)
|
||||||
|
{
|
||||||
|
while (*s1 != '\0' && tolower(*s1) == tolower(*s2)) {
|
||||||
|
s1++;
|
||||||
|
s2++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)*s1 - (int)*s2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int strncasecmp(const char *s1, const char *s2, size_t n)
|
||||||
|
{
|
||||||
|
if (n == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while (n > 1 && *s1 != '\0' && tolower(*s1) == tolower(*s2)) {
|
||||||
|
s1++;
|
||||||
|
s2++;
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)*s1 - (int)*s2;
|
||||||
|
}
|
9
strcasecmp.h
Normal file
9
strcasecmp.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#ifndef STRCASECMP_H
|
||||||
|
#define STRCASECMP_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
int strcasecmp(const char *s1, const char *s2);
|
||||||
|
int strncasecmp(const char *s1, const char *s2, size_t n);
|
||||||
|
|
||||||
|
#endif
|
60
tcpconnection.c
Normal file
60
tcpconnection.c
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#pragma noroot
|
||||||
|
|
||||||
|
#include <tcpip.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <orca.h>
|
||||||
|
#include <misctool.h>
|
||||||
|
#include "session.h"
|
||||||
|
#include "tcpconnection.h"
|
||||||
|
|
||||||
|
/* Make a TCP connection for a session.
|
||||||
|
*
|
||||||
|
* On success, returns 0 and sets sess->ipid.
|
||||||
|
* On failure, returns an error code.
|
||||||
|
*/
|
||||||
|
enum NetDiskError StartTCPConnection(Session *sess) {
|
||||||
|
Word tcperr;
|
||||||
|
srBuff mySRBuff;
|
||||||
|
LongWord initialTime;
|
||||||
|
|
||||||
|
/* End any existing TCP connection */
|
||||||
|
EndTCPConnection(sess);
|
||||||
|
|
||||||
|
sess->ipid =
|
||||||
|
TCPIPLogin(userid(), sess->ipAddr, sess->port, 0, 0x40);
|
||||||
|
if (toolerror())
|
||||||
|
return NETWORK_ERROR;
|
||||||
|
|
||||||
|
tcperr = TCPIPOpenTCP(sess->ipid);
|
||||||
|
if (toolerror()) {
|
||||||
|
TCPIPLogout(sess->ipid);
|
||||||
|
return NETWORK_ERROR;
|
||||||
|
} else if (tcperr != tcperrOK) {
|
||||||
|
TCPIPLogout(sess->ipid);
|
||||||
|
return NO_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialTime = GetTick();
|
||||||
|
do {
|
||||||
|
TCPIPPoll();
|
||||||
|
TCPIPStatusTCP(sess->ipid, &mySRBuff);
|
||||||
|
} while (mySRBuff.srState == TCPSSYNSENT && GetTick()-initialTime < 15*60);
|
||||||
|
if (mySRBuff.srState != TCPSESTABLISHED) {
|
||||||
|
TCPIPAbortTCP(sess->ipid);
|
||||||
|
TCPIPLogout(sess->ipid);
|
||||||
|
return NO_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
sess->tcpLoggedIn = TRUE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EndTCPConnection(Session *sess) {
|
||||||
|
if (sess->tcpLoggedIn) {
|
||||||
|
TCPIPPoll();
|
||||||
|
TCPIPAbortTCP(sess->ipid);
|
||||||
|
TCPIPLogout(sess->ipid);
|
||||||
|
sess->ipid = 0;
|
||||||
|
sess->tcpLoggedIn = FALSE;
|
||||||
|
}
|
||||||
|
}
|
10
tcpconnection.h
Normal file
10
tcpconnection.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef TCPCONNECTION_H
|
||||||
|
#define TCPCONNECTION_H
|
||||||
|
|
||||||
|
#include "session.h"
|
||||||
|
#include "netdiskerror.h"
|
||||||
|
|
||||||
|
enum NetDiskError StartTCPConnection(Session *sess);
|
||||||
|
void EndTCPConnection(Session *sess);
|
||||||
|
|
||||||
|
#endif
|
125
urlparser.c
Normal file
125
urlparser.c
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
#ifdef __ORCAC__
|
||||||
|
# pragma noroot
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "urlparser.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse the URL and break it up into its component parts.
|
||||||
|
*
|
||||||
|
* This handles http URLs and other schemes with the same syntax.
|
||||||
|
*
|
||||||
|
* It can also handle relative URLs containing an absolute-path reference
|
||||||
|
* (i.e. starting with /) and URL-like addresses without the scheme prefix.
|
||||||
|
* In these cases, the portions of the URL not provided will be NULL.
|
||||||
|
*
|
||||||
|
* This modifies the original string passed in, breaking it up into components.
|
||||||
|
*/
|
||||||
|
URLParts ParseURL(char *url) {
|
||||||
|
char *sep, sep2;
|
||||||
|
URLParts urlParts = {0};
|
||||||
|
|
||||||
|
sep = strrchr(url, '#');
|
||||||
|
if (sep) {
|
||||||
|
*sep = '\0';
|
||||||
|
urlParts.fragment = sep + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Detect relative URL (absolute-path reference only) */
|
||||||
|
if (url[0] == '/') {
|
||||||
|
if (url[1] != '/') {
|
||||||
|
urlParts.path = url + 1;
|
||||||
|
return urlParts;
|
||||||
|
} else {
|
||||||
|
urlParts.errorFound = 1;
|
||||||
|
return urlParts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sep = strchr(url, ':');
|
||||||
|
if (sep) {
|
||||||
|
urlParts.scheme = url;
|
||||||
|
*sep = '\0';
|
||||||
|
url = sep + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (urlParts.scheme != NULL) {
|
||||||
|
if (strncmp(url, "//", 2) == 0) {
|
||||||
|
url += 2;
|
||||||
|
} else {
|
||||||
|
urlParts.errorFound = 1;
|
||||||
|
return urlParts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
urlParts.path = strchr(url, '/');
|
||||||
|
if (urlParts.path == NULL) {
|
||||||
|
urlParts.path = "";
|
||||||
|
} else {
|
||||||
|
*urlParts.path = '\0';
|
||||||
|
urlParts.path++;
|
||||||
|
}
|
||||||
|
|
||||||
|
sep = strchr(url, '@');
|
||||||
|
if (sep) {
|
||||||
|
*sep = '\0';
|
||||||
|
urlParts.username = url;
|
||||||
|
|
||||||
|
urlParts.password = strchr(url, ':');
|
||||||
|
if (urlParts.password != NULL) {
|
||||||
|
*urlParts.password = '\0';
|
||||||
|
urlParts.password++;
|
||||||
|
}
|
||||||
|
|
||||||
|
url = sep + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
urlParts.host = url;
|
||||||
|
|
||||||
|
/* Handle IPv6 address syntax */
|
||||||
|
if (*url == '[') {
|
||||||
|
sep = strchr(url, ']');
|
||||||
|
if (sep) {
|
||||||
|
url = sep + 1;
|
||||||
|
} else {
|
||||||
|
urlParts.errorFound = 1;
|
||||||
|
return urlParts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sep = strchr(url, ':');
|
||||||
|
if (sep) {
|
||||||
|
*sep = '\0';
|
||||||
|
urlParts.port = sep + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return urlParts;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef URLPARSER_TEST
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
URLParts urlParts;
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
urlParts = ParseURL(argv[1]);
|
||||||
|
|
||||||
|
printf("scheme: %s\n", urlParts.scheme ? urlParts.scheme : "(NULL)");
|
||||||
|
printf("username: %s\n", urlParts.username ? urlParts.username : "(NULL)");
|
||||||
|
printf("password: %s\n", urlParts.password ? urlParts.password : "(NULL)");
|
||||||
|
printf("host: %s\n", urlParts.host ? urlParts.host : "(NULL)");
|
||||||
|
printf("port: %s\n", urlParts.port ? urlParts.port : "(NULL)");
|
||||||
|
printf("path: %s\n", urlParts.path ? urlParts.path : "(NULL)");
|
||||||
|
printf("fragment: %s\n", urlParts.fragment ? urlParts.fragment : "(NULL)");
|
||||||
|
|
||||||
|
if (urlParts.errorFound) {
|
||||||
|
printf("Error found\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
25
urlparser.h
Normal file
25
urlparser.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef URLPARSER_H
|
||||||
|
#define URLPARSER_H
|
||||||
|
|
||||||
|
#ifdef __ORCAC__
|
||||||
|
# include <types.h>
|
||||||
|
#else
|
||||||
|
typedef _Bool Boolean;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct URLParts {
|
||||||
|
char *scheme;
|
||||||
|
char *username;
|
||||||
|
char *password;
|
||||||
|
char *host;
|
||||||
|
char *port;
|
||||||
|
char *path; /* Omits leading '/'. Includes query, if any. */
|
||||||
|
char *fragment;
|
||||||
|
|
||||||
|
Boolean errorFound;
|
||||||
|
} URLParts;
|
||||||
|
|
||||||
|
|
||||||
|
URLParts ParseURL(char *url);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user