~ubuntu-branches/ubuntu/edgy/sope/edgy

« back to all changes in this revision

Viewing changes to sope-mime/NGMail/NGSmtpClient.m

  • Committer: Bazaar Package Importer
  • Author(s): Sebastian Ley
  • Date: 2005-08-19 16:53:31 UTC
  • Revision ID: james.westby@ubuntu.com-20050819165331-hs683wz1osm708pw
Tags: upstream-4.4rc.2
ImportĀ upstreamĀ versionĀ 4.4rc.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
  Copyright (C) 2000-2005 SKYRIX Software AG
 
3
 
 
4
  This file is part of SOPE.
 
5
 
 
6
  SOPE is free software; you can redistribute it and/or modify it under
 
7
  the terms of the GNU Lesser General Public License as published by the
 
8
  Free Software Foundation; either version 2, or (at your option) any
 
9
  later version.
 
10
 
 
11
  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
 
12
  WARRANTY; without even the implied warranty of MERCHANTABILITY or
 
13
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 
14
  License for more details.
 
15
 
 
16
  You should have received a copy of the GNU Lesser General Public
 
17
  License along with SOPE; see the file COPYING.  If not, write to the
 
18
  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 
19
  02111-1307, USA.
 
20
*/
 
21
 
 
22
#include "NGSmtpClient.h"
 
23
#include "NGSmtpSupport.h"
 
24
#include "NGSmtpReplyCodes.h"
 
25
#include "common.h"
 
26
 
 
27
@interface NGSmtpClient(PrivateMethods)
 
28
- (void)_fetchExtensionInfo;
 
29
@end
 
30
 
 
31
@implementation NGSmtpClient
 
32
 
 
33
+ (int)version {
 
34
  return 2;
 
35
}
 
36
 
 
37
+ (id)smtpClient {
 
38
  NGActiveSocket *s;
 
39
  s = [NGActiveSocket socketInDomain:[NGInternetSocketDomain domain]];
 
40
  return [[[self alloc] initWithSocket:s] autorelease];
 
41
}
 
42
 
 
43
- (id)init {
 
44
  NSLog(@"%@: init not supported, use initWithSocket: ..", self);
 
45
  [self release];
 
46
  return nil;
 
47
}
 
48
 
 
49
- (id)initWithSocket:(id<NGActiveSocket>)_socket { // designated initializer
 
50
  if ((self = [super init])) {
 
51
    self->socket = [_socket retain];
 
52
    NSAssert(self->socket, @"invalid socket parameter");
 
53
 
 
54
    [self setDebuggingEnabled:YES];
 
55
 
 
56
    self->connection = 
 
57
      [(NGBufferedStream *)[NGBufferedStream alloc] initWithSource:_socket];
 
58
    self->text = 
 
59
      [(NGCTextStream *)[NGCTextStream alloc] initWithSource:self->connection];
 
60
 
 
61
    self->state = [self->socket isConnected]
 
62
      ? NGSmtpState_connected
 
63
      : NGSmtpState_unconnected;
 
64
  }
 
65
  return self;
 
66
}
 
67
 
 
68
- (void)dealloc {
 
69
  [self->text       release];
 
70
  [self->connection release];
 
71
  [self->socket     release];
 
72
  [super dealloc];
 
73
}
 
74
 
 
75
// accessors
 
76
 
 
77
- (id<NGActiveSocket>)socket {
 
78
  return self->socket;
 
79
}
 
80
 
 
81
- (NGSmtpState)state {
 
82
  return self->state;
 
83
}
 
84
 
 
85
- (void)setDebuggingEnabled:(BOOL)_flag {
 
86
  self->isDebuggingEnabled = _flag;
 
87
}
 
88
- (BOOL)isDebuggingEnabled {
 
89
  return self->isDebuggingEnabled;
 
90
}
 
91
 
 
92
// connection
 
93
 
 
94
- (BOOL)connectToHost:(id)_host {
 
95
  return [self connectToAddress:
 
96
                 [NGInternetSocketAddress addressWithService:@"smtp"
 
97
                                          onHost:_host protocol:@"tcp"]];
 
98
}
 
