From: helge Date: Fri, 28 Jan 2005 14:50:35 +0000 (+0000) Subject: implemented bug #1074 (attachment download) X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9641cbf1e822893fbbca9a91b24742f2c0e53dd1;p=scalable-opengroupware.org implemented bug #1074 (attachment download) git-svn-id: http://svn.opengroupware.org/SOGo/trunk@503 d1b88da0-ebda-0310-925b-ed51d893ca5b --- diff --git a/SOGo/SoObjects/Mailer/ChangeLog b/SOGo/SoObjects/Mailer/ChangeLog index fed1b2af..6e87c5bb 100644 --- a/SOGo/SoObjects/Mailer/ChangeLog +++ b/SOGo/SoObjects/Mailer/ChangeLog @@ -1,3 +1,16 @@ +2005-01-28 Helge Hess + + * v0.9.57 + + * SOGoMailBodyPart.m: enhanced lookup to allow for arbitary filenames + being attached to a body-part name. Improves download behaviour. + + * SOGoMailBaseObject.m, SOGoMailObject.m: moved + -isBodyPartKey:inContext: method to base object for reuse in + SOGoMailBodyPart + + * SOGoMailBodyPart.m: minor code cleanup + 2005-01-26 Helge Hess * v0.9.56 diff --git a/SOGo/SoObjects/Mailer/SOGoMailAccount.m b/SOGo/SoObjects/Mailer/SOGoMailAccount.m index ae5e3353..bc3c0031 100644 --- a/SOGo/SoObjects/Mailer/SOGoMailAccount.m +++ b/SOGo/SoObjects/Mailer/SOGoMailAccount.m @@ -77,7 +77,11 @@ static NSString *sieveFolderName = @"Filters"; [self errorWithFormat:@"missing login in account folder name: %@", s]; return nil; } - + if ([s hasSuffix:@":80"]) { // HACK + [self logWithFormat:@"WARNING: incorrect value for IMAP4 URL: '%@'", s]; + s = [s substringToIndex:([s length] - 3)]; + } + s = [([self useSSL] ? @"imaps://" : @"imap://") stringByAppendingString:s]; s = [s stringByAppendingString:@"/"]; diff --git a/SOGo/SoObjects/Mailer/SOGoMailBaseObject.h b/SOGo/SoObjects/Mailer/SOGoMailBaseObject.h index 8aca72f6..ee8455e9 100644 --- a/SOGo/SoObjects/Mailer/SOGoMailBaseObject.h +++ b/SOGo/SoObjects/Mailer/SOGoMailBaseObject.h @@ -28,6 +28,14 @@ SOGoMailBaseObject Common base class for mailer SoObjects. + + Subclasses: + SOGoDraftObject + SOGoDraftsFolder + SOGoMailAccount + SOGoMailBodyPart + SOGoMailFolder + SOGoMailObject */ @class NSString, NSArray, NSURL; @@ -56,6 +64,10 @@ - (void)flushMailCaches; +/* IMAP4 names */ + +- (BOOL)isBodyPartKey:(NSString *)_key inContext:(id)_ctx; + @end #endif /* __Mailer_SOGoMailBaseObject_H__ */ diff --git a/SOGo/SoObjects/Mailer/SOGoMailBaseObject.m b/SOGo/SoObjects/Mailer/SOGoMailBaseObject.m index 1e72e834..344ec6b7 100644 --- a/SOGo/SoObjects/Mailer/SOGoMailBaseObject.m +++ b/SOGo/SoObjects/Mailer/SOGoMailBaseObject.m @@ -144,6 +144,22 @@ static BOOL debugOn = YES; [[self mailManager] flushCachesForURL:[self imap4URL]]; } +/* IMAP4 names */ + +- (BOOL)isBodyPartKey:(NSString *)_key inContext:(id)_ctx { + /* + Every key starting with a digit is consider an IMAP4 mime part key, used in + SOGoMailObject and SOGoMailBodyPart. + */ + if ([_key length] == 0) + return NO; + + if (isdigit([_key characterAtIndex:0])) + return YES; + + return NO; +} + /* debugging */ - (NSString *)loggingPrefix { diff --git a/SOGo/SoObjects/Mailer/SOGoMailBodyPart.h b/SOGo/SoObjects/Mailer/SOGoMailBodyPart.h index 606f1442..815d78f5 100644 --- a/SOGo/SoObjects/Mailer/SOGoMailBodyPart.h +++ b/SOGo/SoObjects/Mailer/SOGoMailBodyPart.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 SKYRIX Software AG + Copyright (C) 2004-2005 SKYRIX Software AG This file is part of OpenGroupware.org. @@ -28,7 +28,7 @@ SOGoMailBodyPart Parent object: SOGoMailObject or SOGoMailBodyPart Child objects: SOGoMailBodyPart's - + Represents a MIME part of a mail as retrieved using IMAP4 body structure commands in NGImap4. */ diff --git a/SOGo/SoObjects/Mailer/SOGoMailBodyPart.m b/SOGo/SoObjects/Mailer/SOGoMailBodyPart.m index 366bd443..bc68c637 100644 --- a/SOGo/SoObjects/Mailer/SOGoMailBodyPart.m +++ b/SOGo/SoObjects/Mailer/SOGoMailBodyPart.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 SKYRIX Software AG + Copyright (C) 2004-2005 SKYRIX Software AG This file is part of OpenGroupware.org. @@ -112,8 +112,18 @@ /* lookup body part */ - if ((obj = [self lookupImap4BodyPartKey:_key inContext:_ctx]) != nil) - return obj; + if ([self isBodyPartKey:_key inContext:_ctx]) { + if ((obj = [self lookupImap4BodyPartKey:_key inContext:_ctx]) != nil) + return obj; + } + + /* + Treat other keys which have a path-extension as 'virtual' noops to allow + addition of path names to the attachment path, eg: + http://.../login@server/INBOX/1/2/3/MyDocument.pdf + */ + if ([[_key pathExtension] length] > 0) + return self; /* return 404 to stop acquisition */ return [NSException exceptionWithHTTPStatus:404 /* Not Found */]; @@ -161,15 +171,15 @@ mt = [_info valueForKey:@"type"]; if (![mt isNotNull]) return nil; st = [_info valueForKey:@"subtype"]; if (![st isNotNull]) return nil; - + type = [NSMutableString stringWithCapacity:16]; - [type appendString:mt]; + [type appendString:[mt lowercaseString]]; [type appendString:@"/"]; - [type appendString:st]; + [type appendString:[st lowercaseString]]; parameters = [_info valueForKey:@"parameterList"]; ke = [parameters keyEnumerator]; - while ((pn = [ke nextObject])) { + while ((pn = [ke nextObject]) != nil) { [type appendString:@"; "]; [type appendString:pn]; [type appendString:@"=\""]; @@ -179,6 +189,19 @@ return type; } +- (NSString *)contentTypeForPathExtension:(NSString *)pe { + if ([pe length] == 0) + return @"application/octet-stream"; + + /* TODO: add some map */ + if ([pe isEqualToString:@"gif"]) return @"image/gif"; + if ([pe isEqualToString:@"png"]) return @"image/png"; + if ([pe isEqualToString:@"jpg"]) return @"image/jpeg"; + if ([pe isEqualToString:@"txt"]) return @"text/plain"; + + return @"application/octet-stream"; +} + - (NSString *)davContentType { // TODO: what about the content-type and other headers? // => we could pass them in as the extension? (eg generate 1.gif!) @@ -192,16 +215,7 @@ /* construct type */ pe = [[self nameInContainer] pathExtension]; - if ([pe length] == 0) - return @"application/octet-stream"; - - /* TODO: add some map */ - if ([pe isEqualToString:@"gif"]) return @"image/gif"; - if ([pe isEqualToString:@"png"]) return @"image/png"; - if ([pe isEqualToString:@"jpg"]) return @"image/jpeg"; - if ([pe isEqualToString:@"txt"]) return @"text/plain"; - - return @"application/octet-stream"; + return [self contentTypeForPathExtension:pe]; } /* actions */ diff --git a/SOGo/SoObjects/Mailer/SOGoMailObject.h b/SOGo/SoObjects/Mailer/SOGoMailObject.h index 58f8d759..e35ad5d1 100644 --- a/SOGo/SoObjects/Mailer/SOGoMailObject.h +++ b/SOGo/SoObjects/Mailer/SOGoMailObject.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 SKYRIX Software AG + Copyright (C) 2004-2005 SKYRIX Software AG This file is part of OpenGroupware.org. @@ -18,7 +18,6 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -// $Id$ #ifndef __Mailer_SOGoMailObject_H__ #define __Mailer_SOGoMailObject_H__ diff --git a/SOGo/SoObjects/Mailer/SOGoMailObject.m b/SOGo/SoObjects/Mailer/SOGoMailObject.m index f79ead81..4194cda3 100644 --- a/SOGo/SoObjects/Mailer/SOGoMailObject.m +++ b/SOGo/SoObjects/Mailer/SOGoMailObject.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 SKYRIX Software AG + Copyright (C) 2004-2005 SKYRIX Software AG This file is part of OpenGroupware.org. @@ -18,7 +18,6 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -// $Id$ #include "SOGoMailObject.h" #include "SOGoMailManager.h" @@ -273,19 +272,6 @@ static BOOL heavyDebug = NO; /* name lookup */ -- (BOOL)isBodyPartKey:(NSString *)_key inContext:(id)_ctx { - /* - Every key starting with a digit is consider an IMAP4 mime part key. - */ - if ([_key length] == 0) - return NO; - - if (isdigit([_key characterAtIndex:0])) - return YES; - - return NO; -} - - (id)lookupImap4BodyPartKey:(NSString *)_key inContext:(id)_ctx { // TODO: we might want to check for existence prior controller creation return [[[SOGoMailBodyPart alloc] initWithName:_key diff --git a/SOGo/SoObjects/Mailer/Version b/SOGo/SoObjects/Mailer/Version index 73743e43..20b1dd20 100644 --- a/SOGo/SoObjects/Mailer/Version +++ b/SOGo/SoObjects/Mailer/Version @@ -1,6 +1,6 @@ # Version file -SUBMINOR_VERSION:=56 +SUBMINOR_VERSION:=57 # v0.9.55 requires NGExtensions v4.5.136 # v0.9.44 requires libNGMime v4.3.194 diff --git a/SOGo/UI/Mailer/ChangeLog b/SOGo/UI/Mailer/ChangeLog index 4f979b4c..e76c3e97 100644 --- a/SOGo/UI/Mailer/ChangeLog +++ b/SOGo/UI/Mailer/ChangeLog @@ -1,3 +1,15 @@ +2005-01-28 Helge Hess + + * v0.9.77 + + * added UIxMailSizeFormatter to render big file size numbers with some + M or K suffix + + * UIxMailPartViewer.m: added methods to retrieve filename, file + extension and file size formatter + + * UIxMailSortableTableHeader.m: minor code cleanups + 2005-01-27 Helge Hess * started attachment (download) viewer (#1074) (v0.9.76) diff --git a/SOGo/UI/Mailer/GNUmakefile b/SOGo/UI/Mailer/GNUmakefile index 39658678..c9154123 100644 --- a/SOGo/UI/Mailer/GNUmakefile +++ b/SOGo/UI/Mailer/GNUmakefile @@ -45,6 +45,8 @@ MailerUI_OBJC_FILES += \ \ UIxFilterList.m \ UIxSieveEditor.m \ + \ + UIxMailSizeFormatter.m \ MailerUI_RESOURCE_FILES += \ Version \ diff --git a/SOGo/UI/Mailer/UIxMailEditor.m b/SOGo/UI/Mailer/UIxMailEditor.m index 449d0aaa..57a04567 100644 --- a/SOGo/UI/Mailer/UIxMailEditor.m +++ b/SOGo/UI/Mailer/UIxMailEditor.m @@ -159,13 +159,17 @@ static NSArray *infoKeys = nil; } - (id)lookupSentFolder { - /* lookup INBOX/Sent folder */ + /* + Lookup INBOX/Sent folder. This must be in the UI layer, because the Sent + folder could be a user defined folder (so we cannot have a simple + -saveToSent action on the SOGoDraftObject). + */ SOGoMailAccount *account; SOGoMailFolder *folder; - + if (self->sentFolder != nil) return self; - + account = [[self clientObject] mailAccountFolder]; if ([account isKindOfClass:[NSException class]]) return account; diff --git a/SOGo/UI/Mailer/UIxMailMainFrame.m b/SOGo/UI/Mailer/UIxMailMainFrame.m index d545c4fb..01f7b44d 100644 --- a/SOGo/UI/Mailer/UIxMailMainFrame.m +++ b/SOGo/UI/Mailer/UIxMailMainFrame.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 SKYRIX Software AG + Copyright (C) 2004-2005 SKYRIX Software AG This file is part of OpenGroupware.org. @@ -18,7 +18,6 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -// $Id: UIxMailMainFrame.m 278 2004-08-26 23:29:09Z helge $ #include diff --git a/SOGo/UI/Mailer/UIxMailPartImageViewer.m b/SOGo/UI/Mailer/UIxMailPartImageViewer.m index d77f3e04..1dc4b7d3 100644 --- a/SOGo/UI/Mailer/UIxMailPartImageViewer.m +++ b/SOGo/UI/Mailer/UIxMailPartImageViewer.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 SKYRIX Software AG + Copyright (C) 2004-2005 SKYRIX Software AG This file is part of OpenGroupware.org. diff --git a/SOGo/UI/Mailer/UIxMailPartLinkViewer.m b/SOGo/UI/Mailer/UIxMailPartLinkViewer.m index 6b27fb39..b43395f0 100644 --- a/SOGo/UI/Mailer/UIxMailPartLinkViewer.m +++ b/SOGo/UI/Mailer/UIxMailPartLinkViewer.m @@ -59,12 +59,34 @@ - (NSString *)pathToAttachment { NSString *url, *n; + + /* path to mail controller object */ url = [[self clientObject] baseURLInContext:[self context]]; if (![url hasSuffix:@"/"]) url = [url stringByAppendingString:@"/"]; + /* mail relative path to body-part */ + n = [[self partPath] componentsJoinedByString:@"/"]; - return [url stringByAppendingString:n]; + url = [url stringByAppendingString:n]; + + /* + If we have an attachment name, we attach it, this is properly handled by + SOGoMailBodyPart. + */ + + n = [[[self bodyInfo] valueForKey:@"parameterList"] valueForKey:@"name"]; + if ([n isNotNull] && [n length] > 0) { + url = [url stringByAppendingString:@"/"]; + url = [url stringByAppendingString:n]; + } + else if ([(n = [[self bodyInfo] valueForKey:@"type"]) isNotNull]) { + /* attach extension */ + url = [url stringByAppendingString:@"."]; + url = [url stringByAppendingString:[self preferredPathExtension]]; + } + + return url; } @end /* UIxMailPartLinkViewer */ diff --git a/SOGo/UI/Mailer/UIxMailPartLinkViewer.wox b/SOGo/UI/Mailer/UIxMailPartLinkViewer.wox index a516ec71..6d1b89cd 100644 --- a/SOGo/UI/Mailer/UIxMailPartLinkViewer.wox +++ b/SOGo/UI/Mailer/UIxMailPartLinkViewer.wox @@ -1,9 +1,36 @@
- [abc ] +
+ +
+ : + / + , + + : + +
+ + +
diff --git a/SOGo/UI/Mailer/UIxMailPartTextViewer.m b/SOGo/UI/Mailer/UIxMailPartTextViewer.m index 990a4060..40370e49 100644 --- a/SOGo/UI/Mailer/UIxMailPartTextViewer.m +++ b/SOGo/UI/Mailer/UIxMailPartTextViewer.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 SKYRIX Software AG + Copyright (C) 2004-2005 SKYRIX Software AG This file is part of OpenGroupware.org. @@ -21,6 +21,15 @@ #include "UIxMailPartViewer.h" +/* + UIxMailPartTextViewer + + Show plain/text mail parts in a
 section.
