Add initial code for AFP Mounter CDev.

This code tries to call EasyMount using the request procedure documented in its ERS, but it turns out that doesn't work. The request procedure trashes its return address on the stack, so it crashes whenever you call it.
This commit is contained in:
Stephen Heumann 2017-04-11 17:34:00 -05:00
parent dca542e0cd
commit 73399e8eda
7 changed files with 804 additions and 10 deletions

View File

@ -7,8 +7,8 @@ CFLAGS = -i -w -O95
DSITEST_OBJS = dsitest.o aspinterface.o dsi.o readtcp.o endian.o tcpconnection.o atipmapping.o asmglue.o cmdproc.o installcmds.o
DSITEST_PROG = dsitest
AFPMOUNTER_OBJS = afpmounter.o callat.o endian.o
AFPMOUNTER_PROG = afpmounter
MOUNTAFP_OBJS = afpmounter.o callat.o endian.o
MOUNTAFP_PROG = mountafp
DUMPCMDTBL_OBJS = dumpcmdtbl.o asmglue.o
DUMPCMDTBL_PROG = dumpcmdtbl
@ -16,38 +16,52 @@ DUMPCMDTBL_PROG = dumpcmdtbl
AFPBRIDGE_OBJS = afpinit.o afpbridge.o aspinterface.o dsi.o readtcp.o endian.o tcpconnection.o atipmapping.o asmglue.o installcmds.o cmdproc.o callat.o
AFPBRIDGE_PROG = AFPBridge
PROGS = $(DSITEST_PROG) $(AFPMOUNTER_PROG) $(DUMPCMDTBL_PROG) $(AFPBRIDGE_PROG)
AFPMOUNTER_OBJS = afpcdev.o afpurlparser.o cdevutil.o
AFPMOUNTER_RSRC = afpcdev.rez
AFPMOUNTER_CDEV = AFPMounter
PROGS = $(DSITEST_PROG) $(MOUNTAFP_PROG) $(DUMPCMDTBL_PROG) $(AFPBRIDGE_PROG) $(AFPMOUNTER_CDEV)
.PHONY: default
default: $(PROGS)
$(DSITEST_PROG): $(DSITEST_OBJS)
$(CC) $(CFLAGS) -o $@ $(DSITEST_OBJS)
$(CC) $(CFLAGS) -o $@ $<
$(AFPMOUNTER_PROG): $(AFPMOUNTER_OBJS)
$(CC) $(CFLAGS) -o $@ $(AFPMOUNTER_OBJS)
$(MOUNTAFP_PROG): $(MOUNTAFP_OBJS)
$(CC) $(CFLAGS) -o $@ $<
$(DUMPCMDTBL_PROG): $(DUMPCMDTBL_OBJS)
$(CC) $(CFLAGS) -o $@ $(DUMPCMDTBL_OBJS)
$(CC) $(CFLAGS) -o $@ $<
$(AFPBRIDGE_PROG): $(AFPBRIDGE_OBJS)
$(CC) $(CFLAGS) -M -o $@ $(AFPBRIDGE_OBJS) > $@.map
$(CC) $(CFLAGS) -M -o $@ $< > $@.map
chtyp -tpif $@
$(AFPMOUNTER_CDEV).obj: $(AFPMOUNTER_OBJS)
$(CC) $(CFLAGS) -o $@ $<
$(AFPMOUNTER_CDEV): $(AFPMOUNTER_CDEV).obj $(AFPMOUNTER_RSRC)
$(REZ) $(AFPMOUNTER_RSRC) -o $@
chtyp -tcdv $@
%.macros: %.asm
macgen $< $@ /lang/orca/Libraries/ORCAInclude/m16.*
.PHONY: install
install: $(AFPBRIDGE_PROG)
install: $(AFPBRIDGE_PROG) $(AFPMOUNTER_CDEV)
cp $(AFPBRIDGE_PROG) "*/System/System.Setup"
cp $(AFPMOUNTER_CDEV) "*/System/CDevs"
$(RM) "*/System/CDevs/CDev.Data" > .null
.PHONY: import
import:
chtyp -ttxt *.mk
chtyp -lcc *.c *.h
chtyp -lasm *.asm *.macros
chtyp -lrez *.rez
udl -g *
.PHONY: clean
clean:
$(RM) $(PROGS) *.o *.root *.map > .null
$(RM) $(PROGS) *.o *.root *.obj *.map > .null

241
afpcdev.c Normal file
View File

