| 1 | /* Copyright (C) 2008-2009 Peter Speck |
|---|
| 2 | * |
|---|
| 3 | * This program is free software: you can redistribute it and/or modify |
|---|
| 4 | * it under the terms of the GNU General Public License as published by |
|---|
| 5 | * the Free Software Foundation, either version 3 of the License, or |
|---|
| 6 | * (at your option) any later version. |
|---|
| 7 | * |
|---|
| 8 | * This program is distributed in the hope that it will be useful, |
|---|
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 11 | * GNU General Public License for more details. |
|---|
| 12 | * |
|---|
| 13 | * You should have received a copy of the GNU General Public License |
|---|
| 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 15 | */ |
|---|
| 16 | |
|---|
| 17 | #import "SuspectsController.h" |
|---|
| 18 | #import "GBDebug.h" |
|---|
| 19 | #import "XmlDoc.h" |
|---|
| 20 | #import "Xml.h" |
|---|
| 21 | #import "ServerCommunication.h" |
|---|
| 22 | #import "AlertHelper.h" |
|---|
| 23 | #import "Prefs.h" |
|---|
| 24 | #import "FilterTabController.h" |
|---|
| 25 | #import "FilterRuleEdit.h" |
|---|
| 26 | |
|---|
| 27 | @interface SuspectsController () |
|---|
| 28 | |
|---|
| 29 | - (void)timerCallback:(NSTimer*)theTimer; |
|---|
| 30 | - (void)updateTableLayout; |
|---|
| 31 | |
|---|
| 32 | - (void)tableViewSelectionDidChange:(NSNotification *)aNotification; |
|---|
| 33 | @end |
|---|
| 34 | |
|---|
| 35 | @implementation SuspectsController |
|---|
| 36 | |
|---|
| 37 | - (id)initWithFilterTabController:(FilterTabController*)filterTabController |
|---|
| 38 | withContainerView:(NSView*)containerView |
|---|
| 39 | withBundle:(NSBundle*)bundle |
|---|
| 40 | { |
|---|
| 41 | if (!(self = [super init])) |
|---|
| 42 | return nil; |
|---|
| 43 | NSAssert([NSBundle loadNibNamed: @"Suspects" owner: self], @"Can't load nib 'Suspects'"); |
|---|
| 44 | NSAssert(suspectTableView, @"suspectTableView is null"); |
|---|
| 45 | _filterTabController = [filterTabController retain]; |
|---|
| 46 | _comm = [[filterTabController comm] retain]; |
|---|
| 47 | _prefs = [[_comm prefs] retain]; |
|---|
| 48 | _alertHelper = [[_prefs alertHelper] retain]; |
|---|
| 49 | _bundle = [[filterTabController bundle] retain]; |
|---|
| 50 | _suspectList = [[NSMutableArray arrayWithCapacity:200] retain]; |
|---|
| 51 | _showDetails = [_prefs boolForXPath:@"suspects/@show-details"]; |
|---|
| 52 | _showJavascripts = [_prefs boolForXPath:@"suspects/@show-javascripts"]; |
|---|
| 53 | _enabled = [_prefs boolForXPath:@"suspects/@enabled"]; |
|---|
| 54 | [showDetailsCB setState:_showDetails]; |
|---|
| 55 | [showJavascriptsCB setState:_showJavascripts]; |
|---|
| 56 | [enableCB setState:_enabled]; |
|---|
| 57 | [containerView addSubview:subView]; |
|---|
| 58 | [suspectTableView setDataSource:self]; |
|---|
| 59 | [suspectTableView setDelegate:self]; |
|---|
| 60 | [self updateTableLayout]; |
|---|
| 61 | [self tableViewSelectionDidChange:NULL]; |
|---|
| 62 | [suspectTableView setTarget:self]; |
|---|
| 63 | [suspectTableView setDoubleAction:@selector(doubleClickInTable:)]; |
|---|
| 64 | [self notifyUpdatedAuthorizationState]; |
|---|
| 65 | [suspectTableView registerForDraggedTypes:[NSArray arrayWithObjects:NSStringPboardType, NSURLPboardType, NULL]]; |
|---|
| 66 | [suspectTableView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO]; |
|---|
| 67 | [suspectTableView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:YES]; |
|---|
| 68 | return self; |
|---|
| 69 | } |
|---|
| 70 | |
|---|
| 71 | - (void)dealloc |
|---|
| 72 | { |
|---|
| 73 | DebugNSLog(@"Suspects.dealloc"); |
|---|
| 74 | [self notifyBecameHidden]; |
|---|
| 75 | [subView removeFromSuperview]; |
|---|
| 76 | [subView release]; |
|---|
| 77 | [_alertHelper release]; |
|---|
| 78 | [_prefs release]; |
|---|
| 79 | [_comm release]; |
|---|
| 80 | [_suspectList release]; |
|---|
| 81 | [_bundle release]; |
|---|
| 82 | [_filterTabController release]; |
|---|
| 83 | [super dealloc]; |
|---|
| 84 | } |
|---|
| 85 | |
|---|
| 86 | - (void)startTimerWithImmediateExecute:(BOOL)immediate |
|---|
| 87 | { |
|---|
| 88 | if (!_enabled) |
|---|
| 89 | return; |
|---|
| 90 | if (!immediate && [_timer isValid]) |
|---|
| 91 | return; |
|---|
| 92 | [self stopTimer]; |
|---|
| 93 | if (immediate) |
|---|
| 94 | [self timerCallback:NULL]; |
|---|
| 95 | _timer = [[NSTimer scheduledTimerWithTimeInterval:2.0 |
|---|
| 96 | target:self |
|---|
| 97 | selector:@selector(timerCallback:) |
|---|
| 98 | userInfo:NULL |
|---|
| 99 | repeats:NO] retain]; |
|---|
| 100 | } |
|---|
| 101 | |
|---|
| 102 | - (void)stopTimer |
|---|
| 103 | { |
|---|
| 104 | if (!_timer) |
|---|
| 105 | return; |
|---|
| 106 | [_timer invalidate]; |
|---|
| 107 | [_timer release]; |
|---|
| 108 | _timer = NULL; |
|---|
| 109 | } |
|---|
| 110 | |
|---|
| 111 | - (void)notifyBecameVisible |
|---|
| 112 | { |
|---|
| 113 | _visible = YES; |
|---|
| 114 | [self startTimerWithImmediateExecute:YES]; |
|---|
| 115 | } |
|---|
| 116 | |
|---|
| 117 | - (void)notifyBecameHidden |
|---|
| 118 | { |
|---|
| 119 | _visible = NO; |
|---|
| 120 | [self stopTimer]; |
|---|
| 121 | } |
|---|
| 122 | |
|---|
| 123 | - (void)notifyUpdatedAuthorizationState |
|---|
| 124 | { |
|---|
| 125 | [enableCB setEnabled:[_prefs hasGlimmerAuth]]; |
|---|
| 126 | [self tableViewSelectionDidChange:NULL]; |
|---|
| 127 | } |
|---|
| 128 | |
|---|
| 129 | - (void)timerCallback:(NSTimer*)theTimer |
|---|
| 130 | { |
|---|
| 131 | [self stopTimer]; |
|---|
| 132 | if (!_enabled) { |
|---|
| 133 | [_suspectList removeAllObjects]; |
|---|
| 134 | [suspectTableView reloadData]; |
|---|
| 135 | return; |
|---|
| 136 | } |
|---|
| 137 | if (!_visible) { |
|---|
| 138 | DebugNSLog(@"Suspects, Timer callback without being visible"); |
|---|
| 139 | return; |
|---|
| 140 | } |
|---|
| 141 | NSString* pathArgs = [NSString stringWithFormat:@"/suspect/list?js=%@", _showJavascripts ? @"true" : @"false"]; |
|---|
| 142 | NSError* error = NULL; |
|---|
| 143 | XmlDoc* doc = [_comm curlToXml:pathArgs |
|---|
| 144 | withActionDescription:@"get list of suspects" |
|---|
| 145 | withErrorRef:&error]; |
|---|
| 146 | [self startTimerWithImmediateExecute:NO]; |
|---|
| 147 | if (!doc) |
|---|
| 148 | return; |
|---|
| 149 | NSArray* a = [[doc rootE] elementsForName:@"suspect"]; |
|---|
| 150 | NSUInteger num = [a count]; |
|---|
| 151 | if (!num) { |
|---|
| 152 | [_suspectList removeAllObjects]; |
|---|
| 153 | [suspectTableView reloadData]; |
|---|
| 154 | [self tableViewSelectionDidChange:NULL]; |
|---|
| 155 | return; |
|---|
| 156 | } |
|---|
| 157 | NSString* selectedHost = NULL; |
|---|
| 158 | NSString* selectedPath = NULL; |
|---|
| 159 | NSInteger row = [suspectTableView selectedRow]; |
|---|
| 160 | if (row >= 0) { |
|---|
| 161 | NSXMLElement* e = [_suspectList objectAtIndex:row]; |
|---|
| 162 | selectedHost = [Xml getAttribute:e withName:@"host"]; |
|---|
| 163 | selectedPath = [Xml getAttribute:e withName:@"path"]; |
|---|
| 164 | } |
|---|
| 165 | [_suspectList setArray:a]; |
|---|
| 166 | [suspectTableView reloadData]; |
|---|
| 167 | if (selectedHost) { |
|---|
| 168 | BOOL found = NO; |
|---|
| 169 | for (NSUInteger i = 0; i < num; i++) { |
|---|
| 170 | NSXMLElement* e = [a objectAtIndex:i]; |
|---|
| 171 | if ([selectedHost isEqual:[Xml getAttribute:e withName:@"host"]] |
|---|
| 172 | && [selectedPath isEqual:[Xml getAttribute:e withName:@"path"]]) { |
|---|
| 173 | NSIndexSet* set = [NSIndexSet indexSetWithIndex:i]; |
|---|
| 174 | [suspectTableView selectRowIndexes:set byExtendingSelection:NO]; |
|---|
| 175 | //DebugNSLog(@"Suspects.timer.selected-row: %d -> %d", row, i); |
|---|
| 176 | found = YES; |
|---|
| 177 | break; |
|---|
| 178 | } |
|---|
| 179 | } |
|---|
| 180 | if (!found) { |
|---|
| 181 | [suspectTableView deselectAll:self]; |
|---|
| 182 | } |
|---|
| 183 | } |
|---|
| 184 | } |
|---|
| 185 | |
|---|
| 186 | #pragma mark ------------------- IB |
|---|
| 187 | |
|---|
| 188 | - (void)updateTableLayout |
|---|
| 189 | { |
|---|
| 190 | [suspectTableView setRowHeight:_showDetails ? 44 : 17]; |
|---|
| 191 | } |
|---|
| 192 | |
|---|
| 193 | - (void)tableViewSelectionDidChange:(NSNotification *)aNotification |
|---|
| 194 | { |
|---|
| 195 | [addBtn setEnabled:[_prefs hasGlimmerAuth] && [suspectTableView selectedRow] >= 0]; |
|---|
| 196 | } |
|---|
| 197 | |
|---|
| 198 | |
|---|
| 199 | - (IBAction)showDetailsAction:(id)sender |
|---|
| 200 | { |
|---|
| 201 | _showDetails = [showDetailsCB state]; |
|---|
| 202 | [_prefs setBool:_showDetails forXPath:@"suspects/@show-details"]; |
|---|
| 203 | [self updateTableLayout]; |
|---|
| 204 | [suspectTableView reloadData]; |
|---|
| 205 | if ([_prefs hasGlimmerAuth]) |
|---|
| 206 | [_prefs markAsDirty]; |
|---|
| 207 | } |
|---|
| 208 | |
|---|
| 209 | - (IBAction)showJavascriptsAction:(id)sender; |
|---|
| 210 | { |
|---|
| 211 | _showJavascripts = [showJavascriptsCB state]; |
|---|
| 212 | [_prefs setBool:_showJavascripts forXPath:@"suspects/@show-javascripts"]; |
|---|
| 213 | if ([_prefs hasGlimmerAuth]) |
|---|
| 214 | [_prefs markAsDirty]; |
|---|
| 215 | [self startTimerWithImmediateExecute:YES]; |
|---|
| 216 | } |
|---|
| 217 | |
|---|
| 218 | - (IBAction)enableAction:(id)sender |
|---|
| 219 | { |
|---|
| 220 | _enabled = [enableCB state]; |
|---|
| 221 | DebugNSLog(@"enableAction: %d", _enabled); |
|---|
| 222 | [_prefs setBool:_enabled forXPath:@"suspects/@enabled"]; |
|---|
| 223 | [_prefs markAsDirty]; |
|---|
| 224 | [_comm saveDirtyPrefsAndNotify]; |
|---|
| 225 | if (_enabled) { |
|---|
| 226 | [self startTimerWithImmediateExecute:YES]; |
|---|
| 227 | } else { |
|---|
| 228 | [self stopTimer]; |
|---|
| 229 | [_suspectList removeAllObjects]; |
|---|
| 230 | [suspectTableView reloadData]; |
|---|
| 231 | } |
|---|
| 232 | } |
|---|
| 233 | |
|---|
| 234 | - (void)openFilterSheet:(NSInteger)row |
|---|
| 235 | { |
|---|
| 236 | if (row < 0 || row >= (int)[_suspectList count]) |
|---|
| 237 | return; |
|---|
| 238 | NSXMLElement* suspectE = [_suspectList objectAtIndex:row]; |
|---|
| 239 | [[[FilterRuleEdit alloc] initWithFilterTabController:_filterTabController |
|---|
| 240 | withOwnerFilter:NULL |
|---|
| 241 | withRuleElement:NULL |
|---|
| 242 | withSuspectElement:suspectE] autorelease]; |
|---|
| 243 | } |
|---|
| 244 | |
|---|
| 245 | - (IBAction)addAction:(id)sender |
|---|
| 246 | { |
|---|
| 247 | [self openFilterSheet:[suspectTableView selectedRow]]; |
|---|
| 248 | } |
|---|
| 249 | |
|---|
| 250 | - (IBAction)doubleClickInTable:(id)sender |
|---|
| 251 | { |
|---|
| 252 | [self openFilterSheet:[suspectTableView clickedRow]]; |
|---|
| 253 | } |
|---|
| 254 | |
|---|
| 255 | |
|---|
| 256 | - (NSString *)tableView:(NSTableView *)aTableView |
|---|
| 257 | toolTipForCell:(NSCell *)aCell |
|---|
| 258 | rect:(NSRectPointer)rect |
|---|
| 259 | tableColumn:(NSTableColumn *)aTableColumn |
|---|
| 260 | row:(NSInteger)row |
|---|
| 261 | mouseLocation:(NSPoint)mouseLocation |
|---|
| 262 | { |
|---|
| 263 | if (aTableView != suspectTableView || _showDetails) |
|---|
| 264 | return @""; |
|---|
| 265 | NSXMLElement* e = [_suspectList objectAtIndex:row]; |
|---|
| 266 | return [NSString stringWithFormat:@"%@\nFrom: %@", |
|---|
| 267 | [Xml getAttribute:e withName:@"why"], |
|---|
| 268 | [Xml getAttribute:e withName:@"referer"]]; |
|---|
| 269 | } |
|---|
| 270 | |
|---|
| 271 | - (NSString*)urlForRow:(NSUInteger)row |
|---|
| 272 | { |
|---|
| 273 | NSXMLElement* e = [_suspectList objectAtIndex:row]; |
|---|
| 274 | NSString* host = [Xml getAttribute:e withName:@"host"]; |
|---|
| 275 | NSString* path = [Xml getAttribute:e withName:@"path"]; |
|---|
| 276 | NSString* query = [Xml getAttribute:e withName:@"query"]; |
|---|
| 277 | NSString* url = [host stringByAppendingString:path]; |
|---|
| 278 | if ([query length]) |
|---|
| 279 | url = [[url stringByAppendingString:@"?"] stringByAppendingString:query]; |
|---|
| 280 | return url; |
|---|
| 281 | } |
|---|
| 282 | |
|---|
| 283 | - (BOOL)tableView:(NSTableView *)view writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard |
|---|
| 284 | { |
|---|
| 285 | DebugNSLog(@"writeRows.%d: %@", (view == suspectTableView), rowIndexes); |
|---|
| 286 | if (view != suspectTableView || [rowIndexes count] != 1) |
|---|
| 287 | return NO; |
|---|
| 288 | NSString* u = [@"http://" stringByAppendingString:[self urlForRow:[rowIndexes firstIndex]]]; |
|---|
| 289 | [pboard declareTypes:[NSArray arrayWithObjects:NSStringPboardType, NSURLPboardType, NULL] owner:nil]; |
|---|
| 290 | [pboard setString:u forType:NSStringPboardType]; |
|---|
| 291 | @try { |
|---|
| 292 | [[NSURL URLWithString:u] writeToPasteboard:pboard]; |
|---|
| 293 | } |
|---|
| 294 | @catch (NSException* ex) { |
|---|
| 295 | NSLog(@"Couldn't copy URL to pasteboard: %@", u); |
|---|
| 296 | } |
|---|
| 297 | return YES; |
|---|
| 298 | } |
|---|
| 299 | |
|---|
| 300 | - (void)copyFromSuspectsTable |
|---|
| 301 | { |
|---|
| 302 | NSInteger row = [suspectTableView selectedRow]; |
|---|
| 303 | if (row < 0 || row >= (NSInteger)[_suspectList count]) |
|---|
| 304 | return; |
|---|
| 305 | NSIndexSet *set = [NSIndexSet indexSetWithIndex:row]; |
|---|
| 306 | [self tableView:suspectTableView |
|---|
| 307 | writeRowsWithIndexes:set |
|---|
| 308 | toPasteboard:[NSPasteboard generalPasteboard]]; |
|---|
| 309 | } |
|---|
| 310 | |
|---|
| 311 | @end |
|---|
| 312 | |
|---|
| 313 | @implementation SuspectsController(NSTableDataSource) |
|---|
| 314 | |
|---|
| 315 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView |
|---|
| 316 | { |
|---|
| 317 | NSAssert(tableView == suspectTableView, @"tableView == suspectTableView"); |
|---|
| 318 | return [_suspectList count]; |
|---|
| 319 | } |
|---|
| 320 | |
|---|
| 321 | - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)col row:(NSInteger)row |
|---|
| 322 | { |
|---|
| 323 | NSAssert(tableView == suspectTableView, @"tableView == suspectTableView"); |
|---|
| 324 | if (!_showDetails) |
|---|
| 325 | return [self urlForRow:row]; |
|---|
| 326 | NSXMLElement* e = [_suspectList objectAtIndex:row]; |
|---|
| 327 | return [NSString stringWithFormat:@"%@\n from: %@\n %@", |
|---|
| 328 | [self urlForRow:row], |
|---|
| 329 | [Xml getAttribute:e withName:@"referer"], |
|---|
| 330 | [Xml getAttribute:e withName:@"why"]]; |
|---|
| 331 | } |
|---|
| 332 | |
|---|
| 333 | @end |
|---|
| 334 | |
|---|
| 335 | @implementation SuspectsTableView |
|---|
| 336 | |
|---|
| 337 | - (IBAction)copy:(id)sender |
|---|
| 338 | { |
|---|
| 339 | id del = [self delegate]; |
|---|
| 340 | if ([del isKindOfClass:[SuspectsController class]]) { |
|---|
| 341 | SuspectsController* sc = (SuspectsController*)del; |
|---|
| 342 | [sc copyFromSuspectsTable]; |
|---|
| 343 | } |
|---|
| 344 | } |
|---|
| 345 | |
|---|
| 346 | @end |
|---|