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