@ -0,0 +1,241 @@
#pragma cdev cdevMain
#include <types.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <locator.h>
#include <gsos.h>
#include <orca.h>
#include <AppleTalk.h>
#include <quickdraw.h>
#include <window.h>
#include <control.h>
#include "afpurlparser.h"
#include "cdevutil.h"
#define MachineCDEV 1
#define BootCDEV 2
#define InitCDEV 4
#define CloseCDEV 5
#define EventsCDEV 6
#define CreateCDEV 7
#define AboutCDEV 8
#define RectCDEV 9
#define HitCDEV 10
#define RunCDEV 11
#define EditCDEV 12
#define serverAddressTxt 2
#define urlLine 3
#define saveAliasBtn 4
#define connectBtn 1
#define fstMissingError 3000
#define noEasyMountError 3001
FSTInfoRecGS fstInfoRec;
char urlBuf[257];
WindowPtr wPtr = NULL;
typedef struct EasyMountRec {
Word size;
char entity[97];
char volume[28];
char user[32];
char password[8];
char volpass[8];
/* Below fields are new in System 6.0.1 version */
Word version; /* 1 = file alias, 2 = 6.0.1-style server alias */
char volume2[29];
} EasyMountRec;
EasyMountRec easyMountRec;
struct ResultRec {
Word recvCount;
Word result;
} resultRec;
char afpOverTCPZone[] = "AFP over TCP";
void fillEasyMountRec(char *server, char *zone, char *volume, char *user,
char *password, char *volpass)
{
unsigned int i;
char *next;
memset(&easyMountRec, 0, sizeof(easyMountRec));
easyMountRec.size = offsetof(EasyMountRec, volume2) + 2 + strlen(volume);
easyMountRec.version = 2;
i = 0;
next = server;
easyMountRec.entity[i++] = strlen(next);
while (*next != 0 && i < sizeof(easyMountRec.entity) - 2) {
easyMountRec.entity[i++] = *next++;
}
next = "AFPServer";
easyMountRec.entity[i++] = strlen(next);
while (*next != 0 && i < sizeof(easyMountRec.entity) - 1) {
easyMountRec.entity[i++] = *next++;
}
next = zone;
easyMountRec.entity[i++] = strlen(next);
while (*next != 0 && i < sizeof(easyMountRec.entity)) {
easyMountRec.entity[i++] = *next++;
}
easyMountRec.volume[0] = strlen(volume);
strncpy(&easyMountRec.volume[1], volume, sizeof(easyMountRec.volume) - 1);
easyMountRec.user[0] = strlen(user);
strncpy(&easyMountRec.user[1], user, sizeof(easyMountRec.user) - 1);
strncpy(&easyMountRec.password[0], password, sizeof(easyMountRec.password));
strncpy(&easyMountRec.volpass[0], volpass, sizeof(easyMountRec.volpass));
easyMountRec.volume2[0] = strlen(volume) + 1;
easyMountRec.volume2[1] = ':';
strncpy(&easyMountRec.volume2[2], volume, sizeof(easyMountRec.volume2) - 2);
}
Word tryConnect(enum protocol protocol, AFPURLParts *urlParts)
{
resultRec.recvCount = 0;
resultRec.result = 0;
if (protocol == proto_AT) {
fillEasyMountRec(urlParts->server, urlParts->zone,
urlParts->volume, urlParts->username,
urlParts->password, urlParts->volpass);
} else if (protocol == proto_TCP) {
fillEasyMountRec(urlParts->server, afpOverTCPZone,
urlParts->volume, urlParts->username,
urlParts->password, urlParts->volpass);
} else {
return atInvalidCmdErr;
}
SendRequest(0x8000, sendToName+stopAfterOne, (Long)"\pApple~EasyMount~",
(Long)&easyMountRec, (Ptr)&resultRec);
if (resultRec.recvCount == 0) {
return atInvalidCmdErr;
} else {
return resultRec.result;
}
}
void connect(AFPURLParts *urlParts)
{
Word result;
if (urlParts->protocol == proto_AT || urlParts->protocol == proto_TCP) {
result = tryConnect(urlParts->protocol, urlParts);
} else if (urlParts->protocol == proto_unknown) {
/*
* If server name contains a dot it's probably an IP address or
* domain name, so try TCP first. Otherwise try AppleTalk first.
* In either case, we proceed to try the other if we get an NBP error.
*/
if (strchr(urlParts->server, '.') != NULL) {
if (((result = tryConnect(proto_TCP, urlParts)) & 0xFF00) == 0x0400)
result = tryConnect(proto_AT, urlParts);
} else {
if (((result = tryConnect(proto_AT, urlParts)) & 0xFF00) == 0x0400)
result = tryConnect(proto_TCP, urlParts);
}
} else {
AlertWindow(awResource, NULL, protoInvalidError);
return;
}
if (resultRec.recvCount == 0) {
AlertWindow(awResource, NULL, noEasyMountError);
return;
}
}
void finalizeURLParts(AFPURLParts *urlParts)
{
if (urlParts->server == NULL)
urlParts->server = "";
if (urlParts->zone == NULL)
urlParts->zone = "*";
if (urlParts->username == NULL)
urlParts->username = "";
if (urlParts->password == NULL)
urlParts->password = "";
if (urlParts->auth == NULL)
urlParts->auth = "";
if (urlParts->volpass == NULL)
urlParts->volpass = "";
if (urlParts->volume == NULL)
urlParts->volume = "";
}
void DoConnect(char *url)
{
AFPURLParts urlParts;
int validationError;
urlParts = parseAFPURL(url);
if (validationError = validateAFPURL(&urlParts)) {
AlertWindow(awResource, NULL, validationError);
return;
}
finalizeURLParts(&urlParts);
connect(&urlParts);
}
void DoHit(long ctlID)
{
if (ctlID == connectBtn) {
GetLETextByID(wPtr, urlLine, (StringPtr)&urlBuf);
DoConnect(urlBuf+1);
} else if (ctlID == saveAliasBtn) {
}
}
long DoMachine(void)
{
unsigned int i;
/* Check for presence of AppleShare FST. */
fstInfoRec.pCount = 2;
fstInfoRec.fileSysID = 0;
for (i = 1; fstInfoRec.fileSysID != appleShareFSID; i++) {
fstInfoRec.fstNum = i;
GetFSTInfoGS(&fstInfoRec);
if (toolerror() == paramRangeErr) {
InitCursor();
AlertWindow(awResource, NULL, fstMissingError);
return 0;
}
}
return 1;
}
LongWord cdevMain (LongWord data2, LongWord data1, Word message)
{
long result = 0;
switch(message) {
case MachineCDEV: result = DoMachine(); break;
case HitCDEV: DoHit(data2); break;
case InitCDEV: wPtr = (WindowPtr)data1; break;
case CloseCDEV: wPtr = NULL; break;
}
ret:
FreeAllCDevMem();
return result;
}