+  
+  TODO: add server side wrapping.
+  TODO: add contained link detection.
+*/
+
 @interface UIxMailPartTextViewer : UIxMailPartViewer
 {
 }
@@ -31,10 +40,4 @@
 
 @implementation UIxMailPartTextViewer
 
-- (void)dealloc {
-  [super dealloc];
-}
-
-/* accessors */
-
 @end /* UIxMailPartTextViewer */
diff --git a/SOGo/UI/Mailer/UIxMailPartViewer.h b/SOGo/UI/Mailer/UIxMailPartViewer.h
index 551085e7..bd0723df 100644
--- a/SOGo/UI/Mailer/UIxMailPartViewer.h
+++ b/SOGo/UI/Mailer/UIxMailPartViewer.h
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2004 SKYRIX Software AG
+  Copyright (C) 2004-2005 SKYRIX Software AG
 
   This file is part of OpenGroupware.org.
 
@@ -30,7 +30,7 @@
   This class is the superclass for MIME content viewers.
 */
 
-@class NSData, NSArray;
+@class NSData, NSArray, NSFormatter;
 
 @interface UIxMailPartViewer : UIxComponent
 {
@@ -51,6 +51,11 @@
 - (NSData *)decodedFlatContent;
 - (NSString *)flatContentAsString;
 
+- (NSString *)preferredPathExtension;
+- (NSString *)filename;
+- (NSString *)filenameForDisplay;
+- (NSFormatter *)sizeFormatter;
+
 /* caches */
 
 - (void)resetPathCaches;
diff --git a/SOGo/UI/Mailer/UIxMailPartViewer.m b/SOGo/UI/Mailer/UIxMailPartViewer.m
index c0889777..6a174ee0 100644
--- a/SOGo/UI/Mailer/UIxMailPartViewer.m
+++ b/SOGo/UI/Mailer/UIxMailPartViewer.m
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2004 SKYRIX Software AG
+  Copyright (C) 2004-2005 SKYRIX Software AG
 
   This file is part of OpenGroupware.org.
 
@@ -21,6 +21,7 @@
 
 #include "UIxMailPartViewer.h"
 #include "UIxMailRenderingContext.h"
+#include "UIxMailSizeFormatter.h"
 #include "WOContext+UIxMailer.h"
 #include 
 #include "common.h"
@@ -29,8 +30,8 @@
 
 - (void)dealloc {
   [self->flatContent release];
-  [self->bodyInfo release];
-  [self->partPath release];
+  [self->bodyInfo    release];
+  [self->partPath    release];
   [super dealloc];
 }
 
@@ -131,4 +132,64 @@
   return s;
 }
 
