initial version

This commit is contained in:
Kelvin Sherlock 2012-03-08 21:15:46 -05:00
commit f305f78412
4 changed files with 959 additions and 0 deletions

171
connect.c Normal file
View File

@ -0,0 +1,171 @@
#include "Connect.h"
//static char pstring[256];
static Word LoginAndOpen(ConnectBuffer *buffer)
{
Word ipid;
Word terr;
ipid = TCPIPLogin(
buffer->memID,
buffer->dnr.DNRIPaddress,
buffer->port,
0x0000, 0x0040);
if (_toolErr)
{
buffer->state = ConnectStateError;
return -1;
}
terr = TCPIPOpenTCP(ipid);
if (_toolErr || terr)
{
TCPIPLogout(ipid);
buffer->state = ConnectStateError;
buffer->terr = terr;
buffer->ipid = 0;
return -1;
}
buffer->ipid = ipid;
buffer->state = ConnectStateConnecting;
return 0;
}
Word ConnectionPoll(ConnectBuffer *buffer)
{
Word state;
if (!buffer) return -1;
state = buffer->state;
if (state == 0) return -1;
if (state == ConnectStateConnected) return 1;
if (state == ConnectStateDisconnected) return 1;
if (state == ConnectStateError) return -1;
TCPIPPoll();
if (state == ConnectStateDNR)
{
if (buffer->dnr.DNRstatus == DNR_OK)
{
return LoginAndOpen(buffer);
}
else if (buffer->dnr.DNRstatus != DNR_Pending)
{
buffer->state = ConnectStateError;
return -1;
}
}
if (state == ConnectStateConnecting || state == ConnectStateDisconnecting)
{
Word terr;
static srBuff sr;
terr = TCPIPStatusTCP(buffer->ipid, &sr);
if (state == ConnectStateDisconnecting)
{
// these are not errors.
if (terr == tcperrConClosing || terr == tcperrClosing)
terr = tcperrOK;
}
if (terr || _toolErr)
{
//CloseAndLogout(buffer);
TCPIPCloseTCP(buffer->ipid);
TCPIPLogout(buffer->ipid);
buffer->ipid = 0;
buffer->state = ConnectStateError;
buffer->terr = terr;
return -1;
}
if (sr.srState == TCPSESTABLISHED) // && state == ConnectStateConnecting)
{
buffer->state = ConnectStateConnected;
return 1;
}
if (sr.srState == TCPSCLOSED || sr.srState == TCPSTIMEWAIT)
{
TCPIPLogout(buffer->ipid);
buffer->ipid = 0;
buffer->state = ConnectStateDisconnected;
return 1;
}
}
return 0;
}
Word ConnectionOpen(ConnectBuffer *buffer, const char *host, Word port)
{
buffer->state = 0;
buffer->ipid = 0;
buffer->terr = 0;
buffer->port = port;
// 1. check if we need to do DNR.
if (TCPIPValidateIPString(host))
{
cvtRec cvt;
TCPIPConvertIPToHex(&cvt, host);
buffer->dnr.DNRIPaddress = cvt.cvtIPAddress;
buffer->dnr.DNRstatus = DNR_OK; // fake it.
return LoginAndOpen(buffer);
}
// do dnr.
TCPIPDNRNameToIP(host, &buffer->dnr);
if (_toolErr)
{
buffer->state = ConnectStateError;
return -1;
}
buffer->state = ConnectStateDNR;
return 0;
}
void ConnectionInit(ConnectBuffer *buffer, Word memID)
{
buffer->memID = memID;
buffer->ipid = 0;
buffer->terr = 0;
buffer->state = 0;
buffer->port = 0;
buffer->dnr.DNRstatus = 0;
buffer->dnr.DNRIPaddress = 0;
}
Word ConnectionClose(ConnectBuffer *buffer)
{
Word state = buffer->state;
// todo -- how do you close if not yet connected?
if (state == ConnectStateConnected)
{
buffer->state = ConnectStateDisconnecting;
buffer->terr = TCPIPCloseTCP(buffer->ipid);
return 0;
}
if (state == ConnectStateDNR)
{
TCPIPCancelDNR(&buffer->dnr);
buffer->state = 0;
return 1;
}
return -1;
}

206
gopher.c Normal file
View File