272
afpcdev.rez Normal file
View File

@ -0,0 +1,272 @@
#include "types.rez"
resource rCDEVFlags (1) {
wantMachine+wantHit+wantInit+wantClose,
1, /* enabled */
1, /* version */
1, /* min ROM version */
0, /* reserved */
{0, 0, 55, 320}, /* rectangle */
"AFP Mounter", /* name */
"Stephen Heumann", /* author */
"v1.0b1" /* version string */
};
read rCDevCode (0x1,convert,locked) "AFPMounter.obj";
resource rIcon (1) {
0x8000, /* color icon */
20, /* dimensions */
28,
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFF"
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFF"
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFF"
$"F00000000FFFFFFFFFFFFFFFFFFF"
$"F0DDDDDD0FFFFFFFFFFFFFFFFFFF"
$"F0DDDDDD04FFFFFFFF4FFFFFFFFF"
$"F0DDDDDD04FFFFFFFF4F0FF000FF"
$"F0DDDDDD444FFFFF0444A0F0DD0F"
$"F0000000444FFFF0E4440A00DD0F"
$"FF0FFFF4F4F4FFF04E404AA0DD0F"
$"F000000404F4FFF04E404A00DD0F"
$"F0F4FF4F04FF4F04004004000000"
$"F000040004FFF44303433343330F"
$"4444444444444444444444444444"
$"3333333334333333334333333333"
$"4444444444444444444444444444"
$"FFFFFFFFF4FFFFFFFF4FFFFFFFFF"
$"FFFFFFFFF4FFFFFFFF4FFFFFFFFF"
$"FFFFFFFFF4FFFFFFFF4FFFFFFFFF"
$"FFFFFFFFF4FFFFFFFF4FFFFFFFFF",
$"0000000000000000000000000000"
$"0000000000000000000000000000"
$"0000000000000000000000000000"
$"0FFFFFFFF0000000000000000000"
$"0FFFFFFFF0000000000000000000"
$"0FFFFFFFFF00000000F000000000"
$"0FFFFFFFFF00000000F0F00FFF00"
$"0FFFFFFFFFF00000FFFFFF0FFFF0"
$"0FFFFFFFFFF0000FFFFFFFFFFFF0"
$"00FFFFFF0F0F000FFFFFFFFFFFF0"
$"0FFFFFFFFF0F000FFFFFFFFFFFF0"
$"0FFFFFFFFF00F0FFFFFFFFFFFFFF"
$"0FFFFFFFFF000FFFFFFFFFFFFFF0"
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFF"
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFF"
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFF"
$"000000000F00000000F000000000"
$"000000000F00000000F000000000"
$"000000000F00000000F000000000"
$"000000000F00000000F000000000"
};
#define cdevWindow 1000
#define helpWindow 2000
#define serverAddressTxt 2
#define urlLine 3
#define saveAliasBtn 4
#define connectBtn 1
#define helpTxt 5
/*
* Controls in the control panel window
*/
resource rControlList (1) {
{
cdevWindow+serverAddressTxt,
cdevWindow+urlLine,
cdevWindow+saveAliasBtn,
cdevWindow+connectBtn
};
};
resource rControlTemplate (cdevWindow+serverAddressTxt) {
serverAddressTxt, /* control ID */
{4, 10, 15, 310}, /* control rect */
statTextControl {{
0, /* flags */
$1000+RefIsResource, /* moreFlags */
0, /* refCon */
cdevWindow+serverAddressTxt /* title ref */
}};
};
resource rTextForLETextBox2 (cdevWindow+serverAddressTxt) {
"AFP Server Address:"
};
resource rControlTemplate (cdevWindow+urlLine) {
urlLine,
{15, 10, 28, 310},
editLineControl {{
0,
$7000+RefIsResource,
0,
255, /* max size */
cdevWindow+urlLine /* text ref */
}};
};
resource rPString (cdevWindow+urlLine) { "afp://" };
resource rControlTemplate (cdevWindow+saveAliasBtn) {
saveAliasBtn,
{35, 10, 0, 0},
SimpleButtonControl {{
NormalButton,
$1000+RefIsResource,
0,
cdevWindow+saveAliasBtn
}};
};
resource rPString(cdevWindow+saveAliasBtn) { "Save Alias..." };
resource rControlTemplate (cdevWindow+connectBtn) {
connectBtn,
{35, 220, 0, 0},
SimpleButtonControl {{
DefaultButton,
$3000+RefIsResource,
0,
cdevWindow+connectBtn,
0, /* color table ref */
{"\$0D","\$0D",0,0} /* key equivalent = Return */
}};
};
resource rPString(cdevWindow+connectBtn) { "Connect" };
/*
* Controls in the help window
*/
resource rControlList (2) {
{
helpWindow+helpTxt
};
};
resource rControlTemplate (helpWindow+helpTxt) {
helpTxt,
{38, 5, 138, 280},
statTextControl {{
0, /* flags */
$1000+RefIsResource, /* moreFlags */
0, /* refCon */
helpWindow+helpTxt /* title ref */
}};
};
resource rTextForLETextBox2 (helpWindow+helpTxt) {
"The AFP Mounter control panel allows you to connect to "
"file servers using the Apple Filing Protocol (AFP) "
"over either AppleTalk or TCP/IP networks. "
"The server address can be specified by URLs "
"of the following forms:\n\$01X\$03\$00"
"afp://[user:password@]server[:port]/\n\$01X\$00\$00"
"volume (to connect using TCP/IP)\n\$01X\$03\$00"
"afp:/at/[user:password@]server:zone/\n\$01X\$00\$00"
"volume (to connect using AppleTalk)\n"
};
/*
* Error messages
*/
#define fstMissingError 3000
#define noEasyMountError 3001
#define protoInvalidError 4000
#define noServerOrVolumeNameError 4001
#define serverNameTooLongError 4002
#define volumeNameTooLongError 4003
#define zoneTooLongError 4004
#define usernameTooLongError 4005
#define passwordTooLongError 4006
#define volpassTooLongError 4007
#define userXorPasswordError 4008
#define badUAMError 4009
resource rAlertString (fstMissingError) {
"72:"
"To use the AFP Mounter control panel, the AppleShare FST "
"and the AppleTalk-related system components it requires "
"must be installed and enabled."
":^#0\$00"
};
resource rAlertString (noEasyMountError) {
"72:"
"Communication with EasyMount failed.\n"
"\n"
"To use the AFP Mounter control panel, EasyMount must be "
"installed and enabled."
":^#0\$00"
};
resource rAlertString (protoInvalidError) {
"32:"
"The specified address is not a valid AFP URL."
":^#0\$00"
};
resource rAlertString (noServerOrVolumeNameError) {
"32:"
"Please specify at least a server and volume name in the AFP URL."
":^#0\$00"
};
resource rAlertString (serverNameTooLongError) {
"32:"
"The server name is too long (maximum 32 characters)."
":^#0\$00"
};
resource rAlertString (volumeNameTooLongError) {
"32:"
"The volume name is too long (maximum 27 characters)."
":^#0\$00"
};
resource rAlertString (zoneTooLongError) {
"32:"
"The zone name is too long (maximum 32 characters)."
":^#0\$00"
};
resource rAlertString (usernameTooLongError) {
"32:"
"The username is too long (maximum 31 characters)."
":^#0\$00"
};
resource rAlertString (passwordTooLongError) {
"32:"
"The password is too long (maximum 8 characters)."
":^#0\$00"
};
resource rAlertString (volpassTooLongError) {
"32:"
"The volume password is too long (maximum 8 characters)."
":^#0\$00"
};
resource rAlertString (userXorPasswordError) {
"42:"
"Please specify both a username and a password, or neither."
":^#0\$00"
};
resource rAlertString (badUAMError) {
"62:"
"The requested user authentication method is not supported "
"or cannot be used with the specified URL."
":^#0\$00"
};