99
 
 
100
- (BOOL)connectToAddress:(id<NGSocketAddress>)_address {
 
101
  NGSmtpResponse *greeting = nil;
 
102
 
 
103
  [self requireState:NGSmtpState_unconnected];
 
104
  
 
105
  if (self->isDebuggingEnabled)
 
106
    [NGTextErr writeFormat:@"C: connect to %@\n", _address];
 
107
  
 
108
  [self->socket connectToAddress:_address];
 
109
 
 
110
  // receive greetings from server
 
111
  greeting = [self receiveReply];
 
112
  if (self->isDebuggingEnabled)
 
113
    [NGTextErr writeFormat:@"S: %@\n", greeting];
 
114
 
 
115
  if ([greeting isPositive]) {
 
116
    [self gotoState:NGSmtpState_connected];
 
117
    [self _fetchExtensionInfo];
 
118
 
 
119
    if (self->isDebuggingEnabled) {
 
120
      if (self->extensions.hasPipelining)
 
121
        [NGTextErr writeFormat:@"S: pipelining extension supported.\n"];
 
122
      if (self->extensions.hasSize)
 
123
        [NGTextErr writeFormat:@"S: size extension supported.\n"];
 
124
      if (self->extensions.hasHelp)
 
125
        [NGTextErr writeFormat:@"S: help extension supported.\n"];
 
126
      if (self->extensions.hasExpand)
 
127
        [NGTextErr writeFormat:@"S: expand extension supported.\n"];
 
128
    }
 
129
    return YES;
 
130
  }
 
131
  else {
 
132
    [self disconnect];
 
133
    return NO;
 
134
  }
 
135
}
 
136
 
 
137
- (void)disconnect {
 
138
  [text   flush];
 
139
  [socket close];
 
140
  [self gotoState:NGSmtpState_unconnected];
 
141
}
 
142
 
 
143
// state
 
144
 
 
145
- (void)requireState:(NGSmtpState)_state {
 
146
  if (_state != [self state]) {
 
147
    [NSException raise:@"SMTPException"
 
148
                 format:@"require state %i, now in %i", _state, [self state]];
 
149
  }
 
150
}
 
151
- (void)denyState:(NGSmtpState)_state {
 
152
  if ([self state] == _state) {
 
153
    [NSException raise:@"SMTPException"
 
154
                 format:@"not allowed in state %i", [self state]];
 
155
  }
 
156
}
 
157
 
 
158
- (void)gotoState:(NGSmtpState)_state {
 
159
  self->state = _state;
 
160
}
 
161
 
 
162
- (BOOL)isTransactionInProgress {
 
163
  return (self->state == NGSmtpState_TRANSACTION);
 
164
}
 
165
- (void)abortTransaction {
 
166
  [self gotoState:NGSmtpState_connected];
 
167
}
 
168
 
 
169
// replies
 
170
 
 
171
- (NGSmtpResponse *)receiveReply {
 
172
  NSMutableString *desc = nil;
 
173
  NSString        *line = nil;
 
174
  NGSmtpReplyCode code  = -1;
 
175
 
 
176
  line = [self->text readLineAsString];
 
177
  if ([line length] < 4) {
 
178
    NSLog(@"SMTP: reply has invalid format (%@)", line);
 
179
    return nil;
 
180
  }
 
181
  code = [[line substringToIndex:3] intValue];
 
182
  //if (self->isDebuggingEnabled)
 
183
  //  [NGTextErr writeFormat:@"S: reply with code %i follows ..\n", code];
 
184
 
 
185
  desc = [NSMutableString stringWithCapacity:[line length]];
 
186
  while ([line characterAtIndex:3] == '-') {
 
187
    if ([line length] < 4) {
 
188
      NSLog(@"SMTP: reply has invalid format (text=%@, line=%@)", desc, line);
 
189
      break;
 
190
    }
 
191
    [desc appendString:[line substringFromIndex:4]];
 
192
    [desc appendString:@"\n"];
 
193
    line = [self->text readLineAsString];
 
194
  }
 
195
  if ([line length] >= 4)
 
196
    [desc appendString:[line substringFromIndex:4]];
 
197
 
 
198
  return [NGSmtpResponse responseWithCode:code text:desc];
 
199
}
 
