~ubuntu-branches/ubuntu/karmic/gnustep-base/karmic

« back to all changes in this revision

Viewing changes to Source/GSHTTPURLHandle.m

  • Committer: Bazaar Package Importer
  • Author(s): Eric Heintzmann
  • Date: 2005-04-17 00:14:38 UTC
  • mfrom: (1.2.1 upstream) (2.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050417001438-enf0y07c9tku85z1
Tags: 1.10.3-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/** GSHTTPURLHandle.m - Class GSHTTPURLHandle
2
2
   Copyright (C) 2000 Free Software Foundation, Inc.
3
 
   
4
 
   Written by:  Mark Allison <mark@brainstorm.co.uk>
5
 
   Integrated:  Richard Frith-Macdonald <rfm@gnu.org>
6
 
   Date:        November 2000           
7
 
   
 
3
 
 
4
   Written by:          Mark Allison <mark@brainstorm.co.uk>
 
5
   Integrated by:       Richard Frith-Macdonald <rfm@gnu.org>
 
6
   Date:                November 2000           
 
7
 
8
8
   This file is part of the GNUstep Library.
9
 
   
 
9
 
10
10
   This library is free software; you can redistribute it and/or
11
11
   modify it under the terms of the GNU Library General Public
12
12
   License as published by the Free Software Foundation; either
13
13
   version 2 of the License, or (at your option) any later version.
14
 
   
 
14
 
15
15
   This library is distributed in the hope that it will be useful,
16
16
   but WITHOUT ANY WARRANTY; without even the implied warranty of
17
17
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
18
   Library General Public License for more details.
19
 
   
 
19
 
20
20
   You should have received a copy of the GNU Library General Public
21
21
   License along with this library; if not, write to the Free
22
22
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
23
23
*/
24
24
 
25
25
#include "config.h"
26
 
#include <Foundation/NSArray.h>
27
 
#include <Foundation/NSString.h>
28
 
#include <Foundation/NSException.h>
29
 
#include <Foundation/NSValue.h>
30
 
#include <Foundation/NSData.h>
31
 
#include <Foundation/NSURL.h>
32
 
#include <Foundation/NSURLHandle.h>
33
 
#include <Foundation/NSNotification.h>
34
 
#include <Foundation/NSRunLoop.h>
35
 
#include <Foundation/NSByteOrder.h>
36
 
#include <Foundation/NSLock.h>
37
 
#include <Foundation/NSFileHandle.h>
38
 
#include <Foundation/NSDebug.h>
39
 
#include <Foundation/GSMime.h>
 
26
#include "Foundation/NSArray.h"
 
27
#include "Foundation/NSString.h"
 
28
#include "Foundation/NSException.h"
 
29
#include "Foundation/NSValue.h"
 
30
#include "Foundation/NSData.h"
 
31
#include "Foundation/NSURL.h"
 
32
#include "Foundation/NSURLHandle.h"
 
33
#include "Foundation/NSNotification.h"
 
34
#include "Foundation/NSRunLoop.h"
 
35
#include "Foundation/NSByteOrder.h"
 
36
#include "Foundation/NSLock.h"
 
37
#include "Foundation/NSFileHandle.h"
 
38
#include "Foundation/NSDebug.h"
 
39
#include "Foundation/NSHost.h"
 
40
#include "Foundation/NSProcessInfo.h"
 
41
#include "Foundation/NSPathUtilities.h"
 
42
#include "GNUstepBase/GSMime.h"
 
43
#include "GNUstepBase/GSLock.h"
40
44
#include <string.h>
41
 
#if HAVE_UNISTD_H
 
45
#ifdef HAVE_UNISTD_H
42
46
#include <unistd.h>
43
47
#endif
44
48
#include <sys/file.h>
49
53
 
50
54
static NSString *httpVersion = @"1.1";
51
55
 
52
 
char emp[64] = {
53
 
    'A','B','C','D','E','F','G','H','I','J','K','L','M',
54
 
    'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
55
 
    'a','b','c','d','e','f','g','h','i','j','k','l','m',
56
 
    'n','o','p','q','r','s','t','u','v','w','x','y','z',
57
 
    '0','1','2','3','4','5','6','7','8','9','+','/'
58
 
};
59
 
 
60
56
@interface GSHTTPURLHandle : NSURLHandle
61
57
{
62
58
  BOOL                  tunnel;
63
59
  BOOL                  debug;
 
60
  BOOL                  keepalive;
64
61
  NSFileHandle          *sock;
65
62
  NSURL                 *url;
 
63
  NSURL                 *u;
66
64
  NSMutableData         *dat;
67
65
  GSMimeParser          *parser;
68
66
  GSMimeDocument        *document;
70
68
  NSMutableDictionary   *wProperties;
71
69
  NSData                *wData;
72
70
  NSMutableDictionary   *request;
73
 
  unsigned int          contentLength;
 
71
  unsigned int          bodyPos;
 
72
  unsigned int          redirects;
74
73
  enum {
75
74
    idle,
76
75
    connecting,
78
77
    reading,
79
78
  } connectionState;
80
79
}
81
 
- (NSString*) encodebase64: (NSString*) input;
82
80
- (void) setDebug: (BOOL)flag;
 
81
- (void) _tryLoadInBackground: (NSURL*)fromURL;
83
82
@end
84
83
 
85
 
 
 
84
/**
 
85
 * <p>
 
86
 *   This is a <em>PRIVATE</em> subclass of NSURLHandle.
 
87
 *   It is documented here in order to give you information about the
 
88
 *   default behavior of an NSURLHandle created to deal with a URL
 
89
 *   that has either the <code>http</code> or <code>https</code> scheme.
 
90
 *   The name and/or other implementation details of this class
 
91
 *   may be changed at any time.
 
92
 * </p>
 
93
 * <p>
 
94
 *   A GSHTTPURLHandle instance is used to manage connections to
 
95
 *   <code>http</code> and <code>https</code> URLs.
 
96
 *    Secure connections are handled automatically
 
97
 *   (using openSSL) for URLs with the scheme <code>https</code>.
 
98
 *   Connection via proxy server is supported, as is proxy tunneling
 
99
 *   for secure connections.  Basic parsing of <code>http</code>
 
100
 *   headers is performed to extract <code>http</code> status
 
101
 *   information, cookies etc.  Cookies are
 
102
 *   retained and automatically sent during subsequent requests where
 
103
 *   the cookie is valid.
 
104
 * </p>
 
105
 * <p>
 
106
 *   Header information from the current page may be obtained using
 
107
 *   -propertyForKey and -propertyForKeyIfAvailable.  <code>HTTP</code>
 
108
 *   status information can be retrieved as by calling either of these
 
109
 *   methods specifying one of the following keys:
 
110
 * </p>
 
111
 * <list>
 
112
 *   <item>
 
113
 *     NSHTTPPropertyStatusCodeKey - numeric status code
 
114
 *   </item>
 
115
 *   <item>
 
116
 *     NSHTTPPropertyStatusReasonKey - text describing status
 
117
 *   </item>
 
118
 *   <item>
 
119
 *     NSHTTPPropertyServerHTTPVersionKey - <code>http</code>
 
120
 *     version supported by remote server
 
121
 *   </item>
 
122
 * </list>
 
123
 * <p>
 
124
 *   According to MacOS-X headers, the following should also
 
125
 *   be supported, but currently are not:
 
126
 * </p>
 
127
 * <list>
 
128
 *   <item>NSHTTPPropertyRedirectionHeadersKey</item>
 
129
 *   <item>NSHTTPPropertyErrorPageDataKey</item>
 
130
 * </list>
 
131
 * <p>
 
132
 *   The omission of these headers is not viewed as important at
 
133
 *   present, since the MacOS-X public beta implementation doesn't
 
134
 *   work either.
 
135
 * </p>
 
136
 * <p>
 
137
 *   Other calls to -propertyForKey and -propertyForKeyIfAvailable may
 
138
 *   be made specifying a <code>http</code> header field name.
 
139
 *   For example specifying a key name of &quot;Content-Length&quot;
 
140
 *   would return the value of the &quot;Content-Length&quot; header
 
141
 *   field.
 
142
 * </p>
 
143
 * <p>
 
144
 *   [GSHTTPURLHandle-writeProperty:forKey:]
 
145
 *   can be used to specify the parameters
 
146
 *   for the <code>http</code> request.  The default request uses the
 
147
 *   &quot;GET&quot; method when fetching a page, and the
 
148
 *   &quot;POST&quot; method when using -writeData:.
 
149
 *   This can be over-ridden by calling -writeProperty:forKey: with
 
150
 *   the key name &quot;GSHTTPPropertyMethodKey&quot; and specifying an
 
151
 *   alternative method (i.e &quot;PUT&quot;).
 
152
 * </p>
 
153
 * <p>
 
154
 *   A Proxy may be specified by calling -writeProperty:forKey:
 
155
 *   with the keys &quot;GSHTTPPropertyProxyHostKey&quot; and
 
156
 *   &quot;GSHTTPPropertyProxyPortKey&quot; to set the host and port
 
157
 *   of the proxy server respectively.  The GSHTTPPropertyProxyHostKey
 
158
 *   property can be set to either the IP address or the hostname of
 
159
 *   the proxy server.  If an attempt is made to load a page via a
 
160
 *   secure connection when a proxy is specified, GSHTTPURLHandle will
 
161
 *   attempt to open an SSL Tunnel through the proxy.
 
162
 * </p>
 
163
 * <p>
 
164
 *   Requests to the remote server may be forced to be bound to a
 
165
 *   particular local IP address by using the key
 
166
 *   &quot;GSHTTPPropertyLocalHostKey&quot;  which must contain the
 
167
 *   IP address of a network interface on the local host.
 
168
 * </p>
 
169
 */
86
170
@implementation GSHTTPURLHandle
87
171
 
88
172
static NSMutableDictionary      *urlCache = nil;
91
175
static Class                    sslClass = 0;
92
176
 
93
177
static NSLock                   *debugLock = nil;
94
 
static char                     debugFile[128];
 
178
static NSString                 *debugFile;
95
179
 
96
 
static void debugRead(NSData *data)
 
180
static void debugRead(GSHTTPURLHandle *handle, NSData *data)
97
181
{
98
182
  NSString      *s;
99
183
  int           d;
100
184
 
101
185
  [debugLock lock];
102
 
  d = open(debugFile, O_WRONLY|O_CREAT|O_APPEND, 0644);
 
186
  d = open([debugFile  fileSystemRepresentation],
 
187
           O_WRONLY|O_CREAT|O_APPEND, 0644);
103
188
  if (d >= 0)
104
189
    {
105
 
      s = [NSString stringWithFormat: @"\nRead %@ %u bytes - '",
106
 
        [NSDate date], [data length]];
 
190
      s = [NSString stringWithFormat: @"\nRead for %x at %@ %u bytes - '",
 
191
        handle, [NSDate date], [data length]];
107
192
      write(d, [s cString], [s cStringLength]);
108
193
      write(d, [data bytes], [data length]);
109
194
      write(d, "'", 1);
111
196
    }
112
197
  [debugLock unlock];
113
198
}
114
 
static void debugWrite(NSData *data)
 
199
static void debugWrite(GSHTTPURLHandle *handle, NSData *data)
115
200
{
116
201
  NSString      *s;
117
202
  int           d;
118
203
 
119
204
  [debugLock lock];
120
 
  d = open(debugFile, O_WRONLY|O_CREAT|O_APPEND, 0644);
 
205
  d = open([debugFile  fileSystemRepresentation],
 
206
           O_WRONLY|O_CREAT|O_APPEND, 0644);
121
207
  if (d >= 0)
122
208
    {
123
 
      s = [NSString stringWithFormat: @"\nWrite %@ %u bytes - '",
124
 
        [NSDate date], [data length]];
 
209
      s = [NSString stringWithFormat: @"\nWrite for %x at %@ %u bytes - '",
 
210
        handle, [NSDate date], [data length]];
125
211
      write(d, [s cString], [s cStringLength]);
126
212
      write(d, [data bytes], [data length]);
127
213
      write(d, "'", 1);
153
239
  if (self == [GSHTTPURLHandle class])
154
240
    {
155
241
      urlCache = [NSMutableDictionary new];
156
 
      urlLock = [NSLock new];
157
 
      debugLock = [NSLock new];
158
 
      sprintf(debugFile, "/tmp/GSHTTP.%d", getpid());
 
242
      urlLock = [GSLazyLock new];
 
243
      debugLock = [GSLazyLock new];
 
244
      debugFile = [NSString stringWithFormat: @"%@/GSHTTP.%d",
 
245
                             NSTemporaryDirectory(),
 
246
                             [[NSProcessInfo processInfo] processIdentifier]];
 
247
      RETAIN(debugFile);
 
248
 
159
249
#ifndef __MINGW__
160
250
      sslClass = [NSFileHandle sslClass];
161
251
#endif
165
255
- (void) dealloc
166
256
{
167
257
  RELEASE(sock);
 
258
  RELEASE(u);
168
259
  RELEASE(url);
169
260
  RELEASE(dat);
170
261
  RELEASE(parser);
211
302
  return NO;
212
303
}
213
304
 
 
305
- (void) bgdApply: (NSString*)basic
 
306
{
 
307
  NSNotificationCenter  *nc = [NSNotificationCenter defaultCenter];
 
308
  NSEnumerator          *wpEnumerator;
 
309
  NSMutableString       *s;
 
310
  NSString              *key;
 
311
  NSMutableData         *buf;
 
312
  NSString              *version;
 
313
 
 
314
  if (debug == YES) NSLog(@"%@", NSStringFromSelector(_cmd));
 
315
 
 
316
  s = [basic mutableCopy];
 
317
  if ([[u query] length] > 0)
 
318
    {
 
319
      [s appendFormat: @"?%@", [u query]];
 
320
    }
 
321
 
 
322
  version = [request objectForKey: NSHTTPPropertyServerHTTPVersionKey];
 
323
  if (version == nil)
 
324
    {
 
325
      version = httpVersion;
 
326
    }
 
327
  [s appendFormat: @" HTTP/%@\r\n", version];
 
328
 
 
329
  if ([wProperties objectForKey: @"host"] == nil)
 
330
    {
 
331
      [wProperties setObject: [u host] forKey: @"host"];
 
332
    }
 
333
 
 
334
  if ([wData length] > 0)
 
335
    {
 
336
      [wProperties setObject: [NSString stringWithFormat: @"%d", [wData length]]
 
337
                      forKey: @"content-length"];
 
338
      /*
 
339
       * Assume content type if not specified.
 
340
       */
 
341
      if ([wProperties objectForKey: @"content-type"] == nil)
 
342
        {
 
343
          [wProperties setObject: @"application/x-www-form-urlencoded"
 
344
                          forKey: @"content-type"];
 
345
        }
 
346
    }
 
347
  if ([wProperties objectForKey: @"authorization"] == nil)
 
348
    {
 
349
      if ([u user] != nil)
 
350
        {
 
351
          NSString      *auth;
 
352
 
 
353
          if ([[u password] length] > 0)
 
354
            {
 
355
              auth = [NSString stringWithFormat: @"%@:%@",
 
356
                [u user], [u password]];
 
357
            }
 
358
          else
 
359
            {
 
360
              auth = [NSString stringWithFormat: @"%@", [u user]];
 
361
            }
 
362
          auth = [NSString stringWithFormat: @"Basic %@",
 
363
            [GSMimeDocument encodeBase64String: auth]];
 
364
          [wProperties setObject: auth
 
365
                          forKey: @"authorization"];
 
366
        }
 
367
    }
 
368
 
 
369
  wpEnumerator = [wProperties keyEnumerator];
 
370
  while ((key = [wpEnumerator nextObject]))
 
371
    {
 
372
      [s appendFormat: @"%@: %@\r\n", key, [wProperties objectForKey: key]];
 
373
    }
 
374
  [wProperties removeAllObjects];
 
375
  [s appendString: @"\r\n"];
 
376
  buf = [[s dataUsingEncoding: NSASCIIStringEncoding] mutableCopy];
 
377
 
 
378
  /*
 
379
   * Append any data to be sent
 
380
   */
 
381
  if (wData != nil)
 
382
    {
 
383
      [buf appendData: wData];
 
384
    }
 
385
 
 
386
  /*
 
387
   * Watch for write completion.
 
388
   */
 
389
  [nc addObserver: self
 
390
         selector: @selector(bgdWrite:)
 
391
             name: GSFileHandleWriteCompletionNotification
 
392
           object: sock];
 
393
  connectionState = writing;
 
394
 
 
395
  /*
 
396
   * Send request to server.
 
397
   */
 
398
  if (debug == YES) debugWrite(self, buf);
 
399
  [sock writeInBackgroundAndNotify: buf];
 
400
  RELEASE(buf);
 
401
  RELEASE(s);
 
402
}
 
403
 
214
404
- (void) bgdRead: (NSNotification*) not
215
405
{
216
406
  NSNotificationCenter  *nc = [NSNotificationCenter defaultCenter];
217
407
  NSDictionary          *dict = [not userInfo];
218
408
  NSData                *d;
 
409
  NSRange               r;
219
410
 
220
411
  d = [dict objectForKey: NSFileHandleNotificationDataItem];
221
 
  if (debug == YES) debugRead(d);
 
412
  if (debug == YES) debugRead(self, d);
222
413
 
223
 
  [parser parse: d];
224
 
  if ([parser isComplete] == YES)
 
414
  if ([parser parse: d] == NO)
225
415
    {
226
 
      NSDictionary      *info;
227
 
      NSString          *val;
228
 
 
229
 
      connectionState = idle;
230
 
      [nc removeObserver: self
231
 
                    name: NSFileHandleReadCompletionNotification
232
 
                  object: sock];
233
 
      [sock closeFile];
234
 
      DESTROY(sock);
235
 
      /*
236
 
       * Retrieve essential keys from document
237
 
       */
238
 
      info = [document headerNamed: @"http"];
239
 
      val = [info objectForKey: NSHTTPPropertyServerHTTPVersionKey];
240
 
      if (val != nil)
241
 
        [pageInfo setObject: val forKey: NSHTTPPropertyServerHTTPVersionKey];
242
 
      val = [info objectForKey: NSHTTPPropertyStatusCodeKey];
243
 
      if (val != nil)
244
 
        [pageInfo setObject: val forKey: NSHTTPPropertyStatusCodeKey];
245
 
      val = [info objectForKey: NSHTTPPropertyStatusReasonKey];
246
 
      if (val != nil)
247
 
        [pageInfo setObject: val forKey: NSHTTPPropertyStatusReasonKey];
248
 
      /*
249
 
       * Tell superclass that we have successfully loaded the data.
250
 
       */
251
 
      [self didLoadBytes: [parser data] loadComplete: YES];
 
416
      if (debug == YES)
 
417
        {
 
418
          NSLog(@"HTTP parse failure - %@", parser);
 
419
        }
 
420
      [self endLoadInBackground];
 
421
      [self backgroundLoadDidFailWithReason: @"Response parse failed"];
252
422
    }
253
423
  else
254
424
    {
255
 
      [sock readInBackgroundAndNotify];
 
425
      BOOL      complete = [parser isComplete];
 
426
 
 
427
      if (complete == NO && [parser isInHeaders] == NO)
 
428
        {
 
429
          GSMimeHeader  *info;
 
430
          NSString      *enc;
 
431
          NSString      *len;
 
432
          NSString      *status;
 
433
          float         ver;
 
434
 
 
435
          info = [document headerNamed: @"http"];
 
436
          ver = [[info value] floatValue];
 
437
          status = [info objectForKey: NSHTTPPropertyStatusCodeKey];
 
438
          len = [[document headerNamed: @"content-length"] value];
 
439
          enc = [[document headerNamed: @"content-transfer-encoding"] value];
 
440
          if (enc == nil)
 
441
            {
 
442
              enc = [[document headerNamed: @"transfer-encoding"] value];
 
443
            }
 
444
 
 
445
          if ([status isEqual: @"204"] || [status isEqual: @"304"])
 
446
            {
 
447
              complete = YES;   // No body expected.
 
448
            }
 
449
          else if ([enc isEqualToString: @"chunked"] == YES)    
 
450
            {
 
451
              complete = NO;    // Read chunked body data
 
452
            }
 
453
          else
 
454
            {
 
455
              complete = NO;    // No
 
456
            }
 
457
        }
 
458
      if (complete == YES)
 
459
        {
 
460
          GSMimeHeader  *info;
 
461
          NSString      *val;
 
462
          float         ver;
 
463
 
 
464
          connectionState = idle;
 
465
          [nc removeObserver: self
 
466
                        name: NSFileHandleReadCompletionNotification
 
467
                      object: sock];
 
468
 
 
469
          ver = [[[document headerNamed: @"http"] value] floatValue];
 
470
          val = [[document headerNamed: @"connection"] value];
 
471
          if (ver < 1.1 || (val != nil && [val isEqual: @"close"] == YES))
 
472
            {
 
473
              [sock closeFile];
 
474
              DESTROY(sock);
 
475
            }
 
476
 
 
477
          /*
 
478
           * Retrieve essential keys from document
 
479
           */
 
480
          info = [document headerNamed: @"http"];
 
481
          val = [info objectForKey: NSHTTPPropertyServerHTTPVersionKey];
 
482
          if (val != nil)
 
483
            {
 
484
              [pageInfo setObject: val
 
485
                           forKey: NSHTTPPropertyServerHTTPVersionKey];
 
486
            }
 
487
          val = [info objectForKey: NSHTTPPropertyStatusCodeKey];
 
488
          if (val != nil)
 
489
            {
 
490
              [pageInfo setObject: val forKey: NSHTTPPropertyStatusCodeKey];
 
491
            }
 
492
          val = [info objectForKey: NSHTTPPropertyStatusReasonKey];
 
493
          if (val != nil)
 
494
            {
 
495
              [pageInfo setObject: val forKey: NSHTTPPropertyStatusReasonKey];
 
496
            }
 
497
          /*
 
498
           * Tell superclass that we have successfully loaded the data.
 
499
           */
 
500
          d = [parser data];
 
501
          r = NSMakeRange(bodyPos, [d length] - bodyPos);
 
502
          bodyPos = 0;
 
503
          DESTROY(wData);
 
504
          [self didLoadBytes: [d subdataWithRange: r]
 
505
                loadComplete: YES];
 
506
        }
 
507
      else
 
508
        {
 
509
          /*
 
510
           * Report partial data if possible.
 
511
           */
 
512
          if ([parser isInBody])
 
513
            {
 
514
              d = [parser data];
 
515
              r = NSMakeRange(bodyPos, [d length] - bodyPos);
 
516
              bodyPos = [d length];
 
517
              [self didLoadBytes: [d subdataWithRange: r]
 
518
                    loadComplete: NO];
 
519
            }
 
520
          [sock readInBackgroundAndNotify];
 
521
        }
256
522
    }
257
523
}
258
524
 
264
530
  GSMimeParser          *p = [GSMimeParser new];
265
531
 
266
532
  d = [dict objectForKey: NSFileHandleNotificationDataItem];
267
 
  if (debug == YES) debugRead(d);
 
533
  if (debug == YES) debugRead(self, d);
268
534
 
269
535
  if ([d length] > 0)
270
536
    {
273
539
  [p parse: dat];
274
540
  if ([p isInBody] == YES || [d length] == 0)
275
541
    {
276
 
      NSDictionary      *info;
 
542
      GSMimeHeader      *info;
277
543
      NSString          *val;
278
544
 
279
545
      [p parse: nil];
280
 
      info = [[p document] headerNamed: @"http"];
 
546
      info = [[p mimeDocument] headerNamed: @"http"];
281
547
      val = [info objectForKey: NSHTTPPropertyServerHTTPVersionKey];
282
548
      if (val != nil)
283
549
        [pageInfo setObject: val forKey: NSHTTPPropertyServerHTTPVersionKey];
302
568
 
303
569
- (void) loadInBackground
304
570
{
305
 
  NSNotificationCenter  *nc;
306
 
  NSString              *host = nil;
307
 
  NSString              *port = nil;
308
 
 
309
 
  /*
310
 
   * Don't start a load if one is in progress.
311
 
   */
312
 
  if (connectionState != idle)
313
 
    {
314
 
      NSLog(@"Attempt to load an http handle which is not idle ... ignored");
315
 
      return;
316
 
    }
317
 
 
318
 
  [dat setLength: 0];
319
 
  RELEASE(document);
320
 
  RELEASE(parser);
321
 
  parser = [GSMimeParser new];
322
 
  document = RETAIN([parser document]);
323
 
  [self beginLoadInBackground];
324
 
  if (sock != nil)
325
 
    {
326
 
      [sock closeFile];
327
 
      DESTROY(sock);
328
 
    }
329
 
  contentLength = 0;
330
 
  if ([[request objectForKey: GSHTTPPropertyProxyHostKey] length] == 0)
331
 
    {
332
 
      host = [url host];
333
 
      port = [url scheme];
334
 
      if ([[url scheme] isEqualToString: @"https"])
335
 
        {
336
 
          if (sslClass == 0)
337
 
            {
338
 
              [self backgroundLoadDidFailWithReason:
339
 
                @"https not supported ... needs SSL bundle"];
340
 
              return;
341
 
            }
342
 
          sock = [sslClass
343
 
            fileHandleAsClientInBackgroundAtAddress: host
344
 
                                            service: port
345
 
                                           protocol: @"tcp"];
346
 
        }
347
 
      else
348
 
        {
349
 
          sock = [NSFileHandle 
350
 
            fileHandleAsClientInBackgroundAtAddress: host
351
 
                                            service: port
352
 
                                           protocol: @"tcp"];
353
 
        }
354
 
    }
355
 
  else
356
 
    {
357
 
      if ([[request objectForKey: GSHTTPPropertyProxyPortKey] length] == 0)
358
 
        {
359
 
          [request setObject: @"8080" forKey: GSHTTPPropertyProxyPortKey];
360
 
        }
361
 
      if ([[url scheme] isEqualToString: @"https"])
362
 
        {
363
 
          if (sslClass == 0)
364
 
            {
365
 
              [self backgroundLoadDidFailWithReason:
366
 
                @"https not supported ... needs SSL bundle"];
367
 
              return;
368
 
            }
369
 
          host = [request objectForKey: GSHTTPPropertyProxyHostKey];
370
 
          port = [request objectForKey: GSHTTPPropertyProxyPortKey];
371
 
          sock = [sslClass
372
 
            fileHandleAsClientInBackgroundAtAddress: host 
373
 
                                            service: port
374
 
                                           protocol: @"tcp"];
375
 
        }
376
 
      else
377
 
        {
378
 
          host = [request objectForKey: GSHTTPPropertyProxyHostKey];
379
 
          port = [request objectForKey: GSHTTPPropertyProxyPortKey];
380
 
          sock = [NSFileHandle 
381
 
            fileHandleAsClientInBackgroundAtAddress: host 
382
 
                                            service: port
383
 
                                           protocol: @"tcp"];
384
 
        }
385
 
    }
386
 
  if (sock == nil)
387
 
    {
388
 
      /*
389
 
       * Tell superclass that the load failed - let it do housekeeping.
390
 
       */
391
 
      [self backgroundLoadDidFailWithReason: [NSString stringWithFormat:
392
 
        @"Unable to connect to %@:%@", host, port]];
393
 
      return;
394
 
    }
395
 
  RETAIN(sock);
396
 
  nc = [NSNotificationCenter defaultCenter];
397
 
  [nc addObserver: self
398
 
         selector: @selector(bgdConnect:)
399
 
             name: GSFileHandleConnectCompletionNotification
400
 
           object: sock];
401
 
  connectionState = connecting;
 
571
  [self _tryLoadInBackground: nil];
402
572
}
403
573
 
404
574
- (void) endLoadInBackground
405
575
{
 
576
  DESTROY(wData);
406
577
  if (connectionState != idle)
407
578
    {
408
579
      NSNotificationCenter      *nc = [NSNotificationCenter defaultCenter];
426
597
- (void) bgdConnect: (NSNotification*)notification
427
598
{
428
599
  NSNotificationCenter  *nc = [NSNotificationCenter defaultCenter];
429
 
  
 
600
 
430
601
  NSDictionary          *userInfo = [notification userInfo];
431
 
  NSEnumerator          *wpEnumerator;
432
602
  NSMutableString       *s;
433
603
  NSString              *e;
434
 
  NSString              *key;
435
 
  NSMutableData         *buf;
436
604
  NSString              *method;
 
605
  NSString              *path;
 
606
 
 
607
  if (debug == YES) NSLog(@"%@", NSStringFromSelector(_cmd));
 
608
 
 
609
  path = [[u path] stringByTrimmingSpaces];
 
610
  if ([path length] == 0)
 
611
    {
 
612
      path = @"/";
 
613
    }
437
614
 
438
615
  /*
439
616
   * See if the connection attempt caused an error.
441
618
  e = [userInfo objectForKey: GSFileHandleNotificationError];
442
619
  if (e != nil)
443
620
    {
444
 
      NSLog(@"Unable to connect to %@:%@ via socket",
445
 
        [sock socketAddress], [sock socketService]);
 
621
      NSLog(@"Unable to connect to %@:%@ via socket ... %@",
 
622
        [sock socketAddress], [sock socketService], e);
446
623
      /*
447
624
       * Tell superclass that the load failed - let it do housekeeping.
448
625
       */
459
636
   * Build HTTP request.
460
637
   */
461
638
 
462
 
  /* 
 
639
  /*
463
640
   * If SSL via proxy, set up tunnel first
464
641
   */
465
 
  if ([[url scheme] isEqualToString: @"https"]
 
642
  if ([[u scheme] isEqualToString: @"https"]
466
643
    && [[request objectForKey: GSHTTPPropertyProxyHostKey] length] > 0)
467
644
    {
468
645
      NSRunLoop         *loop = [NSRunLoop currentRunLoop];
472
649
      NSData            *buf;
473
650
      NSDate            *when;
474
651
      NSString          *status;
 
652
      NSString          *version;
475
653
 
476
 
      if ([url port] == nil)
 
654
      version = [request objectForKey: NSHTTPPropertyServerHTTPVersionKey];
 
655
      if (version == nil)
 
656
        {
 
657
          version = httpVersion;
 
658
        }
 
659
      if ([u port] == nil)
477
660
        {
478
661
          cmd = [NSString stringWithFormat: @"CONNECT %@:443 HTTP/%@\r\n\r\n",
479
 
            [url host], httpVersion];
 
662
            [u host], version];
480
663
        }
481
664
      else
482
665
        {
483
666
          cmd = [NSString stringWithFormat: @"CONNECT %@:%@ HTTP/%@\r\n\r\n",
484
 
            [url host], [url port], httpVersion];
 
667
            [u host], [u port], version];
485
668
        }
486
 
      
 
669
 
487
670
      /*
488
671
       * Set up default status for if connection is lost.
489
672
       */
499
682
               object: sock];
500
683
 
501
684
      buf = [cmd dataUsingEncoding: NSASCIIStringEncoding];
502
 
      [sock writeInBackgroundAndNotify: buf]; 
503
 
      if (debug == YES) debugWrite(buf);
 
685
      if (debug == YES) debugWrite(self, buf);
 
686
      [sock writeInBackgroundAndNotify: buf];
504
687
 
505
688
      when = [NSDate alloc];
506
689
      while (tunnel == YES)
525
708
          return;
526
709
        }
527
710
    }
528
 
  if ([[url scheme] isEqualToString: @"https"])
 
711
  if ([[u scheme] isEqualToString: @"https"])
529
712
    {
530
713
      /*
531
714
       * If we are an https connection, negotiate secure connection
533
716
      if ([sock sslConnect] == NO)
534
717
        {
535
718
          [self endLoadInBackground];
536
 
          [self backgroundLoadDidFailWithReason: @"Failed to make ssl connect"];
 
719
          [self backgroundLoadDidFailWithReason:
 
720
            @"Failed to make ssl connect"];
537
721
          return;
538
722
        }
539
723
    }
554
738
        }
555
739
    }
556
740
  if ([[request objectForKey: GSHTTPPropertyProxyHostKey] length] > 0
557
 
    && [[url scheme] isEqualToString: @"https"] == NO)
 
741
    && [[u scheme] isEqualToString: @"https"] == NO)
558
742
    {
559
 
      s = [[NSMutableString alloc] initWithFormat: @"%@ http://%@%@", 
560
 
        method, [url host], [url path]];
561
 
      if ([[url query] length] > 0)
562
 
        {
563
 
          [s appendFormat: @"?%@", [url query]];
564
 
        }
565
 
      [s appendFormat: @" HTTP/%@\r\n", httpVersion];
 
743
      if ([u port] == nil)
 
744
        {
 
745
          s = [[NSMutableString alloc] initWithFormat: @"%@ http://%@%@",
 
746
            method, [u host], path];
 
747
        }
 
748
      else
 
749
        {
 
750
          s = [[NSMutableString alloc] initWithFormat: @"%@ http://%@:%@%@",
 
751
            method, [u host], [u port], path];
 
752
        }
566
753
    }
567
754
  else    // no proxy
568
755
    {
569
 
      s = [[NSMutableString alloc] initWithFormat: @"%@ %@", 
570
 
        method, [url path]];
571
 
      if ([[url query] length] > 0)
572
 
        {
573
 
          [s appendFormat: @"?%@", [url query]];
574
 
        }
575
 
      [s appendFormat: @" HTTP/%@\nHost: %@\r\n", httpVersion, [url host]];
576
 
    }
577
 
 
578
 
  if ([wData length] > 0)
579
 
    {
580
 
      [wProperties setObject: [NSString stringWithFormat: @"%d", [wData length]]
581
 
                      forKey: @"content-length"];
582
 
      /*
583
 
       * Assume content type if not specified.
584
 
       */
585
 
      if ([wProperties objectForKey: @"content-type"] == nil)
586
 
        {
587
 
          [wProperties setObject: @"application/x-www-form-urlencoded"
588
 
                          forKey: @"content-type"];
589
 
        }
590
 
    }
591
 
  if ([url user] != nil)
592
 
    {
593
 
      NSString  *auth;
594
 
 
595
 
      if ([[url password] length] > 0)
596
 
        { 
597
 
          auth = [NSString stringWithFormat: @"%@:%@", 
598
 
            [url user], [url password]];
599
 
        }
600
 
      else
601
 
        {
602
 
          auth = [NSString stringWithFormat: @"%@", [url user]];
603
 
        }
604
 
      auth = [NSString stringWithFormat: @"Basic %@",
605
 
        [self encodebase64: auth]];
606
 
      [wProperties setObject: auth
607
 
                      forKey: @"Authorization"];
608
 
    }
609
 
  wpEnumerator = [wProperties keyEnumerator];
610
 
  while ((key = [wpEnumerator nextObject]))
611
 
    {
612
 
      [s appendFormat: @"%@: %@\r\n", key, [wProperties objectForKey: key]];
613
 
    }
614
 
  [wProperties removeAllObjects];
615
 
  [s appendString: @"\n"];
616
 
  buf = [[s dataUsingEncoding: NSASCIIStringEncoding] mutableCopy];
617
 
 
618
 
  /*
619
 
   * Append any data to be sent
620
 
   */
621
 
  if (wData != nil)
622
 
    {
623
 
      [buf appendData: wData];
624
 
      DESTROY(wData);
625
 
    }
626
 
 
627
 
  /*
628
 
   * Send request to server.
629
 
   */
630
 
  [sock writeInBackgroundAndNotify: buf];
631
 
  if (debug == YES) debugWrite(buf);
632
 
  RELEASE(buf);
 
756
      s = [[NSMutableString alloc] initWithFormat: @"%@ %@",
 
757
        method, path];
 
758
    }
 
759
 
 
760
  [self bgdApply: s];
633
761
  RELEASE(s);
634
 
 
635
 
  /*
636
 
   * Watch for write completion.
637
 
   */
638
 
  [nc addObserver: self
639
 
         selector: @selector(bgdWrite:)
640
 
             name: GSFileHandleWriteCompletionNotification
641
 
           object: sock];
642
 
  connectionState = writing;
643
762
}
644
763
 
645
764
- (void) bgdWrite: (NSNotification*)notification
646
765
{
 
766
  NSNotificationCenter  *nc;
647
767
  NSDictionary          *userInfo = [notification userInfo];
648
768
  NSString              *e;
649
 
 
 
769
 
 
770
  if (debug == YES) NSLog(@"%@", NSStringFromSelector(_cmd));
650
771
  e = [userInfo objectForKey: GSFileHandleNotificationError];
651
772
  if (e != nil)
652
773
    {
653
774
      tunnel = NO;
 
775
      if (keepalive == YES)
 
776
        {
 
777
          /*
 
778
           * The write failed ... connection dropped ... and we
 
779
           * are re-using an existing connection (keepalive = YES)
 
780
           * then we may try again with a new connection.
 
781
           */
 
782
          nc = [NSNotificationCenter defaultCenter];
 
783
          [nc removeObserver: self
 
784
                        name: GSFileHandleWriteCompletionNotification
 
785
                      object: sock];
 
786
          [sock closeFile];
 
787
          DESTROY(sock);
 
788
          connectionState = idle;
 
789
          [self _tryLoadInBackground: u];
 
790
          return;
 
791
        }
654
792
      NSLog(@"Failed to write command to socket - %@", e);
655
793
      /*
656
794
       * Tell superclass that the load failed - let it do housekeeping.
661
799
    }
662
800
  else
663
801
    {
664
 
      NSNotificationCenter      *nc;
665
 
 
666
802
      /*
667
803
       * Don't watch for write completions any more.
668
804
       */
683
819
        }
684
820
      else
685
821
        {
 
822
          bodyPos = 0;
686
823
          [nc addObserver: self
687
824
                 selector: @selector(bgdRead:)
688
825
                     name: NSFileHandleReadCompletionNotification
693
830
    }
694
831
}
695
832
 
 
833
/**
 
834
 *  If necessary, this method calls -loadInForeground to send a
 
835
 *  request to the webserver, and get a page back.  It then returns
 
836
 *  the property for the specified key -
 
837
 * <list>
 
838
 *   <item>
 
839
 *     NSHTTPPropertyStatusCodeKey - numeric status code returned
 
840
 *     by the last request.
 
841
 *   </item>
 
842
 *   <item>
 
843
 *     NSHTTPPropertyStatusReasonKey - text describing status of
 
844
 *     the last request
 
845
 *   </item>
 
846
 *   <item>
 
847
 *     NSHTTPPropertyServerHTTPVersionKey - <code>http</code>
 
848
 *     version supported by remote server
 
849
 *   </item>
 
850
 *   <item>
 
851
 *     Other keys are taken to be the names of <code>http</code>
 
852
 *     headers and the corresponding header value (or nil if there
 
853
 *     is none) is returned.
 
854
 *   </item>
 
855
 * </list>
 
856
 */
696
857
- (id) propertyForKey: (NSString*) propertyKey
697
858
{
698
859
  if (document == nil)
715
876
        }
716
877
      else if ([array count] == 1)
717
878
        {
718
 
          result = [[array objectAtIndex: 0] objectForKey: @"BaseValue"];
 
879
          GSMimeHeader  *hdr = [array objectAtIndex: 0];
 
880
 
 
881
          result = [hdr value];
719
882
        }
720
883
      else
721
884
        {
722
885
          NSEnumerator  *enumerator = [array objectEnumerator];
723
 
          NSDictionary  *val;
 
886
          GSMimeHeader  *val;
724
887
 
725
888
          result = [NSMutableArray arrayWithCapacity: [array count]];
726
889
          while ((val = [enumerator nextObject]) != nil)
727
890
            {
728
 
              [result addObject: [val objectForKey: @"BaseValue"]];
 
891
              [result addObject: [val value]];
729
892
            }
730
893
        }
731
894
    }
737
900
  debug = flag;
738
901
}
739
902
 
 
903
- (void) _tryLoadInBackground: (NSURL*)fromURL
 
904
{
 
905
  NSNotificationCenter  *nc;
 
906
  NSString              *host = nil;
 
907
  NSString              *port = nil;
 
908
  NSString              *s;
 
909
 
 
910
  /*
 
911
   * Don't start a load if one is in progress.
 
912
   */
 
913
  if (connectionState != idle)
 
914
    {
 
915
      NSLog(@"Attempt to load an http handle which is not idle ... ignored");
 
916
      return;
 
917
    }
 
918
 
 
919
  [dat setLength: 0];
 
920
  RELEASE(document);
 
921
  RELEASE(parser);
 
922
  parser = [GSMimeParser new];
 
923
  document = RETAIN([parser mimeDocument]);
 
924
 
 
925
  /*
 
926
   * First time round, fromURL is nil, so we use the url ivar and
 
927
   * we notify that the load is begining.  On retries we get a real
 
928
   * value in fromURL to use.
 
929
   */
 
930
  if (fromURL == nil)
 
931
    {
 
932
      redirects = 0;
 
933
      ASSIGN(u, url);
 
934
      [self beginLoadInBackground];
 
935
    }
 
936
  else
 
937
    {
 
938
      ASSIGN(u, fromURL);
 
939
    }
 
940
 
 
941
  host = [u host];
 
942
  port = (id)[u port];
 
943
  if (port != nil)
 
944
    {
 
945
      port = [NSString stringWithFormat: @"%u", [port intValue]];
 
946
    }
 
947
  else
 
948
    {
 
949
      port = [u scheme];
 
950
    }
 
951
  if ([port isEqualToString: @"https"])
 
952
    {
 
953
      port = @"443";
 
954
    }
 
955
  else if ([port isEqualToString: @"http"])
 
956
    {
 
957
      port = @"80";
 
958
    }
 
959
 
 
960
  if (sock == nil)
 
961
    {
 
962
      keepalive = NO;   // New connection
 
963
      /*
 
964
       * If we have a local address specified,
 
965
       * tell the file handle to bind to it.
 
966
       */
 
967
      s = [request objectForKey: GSHTTPPropertyLocalHostKey];
 
968
      if ([s length] > 0)
 
969
        {
 
970
          s = [NSString stringWithFormat: @"bind-%@", s];
 
971
        }
 
972
      else
 
973
        {
 
974
          s = @"tcp";   // Bind to any.
 
975
        }
 
976
 
 
977
      if ([[request objectForKey: GSHTTPPropertyProxyHostKey] length] == 0)
 
978
        {
 
979
          if ([[u scheme] isEqualToString: @"https"])
 
980
            {
 
981
              NSString  *cert;
 
982
 
 
983
              if (sslClass == 0)
 
984
                {
 
985
                  [self backgroundLoadDidFailWithReason:
 
986
                    @"https not supported ... needs SSL bundle"];
 
987
                  return;
 
988
                }
 
989
              sock = [sslClass fileHandleAsClientInBackgroundAtAddress: host
 
990
                                                               service: port
 
991
                                                              protocol: s];
 
992
              cert = [request objectForKey: GSHTTPPropertyCertificateFileKey];
 
993
              if ([cert length] > 0)
 
994
                {
 
995
                  NSString      *key;
 
996
                  NSString      *pwd;
 
997
 
 
998
                  key = [request objectForKey: GSHTTPPropertyKeyFileKey];
 
999
                  pwd = [request objectForKey: GSHTTPPropertyPasswordKey];
 
1000
                  [sock sslSetCertificate: cert privateKey: key PEMpasswd: pwd];
 
1001
                }
 
1002
            }
 
1003
          else
 
1004
            {
 
1005
              sock = [NSFileHandle fileHandleAsClientInBackgroundAtAddress: host
 
1006
                                                                   service: port
 
1007
                                                                  protocol: s];
 
1008
            }
 
1009
        }
 
1010
      else
 
1011
        {
 
1012
          if ([[request objectForKey: GSHTTPPropertyProxyPortKey] length] == 0)
 
1013
            {
 
1014
              [request setObject: @"8080" forKey: GSHTTPPropertyProxyPortKey];
 
1015
            }
 
1016
          if ([[u scheme] isEqualToString: @"https"])
 
1017
            {
 
1018
              if (sslClass == 0)
 
1019
                {
 
1020
                  [self backgroundLoadDidFailWithReason:
 
1021
                    @"https not supported ... needs SSL bundle"];
 
1022
                  return;
 
1023
                }
 
1024
              host = [request objectForKey: GSHTTPPropertyProxyHostKey];
 
1025
              port = [request objectForKey: GSHTTPPropertyProxyPortKey];
 
1026
              sock = [sslClass fileHandleAsClientInBackgroundAtAddress: host
 
1027
                                                               service: port
 
1028
                                                              protocol: s];
 
1029
            }
 
1030
          else
 
1031
            {
 
1032
              host = [request objectForKey: GSHTTPPropertyProxyHostKey];
 
1033
              port = [request objectForKey: GSHTTPPropertyProxyPortKey];
 
1034
              sock = [NSFileHandle
 
1035
                fileHandleAsClientInBackgroundAtAddress: host
 
1036
                                                service: port
 
1037
                                               protocol: s];
 
1038
            }
 
1039
        }
 
1040
      if (sock == nil)
 
1041
        {
 
1042
          extern int errno;
 
1043
 
 
1044
          /*
 
1045
           * Tell superclass that the load failed - let it do housekeeping.
 
1046
           */
 
1047
          [self backgroundLoadDidFailWithReason: [NSString stringWithFormat:
 
1048
            @"Unable to connect to %@:%@ ... %s",
 
1049
            host, port, GSLastErrorStr(errno)]];
 
1050
          return;
 
1051
        }
 
1052
      RETAIN(sock);
 
1053
      nc = [NSNotificationCenter defaultCenter];
 
1054
      [nc addObserver: self
 
1055
             selector: @selector(bgdConnect:)
 
1056
                 name: GSFileHandleConnectCompletionNotification
 
1057
               object: sock];
 
1058
      connectionState = connecting;
 
1059
    }
 
1060
  else
 
1061
    {
 
1062
      NSString  *method;
 
1063
      NSString  *path;
 
1064
      NSString  *basic;
 
1065
 
 
1066
      keepalive = YES;  // Reusing a connection.
 
1067
      method = [request objectForKey: GSHTTPPropertyMethodKey];
 
1068
      if (method == nil)
 
1069
        {
 
1070
          if ([wData length] > 0)
 
1071
            {
 
1072
              method = @"POST";
 
1073
            }
 
1074
          else
 
1075
            {
 
1076
              method = @"GET";
 
1077
            }
 
1078
        }
 
1079
      path = [[u path] stringByTrimmingSpaces];
 
1080
      if ([path length] == 0)
 
1081
        {
 
1082
          path = @"/";
 
1083
        }
 
1084
      basic = [NSString stringWithFormat: @"%@ %@", method, path];
 
1085
      [self bgdApply: basic];
 
1086
    }
 
1087
}
 
1088
 
 
1089
/**
 
1090
 * Writes the specified data as the body of an <code>http</code>
 
1091
 * or <code>https</code> request to the web server.
 
1092
 * Returns YES on success,
 
1093
 * NO on failure.  By default, this method performs a POST operation.
 
1094
 * On completion, the resource data for this handle is set to the
 
1095
 * page returned by the request.
 
1096
 */
740
1097
- (BOOL) writeData: (NSData*)d
741
1098
{
742
1099
  ASSIGN(wData, d);
743
1100
  return YES;
744
1101
}
745
1102
 
 
1103
/**
 
1104
 * Sets a property to be used in the next request made by this handle.
 
1105
 * The property is set as a header in the next request, unless it is
 
1106
 * one of the following -
 
1107
 * <list>
 
1108
 *   <item>
 
1109
 *     GSHTTPPropertyBodyKey - set an NSData item to be sent to
 
1110
 *     the server as the body of the request.
 
1111
 *   </item>
 
1112
 *   <item>
 
1113
 *     GSHTTPPropertyMethodKey - override the default method of
 
1114
 *     the request (eg. &quot;PUT&quot;).
 
1115
 *   </item>
 
1116
 *   <item>
 
1117
 *     GSHTTPPropertyProxyHostKey - specify the name or IP address
 
1118
 *     of a host to proxy through.
 
1119
 *   </item>
 
1120
 *   <item>
 
1121
 *     GSHTTPPropertyProxyPortKey - specify the port number to
 
1122
 *     connect to on the proxy host.  If not give, this defaults
 
1123
 *     to 8080 for <code>http</code> and 4430 for <code>https</code>.
 
1124
 *   </item>
 
1125
 *   <item>
 
1126
 *     Any NSHTTPProperty... key
 
1127
 *   </item>
 
1128
 * </list>
 
1129
 */
746
1130
- (BOOL) writeProperty: (id) property forKey: (NSString*) propertyKey
747
1131
{
748
 
  if (propertyKey == nil || [propertyKey isKindOfClass: [NSString class]] == NO)
 
1132
  if (propertyKey == nil
 
1133
    || [propertyKey isKindOfClass: [NSString class]] == NO)
749
1134
    {
750
1135
      [NSException raise: NSInvalidArgumentException
751
1136
                  format: @"%@ with invalid key", NSStringFromSelector(_cmd)];
752
1137
    }
753
 
  if ([propertyKey hasPrefix: @"GSHTTPProperty"])
 
1138
  if ([propertyKey hasPrefix: @"GSHTTPProperty"]
 
1139
    || [propertyKey hasPrefix: @"NSHTTPProperty"])
754
1140
    {
755
1141
      if (property == nil)
756
1142
        {
776
1162
  return YES;
777
1163
}
778
1164
 
779
 
- (NSString*) encodebase64: (NSString*) input
780
 
{
781
 
   char                 *str = calloc([input length], sizeof(char));
782
 
   char                 *sptr = str;
783
 
   NSMutableString      *nstr = [NSMutableString string];
784
 
   int i;
785
 
 
786
 
   strcpy(str, [input cString]);
787
 
 
788
 
   for (i=0; i < [input length]; i += 3) 
789
 
     {
790
 
       [nstr appendFormat: @"%c", emp[*sptr >> 2]];
791
 
       [nstr appendFormat: @"%c", 
792
 
         emp[((*sptr << 4) & 060) | ((sptr[1] >> 4) & 017)]];
793
 
       [nstr appendFormat: @"%c", 
794
 
         emp[((sptr[1] << 2) & 074) | ((sptr[2] >> 6) & 03)]];
795
 
       [nstr appendFormat: @"%c", emp[sptr[2] & 077]];
796
 
       sptr += 3;
797
 
     }
798
 
 
799
 
   /* If len was not a multiple of 3, then we have encoded too
800
 
    * many characters.  Adjust appropriately.
801
 
    */
802
 
   if (i == [input length] + 1) 
803
 
     {
804
 
       /* There were only 2 bytes in that last group */
805
 
       [nstr deleteCharactersInRange: NSMakeRange([nstr length] - 1, 1)];
806
 
     } 
807
 
   else if (i == [input length] + 2) 
808
 
     {
809
 
       /* There was only 1 byte in that last group */
810
 
       [nstr deleteCharactersInRange: NSMakeRange([nstr length] - 2, 2)];
811
 
     }
812
 
   free (str);
813
 
   return (nstr);
814
 
}
815
1165
@end
816
1166