+/* path extension */
+
+- (NSString *)pathExtensionForType:(NSString *)_mt subtype:(NSString *)_st {
+  // TODO: support /etc/mime.types
+  
+  if (![_mt isNotNull] || ![_st isNotNull])
+    return nil;
+  if ([_mt length] == 0) return nil;
+  if ([_st length] == 0) return nil;
+  _mt = [_mt lowercaseString];
+  _st = [_st lowercaseString];
+  
+  if ([_mt isEqualToString:@"image"]) {
+    if ([_st isEqualToString:@"gif"])  return @"gif";
+    if ([_st isEqualToString:@"jpeg"]) return @"jpg";
+    if ([_st isEqualToString:@"png"])  return @"png";
+  }
+  else if ([_mt isEqualToString:@"text"]) {
+    if ([_st isEqualToString:@"plain"]) return @"txt";
+    if ([_st isEqualToString:@"xml"])   return @"xml";
+  }
+  else if ([_mt isEqualToString:@"application"]) {
+    if ([_st isEqualToString:@"pdf"]) return @"pdf";
+  }
+  return nil;
+}
+
+- (NSString *)preferredPathExtension {
+  return [self pathExtensionForType:[[self bodyInfo] valueForKey:@"type"]
+	       subtype:[[self bodyInfo] valueForKey:@"subtype"]];
+}
+
+- (NSString *)filename {
+  id tmp;
+  
+  tmp = [[self bodyInfo] valueForKey:@"parameterList"];
+  if (![tmp isNotNull])
+    return nil;
+  
+  tmp = [tmp valueForKey:@"name"];
+  if (![tmp isNotNull])
+    return nil;
+  
+  return [tmp length] > 0 ? tmp : nil;
+}
+
+- (NSString *)filenameForDisplay {
+  NSString *s;
+  
+  if ((s = [self filename]) != nil)
+    return s;
+
+  s = [[self partPath] componentsJoinedByString:@"-"];
+  return [@"untitled-" stringByAppendingString:s];
+}
+
+- (NSFormatter *)sizeFormatter {
+  return [UIxMailSizeFormatter sharedMailSizeFormatter];
+}
+
 @end /* UIxMailPartViewer */