@ -0,0 +1,206 @@
#include <Locator.h>
#include <Memory.h>
#include <tcpip.h>
//#include "url.h"
#include "Connect.h"
#include <stdio.h>
/*
* connect gopher.floodgap.com:70
* send path
* read output.
* text -- ends with . <CR><LF>
* bin -- ends when connection is closed.
*/
// startup/shutdown flags.
enum {
kLoaded = 1,
kStarted = 2,
kConnected = 4
};
Word StartUp(void)
{
word status;
word flags = 0;
status = TCPIPStatus();
if (_toolErr)
{
LoadOneTool(54,0x0200);
if (_toolErr) return -1;
status = 0;
flags |= kLoaded;
}
if (!status)
{
TCPIPStartUp();
if (_toolErr) return -1;
flags |= kStarted;
}
status = TCPIPGetConnectStatus();
if (!status)
{
TCPIPConnect(NULL);
flags |= kConnected;
}
return flags;
}
void ShutDown(word flags)
{
if (flags & kConnected)
{
TCPIPDisconnect(false, NULL);
if (_toolErr) return;
}
if (flags & kStarted)
{
TCPIPShutDown();
if (_toolErr) return;
}
if (flags & kLoaded)
{
UnloadOneTool(54);
}
}
#if 0
void do_url(const char *url)
{
URLComponents components;
ConnectBuffer buffer;
char *host;
if (ParseURL(url, strlen(url), &components))
{
fprintf(stderr, "Invalid URL: %s\n", url);
return;
}
if (!url.host.length)
{
fprintf(stderr, "No host\n");
return;
}
if (!components.portNumber) components.portNumber = 70;
host = malloc(url.host.length + 1);
URLComponentGetCString(url, &components, host, URLComponentHost);
ConnectInit(&buffer, myID);
ConnectCString(&buffer, host, components.portNumber);
if (components.path.length)
{
rv = TCPIPWriteTCP(buffer.ipid,
url + components.path.location,
components.path.length,
0, 0);
}
rv = TCPIPWriteTCP(buffer.ipid, "\r\n", 2, 1, 0);
while (!ConnectionPoll(&buffer)) ;
if (buffer.state == ConnectionStateError)
{
fprintf(stderr, "Unable to open host: %s:%u\n", host, components.portNumber);
free(host);
return;
}
ConnectionClose(&buffer);
while (!ConnectionPoll(&buffer)) ; // wait for it to close.
free (host);
}
#endif
int main(int argc, char **argv)
{
Handle h;
ConnectBuffer buffer;
int i;
Word rv;
Word flags;
// getopt for -b binary
#if 0
for (i = 1; i < argc; ++i)
{
do_url(argv[i]);
}
#endif
flags = StartUp();
fprintf(stdout, "flags: %u\n", flags);
ConnectionInit(&buffer, MMStartUp());
rv = ConnectionOpen(&buffer, "\pgopher.floodgap.com", 70);
while (!rv)
{
rv = ConnectionPoll(&buffer);
}
if (buffer.state == ConnectStateConnected)
{
fprintf(stdout, "Connected!\n");
}
fprintf(stdout, "%x %x %x\n", rv, buffer.state, buffer.terr);
rv = TCPIPWriteTCP(buffer.ipid, "\r\n", 2, 1, 0);
if (rv)
{
fprintf(stdout, "TCPIPWriteTCP: %x\n", rv);
}
for(;;)
{
rrBuff rb;
//rlrBuff rb;
Handle h;
Word count;
TCPIPPoll();
rv = TCPIPReadTCP(buffer.ipid, 2, NULL, 4096, &rb);
//rv = TCPIPReadLineTCP(buffer.ipid, "\p\r\n", 2, NULL, 4096, &rb);
if (rv) break;
h = rb.rrBuffHandle;
count = rb.rrBuffCount;
if (!h) continue;
HLock(h);
fwrite(*h, count, 1, stdout);
fputs("", stdout);
DisposeHandle(h);
}
rv = ConnectionClose(&buffer);
while (!rv)
{
rv = ConnectionPoll(&buffer);
}
fprintf(stdout, "%x %x %x\n", rv, buffer.state, buffer.terr);
ShutDown(flags);
}

519
url.c Normal file
View File

