]> err.no Git - sope/blob - sope-mime/NGImap4/NGImap4ConnectionManager.m
synced with latest additions and bumped framework versions
[sope] / sope-mime / NGImap4 / NGImap4ConnectionManager.m
1 /*
2   Copyright (C) 2004-2005 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
6   OGo 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   OGo 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 OGo; 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 "NGImap4ConnectionManager.h"
23 #include "NGImap4Connection.h"
24 #include "NGImap4Client.h"
25 #include "imCommon.h"
26
27 @implementation NGImap4ConnectionManager
28
29 static BOOL           debugOn    = NO;
30 static BOOL           debugCache = NO;
31 static BOOL           poolingOff = NO;
32 static NSTimeInterval PoolScanInterval = 5 * 60 /* every five minutes */;
33
34 + (void)initialize {
35   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
36   
37   debugOn      = [ud boolForKey:@"NGImap4EnableIMAP4Debug"];
38   debugCache   = [ud boolForKey:@"NGImap4EnableIMAP4CacheDebug"];
39   poolingOff   = [ud boolForKey:@"NGImap4DisableIMAP4Pooling"];
40   
41   if (debugOn)    NSLog(@"Note: NGImap4EnableIMAP4Debug is enabled!");
42   if (poolingOff) NSLog(@"WARNING: IMAP4 connection pooling is disabled!");
43 }
44
45 + (id)defaultConnectionManager {
46   static NGImap4ConnectionManager *manager = nil; // THREAD
47   if (manager == nil) 
48     manager = [[self alloc] init];
49   return manager;
50 }
51
52 - (id)init {
53   if ((self = [super init])) {
54     if (!poolingOff) {
55       self->urlToEntry = [[NSMutableDictionary alloc] initWithCapacity:256];
56     }
57     
58     self->gcTimer = [[NSTimer scheduledTimerWithTimeInterval:
59                                 PoolScanInterval
60                               target:self selector:@selector(_garbageCollect:)
61                               userInfo:nil repeats:YES] retain];
62   }
63   return self;
64 }
65
66 - (void)dealloc {
67   if (self->gcTimer) [self->gcTimer invalidate];
68   [self->urlToEntry release];
69   [self->gcTimer    release];
70   [super dealloc];
71 }
72
73 /* cache */
74
75 - (id)cacheKeyForURL:(NSURL *)_url {
76   // protocol, user, host, port
77   return [NSString stringWithFormat:@"%@://%@@%@:%@",
78                    [_url scheme], [_url user], [_url host], [_url port]];
79 }
80
81 - (NGImap4Connection *)entryForURL:(NSURL *)_url {
82   if (_url == nil)
83     return nil;
84   
85   return [self->urlToEntry objectForKey:[self cacheKeyForURL:_url]];
86 }
87 - (void)cacheEntry:(NGImap4Connection *)_entry forURL:(NSURL *)_url {
88   if (_entry == nil) _entry = (id)[NSNull null];
89   [self->urlToEntry setObject:_entry forKey:[self cacheKeyForURL:_url]];
90 }
91
92 - (void)_garbageCollect:(NSTimer *)_timer {
93   // TODO: scan for old IMAP4 channels
94   [self debugWithFormat:@"should collect IMAP4 channels (%d active)",
95           [self->urlToEntry count]];
96 }
97
98 - (NGImap4Connection *)connectionForURL:(NSURL *)_url password:(NSString *)_p {
99   /*
100     Three cases:
101     a) not yet connected             => create new entry and connect
102     b) connected, correct password   => return cached entry
103     c) connected, different password => try to recreate entry
104   */
105   NGImap4Connection *entry;
106   NGImap4Client *client;
107
108   /* check cache */
109   
110   if ((entry = [self entryForURL:_url]) != nil) {
111     if ([entry isValidPassword:_p]) {
112       if (debugCache)
113         [self logWithFormat:@"valid password, reusing cache entry ..."];
114       return entry;
115     }
116     
117     /* different password, password could have changed! */
118     if (debugCache)
119       [self logWithFormat:@"different password than cached entry: %@", _url];
120     entry = nil;
121   }
122   else
123     [self debugWithFormat:@"no connection cached yet for url: %@", _url];
124   
125   /* try to login */
126   
127   client = [entry isValidPassword:_p]
128     ? [entry client]
129     : [self imap4ClientForURL:_url password:_p];
130   
131   if (client == nil)
132     return nil;
133   
134   /* sideeffect of -imap4ClientForURL:password: is to create a cache entry */
135   return [self entryForURL:_url];
136 }
137
138 /* client object */
139
140 - (NGImap4Client *)imap4ClientForURL:(NSURL *)_url password:(NSString *)_pwd {
141   // TODO: move to some global IMAP4 connection pool manager
142   NGImap4Connection *entry;
143   NGImap4Client *client;
144   NSDictionary  *result;
145   
146   if (_url == nil)
147     return nil;
148
149   /* check connection pool */
150   
151   if ((entry = [self entryForURL:_url]) != nil) {
152     if ([entry isValidPassword:_pwd]) {
153       [self debugWithFormat:@"reused IMAP4 connection for URL: %@", _url];
154       return [entry client];
155     }
156     
157     /* different password, password could have changed! */
158     entry = nil;
159   }
160   
161   /* setup connection and attempt login */
162   
163   if ((client = [NGImap4Client clientWithURL:_url]) == nil)
164     return nil;
165   
166   result = [client login:[_url user] password:_pwd];
167   if (![[result valueForKey:@"result"] boolValue]) {
168     [self errorWithFormat:
169             @"IMAP4 login failed:\n"
170             @"  host=%@, user=%@, pwd=%s\n"
171             @"  url=%@\n  base=%@\n  base-class=%@)\n"
172             @"  = %@", 
173             [_url host], [_url user], [_pwd length] > 0 ? "yes" : "no", 
174             [_url absoluteString],
175             [_url baseURL],
176             NSStringFromClass([[_url baseURL] class]),
177             client];
178     return nil;
179   }
180   
181   [self debugWithFormat:@"created new IMAP4 connection for URL: %@", _url];
182   
183   /* cache connection in pool */
184   
185   entry = [[NGImap4Connection alloc] initWithClient:client 
186                                            password:_pwd];
187   [self cacheEntry:entry forURL:_url];
188   [entry release]; entry = nil;
189   
190   return client;
191 }
192
193 - (void)flushCachesForURL:(NSURL *)_url {
194   NGImap4Connection *entry;
195   
196   if ((entry = [self entryForURL:_url]) == nil) /* nothing cached */
197     return;
198   
199   [entry flushFolderHierarchyCache];
200   [entry flushMailCaches];
201 }
202
203 /* debugging */
204
205 - (BOOL)isDebuggingEnabled {
206   return debugOn;
207 }
208
209 @end /* NGImap4ConnectionManager */