200
 
 
201
// commands
 
202
 
 
203
- (NGSmtpResponse *)sendCommand:(NSString *)_command {
 
204
  if (self->isDebuggingEnabled) {
 
205
    [NGTextOut writeFormat:@"C: %@\n", _command];
 
206
    [NGTextOut flush];
 
207
  }
 
208
  
 
209
  [text writeString:_command];
 
210
  [text writeString:@"\r\n"];
 
211
  [text flush];
 
212
  return [self receiveReply];
 
213
}
 
214
- (NGSmtpResponse *)sendCommand:(NSString *)_command argument:(NSString *)_argument {
 
215
  if (self->isDebuggingEnabled) {
 
216
    [NGTextOut writeFormat:@"C: %@ %@\n", _command, _argument];
 
217
    [NGTextOut flush];
 
218
  }
 
219
  
 
220
  [text writeString:_command];
 
221
  [text writeString:@" "];
 
222
  [text writeString:_argument];
 
223
  [text writeString:@"\r\n"];
 
224
  [text flush];
 
225
  return [self receiveReply];
 
226
}
 
227
 
 
228
// service commands
 
229
 
 
230
- (void)_fetchExtensionInfo {
 
231
  NGSmtpResponse *reply = nil;
 
232
  NSString       *hostName = nil;
 
233
  
 
234
  hostName = [(NGInternetSocketAddress *)[self->socket localAddress] hostName];
 
235
 
 
236
  reply = [self sendCommand:@"EHLO" argument:hostName];
 
237
  if ([reply code] == NGSmtpActionCompleted) {
 
238
    NSEnumerator *lines = [[[reply text] componentsSeparatedByString:@"\n"]
 
239
                                   objectEnumerator];
 
240
    NSString     *line = nil;
 
241
 
 
242
    if (self->isDebuggingEnabled) [NGTextErr writeFormat:@"S: %@\n", reply];
 
243
 
 
244
    while ((line = [lines nextObject])) {
 
245
      if ([line hasPrefix:@"EXPN"])
 
246
        self->extensions.hasExpand = YES;
 
247
      else if ([line hasPrefix:@"SIZE"])
 
248
        self->extensions.hasSize = YES;
 
249
      else if ([line hasPrefix:@"PIPELINING"])
 
250
        self->extensions.hasPipelining = YES;
 
251
      else if ([line hasPrefix:@"HELP"])
 
252
        self->extensions.hasHelp = YES;
 
253
    }
 
254
    lines = nil;
 
255
  }
 
256
  else {
 
257
    if (self->isDebuggingEnabled) {
 
258
      [NGTextErr writeFormat:@"S: %@\n", reply];
 
259
      [NGTextErr writeFormat:@" .. could not get extension info.\n"];
 
260
    }
 
261
  }
 
262
}
 
263
 
 
264
- (BOOL)_simpleServiceCommand:(NSString *)_command expectCode:(NGSmtpReplyCode)_code {
 
265
  NGSmtpResponse *reply = nil;
 
266
 
 
267
  [self denyState:NGSmtpState_unconnected];
 
268
 
 
269
  reply = [self sendCommand:_command];
 
270
  if (self->isDebuggingEnabled) [NGTextErr writeFormat:@"S: %@\n", reply];
 
271
  if ([reply isPositive]) {
 
272
    if ([reply code] != _code)
 
273
      NSLog(@"SMTP(%@): expected reply code %i, got code %i ..",
 
274
            _command, _code, [reply code]);
 
275
    return YES;
 
276
  }
 
277
  return NO;
 
278
}
 