@ -0,0 +1,519 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "url.h"
enum {
kScheme,
kUser,
kPassword,
kHost,
kPort,
kPath,
kParams,
kQuery,
kFragment
};
/*
* scheme://username:password@domain:port/path;parms?query_string#fragment_id
*
*
*/
int URLGetComponentCString(const char *url, struct URLComponents *components, int type, char *dest)
{
URLRange *rangePtr;
URLRange r;
if (!url || !components) return -1;
if (type < URLComponentScheme || type > URLComponentPathAndQuery) return -1;
rangePtr = &components->scheme;
r = rangePtr[type];
if (!dest) return r.length;
else
{
if (r.length)
{
memcpy(dest, url + r.location, r.length);
}
dest[r.length] = 0;
return r.length;
}
}
#if 0
int schemeType(const char *string)
{
if (!string || !*string) return SCHEME_NONE;
static struct {
int length;
const char *data
} table[] = {
{ 4, "file:" },
{ 3, "ftp:" },
{ 6, "gopher:" },
{ 4, "http:" },
{ 5, "https:" },
{ 6, "mailto:" },
{ 4, "news:" },
{ 4, "nntp:" },
{ 6, "telnet:" }
};
switch(*string)
{
case 'f':
// ftp, file
if (!strcmp(string, "file")) return SCHEME_FILE;
if (!strcmp(string, "ftp")) return SCHEME_FTP;
break;
case 'g':
// gopher
if (!strcmp(string, "gopher")) return SCHEME_GOPHER;
break;
case 'h':
// http, https
if (!strcmp(string, "https")) return SCHEME_HTTPS;
if (!strcmp(string, "http")) return SCHEME_HTTP;
break;
case 'm':
// mailto
if (!strcmp(string, "mailto")) return SCHEME_MAILTO;
break;
case 'n':
// news, nntp
if (!strcmp(string, "news")) return SCHEME_NEWS;
if (!strcmp(string, "nntp")) return SCHEME_NNTP;
break;
case 't':
// telnet
if (!strcmp(string, "telnet")) return SCHEME_TELNET;
break;
}
return SCHEME_UNKNOWN;
}
#endif
int ParseURL(const char *url, int length, struct URLComponents *components)
{
char c;
int state;
int hasScheme;
int hasAt;
int i, l;
URLRange range = { 0, 0 };
URLRange *rangePtr;
if (!components || !url) return 0;
rangePtr = &components->scheme;
memset(components, 0, sizeof(URLComponents));
state = kScheme;
// 1 check for a scheme.
// [A-Za-z0-9.+-]+ ':'
hasScheme = 0;
hasAt = 0;
range.location = 0;
for(i = 0; i < length; ++i)
{
c = url[i];
if (c == ':')
{
hasScheme = 1;
break;
}
if (c >= 'a' && c <= 'z') continue;
if (c >= 'A' && c <= 'Z') continue;
if (c >= '0' && c <= '9') continue;
//if (isalnum(c)) continue;
if (c == '.') continue;
if (c == '+') continue;
if (c == '-') continue;
break;
}
if (hasScheme)
{
if (i == 0) return 0;
range.length = i;
components->scheme = range;
++i; // skip the ':'
}
else
{
state = kPath;
i = 0;
}
// 2. check for //
// //user:password@domain:port/
// otherwise, it's a path
if (strncmp(url + i, "//", 2) == 0)
{
state = kUser;
i += 2;
}
else
{
state = kPath;
}
//range = (URLRange){ i, 0 };
range.location = i; range.length = 0;
for( ; i < length; ++i)
{
c = url[i];
if ((c < 32) || (c & 0x80))
{
// goto invalid_char;
return 0;
}
switch(c)
{
// illegal chars.
// % # not allowed but allowed.
case ' ':
case '"':
case '<':
case '>':
case '[':
case '\\':
case ']':
case '^':
case '`':
case '{':
case '|':
case '}':
case 0x7f:
// goto invalid_char;
return 0;
break;
case ':':
// username -> password
// domain -> port
// password -> error
// port -> error
// all others, no transition.
switch(state)
{
case kUser:
case kHost:
l = i - range.location;
if (l > 0)
{
range.length = l;
rangePtr[state] = range;
}
//range = (URLRange){ i + 1 , 0 };
range.location = i + 1; range.length = 0;
state++;
break;
case kPassword:
case kPort:
return 0;
}
break;
case '@':
// username -> domain
// password -> domain
// all others no transition.
// path -> error?
switch(state)
{
case kUser:
case kPassword:
hasAt = 1;
l = i - range.location;
if (l > 0)
{
range.length = l;
rangePtr[state] = range;
}
//range = (URLRange){ i + 1 , 0 };
range.location = i + 1; range.length = 0;
state = kHost;
break;
}
break;
case '/':
// domain -> path
// port -> path
// (must be switched to domain:port)
// username -> path
// password -> path
// all others, no transition.
// '/' is part of the filename.
switch (state)
{
case kUser:
hasAt = 1; // close enough
state = kHost; // username was actually the domain.
// pass through.
case kPassword: // need to switch to domain:port later.
case kHost:
case kPort:
l = i - range.location;
if (l > 0)
{
range.length = l;
rangePtr[state] = range;
}
//range = (URLRange){ i, 0 }; // '/' is part of path.
range.location = i; range.length = 0;
state = kPath;
break;
}
break;
case ';':
// path -> param
// all others, no transition.
if (state == kPath)
{
l = i - range.location;
if (l > 0)
{
range.length = l;
rangePtr[state] = range;
}
//range = (URLRange){ i + 1 , 0 };
range.location = i + 1; range.length = 0;
state = kParams;
}
break;
case '?':
// path -> query
// param -> query
// all others, no transition.
if (state == kPath || state == kParams)
{
l = i - range.location;
if (l > 0)
{
range.length = l;
rangePtr[state] = range;
}
//range = (URLRange){ i + 1 , 0 };
range.location = i + 1; range.length = 0;
state = kQuery;
}
break;
case '#':
// fragment -> no transition
// everything else transitions to a fragment.
// can immediately end parsing since we know the length...
l = i - range.location;
if (l > 0)
{
range.length = l;
rangePtr[state] = range;
}
state = kFragment;
range.location = i + 1; range.length = 0;
i = length; // break from loop.
break;
}
}
// finish up the last portion.
if (state)
{
l = i - range.location;
if (l > 0)
{
range.length = l;
rangePtr[state] = range;
}
}
if (!hasAt)
{
// user:name was actually domain:port
components->host = components->user;
components->port = components->password;
components->user.location = 0;
components->user.length = 0;
components->password.location = 0;
components->password.length = 0;
}
// port number conversion.
if (components->port.location)
{
const char *tmp = url + components->port.location;
int p = 0;
l = components->port.length;
for (i = 0; i < l; ++i)
{
c = tmp[i];
if ((c < '0') || (c > '9'))
{
p = 0;
break;
}
// convert to number.
c &= 0x0f;
// p *= 10;
// 10x = 2x + 8x = (x << 1) + (x << 3)
if (p)
{
int p2;
p2 = p << 1;
p = p2 << 2;
p = p + p2;
}
p += c;
}
components->portNumber = p;
}
#if 0
// path and query.
// path;params?query
range = components->path;
if (range.length)
{
if (components->params.length)
range.length += components->params.length + 1;
if (components->query.length)
range.length += components->query.length + 1;
components->pathAndQuery = range;
}
#endif
return 1;
}
void test(const char *url)
{
URLComponents data;
int ok;
char *buffer;
buffer = strdup(url); // enough space.
if (!url || !*url) return;
ok = ParseURL(url, strlen(url), &data);
printf("%s (%s)\n", url, ok ? "ok" : "error");
URLGetComponentCString(url, &data, URLComponentScheme, buffer);
printf(" scheme: %s\n", buffer);
URLGetComponentCString(url, &data, URLComponentUser, buffer);
printf(" username: %s\n", buffer);
URLGetComponentCString(url, &data, URLComponentPassword, buffer);
printf(" password: %s\n", buffer);
URLGetComponentCString(url, &data, URLComponentHost, buffer);
printf(" host: %s\n", buffer);
URLGetComponentCString(url, &data, URLComponentPort, buffer);
printf(" port: %s [%d]\n", buffer, data.portNumber);
URLGetComponentCString(url, &data, URLComponentPath, buffer);
printf(" path: %s\n", buffer);
URLGetComponentCString(url, &data, URLComponentParams, buffer);
printf(" params: %s\n", buffer);
URLGetComponentCString(url, &data, URLComponentQuery, buffer);
printf(" query: %s\n", buffer);
URLGetComponentCString(url, &data, URLComponentFragment, buffer);
printf(" fragment: %s\n", buffer);
free(buffer);
}
int main(int argc, char **argv)
{
int i;
for (i = 1; i < argc; ++i)
{
test(argv[i]);
}
return 0;
}

