From 73399e8edab516052bd256a85d14104d2bc9d260 Mon Sep 17 00:00:00 2001 From: Stephen Heumann Date: Tue, 11 Apr 2017 17:34:00 -0500 Subject: [PATCH] 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. --- Makefile.mk | 34 +++++-- afpcdev.c | 241 +++++++++++++++++++++++++++++++++++++++++++ afpcdev.rez | 272 +++++++++++++++++++++++++++++++++++++++++++++++++ afpurlparser.c | 212 ++++++++++++++++++++++++++++++++++++++ afpurlparser.h | 39 +++++++ cdevutil.asm | 10 ++ cdevutil.h | 6 ++ 7 files changed, 804 insertions(+), 10 deletions(-) create mode 100644 afpcdev.c create mode 100644 afpcdev.rez create mode 100644 afpurlparser.c create mode 100644 afpurlparser.h create mode 100644 cdevutil.asm create mode 100644 cdevutil.h diff --git a/Makefile.mk b/Makefile.mk index e53034b..ba9fecf 100644 --- a/Makefile.mk +++ b/Makefile.mk @@ -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 diff --git a/afpcdev.c b/afpcdev.c new file mode 100644 index 0000000..4106010 --- /dev/null +++ b/afpcdev.c @@ -0,0 +1,241 @@ +#pragma cdev cdevMain + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/afpcdev.rez b/afpcdev.rez new file mode 100644 index 0000000..477ccdb --- /dev/null +++ b/afpcdev.rez @@ -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" +}; + diff --git a/afpurlparser.c b/afpurlparser.c new file mode 100644 index 0000000..2f1bf93 --- /dev/null +++ b/afpurlparser.c @@ -0,0 +1,212 @@ +#include +#include +#include "afpurlparser.h" + + +#ifdef AFPURLPARSER_TEST +# include +#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 + diff --git a/afpurlparser.h b/afpurlparser.h new file mode 100644 index 0000000..ded14ff --- /dev/null +++ b/afpurlparser.h @@ -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 diff --git a/cdevutil.asm b/cdevutil.asm new file mode 100644 index 0000000..7ef488a --- /dev/null +++ b/cdevutil.asm @@ -0,0 +1,10 @@ + case on + +dummy private + end + +FreeAllCDevMem start + pea 0 + jsl ~DAID + rtl + end diff --git a/cdevutil.h b/cdevutil.h new file mode 100644 index 0000000..d1a56e6 --- /dev/null +++ b/cdevutil.h @@ -0,0 +1,6 @@ +#ifndef CDEVUTIL_H +#define CDEVUTIL_H + +void FreeAllCDevMem(void); + +#endif