diff --git a/SOGo/UI/Mailer/UIxMailSizeFormatter.h b/SOGo/UI/Mailer/UIxMailSizeFormatter.h
new file mode 100644
index 00000000..267fec08
--- /dev/null
+++ b/SOGo/UI/Mailer/UIxMailSizeFormatter.h
@@ -0,0 +1,41 @@
+/*
+  Copyright (C) 2005 SKYRIX Software AG
+
+  This file is part of OpenGroupware.org.
+
+  OGo is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with OGo; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __Mailer_UIxMailSizeFormatter_H__
+#define __Mailer_UIxMailSizeFormatter_H__
+
+#include 
+
+/*
+  UIxMailSizeFormatter
+  
+  This class formats the size values for display.
+*/
+
+@interface UIxMailSizeFormatter : NSFormatter
+{
+}
+
++ (id)sharedMailSizeFormatter;
+
+@end
+
+#endif /* __Mailer_UIxMailSizeFormatter_H__ */
diff --git a/SOGo/UI/Mailer/UIxMailSizeFormatter.m b/SOGo/UI/Mailer/UIxMailSizeFormatter.m
new file mode 100644
index 00000000..34b35799
--- /dev/null
+++ b/SOGo/UI/Mailer/UIxMailSizeFormatter.m
@@ -0,0 +1,52 @@
+/*
+  Copyright (C) 2005 SKYRIX Software AG
+
+  This file is part of OpenGroupware.org.
+
+  OGo is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with OGo; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "UIxMailSizeFormatter.h"
+#include "common.h"
+
+@implementation UIxMailSizeFormatter
+
++ (id)sharedMailSizeFormatter {
+  static UIxMailSizeFormatter *fmt = nil; // THREAD
+  if (fmt == nil) fmt = [[self alloc] init];
+  return fmt;
+}
+
+/* formatting */
+
+- (NSString *)stringForSize:(unsigned int)size {
+  unsigned char buf[128];
+  
+  if (size < 1024)
+    sprintf(buf, "%d", size);
+  else if (size < 1024 * 1024)
+    sprintf(buf, "%.1fK", ((double)size / 1024));
+  else
+    sprintf(buf, "%.1fM", ((double)size / 1024 / 1024));
+  
+  return [NSString stringWithCString:buf];
+}
+
+- (NSString *)stringForObjectValue:(id)_object {
+  return [self stringForSize:[_object unsignedIntValue]];
+}
+
+@end /* UIxMailSizeFormatter */
diff --git a/SOGo/UI/Mailer/UIxMailSortableTableHeader.m b/SOGo/UI/Mailer/UIxMailSortableTableHeader.m
index 00781f7a..5e004cfd 100644
--- a/SOGo/UI/Mailer/UIxMailSortableTableHeader.m
+++ b/SOGo/UI/Mailer/UIxMailSortableTableHeader.m
@@ -1,7 +1,7 @@
 /*
- Copyright (C) 2000-2004 SKYRIX Software AG
+ Copyright (C) 2004-2005 SKYRIX Software AG
  
- This file is part of OGo
+ This file is part of OpenGroupware.org.
  
  OGo is free software; you can redistribute it and/or modify it under
  the terms of the GNU Lesser General Public License as published by the
@@ -17,12 +17,16 @@
  License along with OGo; see the file COPYING.  If not, write to the
  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
  02111-1307, USA.
- */
-// $Id$
-
+*/
 
 #include 
 
