1/*
2  Copyright (C) 2000-2007 SKYRIX Software AG
3  Copyright (C) 2007      Helge Hess
4
5  This file is part of SOPE.
6
7  SOPE is free software; you can redistribute it and/or modify it under
8  the terms of the GNU Lesser General Public License as published by the
9  Free Software Foundation; either version 2, or (at your option) any
10  later version.
11
12  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
13  WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15  License for more details.
16
17  You should have received a copy of the GNU Lesser General Public
18  License along with SOPE; see the file COPYING.  If not, write to the
19  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20  02111-1307, USA.
21*/
22
23#include "NGMimeFileData.h"
24#include "common.h"
25#include <string.h>
26#include <unistd.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <fcntl.h>
30
31@implementation NGMimeFileData
32
33static NSString      *TmpPath = nil;
34static NSProcessInfo *Pi      = nil;
35static unsigned      tmpmask  = 0600;
36
37+ (void)initialize {
38  NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
39
40  if (TmpPath == nil) {
41    TmpPath = [ud stringForKey:@"NGMimeBuildMimeTempDirectory"];
42    if (TmpPath == nil) TmpPath = @"/tmp/";
43    TmpPath = [[TmpPath stringByAppendingPathComponent:@"OGo"] copy];
44  }
45  if (Pi == nil) Pi = [[NSProcessInfo processInfo] retain];
46}
47
48- (id)initWithPath:(NSString *)_path removeFile:(BOOL)_remove {
49#if !GNUSTEP_BASE_LIBRARY
50  /*
51    see OGo bug #1890, the gstep-base -init clashes and we don't exactly need
52    it ... (but I guess its better to call it on other Foundations)
53  */
54  if ((self = [super init]) != nil) {
55#endif
56    if (![[NSFileManager defaultManager] fileExistsAtPath:_path]) {
57      NSLog(@"ERROR[%s]: missing file at path %@", __PRETTY_FUNCTION__, _path);
58      [self release];
59      return nil;
60    }
61    self->path       = [_path copy];
62    self->removeFile = _remove;
63    self->length     = -1;
64#if !GNUSTEP_BASE_LIBRARY
65  }
66#endif
67  return self;
68}
69
70- (id)initWithBytes:(const void*)_bytes
71  length:(NSUInteger)_length
72{
73  NSString *filename = nil;
74  int      fd;
75
76  filename = [Pi temporaryFileName:TmpPath];
77
78  fd = open([filename fileSystemRepresentation],
79            O_WRONLY | O_CREAT | O_TRUNC, tmpmask);
80  if (fd == -1) {
81    fprintf(stderr, "Could not open file for writing %s: %s\n",
82            [filename fileSystemRepresentation], strerror(errno));
83    [self release];
84    return nil;
85  }
86  if (write(fd, _bytes, _length) != (int)_length) {
87    fprintf(stderr,
88#if GS_64BIT_OLD
89            "Failed to write %i bytes to %s: %s\n",
90#else
91            "Failed to write %li bytes to %s: %s\n",
92#endif
93            _length, [filename fileSystemRepresentation], strerror(errno));
94    close(fd);
95    [self release];
96    return nil;
97  }
98
99  close(fd);
100
101  return [self initWithPath:filename removeFile:YES];
102}
103
104- (void)dealloc {
105  if (self->removeFile) {
106    [[NSFileManager defaultManager]
107                    removeFileAtPath:self->path handler:nil];
108  }
109  [self->path release];
110  [super dealloc];
111}
112
113- (NSData *)_data {
114  return [NSData dataWithContentsOfMappedFile:self->path];
115}
116
117- (id)copyWithZone:(NSZone *)zone {
118  return [self retain];
119}
120
121- (const void*)bytes {
122  return [[self _data] bytes];
123}
124
125- (NSUInteger)length {
126  if (self->length == -1) {
127    self->length = [[[[NSFileManager defaultManager]
128                                     fileAttributesAtPath:self->path
129                                     traverseLink:NO]
130                                     objectForKey:NSFileSize] intValue];
131  }
132  return self->length;
133}
134
135- (BOOL)appendDataToFileDesc:(int)_fd {
136  NGFileStream *fs;
137  int  bufCnt = 8192;
138  char buffer[bufCnt];
139  BOOL result;
140  int  fileLen;
141
142  if (![[NSFileManager defaultManager] isReadableFileAtPath:self->path]) {
143    NSLog(@"ERROR[%s] missing file at path %@", __PRETTY_FUNCTION__,
144          self->path);
145    return NO;
146  }
147
148  fileLen = [self length];
149  result  = YES;
150  fs      = [NGFileStream alloc]; /* to keep gcc 3.4 happy */
151  fs      = [fs initWithPath:self->path];
152
153  if (![fs openInMode:@"r"]) {
154    NSLog(@"%s: could not open file stream ... %@",
155          __PRETTY_FUNCTION__, self->path);
156    [fs release]; fs = nil;
157    return NO;
158  }
159
160  NS_DURING {
161    NSInteger read;
162    NSInteger alreadyRead;
163
164    alreadyRead = 0;
165
166    read = (bufCnt > (fileLen - alreadyRead)) ? fileLen - alreadyRead : bufCnt;
167
168    while ((read = [fs readBytes:buffer count:read])) {
169      alreadyRead += read;
170      if (write(_fd, buffer, read) != read) {
171        fprintf(stderr,
172#if GS_64BIT_OLD
173                "%s: Failed to write %i bytes to file\n",
174#else
175                "%s: Failed to write %li bytes to file\n",
176#endif
177                __PRETTY_FUNCTION__, read);
178        result = NO;
179        break;
180      }
181      if (alreadyRead == fileLen)
182        break;
183    }
184  }
185  NS_HANDLER {
186    printf("got exceptions %s\n", [[localException description] cString]);
187    if (![localException isKindOfClass:[NGEndOfStreamException class]]) {
188      [fs release]; fs = nil;
189      result = NO;
190    }
191  }
192  NS_ENDHANDLER;
193  [fs release]; fs = nil;
194  return result;
195}
196
197/* description */
198
199- (NSString *)description {
200  return [NSString stringWithFormat:@"<0x%p[%@]: path=%@>",
201                     self, NSStringFromClass([self class]), self->path];
202}
203
204@end /* NGMimeFileData */
205