212
afpurlparser.c Normal file
View File

@ -0,0 +1,212 @@
#include <string.h>
#include <ctype.h>
#include "afpurlparser.h"
#ifdef AFPURLPARSER_TEST
# include <stdio.h>
#else
# ifdef __ORCAC__
# pragma noroot
# endif
#endif
int strncasecmp(const char *s1, const char *s2, size_t n)
{
while (tolower(*s1) == tolower(*s2) && *s1 != 0 && n > 1) {
s1++;
s2++;
n--;
}
return (int)*s1 - (int)*s2;
}
static int hextonum(char c) {
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return 0;
}
static void parseEscapes(char *url)
{
unsigned int i, j;
unsigned int len;
len = strlen(url);
for (i = 0, j = 0; url[i] != 0; j++) {
if (url[i] == '%' && isxdigit(url[i+1]) && isxdigit(url[i+2]))
{
url[j] = hextonum(url[i+1]) * 16 + hextonum(url[i+2]);
i += 3;
} else {
url[j] = url[i];
i++;
}
}
url[j] = 0;
}
AFPURLParts parseAFPURL(char *url)
{
char *sep;
AFPURLParts urlParts = {0};
parseEscapes(url);
urlParts.protocol = proto_unknown;
if (strncasecmp(url, "afp:", 4) == 0)
url = url + 4;
if (strncmp(url, "//", 2) == 0) {
urlParts.protocol = proto_TCP;
url += 2;
} else if (strncasecmp(url, "/at/", 4) == 0) {
urlParts.protocol = proto_AT;
url += 4;
}
/* Discard extra slashes */
while (*url == '/')
url++;
sep = strchr(url, '@');
if (sep) {
*sep = 0;
urlParts.username = url;
urlParts.server = sep + 1;
} else {
urlParts.username = NULL;
urlParts.server = url;
}
if (urlParts.username != NULL) {
sep = strchr(urlParts.username, ':');
if (sep) {
*sep = 0;
urlParts.password = sep + 1;
}
sep = strrchr(urlParts.username, ';');
if (sep && strncmp(sep, ";VOLPASS=", 9) == 0) {
*sep = 0;
urlParts.volpass = sep + 9;
}
sep = strrchr(urlParts.username, ';');
if (sep && strncmp(sep, ";AUTH=", 6) == 0) {
*sep = 0;
urlParts.auth = sep + 6;
}
}
sep = strchr(urlParts.server, '/');
if (sep) {
*sep = 0;
urlParts.volume = sep + 1;
/* strip trailing slashes */
while ((sep = strrchr(urlParts.volume, '/')) != NULL && sep[1] == 0)
*sep = 0;
}
sep = strchr(urlParts.server, ':');
if (sep && (urlParts.protocol == proto_AT ||
(urlParts.protocol == proto_unknown
&& strspn(sep+1, "0123456789") != strlen(sep+1))))
{
*sep = 0;
urlParts.zone = sep + 1;
urlParts.protocol = proto_AT;
}
if (urlParts.server == NULL)
urlParts.protocol = proto_invalid;
return urlParts;
}
/* Check if an AFP URL is suitable for our AFP mounter */
int validateAFPURL(AFPURLParts *urlParts)
{
if (urlParts->server == NULL || urlParts->volume == NULL)
return noServerOrVolumeNameError;
if (urlParts->protocol == proto_invalid)
return protoInvalidError;
if (strlen(urlParts->server) > SERVER_MAX)
return serverNameTooLongError;
if (strlen(urlParts->volume) > VOLUME_MAX)
return volumeNameTooLongError;
if (urlParts->zone != NULL && strlen(urlParts->zone) > ZONE_MAX)
return zoneTooLongError;
if (urlParts->username != NULL && strlen(urlParts->username) > USERNAME_MAX)
return usernameTooLongError;
if (urlParts->password != NULL && strlen(urlParts->password) > PASSWORD_MAX)
return passwordTooLongError;
if (urlParts->volpass != NULL && strlen(urlParts->volpass) > VOLPASS_MAX)
return volpassTooLongError;
/* Must have username & password, or neither */
if (urlParts->username == NULL)
if (urlParts->password != NULL && *urlParts->password != 0)
return userXorPasswordError;
if (urlParts->password == NULL)
if (urlParts->username != NULL && *urlParts->username != 0)
return userXorPasswordError;
if (urlParts->auth != NULL) {
if (strncasecmp(urlParts->auth, "No User Authent", 16) == 0) {
if ((urlParts->username != NULL && *urlParts->username != 0)
|| (urlParts->password != NULL && *urlParts->password != 0))
return badUAMError;
} else if (strncasecmp(urlParts->auth, "Randnum Exchange", 17) == 0) {
if (urlParts->username == NULL || *urlParts->username == 0
|| urlParts->password == NULL)
return badUAMError;
} else {
/* unknown/unsupported UAM */
return badUAMError;
}
}
return 0;
}
#ifdef AFPURLPARSER_TEST
int main(int argc, char **argv)
{
AFPURLParts urlParts;
if (argc < 2)
return 1;
urlParts = parseAFPURL(strdup(argv[1]));
printf("protocol = ");
switch(urlParts.protocol) {
case proto_unknown: printf("unknown\n"); break;
case proto_AT: printf("AppleTalk\n"); break;
case proto_TCP: printf("TCP\n"); break;
case proto_invalid: printf("invalid URL\n"); break;
default: printf("Bad URL structure\n"); break;
}
printf("server: %s\n", urlParts.server);
printf("zone: %s\n", urlParts.zone);
printf("username: %s\n", urlParts.username);
printf("password: %s\n", urlParts.password);
printf("auth: %s\n", urlParts.auth);
printf("volpass: %s\n", urlParts.volpass);
printf("volume: %s\n", urlParts.volume);
printf("Validation result: %s\n", validateAFPURL(&urlParts) ? "bad" : "OK");
}
#endif