+/*
+  UIxMailSortableTableHeader
+
+  TODO: document.
+*/
+
 @interface UIxMailSortableTableHeader : WOComponent
 {
   NSString     *label;
@@ -49,21 +53,21 @@
 /* Accessors */
 
 - (void)setLabel:(NSString *)_label {
-  ASSIGN(self->label, _label);
+  ASSIGNCOPY(self->label, _label);
 }
 - (NSString *)label {
   return self->label;
 }
 
 - (void)setSortKey:(NSString *)_sortKey {
-  ASSIGN(self->sortKey, _sortKey);
+  ASSIGNCOPY(self->sortKey, _sortKey);
 }
 - (NSString *)sortKey {
   return self->sortKey;
 }
 
 - (void)setHref:(NSString *)_href {
-  ASSIGN(self->href, _href);
+  ASSIGNCOPY(self->href, _href);
 }
 - (NSString *)href {
   return self->href;
@@ -85,10 +89,11 @@
 
 - (BOOL)isSelected {
   NSString *so;
+  
   so = [self->queryDictionary objectForKey:@"sort"];
-  if(!so) {
+  if (![so isNotNull])
     return self->isDefault;
-  }
+  
   return [so isEqualToString:self->sortKey];
 }
 
@@ -96,9 +101,9 @@
   NSString *desc;
   
   desc = [self->queryDictionary objectForKey:@"desc"];
-  if(!desc)
+  if (desc == nil)
     return NO;
-  return [desc boolValue] ? YES : NO;
+  return [desc boolValue];
 }
 
-@end
+@end /* UIxMailSortableTableHeader */
diff --git a/SOGo/UI/Mailer/Version b/SOGo/UI/Mailer/Version
index b4ba3132..733f93ec 100644
--- a/SOGo/UI/Mailer/Version
+++ b/SOGo/UI/Mailer/Version
@@ -1,7 +1,8 @@
 # version file
 
-SUBMINOR_VERSION:=76
+SUBMINOR_VERSION:=77
 
+# v0.9.77 requires SoObjects/Mailer v0.9.57
 # v0.9.74 requires SoObjects/Mailer v0.9.56
 # v0.9.70 requires NGExtensions     v4.5.136
 # v0.9.69 requires libNGMime        v4.5.203
diff --git a/SOGo/UI/Mailer/mailer.css b/SOGo/UI/Mailer/mailer.css
index fd9142a7..bc6bb1f7 100644
--- a/SOGo/UI/Mailer/mailer.css
+++ b/SOGo/UI/Mailer/mailer.css
@@ -395,3 +395,30 @@ td.attachment_uplabel {
   font-size:  11px;
   text-align: left;
 }
+
+/* attachment link viewer */
+
+div.linked_attachment_frame {
+  background-color: #D4D0C8;
+  padding:          4px;
+}
+
+div.linked_attachment_body {
+  font-family:  Arial, Helvetica, Verdana, Geneva, Tahoma, sans-serif;
+  font-size:    10pt;
+  padding:             4px;
+
+  border-width:        1;
+  border-style:        solid;
+  border-top-color:    white;
+  border-left-color:   white;
+  border-bottom-color: #808080;
+  border-right-color:  #808080;
+}
+
+div.linked_attachment_meta {
+  color:        #444444;
+  font-style:   italic;
+  border-width: 0;
+  padding:      2px;
+}