279
 
 
280
- (BOOL)quit {
 
281
  NGSmtpResponse *reply = nil;
 
282
 
 
283
  [self requireState:NGSmtpState_connected];
 
284
  
 
285
  reply = [self sendCommand:@"QUIT"];
 
286
  if (self->isDebuggingEnabled) [NGTextErr writeFormat:@"S: %@\n", reply];
 
287
  if ([reply isPositive]) {
 
288
    unsigned int waitBytes = 0;
 
289
    
 
290
    if ([reply code] == NGSmtpServiceClosingChannel) {
 
291
      // wait for connection close ..
 
292
      while ([self->connection readByte] != -1)
 
293
        waitBytes++;
 
294
    }
 
295
    else
 
296
      NSLog(@"SMTP(QUIT): unexpected reply code (%i), disconnecting ..", [reply code]);
 
297
    return YES;
 
298
  }
 
299
  return NO;
 
300
}
 
301
 
 
302
- (BOOL)helloWithHostname:(NSString *)_host {
 
303
  NGSmtpResponse *reply = nil;
 
304
 
 
305
  [self denyState:NGSmtpState_unconnected];
 
306
  
 
307
  reply = [self sendCommand:@"HELO" argument:_host];
 
308
  if (self->isDebuggingEnabled) [NGTextErr writeFormat:@"S: %@\n", reply];
 
309
  if ([reply isPositive]) {
 
310
    if ([reply code] != NGSmtpActionCompleted) {
 
311
      NSLog(@"SMTP(HELO): expected reply code %i, got code %i ..",
 
312
            NGSmtpActionCompleted, [reply code]);
 
313
    }
 
314
    return YES;
 
315
  }
 
316
  return NO;
 
317
}
 
318
- (BOOL)hello {
 
319
  NSString *hostName = nil;
 
320
  hostName = [(NGInternetSocketAddress *)[self->socket localAddress] hostName];
 
321
  return [self helloWithHostname:hostName];
 
322
}
 
323
 
 
324
- (BOOL)noop {
 
325
  return [self _simpleServiceCommand:@"NOOP" expectCode:NGSmtpActionCompleted];
 
326
}
 
327
 
 
328
- (BOOL)reset {
 
329
  if ([self _simpleServiceCommand:@"RSET" expectCode:NGSmtpActionCompleted]) {
 
330
    if ([self isTransactionInProgress])
 
331
      [self abortTransaction];
 
332
    return YES;
 
333
  }
 
334
  else
 
335
    return NO;
 
336
}
 
337
 
 
338
- (NSString *)help {
 
339
  NGSmtpResponse *reply = nil;
 
340
 
 
341
  [self denyState:NGSmtpState_unconnected];
 
342
  
 
343
  reply = [self sendCommand:@"HELP"];
 
344
  if (self->isDebuggingEnabled) [NGTextErr writeFormat:@"S: %@\n", reply];
 
345
  if ([reply isPositive]) {
 
346
    if ([reply code] != NGSmtpHelpMessage) {
 
347
      NSLog(@"SMTP(HELP): expected reply code %i, got code %i ..",
 
348
            NGSmtpHelpMessage, [reply code]);
 
349
    }
 
350
    return [reply text];
 
351
  }
 
352
  return nil;
 
353
}
 
354
- (NSString *)helpForTopic:(NSString *)_topic {
 
355
  NGSmtpResponse *reply = nil;
 
356
  [self denyState:NGSmtpState_unconnected];
 
357
  
 
358
  reply = [self sendCommand:@"HELP" argument:_topic];
 
359
  if (self->isDebuggingEnabled) [NGTextErr writeFormat:@"S: %@\n", reply];
 
360
  if ([reply isPositive]) {
 
361
    if ([reply code] != NGSmtpHelpMessage) {
 
362
      NSLog(@"SMTP(HELP): expected reply code %i, got code %i ..",
 
363
            NGSmtpHelpMessage, [reply code]);
 
364
    }
 
365
    return [reply text];
 
366
  }
 
367
  return nil;
 
368
}
 
369
 
 
370
- (BOOL)verifyAddress:(id)_address {
 
371
  NGSmtpResponse *reply = nil;
 
372
  [self denyState:NGSmtpState_unconnected];
 
373
 
 
374
  reply = [self sendCommand:@"VRFY" argument:[_address stringValue]];
 
375
  if (self->isDebuggingEnabled) [NGTextErr writeFormat:@"S: %@\n", reply];
 
376
  if ([reply isPositive]) {
 
377
    if ([reply code] != NGSmtpActionCompleted) {
 
378
      NSLog(@"SMTP(VRFY): expected reply code %i, got code %i ..",
 
379
            NGSmtpActionCompleted, [reply code]);
 
380
    }
 
381
    return YES;
 
382
  }
 
383
  else if ([reply code] == NGSmtpMailboxNotFound) {
 
384
    return NO;
 
385
  }
 
386
  else {
 
387
    NSLog(@"SMTP(VRFY): expected positive or 550 reply code, got code %i ..", [reply code]);
 
388
    return NO;
 
389
  }
 
390
}
 