39
afpurlparser.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef AFPURLPARSER_H
#define AFPURLPARSER_H
enum protocol { proto_unknown, proto_AT, proto_TCP, proto_invalid };
typedef struct AFPURLParts {
enum protocol protocol;
char *server;
char *zone; /* for AppleTalk only */
char *username;
char *password;
char *auth;
char *volpass;
char *volume;
} AFPURLParts;
/* Maximum lengths of components (for use in our mounter, using EasyMount) */
#define SERVER_MAX 32
#define ZONE_MAX 32
#define USERNAME_MAX 31
#define PASSWORD_MAX 8
#define VOLPASS_MAX 8
#define VOLUME_MAX 27
#define protoInvalidError 4000
#define noServerOrVolumeNameError 4001
#define serverNameTooLongError 4002
#define volumeNameTooLongError 4003
#define zoneTooLongError 4004
#define usernameTooLongError 4005
#define passwordTooLongError 4006
#define volpassTooLongError 4007
#define userXorPasswordError 4008
#define badUAMError 4009
AFPURLParts parseAFPURL(char *url);
int validateAFPURL(AFPURLParts *urlParts);
#endif

10
cdevutil.asm Normal file
View File

@ -0,0 +1,10 @@
case on
dummy private
end
FreeAllCDevMem start
pea 0
jsl ~DAID
rtl
end

6
cdevutil.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef CDEVUTIL_H
#define CDEVUTIL_H
void FreeAllCDevMem(void);
#endif