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