home *** CD-ROM | disk | FTP | other *** search
- #import "GopherClient.h"
- #import "StuartSpeaker.h"
- #import "GopherDispatcher.h"
- #import <appkit/Listener.h>
- #import <appkit/Speaker.h>
- #import "PhSpeaker.h"
-
- #include <stdio.h>
-
- // Declared in GopherDispatcher.m
- extern char defaultHost[256];
- extern int defaultPort;
-
- static int isIndexedText = NO;
- static char lastIndexWord[256];
-
- @implementation GopherClient
-
- ///////////////////////////////////////////////////
- //
-
- - (char *)currentCellName
- {
- id CurrentCell;
-
- CurrentCell = [[MainScroller matrixInColumn:[MainScroller selectedColumn]]
- selectedCell];
- return (char *)[CurrentCell stringValue];
- }
-
- //////////////////////////////////////////////////
- // Compute a new location for a window.
-
- static void newLocation(NXPoint *p)
- {
- static count = 0;
-
- p->x += (20.0 * count);
- p->y -= (25.0 * count);
- count = (count > 10)? 0 : count+1;
- }
-
- //////////////////////////////////////////////////
- // Automatically called by the system when an instance is created.
-
- - init
- {
- NXRect theFrame;
-
- [super init];
-
- strncpy(currenthostname, defaultHost, 250);
- strcpy(currentfilename, "");
- currentport = defaultPort;
-
- [NXApp loadNibSection:"GopherClient.nib" owner:self];
- //[myWindow setDelegate:self];
- [myWindow getFrame:&theFrame];
- newLocation(&theFrame.origin);
- [myWindow moveTo:theFrame.origin.x :theFrame.origin.y];
- [myWindow makeKeyAndOrderFront:nil];
-
- [[MainText docView] setAutodisplay:YES];
-
- [MainScroller setCellClass:[GopherBrowserCell class]];
- [MainScroller useScrollButtons:YES];
- [MainScroller loadColumnZero];
-
- theText = (char *)malloc(MAXTEXTLENGTH);
-
- return self;
- }
-
- /////////////////////////////////////////////////
- // Close the window.
-
- - close
- {
- [myWindow performClose:self];
- [self clean];
- [myDispatcher oldFrontWindow:self]; // Theoretically unnecessary
-
- return self;
- }
-
- /////////////////////////////////////////////////
- // Clean things up.
-
- - clean
- {
- [ItemInfoPanel performClose:self];
- return self;
- }
-
- /////////////////////////////////////////////////
- // Target method for the browser.
-
- - cellClicked:sender
- {
- id CurrentCell;
- int remotePort;
- ThreadArgument *theArg;
- extern int defaultApp;
- char *remotePath;
-
- // Stuff borrowed from Rex Pruess's Remotes program
- id stuart;
- int code, ind;
- char *command;
- char *comArgs[10];
- int pid;
- char portStr[16];
- char loginStr[256];
- char errMsg[256];
- extern char **environ; /* User environment */
- #ifdef DEBUG
- char **tempPtr;
- #endif
-
- // Stuff for Rex Pruess's Ph CSO Nameserver client
- int flag;
- int msgDelivered;
- char server[128];
- char site[128];
-
-
-
- CurrentCell = [[sender matrixInColumn:[sender selectedColumn]] selectedCell];
-
- [ItemInfoHostName setStringValue:[CurrentCell remoteHost]];
- [ItemInfoFileName setStringValue:[CurrentCell remoteName]];
- [ItemInfoPortNumber setIntValue:[CurrentCell remotePort]];
-
- switch ([CurrentCell cellType]) {
- case '0' : // Document : retrieve and display
- strncpy(currenthostname, [CurrentCell remoteHost], 255);
- strcpy(currentfilename, [CurrentCell remoteName]);
- currentport = [CurrentCell remotePort];
- [ItemInfoType setStringValue:"ASCII text"];
- [self displayDocument];
- break;
- case '1' : // Directory : do nothing, taken care of in
- // browser:fillMatrix:inColumn:
- isIndexedText = NO;
- [ItemInfoType setStringValue:"Directory"];
- [[MainText docView] setText:""];
- break;
- case '2': // CSO Nameserver
- if (![self createPort:self])
- break;
-
- strncpy (server, [CurrentCell remoteHost], 128);
- strncpy (site, [CurrentCell stringValue], 128);
-
- msgDelivered = [mySpeaker showServer:server site:site ok:&flag];
-
- if (msgDelivered != 0)
- fprintf (stderr, "Sorry, showServer message not delivered.\n");
- else
- if (!flag) {
- fprintf (stderr, "\n*** showServer call failed. ***\n");
- fprintf (stderr, "*** Should only happen if 'server' is empty or too long.\n");
- }
-
- free (mySpeaker);
- [ItemInfoType setStringValue:"Phone Server"];
- break;
- case '7' : // Index search
- [ItemInfoType setStringValue:"Index"];
- isIndexedText = YES;
- break;
- case 'R':
- case 'T':
- case '8': // Telnet session. Revised by David Lacey
- [ItemInfoType setStringValue:"Telnet Session"];
- isIndexedText = NO;
- [[MainText docView] setText:""];
- if ([CurrentCell remotePort] == 0) // Seems common, so fix it
- remotePort = 23;
- else
- remotePort = [CurrentCell remotePort];
-
- // If there is a login ID specified and we're telnetting, alert the user
- if (([CurrentCell cellType] == '8') &&
- *(remotePath = [CurrentCell remoteName])) {
- sprintf (errMsg, "Login to remote system as '%s'.", remotePath);
- NXRunAlertPanel (NULL, errMsg, NULL, NULL, NULL, NULL);
- }
-
- if (defaultApp == 0) { // Stewart or Terminal?
- ind = 0;
- command = "/NextApps/Terminal";
- comArgs[ind++] = "Terminal";
-
- comArgs[ind++] = "-Shell";
-
- switch ([CurrentCell cellType]) {
- case 'R': // RLOGIN
- strcpy (loginStr, "/usr/ucb/rlogin ");
- if (*[CurrentCell remoteName] != '\0') {
- strcat (loginStr, " -l ");
- strcat (loginStr, [CurrentCell remoteName]);
- }
- strcat (loginStr, " ");
- strcat (loginStr, [CurrentCell remoteHost]);
- break;
-
- case '8': // TELNET:
- strcpy (loginStr, "/usr/ucb/telnet ");
- strcat (loginStr, [CurrentCell remoteHost]);
- if (remotePort != 23) {
- sprintf (portStr, " %d", remotePort);
- strcat (loginStr, portStr);
- }
- break;
-
- case 'T': // TN3270:
- strcpy (loginStr, "/usr/ucb/tn3270 ");
- strcat (loginStr, [CurrentCell remoteHost]);
- break;
-
- }
- comArgs[ind++] = loginStr;
- comArgs[ind++] = NULL;
- #ifdef DEBUG
- /* printf ("%s: %s",[NXApp appName], command);
- tempPtr = comArgs;
- while (*++tempPtr != NULL)
- printf (" %s", *tempPtr);
- printf ("\n");
- }
- */
- #endif
- pid = vfork ();
- if (pid == 0) {
- code = execve (command, comArgs, environ);
- perror ([NXApp appName]);
- fprintf (stderr, "%s: Execve error code=%d.\n",[NXApp appName], code);
- _exit (0);
- }
-
- } else { // Send a message to Stuart
-
- stuart = [StuartSpeaker new];
-
- switch ([CurrentCell cellType]) {
- case 'R': // RLOGIN
- strcpy (loginStr, "rlogin ");
- if (*[CurrentCell remoteName] != '\0') {
- strcat (loginStr, " -l ");
- strcat (loginStr, [CurrentCell remoteName]);
- }
- strcat (loginStr, " ");
- strcat (loginStr, [CurrentCell remoteHost]);
- break;
-
- case '8': // TELNET:
- strcpy (loginStr, "telnet ");
- strcat (loginStr, [CurrentCell remoteHost]);
- if (remotePort != 23) {
- sprintf (portStr, " %d", remotePort);
- strcat (loginStr, portStr);
- }
- break;
-
- case 'T': // TN3270:
- strcpy (loginStr, "tn3270 ");
- strcat (loginStr, [CurrentCell remoteHost]);
- break;
-
- }
- [stuart default:"Shell" as:loginStr];
- [stuart default:"WriteStuartrc" as:"NO"];
-
- [stuart setActivate:TRUE];
-
- if ([stuart stuartConnectAndNew]) {
- strcpy (errMsg, "There was a problem communicating with the Stuart ");
- strcat (errMsg, "application. (Speaker port unavailable.) This may ");
- strcat (errMsg, "have occurred because Stuart is not installed on ");
- strcat (errMsg, "your system or because Stuart was slow in starting.");
-
- NXRunAlertPanel (NULL, errMsg, NULL, NULL, NULL, NULL);
- }
- [stuart free];
- }
-
-
- break;
-
- case 's' : // Sound
- isIndexedText = NO;
- [ItemInfoType setStringValue:"Sound"];
- [[MainText docView] setText:""];
- // Check that we're the only thread. We do it here because
- // we can't have NXRunAlertPanel in a thread (why not ?)
- if (mutex_try_lock(soundIsPlaying) == 0) {
- NXRunAlertPanel("Gopher error", "Only one sound at a time !", "OK", NULL, NULL);
- break;
- }
-
- mutex_unlock(soundIsPlaying);
-
- soundShouldStopFlag = 0;
- // The argument is malloc'ed because we may have several threads.
- theArg = malloc(sizeof(ThreadArgument));
- strncpy(theArg->hostname, [CurrentCell remoteHost], 255);
- theArg->portnumber = [CurrentCell remotePort];
- strncpy(theArg->filename, [CurrentCell remoteName], 1023);
- cthread_detach(cthread_fork((cthread_fn_t)PlaySound, (any_t)theArg));
- break;
- default :
- break;
- }
-
- return self;
- }
-
- ///////////////////////////////////////////////////////////////////
- // Highlight next occurance of search string, if it's indexed text.
- //
-
- - highlightNextWord
- {
- int findIndex, spaceIndex;
- int minIndex = -1;
- char *word, *cp = NULL, *textBuffer;
- char wordStowage[256];
- int done = 0;
- NXSelPt begin, end;
-
- strcpy(wordStowage, lastIndexWord);
- word = wordStowage;
-
- [[MainText docView] getSel:&begin :&end];
-
- textBuffer = theText + end.cp;
-
- while (!done) {
-
- cp = strchr(word, ' ');
-
- if (cp == NULL)
- done = 1;
- else
- *cp='\0';
-
- #ifdef DEBUG
- fprintf(stderr, "Word is: %s\n", word);
- #endif
-
- // Don't highlight stupid words
-
- if ((strcasecmp(word, "and") != 0) &&
- (strcasecmp(word, "or") != 0) &&
- (strcasecmp(word, "not") != 0) &&
- (strcasecmp(word, "the") != 0)) {
-
-
- findIndex = (int)strcasestr(textBuffer, word);
-
- if (findIndex == 0)
- ; // Not found
- else {
- if (minIndex == -1)
- minIndex = findIndex;
- else if (minIndex > findIndex)
- minIndex = findIndex;
- }
- }
-
- word = cp +1; // Move on to the next word.
-
- }
-
-
- if (minIndex != -1) {
-
- spaceIndex = (int)index((char *)minIndex, ' ');
-
- if (spaceIndex > (int)index((char*)minIndex, '\r'))
- spaceIndex = (int)index((char*)minIndex, '\r');
-
- findIndex = (int)minIndex - (int)theText;
- spaceIndex = (int)spaceIndex - (int)theText;
-
- // Highlight the found text, make it visible.
-
- [[MainText docView] setSel:findIndex :spaceIndex];
- [[MainText docView] scrollSelToVisible];
- }
- else
- NXBeep();
- return self;
- }
-
- ////////////////////////////////////////////////////////////////
- // Connect to currenthostname on port currentport, retrieve
- // document currentfilename and display it.
-
- - displayDocument
- {
- int iLength = 1, i;
- int currentsocket;
-
- currentsocket = create_socket(currenthostname, currentport);
- if (currentsocket < 0)
- {
- switch (currentsocket)
- {
- case -1 :
- NXRunAlertPanel("Gopher error", "Unknown host", "OK", NULL, NULL);
- return self;
- break;
- case -2 :
- NXRunAlertPanel("Gopher error", "Socket call failed",
- "OK", NULL, NULL);
- return self;
- break;
- case -3 :
- NXRunAlertPanel("Gopher error", "Connect call failed", "OK", NULL, NULL);
- return self;
- break;
- default :
- break;
- }
- }
-
- writestring(currentsocket, currentfilename);
- writestring(currentsocket, "\r\n");
-
- for (i=0; (i < MAXTEXTLENGTH) && iLength; i += iLength)
- iLength = read(currentsocket, theText + i, 10000);
-
- theText[i - 3] = '\0'; // -3 to get rid of final period
- [[MainText docView] setText:theText];
-
- close(currentsocket);
-
- [[MainText docView] setSel:0 :0];
- [[MainText docView] scrollSelToVisible];
-
- if (isIndexedText)
- [self highlightNextWord];
-
-
-
- /* findIndex = (int)strstr(theText, lastIndexWord);
- if (findIndex == 0)
- ; // Not found
- else {
- spaceIndex = (int)index((char *)findIndex, ' ');
- findIndex = (int)findIndex - (int)theText;
- spaceIndex = (int)spaceIndex - (int)theText;
- [[MainText docView] setSel:findIndex :spaceIndex];
- [[MainText docView] scrollSelToVisible];
- }
- }
- */
- return self;
- }
-
- /////////////////////////////////////////////////
- // Find our dispatcher
-
- - setDispatcher:sender
- {
- myDispatcher = sender;
- return self;
- }
-
- /////////////////////////////////////////////////
- // Print the contents of the text window.
-
- - print
- {
- [[MainText docView] printPSCode:self];
-
- return self;
- }
-
- /////////////////////////////////////////////////
- // Saves the contents of the text window.
-
- - saveAs:(char *)filename
- {
- NXStream *tmpstream;
-
- tmpstream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
- [[MainText docView] writeText:tmpstream];
- NXSaveToFile(tmpstream, filename);
- NXCloseMemory(tmpstream, NX_FREEBUFFER);
-
- return self;
- }
-
- /////////////////////////////////////////////////
- //
-
- - searchIndex:sender
- {
- [myIndexPanel orderOut:self];
- [NXApp stopModal:1];
-
- return self;
- }
-
- /////////////////////////////////////////////////
- //
-
- - cancelIndex:sender
- {
- [myIndexPanel orderOut:self];
- [NXApp stopModal:2];
-
- return self;
- }
-
- /////////////////////////////////////////////////
- //
-
- - getIndexString:(char *)theString maxLength:(int)maxlength
- {
- [myIndexPanel makeKeyAndOrderFront:self];
- [IndexTextItem selectText:self];
- if ([NXApp runModalFor:myIndexPanel] == 1) // User clicked OK
- strncpy(theString, [IndexTextItem stringValue], maxlength);
- else
- strcpy(theString, ""); // User clicked Cancel
-
- return self;
- }
-
- /////////////////////////////////////////////////
- //
-
- - showItemInfo
- {
- [ItemInfoPanel makeKeyAndOrderFront:self];
- return self;
- }
-
- /////////////////////////////////////////////////
- // Delegate method for the browser
-
- - (int)browser:sender fillMatrix:matrix inColumn:(int)column
- {
- id tmpcell;
- int iLength;
- char inputline[512];
- char *remotename, *remotehost, *remoteport;
- int currentsocket;
- id CurrentCell;
-
- CurrentCell = [[sender matrixInColumn:[sender selectedColumn]] selectedCell];
-
- switch ([CurrentCell cellType]) {
- case '0' : // Document : should never happen
- break;
- case '1' : // Directory : the browser will
- // automatically update
- strncpy(currenthostname, [CurrentCell remoteHost], 250);
- strcpy(currentfilename, [CurrentCell remoteName]);
- currentport = [CurrentCell remotePort];
- break;
- case '7' : // Index search
- strncpy(currenthostname, [CurrentCell remoteHost], 250);
- [self getIndexString:&lastIndexWord maxLength:250];
- if (strlen(lastIndexWord) == 0) // User clicked Cancel
- return 0;
- strcpy(currentfilename, [CurrentCell remoteName]);
- strcat(currentfilename, "\t");
- strcat(currentfilename, lastIndexWord);
- currentport = [CurrentCell remotePort];
- break;
- case '8' : // Telnet session : should never happen
- break;
- case 's' : // Sound : should never happen
- break;
- default :
- break;
- }
-
- currentsocket = create_socket(currenthostname, currentport);
- if (currentsocket < 0)
- switch (currentsocket) {
- case -1 :
- NXRunAlertPanel("Gopher error", "Unknown host", "OK", NULL, NULL);
- return(0);
- break;
- case -2 :
- NXRunAlertPanel("Gopher error", "Socket call failed", "OK", NULL, NULL);
- return(0);
- break;
- case -3 :
- NXRunAlertPanel("Gopher error", "Connect call failed", "OK", NULL, NULL);
- return(0);
- break;
- default :
- break;
- }
-
- writestring(currentsocket, currentfilename);
- writestring(currentsocket, "\r\n");
-
- for(;;) {
- iLength = readline(currentsocket, inputline, 512);
-
- if (iLength == -1)
- break;
- if (iLength == 0)
- break;
-
- ZapCRLF(inputline);
-
- if ((inputline[0] == '0') || (inputline[0] == '1') ||
- (inputline[0] == '7') || (inputline[0] == '8') ||
- (inputline[0] == 'R') || (inputline[0] == 'T') ||
- (inputline[0] == 's') || (inputline[0] == '2')) {
- if ((inputline[0] == 's') && (shouldUseSound == 0))
- continue;
-
- [matrix addRow];
- tmpcell = [matrix cellAt:([matrix cellCount] -1) :0];
-
- remotename = index(inputline, '\t') + 1;
- *(remotename-1) = '\0';
-
- remotehost = index(remotename, '\t') + 1;
- *(remotehost-1) = '\0';
-
- remoteport = index(remotehost, '\t') + 1;
- *(remoteport-1) = '\0';
-
- [tmpcell setRemoteName:remotename];
- [tmpcell setRemoteHost:remotehost];
- [tmpcell setRemotePort:atoi(remoteport)];
- [tmpcell setStringValue:(inputline+1)];
- [tmpcell setLoaded:YES];
- if (inputline[0] == '0') { // Document
- [tmpcell setLeaf:YES];
- [tmpcell setCellType:'0'];
- }
- else if (inputline[0] == '1') { // Directory
- [tmpcell setLeaf:NO];
- [tmpcell setCellType:'1'];
- }
- else if (inputline[0] == '2') { // CSO Nameserver
- [tmpcell setLeaf:YES];
- [tmpcell setCellType:'2'];
- }
-
- else if (inputline[0] == '7') { // Index
- [tmpcell setLeaf:NO];
- [tmpcell setCellType:'7'];
- }
- else if (inputline[0] == '8') { // Telnet
- [tmpcell setLeaf:YES];
- [tmpcell setCellType:'8'];
- }
- else if (inputline[0] == 'R') { // Rlogin
- [tmpcell setLeaf:YES];
- [tmpcell setCellType:'R'];
- }
- else if (inputline[0] == 'T') { // TN3270
- [tmpcell setLeaf:YES];
- [tmpcell setCellType:'T'];
- }
- else if (inputline[0] == 's') { // Sound
- [tmpcell setLeaf:YES];
- [tmpcell setCellType:'s'];
- }
- }
-
- if ((inputline[0] == '.') && (inputline[1] == '\0'))
- break;
- }
-
- close(currentsocket);
-
- return([matrix cellCount]);
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // Delegate methods for the window
-
- /////////////////////////////////////////////////
- // Called when our window becomes the key window.
-
- - windowDidBecomeKey:sender
- {
- [myDispatcher newFrontWindow:self];
-
- return self;
- }
-
- /////////////////////////////////////////////////
- // Called when our window has stopped being the key window.
-
- - windowDidResignKey:sender
- {
- [myDispatcher oldFrontWindow:self];
-
- return self;
- }
-
- /////////////////////////////////////////////////
- // Called when our window is going to close.
-
- - windowWillClose:sender
- {
- [self clean];
- [myDispatcher oldFrontWindow:self];
-
- return self;
- }
-
-
- // Stuff from Rex Pruess's Ph CSO Nameserver App
-
- /*---------------------------------------------------------------------------
- This internal support method sets the Speaker port and creates the PhSpeaker
- object.
- -----------------------------------------------------------------------------*/
- - (BOOL) createPort:sender
- {
- port_t thePort;
-
- thePort = NXPortFromName ("Ph", NULL);
-
- if (thePort == PORT_NULL) {
- fprintf (stderr, "\n*** PhSpeaker port is NULL.\n");
- fprintf (stderr, "*** Perhaps Ph is not installed in a standard directory.\n");
- return NO;
- }
-
- mySpeaker = [[PhSpeaker alloc] init];
-
- [mySpeaker setSendPort:thePort];
-
- return YES;
- }
-
-
- @end
-