391
 
 
392
// transaction commands
 
393
 
 
394
- (BOOL)mailFrom:(id)_sender {
 
395
  NGSmtpResponse *reply  = nil;
 
396
  NSString       *sender = nil;
 
397
  [self requireState:NGSmtpState_connected];
 
398
 
 
399
  sender = [@"FROM:" stringByAppendingString:[_sender stringValue]];
 
400
  reply  = [self sendCommand:@"MAIL" argument:sender];
 
401
  if ([reply isPositive]) {
 
402
    if ([reply code] != NGSmtpActionCompleted) {
 
403
      NSLog(@"SMTP(MAIL FROM): expected reply code %i, got code %i ..",
 
404
            NGSmtpActionCompleted, [reply code]);
 
405
    }
 
406
    return YES;
 
407
  }
 
408
  return NO;
 
409
}
 
410
 
 
411
- (BOOL)recipientTo:(id)_receiver {
 
412
  NGSmtpResponse *reply = nil;
 
413
  NSString       *rcpt  = nil;
 
414
  
 
415
  [self requireState:NGSmtpState_TRANSACTION];
 
416
  
 
417
  rcpt  = [@"TO:" stringByAppendingString:[_receiver stringValue]];
 
418
  reply = [self sendCommand:@"RCPT" argument:rcpt];
 
419
  if ([reply isPositive]) {
 
420
    if ([reply code] != NGSmtpActionCompleted) {
 
421
      NSLog(@"SMTP(RCPT TO): expected reply code %i, got code %i ..",
 
422
            NGSmtpActionCompleted, [reply code]);
 
423
    }
 
424
    return YES;
 
425
  }
 
426
  return NO;
 
427
}
 
428
 
 
429
- (BOOL)sendData:(NSData *)_data {
 
430
  NGSmtpResponse *reply = nil;
 
431
  
 
432
  [self requireState:NGSmtpState_TRANSACTION];
 
433
 
 
434
  reply = [self sendCommand:@"DATA"];
 
435
  if (self->isDebuggingEnabled) [NGTextErr writeFormat:@"S: %@\n", reply];
 
436
  if (([reply code] >= 300) && ([reply code] < 400)) {
 
437
    if ([reply code] != NGSmtpStartMailInput) {
 
438
      NSLog(@"SMTP(DATA): expected reply code %i, got code %i ..",
 
439
            NGSmtpStartMailInput, [reply code]);
 
440
    }
 
441
    [self->text flush];
 
442
 
 
443
    if (self->isDebuggingEnabled)
 
444
      [NGTextErr writeFormat:@"C: data(%i bytes) ..\n", [_data bytes]];
 
445
    
 
446
    [self->connection safeWriteBytes:[_data bytes] count:[_data length]];
 
447
    [self->connection safeWriteBytes:".\r\n" count:3];
 
448
    [self->connection flush];
 
449
 
 
450
    reply = [self receiveReply];
 
451
    if (self->isDebuggingEnabled) [NGTextErr writeFormat:@"S: %@\n", reply];
 
452
    if ([reply isPositive]) {
 
453
      return YES;
 
454
    }
 
455
    else {
 
456
      NSLog(@"SMTP(DATA): mail input failed, got code %i ..", [reply code]);
 
457
    }
 
458
  }
 
459
  return NO;
 
460
}
 
461
 
 
462
// description
 
463
 
 
464
- (NSString *)description {
 
465
  return [NSString stringWithFormat:@"<SMTP-Client[0x%08X]: socket=%@>",
 
466
                     (unsigned)self, [self socket]];
 
467
}
 
468
 
 
469
@end