63
url.h Normal file
View File

@ -0,0 +1,63 @@
#ifndef __url_h__
#define __url_h__
enum {
SCHEME_UNKNOWN = -1,
SCHEME_NONE = 0,
SCHEME_FILE,
SCHEME_FTP,
SCHEME_GOPHER,
SCHEME_HTTP,
SCHEME_HTTPS,
SCHEME_MAILTO,
SCHEME_NEWS,
SCHEME_NNTP,
SCHEME_TELNET
};
typedef struct URLRange {
int location;
int length;
} URLRange;
enum {
URLComponentScheme,
URLComponentUser,
URLComponentPassword,
URLComponentHost,
URLComponentPort,
URLComponentPath,
URLComponentParams,
URLComponentQuery,
URLComponentFragment,
URLComponentPathAndQuery
};
typedef struct URLComponents {
int schemeType;
int portNumber;
URLRange scheme;
URLRange user;
URLRange password;
URLRange host;
URLRange port;
URLRange path;
URLRange params;
URLRange query;
URLRange fragment;
URLRange pathAndQuery;
} URLComponents;
int ParseURL(const char *url, int length, struct URLComponents *components);
int URLGetComponentCString(const char *url, struct URLComponents *, int, char *);
int URLGetComponentPString(const char *url, struct URLComponents *, int, char *);
//int URLGetComponentGSString(const char *url, struct URLComponents *, int, GSString255Ptr);
#endif