]> git.saurik.com Git - apple/security.git/blob - SharedWebCredentialViewService/SWCViewController.m
Security-58286.51.6.tar.gz
[apple/security.git] / SharedWebCredentialViewService / SWCViewController.m
1 //
2 // SWCViewController.m
3 // SharedWebCredentialViewService
4 //
5 // Copyright (c) 2014 Apple Inc. All Rights Reserved.
6 //
7
8 #import <Foundation/NSXPCConnection.h>
9 #import "SWCViewController.h"
10 #import <UIKit/UIViewController_Private.h>
11 #import <UIKit/UIFont_Private.h>
12
13 #import <UIKit/UIAlertController_Private.h>
14 #import <UIKit/UITableViewCell_Private.h>
15
16 #include <bsm/libbsm.h>
17 #include <ipc/securityd_client.h>
18
19 #include "SharedWebCredential/swcagent_client.h"
20
21 #import "SWCViewController.h"
22
23 const NSString* SWC_PASSWORD_KEY = @"spwd";
24 const NSString* SWC_ACCOUNT_KEY = @"acct";
25 const NSString* SWC_SERVER_KEY = @"srvr";
26
27 //
28 // SWCDictionaryAdditions
29 //
30
31 @interface NSDictionary (SWCDictionaryAdditions)
32 - (NSComparisonResult) compareCredentialDictionaryAscending:(NSDictionary *)other;
33 @end
34
35 @implementation NSDictionary (SWCDictionaryAdditions)
36 - (NSComparisonResult)compareCredentialDictionaryAscending:(NSDictionary *)other
37 {
38 NSComparisonResult result;
39 NSString *str1 = [self objectForKey:SWC_ACCOUNT_KEY], *str2 = [other objectForKey:SWC_ACCOUNT_KEY];
40 if (!str1) str1 = @"";
41 if (!str2) str2 = @"";
42
43 // primary sort by account name
44 result = [str1 localizedCaseInsensitiveCompare:str2];
45 if (result == NSOrderedSame) {
46 // secondary sort by domain name
47 NSString *str3 = [self objectForKey:SWC_SERVER_KEY], *str4 = [other objectForKey:SWC_SERVER_KEY];
48 if (!str3) str3 = @"";
49 if (!str4) str4 = @"";
50
51 result = [str3 localizedCaseInsensitiveCompare:str4];
52 }
53
54 return result;
55 }
56 @end
57
58
59 //
60 // SWCItemCell
61 //
62 @interface SWCItemCell : UITableViewCell
63 {
64 NSDictionary *_dict;
65 BOOL _isTicked;
66 UIView *_bottomLine;
67 UIView *_bottomLineSelected;
68 UIView *_topLine;
69 UIView *_topLineSelected;
70 BOOL _showSeparator;
71 BOOL _showTopSeparator;
72 }
73
74 - (id)initWithDictionary:(NSDictionary *)dict;
75 @property (nonatomic, readonly) id userInfo;
76 @property (nonatomic, assign) BOOL showSeparator;
77 @end
78
79 @implementation SWCItemCell
80
81 @synthesize showSeparator = _showSeparator;
82
83 - (id)initWithDictionary:(NSDictionary *)dict
84 {
85 if ((self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil]))
86 {
87 _dict = dict;
88
89 self.selectionStyle = UITableViewCellSelectionStyleNone;
90
91 self.backgroundColor = [UIColor colorWithWhite:0.1 alpha:0.005];
92
93 self.textLabel.textColor = [UIColor blackColor];
94 self.textLabel.textAlignment = NSTextAlignmentLeft;
95 self.textLabel.adjustsFontSizeToFitWidth = YES;
96 self.textLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
97
98 NSString *title = [dict objectForKey:SWC_ACCOUNT_KEY];
99 self.textLabel.text = title ? title : NSLocalizedString(@"--", nil);
100
101 self.detailTextLabel.textColor = [UIColor darkGrayColor];
102 self.detailTextLabel.textAlignment = NSTextAlignmentLeft;
103 self.detailTextLabel.adjustsFontSizeToFitWidth = YES;
104 self.detailTextLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
105
106 NSString *subtitle = [dict objectForKey:SWC_SERVER_KEY];
107 self.detailTextLabel.text = subtitle ? subtitle : NSLocalizedString(@"--", nil);
108
109 self.backgroundView = [[UIView alloc] init];
110 self.backgroundView.backgroundColor = self.backgroundColor;
111
112 self.imageView.image = [self _checkmarkImage: NO];
113 self.imageView.hidden = YES;
114
115 }
116
117 return self;
118 }
119
120 - (void)setTicked: (BOOL) selected
121 {
122 _isTicked = selected;
123 }
124
125 - (void)layoutSubviews
126 {
127
128 if (_bottomLine) {
129 CGFloat scale = [[UIScreen mainScreen] scale];
130 [_bottomLine setFrame:CGRectMake(0, self.frame.size.height - (1 / scale), self.frame.size.width, 1 / scale)];
131 }
132
133 if (_bottomLineSelected) {
134 CGFloat scale = [[UIScreen mainScreen] scale];
135 [_bottomLineSelected setFrame:CGRectMake(0, self.frame.size.height - (1 / scale), self.frame.size.width, 1 / scale)];
136 }
137
138 if (_topLine) {
139 CGFloat scale = [[UIScreen mainScreen] scale];
140 [_topLine setFrame:CGRectMake(0, 0, self.frame.size.width, 1 / scale)];
141 }
142
143 if (_topLineSelected) {
144 CGFloat scale = [[UIScreen mainScreen] scale];
145 [_topLineSelected setFrame:CGRectMake(0, 0, self.frame.size.width, 1 / scale)];
146 }
147
148 if (_isTicked)
149 {
150 self.imageView.hidden = NO;
151 } else {
152 self.imageView.hidden = YES;
153 }
154
155 [super layoutSubviews];
156
157 }
158
159 - (void)setShowSeparator:(BOOL)showSeparator {
160 if (_showSeparator != showSeparator) {
161 _showSeparator = showSeparator;
162
163 if (_showSeparator) {
164 if (!_bottomLine) {
165 CGRect rectZero = CGRectMake(0, 0, 0, 0);
166 _bottomLine = [[UIView alloc] initWithFrame:rectZero];
167 _bottomLine.backgroundColor = [UIColor colorWithWhite:.5 alpha:.5];
168 [self.backgroundView addSubview:_bottomLine];
169 }
170 if (!_bottomLineSelected) {
171 CGRect rectZero = CGRectMake(0, 0, 0, 0);
172 _bottomLineSelected = [[UIView alloc] initWithFrame:rectZero];
173 _bottomLineSelected.backgroundColor = [UIColor colorWithWhite:.5 alpha:.5];
174 [self.selectedBackgroundView addSubview: _bottomLineSelected];
175 }
176
177 } else {
178 if (_bottomLine) {
179 [_bottomLine removeFromSuperview];
180 _bottomLine = nil;
181 }
182 if (_bottomLineSelected) {
183 [_bottomLineSelected removeFromSuperview];
184 _bottomLineSelected = nil;
185 }
186 }
187 }
188 }
189
190 - (void)setShowTopSeparator:(BOOL)showTopSeparator {
191 if (_showTopSeparator != showTopSeparator) {
192 _showTopSeparator = showTopSeparator;
193
194 if (_showTopSeparator) {
195 if (!_topLine) {
196 CGRect rectZero = CGRectMake(0, 0, 0, 0);
197 _topLine = [[UIView alloc] initWithFrame:rectZero];
198 _topLine.backgroundColor = [UIColor colorWithWhite:.5 alpha:.5];
199 [self.backgroundView addSubview:_topLine];
200 }
201 if (!_topLineSelected) {
202 CGRect rectZero = CGRectMake(0, 0, 0, 0);
203 _topLineSelected = [[UIView alloc] initWithFrame:rectZero];
204 _topLineSelected.backgroundColor = [UIColor colorWithWhite:.5 alpha:.5];
205 [self.selectedBackgroundView addSubview: _topLineSelected];
206 }
207
208 } else {
209 if (_topLine) {
210 [_topLine removeFromSuperview];
211 _topLine = nil;
212 }
213 if (_topLineSelected) {
214 [_topLineSelected removeFromSuperview];
215 _topLineSelected = nil;
216 }
217 }
218 }
219 }
220
221 @end
222
223
224 //
225 // SWCViewController
226 //
227
228 @interface SWCViewController ()
229 {
230 NSMutableArray *_credentials; // array of NSDictionary
231 UILabel *_topLabel;
232 UILabel *_middleLabel;
233 UITableView *_table;
234 NSDictionary *_selectedDict;
235 NSIndexPath *_selectedCell;
236 }
237
238 @end
239
240 @implementation SWCViewController
241
242 - (NSDictionary *)selectedItem
243 {
244 return _selectedDict;
245 }
246
247 - (void)setCredentials:(NSArray *)inArray
248 {
249 NSMutableArray *credentials = [[NSMutableArray alloc] initWithArray:inArray];
250 [credentials sortUsingSelector:@selector(compareCredentialDictionaryAscending:)];
251 _credentials = credentials;
252 if (_table)
253 [_table reloadData];
254 }
255
256 - (void)_enableTable
257 {
258 [_table setUserInteractionEnabled:YES];
259 }
260
261 - (UITableView *)tableView
262 {
263 if (_table == nil) {
264 _table = [[UITableView alloc] init];
265 [_table setTranslatesAutoresizingMaskIntoConstraints:NO];
266 [_table setAutoresizingMask:UIViewAutoresizingNone];
267 [_table setBackgroundColor:[UIColor clearColor]];
268 [_table setSeparatorStyle:UITableViewCellSeparatorStyleNone];
269 }
270 [_table sizeToFit];
271
272 return (UITableView *)_table;
273 }
274
275 -(void)loadView
276 {
277
278 UIView* view = [[UIView alloc] init];
279
280 UITableView* table = [self tableView];
281 [table setDelegate: self];
282 [table setDataSource: self];
283
284 [view addSubview: table];
285
286 CFErrorRef error = NULL;
287 audit_token_t auditToken = {};
288 memset(&auditToken, 0, sizeof(auditToken));
289 CFArrayRef credentialList = swca_copy_pairs(swca_copy_pairs_request_id, &auditToken, &error);
290 if (error) {
291 NSLog(@"Unable to get accounts: %@", [(__bridge NSError*)error localizedDescription]);
292 }
293
294 [self setCredentials:(__bridge NSArray*)credentialList];
295 if (credentialList) {
296 CFRelease(credentialList);
297 }
298
299 table.frame = CGRectMake(0.0, 0.0, 300.0, 45.0);
300 [table layoutIfNeeded]; // so autolayout can do its thing and then we can measure a cell
301 UITableViewCell* cell = table.visibleCells.firstObject;
302 CGFloat heightForOneRow = cell ? CGRectGetHeight(cell.frame) : 45.0;
303
304 NSDictionary* views = NSDictionaryOfVariableBindings(table);
305
306 if ([_credentials count] > 2)
307 {
308
309 CGFloat manyCredentialsHeight = CGFloatMax((heightForOneRow * 2) + 30.0, 120.0);
310 NSDictionary *metrics = @{@"height":@(manyCredentialsHeight)};
311 [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[table]-|" options:0 metrics:metrics views:views]];
312 [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[table(height)]-|" options:0 metrics:metrics views:views]];
313
314 CGFloat scale = [[UIScreen mainScreen] scale];
315 table.layer.borderWidth = 1.0 / scale;
316 table.layer.borderColor = [UIColor colorWithWhite:.5 alpha:.5].CGColor;
317
318 [self setPreferredContentSize:CGSizeMake(0, manyCredentialsHeight + 20.0)];
319
320 } else if ([_credentials count] == 2) {
321
322 NSDictionary *metrics = @{@"height":@(heightForOneRow * 2)};
323 [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[table]|" options:0 metrics:metrics views:views]];
324 [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[table(height)]-|" options:0 metrics:metrics views:views]];
325
326 [self setPreferredContentSize:CGSizeMake(0, heightForOneRow * 2)];
327 [table setScrollEnabled: NO];
328
329 } else { // [_credentials count] == 1
330
331 NSDictionary *metrics = @{@"height":@(heightForOneRow)};
332 [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[table]|" options:0 metrics:metrics views:views]];
333 [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[table(height)]|" options:0 metrics:metrics views:views]];
334
335 [self setPreferredContentSize:CGSizeMake(0, heightForOneRow)];
336 [table setScrollEnabled: NO];
337
338 }
339
340 [self setView:view];
341 }
342
343 -(void)viewWillAppear:(BOOL)animated
344 {
345
346 // Select the first cell by default
347
348 NSDictionary *dict = [_credentials objectAtIndex: 0];
349 _selectedDict = dict;
350 _selectedCell = [NSIndexPath indexPathForItem:0 inSection: 0];
351 SWCItemCell *cell = (SWCItemCell *)[_table cellForRowAtIndexPath: _selectedCell];
352 [cell setTicked: YES];
353 [cell layoutSubviews];
354 [_table selectRowAtIndexPath: _selectedCell animated: NO scrollPosition: UITableViewScrollPositionTop];
355
356 CFErrorRef error = NULL;
357 audit_token_t auditToken = {};
358 memset(&auditToken, 0, sizeof(auditToken));
359 bool result = swca_set_selection(swca_set_selection_request_id,
360 &auditToken, (__bridge CFDictionaryRef)dict, &error);
361 if (!result) {
362 NSLog(@"Unable to select item: %@", [(__bridge NSError*)error localizedDescription]);
363 }
364
365 [super viewWillAppear:animated];
366 }
367
368
369 //
370 // UITableView delegate methods
371 //
372
373 - (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
374 {
375 return [_credentials count];
376 }
377
378 - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
379 {
380 NSUInteger row = indexPath.row;
381
382 NSDictionary *dict = [_credentials objectAtIndex:row];
383 _selectedDict = dict;
384
385 CFErrorRef error = NULL;
386 audit_token_t auditToken = {};
387 memset(&auditToken, 0, sizeof(auditToken));
388 bool result = swca_set_selection(swca_set_selection_request_id,
389 &auditToken, (__bridge CFDictionaryRef)dict, &error);
390 if (!result) {
391 NSLog(@"Unable to select item: %@", [(__bridge NSError*)error localizedDescription]);
392 }
393
394 _selectedCell = indexPath;
395 SWCItemCell *cell = (SWCItemCell *)[tableView cellForRowAtIndexPath: indexPath];
396 [cell setTicked: YES];
397 [cell layoutSubviews];
398 }
399
400 - (void) tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
401 {
402 SWCItemCell *cell = (SWCItemCell *)[tableView cellForRowAtIndexPath: indexPath];
403 [cell setTicked: NO];
404 [cell layoutSubviews];
405
406 }
407
408 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
409 {
410
411 NSDictionary *dict = [_credentials objectAtIndex:[indexPath row]];
412 SWCItemCell *cell = [[SWCItemCell alloc] initWithDictionary:dict];
413
414 // show separator on top cell if there's only or or two items
415 if ([_credentials count] <= 2) {
416 cell.showTopSeparator = YES;
417 } else {
418 cell.showSeparator = YES;
419
420 if (indexPath.row == 0)
421 {
422 cell.showTopSeparator = YES;
423 }
424
425 }
426
427 if (_selectedCell == indexPath)
428 {
429 [cell setTicked: YES];
430 } else {
431 [cell setTicked: NO];
432 }
433
434 return cell;
435 }
436
437
438 @end