1/*
2   Project: FTP
3
4   Copyright (C) 2005-2016 Riccardo Mottola
5
6   Author: Riccardo Mottola
7
8   Created: 2005-03-30
9
10   Application Controller
11
12   This application is free software; you can redistribute it and/or
13   modify it under the terms of the GNU General Public
14   License as published by the Free Software Foundation; either
15   version 2 of the License, or (at your option) any later version.
16
17   This application is distributed in the hope that it will be useful,
18   but WITHOUT ANY WARRANTY; without even the implied warranty of
19   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20   Library General Public License for more details.
21
22   You should have received a copy of the GNU General Public
23   License along with this library; if not, write to the Free
24   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
25*/
26
27#import "AppController.h"
28#import "fileElement.h"
29#import "GetNameController.h"
30
31@implementation fileTransmitParms
32@end
33
34@implementation AppController
35
36
37
38- (id)init
39{
40  NSFont *font;
41
42  if ((self = [super init]))
43    {
44      connMode = defaultMode;
45
46      threadRunning = NO;
47
48      font = [NSFont userFixedPitchFontOfSize: 0];
49      textAttributes = [NSMutableDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
50      [textAttributes retain];
51    }
52  return self;
53}
54
55- (void)dealloc
56{
57  [doConnection invalidate];
58
59  [doConnection release];
60  [textAttributes release];
61  [super dealloc];
62}
63
64- (void)awakeFromNib
65{
66  NSMenu *menu;
67  NSMenuItem *mi;
68
69  /* connection panel */
70  [connServerBox setTitle:NSLocalizedString(@"Server Address and Port", @"Server Address and Port")];
71  [connAccountBox setTitle:NSLocalizedString(@"Account", @"Account")];
72  [connectPanel setTitle:NSLocalizedString(@"Connect", @"Connect")];
73  [connAnon setTitle:NSLocalizedString(@"Anonymous", @"Anonymous connection")];
74  [connCancelButt setTitle:NSLocalizedString(@"Cancel", @"Cancel")];
75  [connConnectButt setTitle:NSLocalizedString(@"Connect (action)", @"Connect (action)")];
76
77  /* main window */
78  [[localPath itemAtIndex:0] setTitle:NSLocalizedString(@"local view", @"local view")];
79  [[remotePath itemAtIndex:0] setTitle:NSLocalizedString(@"remote view", @"remote view")];
80  [[[localView tableColumnWithIdentifier:@"filename"] headerCell] setStringValue:NSLocalizedString(@"Name", @"filename table")];
81  [[[remoteView tableColumnWithIdentifier:@"filename"] headerCell] setStringValue:NSLocalizedString(@"Name", @"filename table")];
82
83  /* menus */
84  mi = [mainMenu itemWithTitle:@"Local"];
85  menu = [mi submenu];
86  [menu setTitle:NSLocalizedString(@"Local", @"Local")];
87  mi = [menu itemWithTag:1];
88  [mi setTitle:NSLocalizedString(@"Rename...", @"Rename...")];
89  mi = [menu itemWithTag:2];
90  [mi setTitle:NSLocalizedString(@"New Folder...", @"New Folder....")];
91  mi = [menu itemWithTag:3];
92  [mi setTitle:NSLocalizedString(@"Delete", @"Delete")];
93  mi = [menu itemWithTag:4];
94  [mi setTitle:NSLocalizedString(@"Refresh", @"Refresh")];
95
96  mi = [mainMenu itemWithTitle:@"Remote"];
97  menu = [mi submenu];
98  [menu setTitle:NSLocalizedString(@"Remote", @"Remote")];
99  mi = [menu itemWithTag:1];
100  [mi setTitle:NSLocalizedString(@"Rename...", @"Rename...")];
101  mi = [menu itemWithTag:2];
102  [mi setTitle:NSLocalizedString(@"New Folder...", @"New Folder....")];
103  mi = [menu itemWithTag:3];
104  [mi setTitle:NSLocalizedString(@"Delete", @"Delete")];
105  mi = [menu itemWithTag:4];
106  [mi setTitle:NSLocalizedString(@"Refresh", @"Refresh")];
107
108  /* log */
109  [logWin setTitle:NSLocalizedString(@"Connection Log", @"Connection Log")];
110
111  [logTextField setSelectable:YES];
112  [logTextField setEditable:NO];
113}
114
115- (void)applicationDidFinishLaunching:(NSNotification *)aNotif
116{
117    NSArray        *dirList;
118    NSUserDefaults *defaults;
119    NSString       *readValue;
120    NSPort         *port1;
121    NSPort         *port2;
122    NSArray        *portArray;
123
124    /* read the user preferences */
125    defaults = [NSUserDefaults standardUserDefaults];
126    readValue = [defaults stringForKey:connectionModeKey];
127
128    /* if no value was set for the key we set passive as mode */
129    if ([readValue isEqualToString:@"default"])
130        connMode = defaultMode;
131    else if ([readValue isEqualToString:@"port"] )
132        connMode = portMode;
133    else if ([readValue isEqualToString:@"passive"] || readValue == nil)
134        connMode = passiveMode;
135    else
136        NSLog(@"Unrecognized value in user preferences for %@: %@", connectionModeKey, readValue);
137
138    /* set double actions for tables */
139    [localView setTarget:self];
140    [localView setDoubleAction:@selector(listDoubleClick:)];
141    [remoteView setTarget:self];
142    [remoteView setDoubleAction:@selector(listDoubleClick:)];
143
144    /* initialize drag-n-drop code */
145    [localView registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
146    [remoteView registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
147
148    /* startup code */
149    local = [[LocalClient alloc] init];
150    [local setWorkingDir:[local homeDir]];
151    dirList = [local dirContents];
152    [progBar setDoubleValue:0.0];  // reset the progress bar
153
154    /* we create a data source and set the tableviews */
155    localTableData = [[FileTable alloc] init];
156    [localTableData initData:dirList];
157    [localView setDataSource:localTableData];
158
159    remoteTableData = [[FileTable alloc] init];
160    [remoteView setDataSource:remoteTableData];
161
162    /* we update the path menu */
163    [self updatePath :localPath :[local workDirSplit]];
164    // #### and a release of this array ?!?
165
166    // we set up distributed objects
167    port1 = [NSPort port];
168    port2 = [NSPort port];
169    doConnection = [[NSConnection alloc] initWithReceivePort:port1
170						     sendPort:port2];
171    [doConnection setRootObject:self];
172
173    /* Ports switched here. */
174    portArray = [NSArray arrayWithObjects:port2, port1, nil];
175    [NSThread detachNewThreadSelector: @selector(connectWithPorts:)
176                             toTarget: [FtpClient class]
177                           withObject: portArray];
178
179    /* show the connection panel */
180    [connectPanel makeKeyAndOrderFront:self];
181    return;
182}
183
184- (NSApplicationTerminateReply)applicationShouldTerminate:(id)sender
185{
186  return NSTerminateNow;
187}
188
189- (void)applicationWillTerminate:(NSNotification *)aNotif
190{
191}
192
193- (BOOL)application:(NSApplication *)application openFile:(NSString *)fileName
194{
195    return NO;
196}
197
198/** update the pop-up menu with a new path */
199- (void)updatePath :(NSPopUpButton *)path :(NSArray *)pathArray
200{
201  [path removeAllItems];
202  [path addItemsWithTitles:pathArray];
203}
204
205/** reads directory contents and reads refreshes the table */
206- (void)readDirWith:(Client *)client toTable:(FileTable *)t andView:(NSTableView*)tv
207{
208  NSArray    *dirList;
209
210  if ((dirList = [client dirContents]) == nil)
211    return;
212  [t initData:dirList];
213  [tv deselectAll:self];
214  [tv reloadData];
215}
216
217/** performs the action of the path pull-down menu
218   it navigates upwards the tree
219   and works for both the local and remote path */
220- (IBAction)changePathFromMenu:(id)sender
221{
222  Client      *theClient;
223  NSTableView *theView;
224  FileTable   *theTable;
225  NSString    *thePath;
226  NSArray     *items;
227  int         selectedIndex;
228  unsigned    i;
229
230  if (sender == localPath)
231    {
232      theClient = local;
233      theView = localView;
234      theTable = localTableData;
235    }
236  else
237    {
238      theClient = ftp;
239      theView = remoteView;
240      theTable = remoteTableData;
241    }
242  thePath = [NSString string];
243  selectedIndex = [sender indexOfItem:[sender selectedItem]];
244  items = [sender itemTitles];
245  for (i = [items count] - 1; i >= selectedIndex; i--)
246    thePath = [thePath stringByAppendingPathComponent: [items objectAtIndex:i]];
247
248  [theClient changeWorkingDir:thePath];
249  [self readDirWith:theClient toTable:theTable andView:theView];
250
251  [self updatePath :sender :[theClient workDirSplit]];
252}
253
254/* perform the action of a double click in a table element
255   a directory should be opened, a file down or uploaded
256   The same method works for local and remote, detecting them */
257- (IBAction)listDoubleClick:(id)sender
258{
259    Client        *theClient;
260    NSTableView   *theView;
261    FileTable     *theTable;
262    int           elementIndex;
263    FileElement   *fileEl;
264    NSString      *thePath;
265    NSPopUpButton *thePathMenu;
266
267    if (threadRunning)
268    {
269        NSLog(@"thread was still running");
270        return;
271    }
272
273    theView = sender;
274    if (theView == localView)
275    {
276        theClient = local;
277        theTable = localTableData;
278        thePathMenu = localPath;
279    } else
280    {
281        theClient = ftp;
282        theTable = remoteTableData;
283        thePathMenu = remotePath;
284    }
285
286    elementIndex = [sender selectedRow];
287    if (elementIndex < 0)
288    {
289        NSLog(@"error: double click with nothing selected");
290        return;
291    }
292    fileEl = [theTable elementAtIndex:elementIndex];
293    NSLog(@"element: %@ %d", [fileEl name], [fileEl isDir]);
294    thePath = [NSString stringWithString:[theClient workingDir]];
295    thePath = [thePath stringByAppendingPathComponent: [fileEl name]];
296    if ([fileEl isDir])
297      {
298        [theClient changeWorkingDir:thePath];
299        [self readDirWith:theClient toTable:theTable andView:theView];
300        [self updatePath :thePathMenu :[theClient workDirSplit]];
301      }
302    else
303      {
304        if (theView == localView)
305          {
306            [self performStoreFile];
307          }
308        else
309          {
310            [self performRetrieveFile];
311          }
312    }
313}
314
315- (BOOL)dropValidate:(id)sender paths:(NSArray *)paths
316{
317  if (threadRunning)
318    {
319      NSLog(@"thread was still running");
320      return NO;
321    }
322
323  if (sender == localTableData)
324    {
325      /* the local view opens the file or the directory, it can be just one */
326      if ([paths count] != 1)
327        return NO;
328    }
329
330  return YES;
331}
332
333- (void)dropAction:(id)sender paths:(NSArray *)paths
334{
335  NSUInteger i;
336  NSFileManager *fm;
337  NSMutableArray *arr;
338
339  arr = [[NSMutableArray alloc] initWithCapacity:[paths count]];
340  fm = [NSFileManager defaultManager];
341  for (i = 0; i < [paths count]; i++)
342    {
343      NSDictionary *attr;
344      NSString *path;
345      FileElement *fEl;
346
347      path = [paths objectAtIndex:i];
348      attr = [fm fileAttributesAtPath:path traverseLink:YES];
349      fEl = [[FileElement alloc] initWithPath:path andAttributes:attr];
350      [arr addObject:fEl];
351      [fEl release];
352    }
353
354  /* locally, we accept only a directory and change to it
355     remotely, we store everything */
356  if (sender == localTableData)
357    {
358      NSString      *fileOrPath;
359      NSString      *thePath;
360      NSFileManager *fm;
361      BOOL          isDir;
362
363      fileOrPath = [paths objectAtIndex:0];
364      fm = [NSFileManager defaultManager];
365
366      if ([fm fileExistsAtPath:fileOrPath isDirectory:&isDir] == NO)
367        {
368          [arr release];
369          return;
370        }
371
372      if (!isDir)
373        thePath = [fileOrPath stringByDeletingLastPathComponent];
374      else
375        thePath = fileOrPath;
376      NSLog(@"trimmed path to: %@", thePath);
377      [local changeWorkingDir:thePath];
378      [self readDirWith:local toTable:localTableData andView:localView];
379
380      [self updatePath :localPath :[local workDirSplit]];
381    }
382  else if (sender == remoteTableData)
383    {
384      NSLog(@"will upload: %@", arr);
385      [self storeFiles];
386    }
387  [arr release];
388}
389
390- (void)tableView:(NSTableView *)tableView didClickTableColumn:(NSTableColumn *)tableColumn
391{
392  if (tableView == localView)
393    {
394      [localTableData sortByIdent: [tableColumn identifier]];
395      [localView reloadData];
396    }
397  else
398    {
399      [remoteTableData sortByIdent: [tableColumn identifier]];
400      [remoteView reloadData];
401    }
402}
403
404- (void)setInterfaceEnabled:(BOOL)flag
405{
406    [localView setEnabled:flag];
407    [remoteView setEnabled:flag];
408    [localPath setEnabled:flag];
409    [remotePath setEnabled:flag];
410    [buttUpload setEnabled:flag];
411    [buttDownload setEnabled:flag];
412}
413
414- (void)setThreadRunningState:(BOOL)flag
415{
416    threadRunning = flag;
417    [self setInterfaceEnabled:!flag];
418}
419
420/** retrieves using selection, by constructing an array and calling retrieveFiles */
421- (void)performRetrieveFile
422{
423  NSEnumerator   *elemEnum;
424  FileElement    *fileEl;
425  id             currEl;
426
427  /* make a copy of the selection */
428  [filesInProcess release];
429  filesInProcess = [[NSMutableArray alloc] init];
430  elemEnum = [remoteView selectedRowEnumerator];
431
432  while ((currEl = [elemEnum nextObject]) != nil)
433    {
434      fileEl = [remoteTableData elementAtIndex:[currEl intValue]];
435      [filesInProcess addObject:fileEl];
436    }
437
438  [self retrieveFiles];
439}
440
441/** stores using selection, by constructing an array and calling storeFiles */
442- (void)performStoreFile
443{
444  NSEnumerator   *elemEnum;
445  FileElement    *fileEl;
446  id             currEl;
447
448  /* make a copy of the selection */
449  [filesInProcess release];
450  filesInProcess = [[NSMutableArray alloc] init];
451  elemEnum = [localView selectedRowEnumerator];
452
453  while ((currEl = [elemEnum nextObject]) != nil)
454    {
455      fileEl = [localTableData elementAtIndex:[currEl intValue]];
456      [filesInProcess addObject:fileEl];
457    }
458  [self storeFiles];
459}
460
461/** Retrieves Array of FileElements */
462- (void)retrieveFiles
463{
464  if([filesInProcess count] > 0)
465    {
466      FileElement *fEl;
467
468      [self setThreadRunningState:YES];
469      fEl = [filesInProcess objectAtIndex:0];
470      NSLog(@"should download (performRETRIEVE): %@", [fEl name]);
471      [ftp retrieveFile:fEl to:local];
472    }
473}
474
475/* called by the worker thread when the element got processed */
476- (oneway void)fileRetrieved:(BOOL)success
477{
478  FileElement *fEl;
479
480  fEl = [filesInProcess objectAtIndex:0];
481  if (success)
482    {
483      if (![localTableData containsFileName:[fEl name]])
484        {
485          FileElement *fEl2;
486
487          fEl2 = [[FileElement alloc] initWithPath:[[local workingDir] stringByAppendingPathComponent:[fEl name]] andAttributes:[fEl attributes]];
488          [localTableData addObject:fEl2];
489          [fEl2 release];
490        }
491    }
492  [filesInProcess removeObjectAtIndex:0];
493  if ([filesInProcess count] > 0)
494    {
495      [self retrieveFiles];
496    }
497  else
498    {
499      [localView deselectAll:self];
500      [localView reloadData];
501      [self setThreadRunningState:NO];
502      [filesInProcess release];
503      filesInProcess = nil;
504    }
505}
506
507/** Stores Array of FileElements */
508- (void)storeFiles
509{
510  if([filesInProcess count] > 0)
511    {
512      FileElement *fEl;
513
514      [self setThreadRunningState:YES];
515      fEl = [filesInProcess objectAtIndex:0];
516      NSLog(@"should download (performStore): %@", [fEl name]);
517      [ftp storeFile:fEl from:local];
518    }
519}
520
521/* called by the worker thread when the element got processed */
522- (oneway void)fileStored:(BOOL)success
523{
524  FileElement *fEl;
525
526  fEl = [filesInProcess objectAtIndex:0];
527  if (success)
528    {
529      if (![remoteTableData containsFileName:[fEl name]])
530        {
531          FileElement *fEl2;
532
533          fEl2 = [[FileElement alloc] initWithPath:[[ftp workingDir] stringByAppendingPathComponent:[fEl name]] andAttributes:[fEl attributes]];
534          [remoteTableData addObject:fEl2];
535          [fEl2 release];
536        }
537    }
538  [filesInProcess removeObjectAtIndex:0];
539  if ([filesInProcess count] > 0)
540    {
541      [self storeFiles];
542    }
543  else
544    {
545      [remoteView deselectAll:self];
546      [remoteView reloadData];
547      [self setThreadRunningState:NO];
548      [filesInProcess release];
549      filesInProcess = nil;
550    }
551}
552
553
554- (IBAction)downloadButton:(id)sender
555{
556  if (threadRunning)
557    {
558      NSLog(@"thread was still running");
559      return;
560    }
561
562  [self performRetrieveFile];
563}
564
565- (IBAction)uploadButton:(id)sender
566{
567  if (threadRunning)
568    {
569      NSLog(@"thread was still running");
570      return;
571    }
572
573  [self performStoreFile];
574}
575
576- (IBAction)localDelete:(id)sender
577{
578  NSEnumerator   *elemEnum;
579  FileElement    *fileEl;
580  id             currEl;
581  NSMutableArray *selArray;
582  NSUInteger     i;
583
584  /* make a copy of the selection */
585  selArray = [[NSMutableArray alloc] init];
586  elemEnum = [localView selectedRowEnumerator];
587  while ((currEl = [elemEnum nextObject]) != nil)
588    {
589      fileEl = [localTableData elementAtIndex:[currEl intValue]];
590      [selArray addObject:fileEl];
591    }
592
593  /* perform deletes */
594  for (i = 0; i < [selArray count]; i++)
595    {
596      fileEl = [selArray objectAtIndex:i];
597      if([local deleteFile:fileEl beingAt:0])
598	[localTableData removeObject:fileEl];
599    }
600  [localView deselectAll:self];
601  [localView reloadData];
602  [selArray release];
603}
604
605- (IBAction)remoteDelete:(id)sender
606{
607  NSEnumerator  *elemEnum;
608  FileElement   *fileEl;
609  id            currEl;
610  NSMutableArray *selArray;
611  NSUInteger     i;
612
613  /* make a copy of the selection */
614  selArray = [[NSMutableArray alloc] init];
615  elemEnum = [remoteView selectedRowEnumerator];
616  while ((currEl = [elemEnum nextObject]) != nil)
617    {
618      fileEl = [remoteTableData elementAtIndex:[currEl intValue]];
619      [selArray addObject:fileEl];
620    }
621
622  /* perform deletes */
623  for (i = 0; i < [selArray count]; i++)
624    {
625      fileEl = [selArray objectAtIndex:i];
626      if ([ftp deleteFile:fileEl beingAt:0])
627	[remoteTableData removeObject:fileEl];
628    }
629  [remoteView deselectAll:self];
630  [remoteView reloadData];
631  [selArray release];
632}
633
634- (IBAction)localRename:(id)sender
635{
636  GetNameController *nameGetter;
637  NSInteger         alertReturn;
638  NSEnumerator      *elemEnum;
639  FileElement       *fileEl;
640  id                currEl;
641
642  elemEnum = [localView selectedRowEnumerator];
643
644  while ((currEl = [elemEnum nextObject]) != nil)
645    {
646      fileEl = [localTableData elementAtIndex:[currEl intValue]];
647
648      nameGetter = [[GetNameController alloc] init];
649      [nameGetter setName:[fileEl name]];
650      [nameGetter setTitle:@"Rename"];
651      [nameGetter setMessage:@"Rename"];
652
653      alertReturn = [nameGetter runAsModal];
654      if (alertReturn == NSAlertDefaultReturn)
655        {
656          NSString *name;
657
658          name = [nameGetter name];
659          NSLog(@"New name: %@", name);
660          [local renameFile:fileEl to:name];
661        }
662      [nameGetter release];
663    }
664  [localView reloadData];
665}
666
667- (IBAction)remoteRename:(id)sender
668{
669  GetNameController *nameGetter;
670  NSInteger         alertReturn;
671  NSEnumerator      *elemEnum;
672  FileElement       *fileEl;
673  id                currEl;
674
675  elemEnum = [remoteView selectedRowEnumerator];
676
677  while ((currEl = [elemEnum nextObject]) != nil)
678    {
679      fileEl = [remoteTableData elementAtIndex:[currEl intValue]];
680
681      nameGetter = [[GetNameController alloc] init];
682      [nameGetter setName:[fileEl name]];
683      [nameGetter setTitle:@"Rename"];
684      [nameGetter setMessage:@"Rename"];
685
686      alertReturn = [nameGetter runAsModal];
687      if (alertReturn == NSAlertDefaultReturn)
688	{
689	  NSString *name;
690
691	  name = [nameGetter name];
692	  NSLog(@"New name: %@", name);
693	  [ftp renameFile:fileEl to:name];
694	}
695      [nameGetter release];
696    }
697  [remoteView reloadData];
698}
699
700- (IBAction)localNewFolder:(id)sender
701{
702  GetNameController *nameGetter;
703  NSInteger         alertReturn;
704
705  nameGetter = [[GetNameController alloc] init];
706  [nameGetter setName:@"New Folder"];
707  [nameGetter setTitle:@"New Folder"];
708  [nameGetter setMessage:@"New Folder"];
709
710  alertReturn = [nameGetter runAsModal];
711  if (alertReturn == NSAlertDefaultReturn)
712    {
713      NSString *name;
714      NSString *fullPath;
715
716      name = [nameGetter name];
717      fullPath = [[local workingDir] stringByAppendingPathComponent:name];
718      if ([local createNewDir:fullPath])
719        {
720          FileElement *fileEl;
721          NSDictionary *attrs;
722
723          attrs = [NSDictionary dictionaryWithObjectsAndKeys:
724                                  NSFileTypeDirectory, NSFileType,
725                                NULL];
726          fileEl = [[FileElement alloc] initWithPath:fullPath andAttributes:attrs];
727          [localTableData addObject:fileEl];
728          [fileEl release];
729          [localView reloadData];
730        }
731    }
732  [nameGetter release];
733}
734
735- (IBAction)remoteNewFolder:(id)sender
736{
737  GetNameController *nameGetter;
738  NSInteger         alertReturn;
739
740  nameGetter = [[GetNameController alloc] init];
741  [nameGetter setName:@"New Folder"];
742  [nameGetter setTitle:@"New Folder"];
743  [nameGetter setMessage:@"New Folder"];
744
745  alertReturn = [nameGetter runAsModal];
746  if (alertReturn == NSAlertDefaultReturn)
747    {
748      NSString *name;
749      NSString *fullPath;
750
751      name = [nameGetter name];
752      fullPath = [[ftp workingDir] stringByAppendingPathComponent:name];
753      if ([ftp createNewDir:fullPath])
754        {
755          FileElement *fileEl;
756          NSDictionary *attrs;
757
758          attrs = [NSDictionary dictionaryWithObjectsAndKeys:
759                                  NSFileTypeDirectory, NSFileType,
760                                NULL];
761          fileEl = [[FileElement alloc] initWithPath:fullPath andAttributes:attrs];
762          [remoteTableData addObject:fileEl];
763          [fileEl release];
764          [remoteView reloadData];
765        }
766    }
767  [nameGetter release];
768}
769
770- (IBAction)localRefresh:(id)sender
771{
772  [self readDirWith:local toTable:localTableData andView:localView];
773}
774
775- (IBAction)remoteRefresh:(id)sender
776{
777  [self readDirWith:ftp toTable:remoteTableData andView:remoteView];
778}
779
780- (oneway void)setTransferBegin:(in bycopy NSString *)name :(unsigned long long)size
781{
782    [infoMessage setStringValue:name];
783    [progBar setDoubleValue:0];
784    beginTimeVal = [NSDate timeIntervalSinceReferenceDate];
785    transferSize = size;
786    NSLog(@"begin transfer size: %llu", transferSize);
787    if (transferSize == 0)
788      {
789	[progBar setIndeterminate:YES];
790	[progBar startAnimation:nil];
791      }
792    [mainWin displayIfNeeded];
793}
794
795- (oneway void)setTransferProgress:(in bycopy NSNumber *)bytesTransferred
796{
797  NSTimeInterval currTimeVal;
798  float    speed;
799  NSString *speedStr;
800  NSString *sizeStr;
801  double   percent;
802  unsigned long long bytes;
803
804  bytes = [bytesTransferred unsignedLongLongValue];
805  currTimeVal = [NSDate timeIntervalSinceReferenceDate];
806  speed = (float)((double)bytes / (double)(currTimeVal - beginTimeVal));
807
808    if (transferSize > 0)
809      {
810	percent = ((double)bytes / (double)transferSize) * 100;
811	[progBar setDoubleValue:percent];
812      }
813
814    speedStr = [NSString alloc];
815    if (speed < 1024)
816        speedStr = [speedStr initWithFormat:@"%3.2fB/s", speed];
817    else if (speed < 1024*1024)
818        speedStr = [speedStr initWithFormat:@"%3.2fKB/s", speed/1024];
819    else
820        speedStr = [speedStr initWithFormat:@"%3.2fMB/s", speed/(1024*1024)];
821    [infoSpeed setStringValue:speedStr];
822    [speedStr release];
823
824    sizeStr = [NSString alloc];
825
826    if (transferSize < 1024 && transferSize != 0) /* except 0, which means unknown */
827        sizeStr = [sizeStr initWithFormat:@"%3.2f : %3.2f B", (float)bytes, (float)transferSize];
828    else if (transferSize < 1024*1024)
829        sizeStr = [sizeStr initWithFormat:@"%3.2f : %3.2f KB", (double)bytes/1024, (double)transferSize/1024];
830    else
831        sizeStr = [sizeStr initWithFormat:@"%3.2f : %3.2f MB", (double)bytes/(1024*1024), (double)transferSize/(1024*1024)];
832    [infoSize setStringValue:sizeStr];
833    [sizeStr release];
834}
835
836- (oneway void)setTransferEnd:(in bycopy NSNumber *)bytesTransferred
837{
838  NSTimeInterval currTimeVal;
839  NSTimeInterval deltaT;
840  float          speed;
841  NSString       *speedStr;
842  NSString       *sizeStr;
843  double         percent;
844  unsigned long long bytes;
845
846  bytes = [bytesTransferred unsignedLongLongValue];
847  currTimeVal = [NSDate timeIntervalSinceReferenceDate];
848  deltaT = (currTimeVal - beginTimeVal);
849    speed = (float)((double)bytes / deltaT);
850    NSLog(@"Elapsed time: %f", (float)deltaT);
851    percent = ((double)bytes / (double)transferSize) * 100;
852    speedStr = [NSString alloc];
853    if (speed < 1024)
854        speedStr = [speedStr initWithFormat:@"%3.2fB/s", speed];
855    else if (speed < 1024*1024)
856        speedStr = [speedStr initWithFormat:@"%3.2fKB/s", speed/1024];
857    else
858        speedStr = [speedStr initWithFormat:@"%3.2fMB/s", speed/(1024*1024)];
859    [infoSpeed setStringValue:speedStr];
860    [speedStr release];
861
862    sizeStr = [NSString alloc];
863    if (transferSize < 1024)
864        sizeStr = [sizeStr initWithFormat:@"%3.2f : %3.2f B", (float)bytes, (float)transferSize];
865    else if (transferSize < 1024*1024)
866        sizeStr = [sizeStr initWithFormat:@"%3.2f : %3.2f KB", (double)bytes/1024, (double)transferSize/1024];
867    else
868        sizeStr = [sizeStr initWithFormat:@"%3.2f : %3.2f MB", (double)bytes/(1024*1024), (double)transferSize/(1024*1024)];
869    [infoSize setStringValue:sizeStr];
870    [sizeStr release];
871
872    if ([progBar isIndeterminate])
873      {
874	[progBar stopAnimation:nil];
875	[progBar setIndeterminate:NO];
876      }
877    [progBar setDoubleValue:percent];
878    [mainWin displayIfNeeded];
879}
880
881- (IBAction)disconnect:(id)sender
882{
883  [ftp disconnect];
884  [mainWin setTitle:@"FTP"];
885  [remotePath removeAllItems];
886  [remotePath addItemWithTitle:@"Remote View"];
887  [remoteTableData clear];
888  [remoteView reloadData];
889  [self setThreadRunningState:NO];
890}
891
892- (IBAction)showPrefPanel:(id)sender
893{
894    [prefPanel makeKeyAndOrderFront:self];
895    switch (connMode)
896    {
897        case defaultMode:
898            [portType selectCellWithTag:0];
899            break;
900        case portMode:
901            [portType selectCellWithTag:1];
902            break;
903        case passiveMode:
904            [portType selectCellWithTag:2];
905            break;
906        default:
907            NSLog(@"Unexpected mode on pref pane setup.");
908    }
909}
910
911- (IBAction)prefSave:(id)sender
912{
913    NSUserDefaults *defaults;
914
915    defaults = [NSUserDefaults standardUserDefaults];
916
917
918    switch ([[portType selectedCell] tag])
919    {
920        case 0:
921            //default
922            NSLog(@"default");
923            connMode = defaultMode;
924            [ftp setPortDefault];
925            [defaults setObject:@"default" forKey:connectionModeKey];
926            break;
927        case 1:
928            //port
929            NSLog(@"port");
930            connMode = portMode;
931            [ftp setPortPort];
932            [defaults setObject:@"port" forKey:connectionModeKey];
933            break;
934        case 2:
935            // passive
936            NSLog(@"passive");
937            connMode = passiveMode;
938            [ftp setPortPassive];
939            [defaults setObject:@"passive" forKey:connectionModeKey];
940            break;
941        default:
942            NSLog(@"unexpected selection");
943    }
944    [prefPanel performClose:nil];
945}
946
947- (IBAction)prefCancel:(id)sender
948{
949  [prefPanel performClose:nil];
950}
951
952- (IBAction)showFtpLog:(id)sender
953{
954  [logWin makeKeyAndOrderFront:self];
955}
956
957/**
958 Called by the server object to register itself.
959 */
960- (void)setServer:(id)anObject
961{
962  ftp = (FtpClient*)[anObject retain];
963}
964
965- (oneway void)appendTextToLog:(NSString *)textChunk
966{
967  NSAttributedString *attrStr;
968
969  attrStr = [[NSAttributedString alloc] initWithString: textChunk
970					    attributes: textAttributes];
971
972  /* add the textChunk to the NSTextView's backing store as an attributed string */
973  [[logTextField textStorage] appendAttributedString: attrStr];
974
975
976  [[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantPast]];
977  [logTextField scrollRangeToVisible:NSMakeRange([[logTextField string] length], 0)];
978
979  [attrStr autorelease];
980}
981
982
983/* --- connection panel methods --- */
984- (IBAction)showConnPanel:(id)sender
985{
986    [connectPanel makeKeyAndOrderFront:self];
987}
988
989- (IBAction)connectConn:(id)sender
990{
991  NSArray *dirList;
992  char tempStr[1024];
993  NSString *u;
994  NSString *p;
995
996  [connectPanel performClose:nil];
997  [mainWin makeKeyAndOrderFront:self];
998
999  ftp = [ftp initWithController:self :connMode];
1000  [[connAddress stringValue] getCString:tempStr];
1001  if ([ftp connect:[connPort intValue] :tempStr] < 0)
1002    {
1003      NSRunAlertPanel(@"Error", @"Connection failed.\nCheck that you typed the host name correctly.", @"Ok", nil, nil);
1004      NSLog(@"connection failed in connectConn");
1005      return;
1006    }
1007  if ([connAnon state] == NSOnState)
1008    {
1009      u = @"anonymous";
1010      p = @"user@myhost.com";
1011    }
1012  else
1013    {
1014      u = [connUser stringValue];
1015      p = [connPass stringValue];
1016    }
1017  if ([ftp authenticate:u :p] < 0)
1018    {
1019      NSRunAlertPanel(@"Error", @"Authentication failed.\nCheck that your username and password are correct.", @"Ok", nil, nil);
1020      NSLog(@"authentication failed.");
1021      return;
1022    }
1023  else
1024    {
1025      [ftp setWorkingDir:[ftp homeDir]];
1026      if ((dirList = [ftp dirContents]) == nil)
1027        return;
1028      [remoteTableData initData:dirList];
1029      [remoteView reloadData];
1030
1031      /* update the path menu */
1032      [self updatePath :remotePath :[ftp workDirSplit]];
1033
1034      /* set the window title */
1035      [mainWin setTitle:[connAddress stringValue]];
1036    }
1037}
1038
1039- (IBAction)cancelConn:(id)sender
1040{
1041  [connectPanel performClose:nil];
1042}
1043
1044- (IBAction)anonymousConn:(id)sender
1045{
1046  if ([connAnon state] == NSOnState)
1047    {
1048      [connUser setEnabled:NO];
1049      [connPass setEnabled:NO];
1050    }
1051  else
1052    {
1053      [connUser setEnabled:YES];
1054      [connPass setEnabled:YES];
1055    }
1056}
1057
1058
1059- (void)showAlertDialog:(NSString *)message
1060{
1061  [message retain];
1062  NSRunAlertPanel(@"Attention", message, @"Ok", nil, nil);
1063  [message release];
1064}
1065
1066- (connectionModes)connectionMode
1067{
1068  return connMode;
1069}
1070
1071@end
1072