diff --git a/Classes/Controllers/TXMasterController.m b/Classes/Controllers/TXMasterController.m index 6b93689630..662dc180c8 100755 --- a/Classes/Controllers/TXMasterController.m +++ b/Classes/Controllers/TXMasterController.m @@ -192,6 +192,10 @@ - (void)applicationDidChangeScreenParameters:(NSNotification *)aNotification [self.window setFrame:windowRect display:YES animate:YES]; } } + + /* Redraw dock icon on potential screen resolution changes. */ + + [self.world updateIcon]; } - (void)applicationDidBecomeActive:(NSNotification *)note diff --git a/Classes/Dialogs/Preferences/TDCPreferencesController.m b/Classes/Dialogs/Preferences/TDCPreferencesController.m index 4d35218720..9a02e43236 100755 --- a/Classes/Dialogs/Preferences/TDCPreferencesController.m +++ b/Classes/Dialogs/Preferences/TDCPreferencesController.m @@ -582,17 +582,15 @@ - (void)onStyleChanged:(id)sender [_NSNotificationCenter() postNotificationName:TXThemePreferenceChangedNotification object:nil userInfo:nil]; } -+ (void)openPathToThemesCallback:(NSNumber *)returnCode ++ (void)openPathToThemesCallback:(TLOPopupPromptReturnType)returnCode { NSString *name = [TPCViewTheme extractThemeName:[TPCPreferences themeName]]; - NSInteger _returnCode = [returnCode integerValue]; - - if (_returnCode == NSAlertSecondButtonReturn) { + if (returnCode == TLOPopupPromptReturnSecondaryType) { return; } - if (_returnCode == NSAlertFirstButtonReturn) { + if (returnCode == TLOPopupPromptReturnPrimaryType) { NSString *path = [[TPCPreferences whereThemesLocalPath] stringByAppendingPathComponent:name]; [_NSWorkspace() openFile:path]; diff --git a/Classes/Headers/External Libraries/GCDAsyncSocketExtensions.h b/Classes/Headers/External Libraries/GCDAsyncSocketExtensions.h index 633c3e6646..96cb3eabe5 100755 --- a/Classes/Headers/External Libraries/GCDAsyncSocketExtensions.h +++ b/Classes/Headers/External Libraries/GCDAsyncSocketExtensions.h @@ -10,6 +10,13 @@ + (BOOL)badSSLCertErrorFound:(NSError *)error; + (NSString *)posixErrorStringFromErrno:(NSInteger)code; + +- (void)requestSSLTrustFor:(NSWindow *)docWindow + modalDelegate:(id)adelegate + didEndSelector:(SEL)didEndSelector + contextInfo:(void *)contextInfo + defaultButton:(NSString *)defaultButton + alternateButton:(NSString *)alternateButton; @end @interface AsyncSocket (RLMAsyncSocketExtensions) diff --git a/Classes/Headers/IRC.h b/Classes/Headers/IRC.h index 3784ba8ea4..4d6cd367e6 100755 --- a/Classes/Headers/IRC.h +++ b/Classes/Headers/IRC.h @@ -80,6 +80,7 @@ #define IRCCommandIndexSend @"SEND" #define IRCCommandIndexServer @"SERVER" #define IRCCommandIndexShun @"SHUN" +#define IRCCommandIndexSslcontext @"SSLCONTEXT" #define IRCCommandIndexT @"T" #define IRCCommandIndexTempshun @"TEMPSHUN" #define IRCCommandIndexTime @"TIME" diff --git a/Classes/Headers/TLOPopupPrompts.h b/Classes/Headers/TLOPopupPrompts.h index 684532823e..a19f192a78 100755 --- a/Classes/Headers/TLOPopupPrompts.h +++ b/Classes/Headers/TLOPopupPrompts.h @@ -5,10 +5,16 @@ #define TXPopupPromptSuppressionPrefix @"Text Input Prompt Suppression -> " -@interface TLOPopupPrompts : NSObject -+ (void)popupPromptNULLSelector:(NSInteger)returnCode; +typedef enum TLOPopupPromptReturnType : NSInteger { + TLOPopupPromptReturnPrimaryType, + TLOPopupPromptReturnSecondaryType, + TLOPopupPromptReturnOtherType, +} TLOPopupPromptReturnType; -+ (BOOL)dialogWindowWithQuestion:(NSString *)bodyText +@interface TLOPopupPrompts : NSObject ++ (void)popupPromptNilSelector:(TLOPopupPromptReturnType)returnCode; + ++ (BOOL)dialogWindowWithQuestion:(NSString *)bodyText title:(NSString *)titleText defaultButton:(NSString *)buttonDefault alternateButton:(NSString *)buttonAlternate @@ -16,7 +22,7 @@ suppressionKey:(NSString *)suppressKey suppressionText:(NSString *)suppressText; -+ (NSString *)dialogWindowWithInput:(NSString *)bodyText ++ (NSString *)dialogWindowWithInput:(NSString *)bodyText title:(NSString *)titleText defaultButton:(NSString *)buttonDefault alternateButton:(NSString *)buttonAlternate @@ -25,7 +31,7 @@ - (void)sheetWindowWithQuestion:(NSWindow *)window target:(id)targetClass action:(SEL)actionSelector - body:(NSString *)bodyText + body:(NSString *)bodyText title:(NSString *)titleText defaultButton:(NSString *)buttonDefault alternateButton:(NSString *)buttonAlternate diff --git a/Classes/Headers/TLOSocketClient.h b/Classes/Headers/TLOSocketClient.h index de4e2d90ad..26c295a928 100755 --- a/Classes/Headers/TLOSocketClient.h +++ b/Classes/Headers/TLOSocketClient.h @@ -29,6 +29,12 @@ - (NSData *)readLine; - (void)write:(NSData *)data; + +- (void)openSSLCertificateTrustDialog; + +/* openSSLCertificateTrustDialog: results are not guaranteed + when supplied with a suppression key. Pass nil to it for now. */ +- (void)openSSLCertificateTrustDialog:(NSString *)suppressKey; @end @interface NSObject (TCPClientDelegate) diff --git a/Classes/IRC/IRCClient.m b/Classes/IRC/IRCClient.m index 10fef3e80b..2b784231c2 100755 --- a/Classes/IRC/IRCClient.m +++ b/Classes/IRC/IRCClient.m @@ -30,62 +30,62 @@ - (id)init if ((self = [super init])) { self.tryingNickNumber = -1; self.capPaused = 0; - + self.isAway = NO; self.userhostInNames = NO; self.multiPrefix = NO; self.identifyMsg = NO; self.identifyCTCP = NO; self.hasIRCopAccess = NO; - + self.channels = [NSMutableArray new]; self.highlights = [NSMutableArray new]; self.commandQueue = [NSMutableArray new]; self.acceptedCaps = [NSMutableArray new]; self.pendingCaps = [NSMutableArray new]; - + self.trackedUsers = [NSMutableDictionary new]; - + self.isupport = [IRCISupportInfo new]; - + self.reconnectTimer = [TLOTimer new]; self.reconnectTimer.delegate = self; self.reconnectTimer.reqeat = NO; self.reconnectTimer.selector = @selector(onReconnectTimer:); - + self.retryTimer = [TLOTimer new]; self.retryTimer.delegate = self; self.retryTimer.reqeat = NO; self.retryTimer.selector = @selector(onRetryTimer:); - + self.autoJoinTimer = [TLOTimer new]; self.autoJoinTimer.delegate = self; self.autoJoinTimer.reqeat = YES; self.autoJoinTimer.selector = @selector(onAutoJoinTimer:); - + self.commandQueueTimer = [TLOTimer new]; self.commandQueueTimer.delegate = self; self.commandQueueTimer.reqeat = NO; self.commandQueueTimer.selector = @selector(onCommandQueueTimer:); - + self.pongTimer = [TLOTimer new]; self.pongTimer.delegate = self; self.pongTimer.reqeat = YES; self.pongTimer.selector = @selector(onPongTimer:); - + self.isonTimer = [TLOTimer new]; self.isonTimer.delegate = self; self.isonTimer.reqeat = YES; self.isonTimer.selector = @selector(onISONTimer:); - + #ifdef IS_TRIAL_BINARY self.trialPeriodTimer = [TLOTimer new]; self.trialPeriodTimer.delegate = self; self.trialPeriodTimer.reqeat = NO; - self.trialPeriodTimer.selector = @selector(onTrialPeriodTimer:); + self.trialPeriodTimer.selector = @selector(onTrialPeriodTimer:); #endif } - + return self; } @@ -97,13 +97,13 @@ - (void)dealloc [self.pongTimer stop]; [self.reconnectTimer stop]; [self.retryTimer stop]; - + [self.conn close]; - + #ifdef IS_TRIAL_BINARY [self.trialPeriodTimer stop]; #endif - + } - (void)setup:(IRCClientConfig *)seed @@ -115,27 +115,27 @@ - (void)updateConfig:(IRCClientConfig *)seed { self.config = nil; self.config = [seed mutableCopy]; - + NSArray *chans = self.config.channels; - + NSMutableArray *ary = [NSMutableArray array]; - + for (IRCChannelConfig *i in chans) { IRCChannel *c = [self findChannel:i.name]; - + if (c) { [c updateConfig:i]; - + [ary safeAddObject:c]; - + [self.channels removeObjectIdenticalTo:c]; } else { c = [self.world createChannel:i client:self reload:NO adjust:NO]; - + [ary safeAddObject:c]; } } - + for (IRCChannel *c in self.channels) { if (c.isChannel) { [self partChannel:c]; @@ -143,12 +143,12 @@ - (void)updateConfig:(IRCClientConfig *)seed [ary safeAddObject:c]; } } - + [self.channels removeAllObjects]; [self.channels addObjectsFromArray:ary]; - + [self.config.channels removeAllObjects]; - + [self.world reloadTree]; [self.world adjustSelection]; } @@ -156,32 +156,32 @@ - (void)updateConfig:(IRCClientConfig *)seed - (IRCClientConfig *)storedConfig { IRCClientConfig *u = [self.config mutableCopy]; - + [u.channels removeAllObjects]; - + for (IRCChannel *c in self.channels) { if (c.isChannel) { [u.channels safeAddObject:[c.config mutableCopy]]; } } - + return u; } - (NSMutableDictionary *)dictionaryValue { NSMutableDictionary *dic = [self.config dictionaryValue]; - + NSMutableArray *ary = [NSMutableArray array]; - + for (IRCChannel *c in self.channels) { if (c.isChannel) { [ary safeAddObject:[c dictionaryValue]]; } } - + dic[@"channelList"] = ary; - + return dic; } @@ -211,14 +211,14 @@ - (BOOL)isReconnecting #pragma mark - #pragma mark User Tracking -- (void)handleUserTrackingNotification:(IRCAddressBook *)ignoreItem +- (void)handleUserTrackingNotification:(IRCAddressBook *)ignoreItem nickname:(NSString *)nick hostmask:(NSString *)host langitem:(NSString *)localKey { if ([ignoreItem notifyJoins] == YES) { NSString *text = TXTFLS(localKey, host, ignoreItem.hostmask); - + [self notifyEvent:TXNotificationAddressBookMatchType lineType:TVCLogLineNoticeType target:nil @@ -231,23 +231,23 @@ - (void)populateISONTrackedUsersList:(NSMutableArray *)ignores { if (self.hasIRCopAccess) return; if (self.isLoggedIn == NO) return; - + if (PointerIsEmpty(self.trackedUsers)) { self.trackedUsers = [NSMutableDictionary new]; } - + if (NSObjectIsNotEmpty(self.trackedUsers)) { NSMutableDictionary *oldEntries = [NSMutableDictionary dictionary]; NSMutableDictionary *newEntries = [NSMutableDictionary dictionary]; - + for (NSString *lname in self.trackedUsers) { oldEntries[lname] = (self.trackedUsers)[lname]; } - + for (IRCAddressBook *g in ignores) { if (g.notifyJoins) { NSString *lname = [g trackingNickname]; - + if ([lname isNickname]) { if ([oldEntries containsKeyIgnoringCase:lname]) { newEntries[lname] = oldEntries[lname]; @@ -257,20 +257,20 @@ - (void)populateISONTrackedUsersList:(NSMutableArray *)ignores } } } - + self.trackedUsers = newEntries; } else { for (IRCAddressBook *g in ignores) { if (g.notifyJoins) { NSString *lname = [g trackingNickname]; - + if ([lname isNickname]) { [self.trackedUsers setBool:NO forKey:[g trackingNickname]]; } } } } - + if (NSObjectIsNotEmpty(self.trackedUsers)) { [self performSelector:@selector(startISONTimer)]; } else { @@ -281,14 +281,14 @@ - (void)populateISONTrackedUsersList:(NSMutableArray *)ignores - (void)startISONTimer { if (self.isonTimer.isActive) return; - + [self.isonTimer start:_isonCheckIntervalL]; } - (void)stopISONTimer { [self.isonTimer stop]; - + [self.trackedUsers removeAllObjects]; } @@ -298,13 +298,13 @@ - (void)onISONTimer:(id)sender if (NSObjectIsEmpty(self.trackedUsers) || self.hasIRCopAccess) { return [self stopISONTimer]; } - + NSMutableString *userstr = [NSMutableString string]; - + for (NSString *name in self.trackedUsers) { [userstr appendFormat:@" %@", name]; } - + [self send:IRCCommandIndexIson, userstr, nil]; } } @@ -315,7 +315,7 @@ - (void)onISONTimer:(id)sender - (void)autoConnect:(NSInteger)delay { _connectDelay = delay; - + [self connect]; } @@ -323,11 +323,11 @@ - (void)terminate { [self quit]; [self closeDialogs]; - + for (IRCChannel *c in self.channels) { [c terminate]; } - + [self disconnect]; } @@ -339,7 +339,7 @@ - (void)closeDialogs - (void)preferencesChanged { self.log.maxLines = [TPCPreferences maxLogLines]; - + for (IRCChannel *c in self.channels) { [c preferencesChanged]; } @@ -353,25 +353,25 @@ - (void)reloadTree - (IRCAddressBook *)checkIgnoreAgainstHostmask:(NSString *)host withMatches:(NSArray *)matches { host = [host lowercaseString]; - + for (IRCAddressBook *g in self.config.ignores) { if ([g checkIgnore:host]) { NSDictionary *ignoreDict = [g dictionaryValue]; - + NSInteger totalMatches = 0; - + for (NSString *matchkey in matches) { if ([ignoreDict boolForKey:matchkey] == YES) { totalMatches++; } } - + if (totalMatches > 0) { return g; } } } - + return nil; } @@ -380,23 +380,23 @@ - (BOOL)outputRuleMatchedInMessage:(NSString *)raw inChannel:(IRCChannel *)chan if ([TPCPreferences removeAllFormatting]) { raw = [raw stripEffects]; } - + NSString *rulekey = [TVCLogLine lineTypeString:type]; - + NSDictionary *rules = self.world.bundlesWithOutputRules; - + if (NSObjectIsNotEmpty(rules)) { NSDictionary *ruleData = [rules dictionaryForKey:rulekey]; - + if (NSObjectIsNotEmpty(ruleData)) { for (NSString *ruleRegex in ruleData) { if ([TLORegularExpression string:raw isMatchedByRegex:ruleRegex]) { NSArray *regexData = [ruleData arrayForKey:ruleRegex]; - + BOOL console = [regexData boolAtIndex:0]; BOOL channel = [regexData boolAtIndex:1]; BOOL queries = [regexData boolAtIndex:2]; - + if ([chan isKindOfClass:[IRCChannel class]]) { if ((chan.isTalk && queries) || (chan.isChannel && channel) || (chan.isClient && console)) { return YES; @@ -410,7 +410,7 @@ - (BOOL)outputRuleMatchedInMessage:(NSString *)raw inChannel:(IRCChannel *)chan } } } - + return NO; } @@ -422,29 +422,29 @@ - (void)createChanBanListDialog if (PointerIsEmpty(self.chanBanListSheet)) { IRCClient *u = [self.world selectedClient]; IRCChannel *c = [self.world selectedChannel]; - + if (PointerIsEmpty(u) || PointerIsEmpty(c)) return; - + self.chanBanListSheet = [TDChanBanSheet new]; self.chanBanListSheet.delegate = self; self.chanBanListSheet.window = self.world.window; } else { [self.chanBanListSheet ok:nil]; - + self.chanBanListSheet = nil; - + [self createChanBanListDialog]; - + return; } - + [self.chanBanListSheet show]; } - (void)chanBanDialogOnUpdate:(TDChanBanSheet *)sender { [sender.list removeAllObjects]; - + [self send:IRCCommandIndexMode, [self.world.selectedChannel name], @"+b", nil]; } @@ -455,7 +455,7 @@ - (void)chanBanDialogWillClose:(TDChanBanSheet *)sender [self sendLine:[NSString stringWithFormat:@"%@ %@ %@", IRCCommandIndexMode, [self.world selectedChannel].name, mode]]; } } - + self.chanBanListSheet = nil; } @@ -466,16 +466,16 @@ - (void)createChanInviteExceptionListDialog { if (self.inviteExceptionSheet) { [self.inviteExceptionSheet ok:nil]; - + self.inviteExceptionSheet = nil; - + [self createChanInviteExceptionListDialog]; } else { IRCClient *u = [self.world selectedClient]; IRCChannel *c = [self.world selectedChannel]; - + if (PointerIsEmpty(u) || PointerIsEmpty(c)) return; - + self.inviteExceptionSheet = [TDChanInviteExceptionSheet new]; self.inviteExceptionSheet.delegate = self; self.inviteExceptionSheet.window = self.world.window; @@ -486,7 +486,7 @@ - (void)createChanInviteExceptionListDialog - (void)chanInviteExceptionDialogOnUpdate:(TDChanInviteExceptionSheet *)sender { [sender.list removeAllObjects]; - + [self send:IRCCommandIndexMode, [self.world.selectedChannel name], @"+I", nil]; } @@ -497,7 +497,7 @@ - (void)chanInviteExceptionDialogWillClose:(TDChanInviteExceptionSheet *)sender [self sendLine:[NSString stringWithFormat:@"%@ %@ %@", IRCCommandIndexMode, [self.world selectedChannel].name, mode]]; } } - + self.inviteExceptionSheet = nil; } @@ -509,29 +509,29 @@ - (void)createChanBanExceptionListDialog if (PointerIsEmpty(self.banExceptionSheet)) { IRCClient *u = [self.world selectedClient]; IRCChannel *c = [self.world selectedChannel]; - + if (PointerIsEmpty(u) || PointerIsEmpty(c)) return; - + self.banExceptionSheet = [TDChanBanExceptionSheet new]; self.banExceptionSheet.delegate = self; self.banExceptionSheet.window = self.world.window; } else { [self.banExceptionSheet ok:nil]; - + self.banExceptionSheet = nil; - + [self createChanBanExceptionListDialog]; - + return; } - + [self.banExceptionSheet show]; } - (void)chanBanExceptionDialogOnUpdate:(TDChanBanExceptionSheet *)sender { [sender.list removeAllObjects]; - + [self send:IRCCommandIndexMode, [self.world.selectedChannel name], @"+e", nil]; } @@ -542,7 +542,7 @@ - (void)chanBanExceptionDialogWillClose:(TDChanBanExceptionSheet *)sender [self sendLine:[NSString stringWithFormat:@"%@ %@ %@", IRCCommandIndexMode, [self.world selectedChannel].name, mode]]; } } - + self.banExceptionSheet = nil; } @@ -563,7 +563,7 @@ - (void)createChannelListDialog - (void)listDialogOnUpdate:(TDCListDialog *)sender { [sender.list removeAllObjects]; - + [self sendLine:IRCCommandIndexList]; } @@ -583,7 +583,7 @@ - (void)listDialogWillClose:(TDCListDialog *)sender - (void)startPongTimer { if (self.pongTimer.isActive) return; - + [self.pongTimer start:_pongCheckInterval]; } @@ -599,13 +599,13 @@ - (void)onPongTimer:(id)sender if (self.isConnected == NO) { return [self stopPongTimer]; } - + NSInteger timeSpent = [NSDate secondsSinceUnixTimestamp:self.lastMessageReceived]; NSInteger minsSpent = (timeSpent / 60); - + if (timeSpent >= _timeoutInterval) { [self printDebugInformation:TXTFLS(@"IRCDisconnectedByTimeout", minsSpent) channel:nil]; - + [self disconnect]; } else if (timeSpent >= _pingInterval) { [self send:IRCCommandIndexPing, self.serverHostname, nil]; @@ -616,7 +616,7 @@ - (void)startReconnectTimer { if (self.config.autoReconnect) { if (self.reconnectTimer.isActive) return; - + [self.reconnectTimer start:_reconnectInterval]; } } @@ -634,7 +634,7 @@ - (void)onReconnectTimer:(id)sender - (void)startRetryTimer { if (self.retryTimer.isActive) return; - + [self.retryTimer start:_retryInterval]; } @@ -664,22 +664,22 @@ - (void)onAutoJoinTimer:(id)sender { if ([TPCPreferences autojoinWaitForNickServ] == NO || NSObjectIsEmpty(self.config.nickPassword)) { [self performAutoJoin]; - + self.autojoinInitialized = YES; } else { if (self.serverHasNickServ) { if (self.autojoinInitialized) { [self performAutoJoin]; - + self.autojoinInitialized = YES; } } else { [self performAutoJoin]; - + self.autojoinInitialized = YES; } } - + [self.autoJoinTimer stop]; } @@ -694,25 +694,26 @@ - (void)connect - (void)connect:(IRCConnectMode)mode { [self stopReconnectTimer]; - + self.connectType = mode; self.disconnectType = IRCDisconnectNormalMode; - + if (self.isConnected) { [self.conn close]; } - + self.retryEnabled = YES; self.isConnecting = YES; self.reconnectEnabled = YES; - + NSString *host = self.config.host; - + switch (mode) { case IRCConnectNormalMode: [self printSystemBoth:nil text:TXTFLS(@"IRCIsConnecting", host, self.config.port)]; break; case IRCNormalReconnectionMode: + case IRCBadSSLCertificateReconnectMode: [self printSystemBoth:nil text:TXTLS(@"IRCIsReconnecting")]; [self printSystemBoth:nil text:TXTFLS(@"IRCIsConnecting", host, self.config.port)]; break; @@ -722,17 +723,17 @@ - (void)connect:(IRCConnectMode)mode break; default: break; } - + if (PointerIsEmpty(self.conn)) { self.conn = [IRCConnection new]; self.conn.delegate = self; } - + self.conn.host = host; self.conn.port = self.config.port; self.conn.useSSL = self.config.useSSL; self.conn.encoding = self.config.encoding; - + switch (self.config.proxyType) { case TXConnectionSystemSocksProxyType: self.conn.useSystemSocks = YES; @@ -747,9 +748,9 @@ - (void)connect:(IRCConnectMode)mode break; default: break; } - + [self.conn open]; - + [self reloadTree]; } @@ -758,7 +759,7 @@ - (void)disconnect if (self.conn) { [self.conn close]; } - + [self stopPongTimer]; [self changeStateOff]; } @@ -772,23 +773,23 @@ - (void)quit:(NSString *)comment { if (self.isLoggedIn == NO) { [self disconnect]; - + return; } - + [self stopPongTimer]; - + self.isQuitting = YES; self.reconnectEnabled = NO; - + [self.conn clearSendQueue]; - + if (NSObjectIsEmpty(comment)) { comment = self.config.leavingComment; } - + [self send:IRCCommandIndexQuit, comment, nil]; - + [self performSelector:@selector(disconnect) withObject:nil afterDelay:2.0]; } @@ -800,10 +801,10 @@ - (void)cancelReconnect - (void)changeNick:(NSString *)newNick { if (self.isConnected == NO) return; - + self.inputNick = newNick; self.sentNick = newNick; - + [self send:IRCCommandIndexNick, newNick, nil]; } @@ -813,7 +814,7 @@ - (void)_joinKickedChannel:(IRCChannel *)channel if (channel.status == IRCChannelTerminated) { return; } - + [self joinChannel:channel]; } } @@ -833,7 +834,7 @@ - (void)partUnlistedChannel:(NSString *)channel [self partUnlistedChannel:channel withComment:nil]; } -- (void)partChannel:(IRCChannel *)channel +- (void)partChannel:(IRCChannel *)channel { [self partChannel:channel withComment:nil]; } @@ -841,15 +842,15 @@ - (void)partChannel:(IRCChannel *)channel - (void)joinChannel:(IRCChannel *)channel password:(NSString *)password { if (self.isLoggedIn == NO) return; - + if (channel.isActive) return; if (channel.isChannel == NO) return; - + channel.status = IRCChannelJoining; - + if (NSObjectIsEmpty(password)) password = channel.config.password; if (NSObjectIsEmpty(password)) password = nil; - + [self forceJoinChannel:channel.name password:password]; } @@ -857,11 +858,11 @@ - (void)joinUnlistedChannel:(NSString *)channel password:(NSString *)password { if ([channel isChannelName]) { IRCChannel *chan = [self findChannel:channel]; - + if (chan) { return [self joinChannel:chan password:password]; } - + [self forceJoinChannel:channel password:password]; } else { if ([channel isEqualToString:@"0"]) { @@ -879,10 +880,10 @@ - (void)partUnlistedChannel:(NSString *)channel withComment:(NSString *)comment { if ([channel isChannelName]) { IRCChannel *chan = [self findChannel:channel]; - + if (chan) { chan.status = IRCChannelParted; - + return [self partChannel:chan withComment:comment]; } } @@ -891,23 +892,23 @@ - (void)partUnlistedChannel:(NSString *)channel withComment:(NSString *)comment - (void)partChannel:(IRCChannel *)channel withComment:(NSString *)comment { if (self.isLoggedIn == NO) return; - + if (channel.isActive == NO) return; if (channel.isChannel == NO) return; - + channel.status = IRCChannelParted; - + if (NSObjectIsEmpty(comment)) { comment = self.config.leavingComment; } - + [self send:IRCCommandIndexPart, channel.name, comment, nil]; } - (void)sendWhois:(NSString *)nick { if (self.isLoggedIn == NO) return; - + [self send:IRCCommandIndexWhois, nick, nick, nil]; } @@ -918,39 +919,39 @@ - (void)changeOp:(IRCChannel *)channel users:(NSArray *)inputUsers mode:(char)mo channel.isActive == NO || channel.isChannel == NO || channel.isOp == NO) return; - + NSMutableArray *users = [NSMutableArray array]; - + for (IRCUser *user in inputUsers) { IRCUser *m = [channel findMember:user.nick]; - + if (m) { if (NSDissimilarObjects(value, [m hasMode:mode])) { [users safeAddObject:m]; } } } - + NSInteger max = self.isupport.modesCount; - + while (users.count) { NSArray *ary = [users subarrayWithRange:NSMakeRange(0, MIN(max, users.count))]; - + NSMutableString *s = [NSMutableString string]; - + [s appendFormat:@"%@ %@ %c", IRCCommandIndexMode, channel.name, ((value) ? '+' : '-')]; - + for (NSInteger i = (ary.count - 1); i >= 0; --i) { [s appendFormat:@"%c", mode]; } - + for (IRCUser *m in ary) { [s appendString:NSStringWhitespacePlaceholder]; [s appendString:m.nick]; } - + [self sendLine:s]; - + [users removeObjectsInRange:NSMakeRange(0, ary.count)]; } } @@ -964,36 +965,36 @@ - (void)quickJoin:(NSArray *)chans { NSMutableString *target = [NSMutableString string]; NSMutableString *pass = [NSMutableString string]; - + for (IRCChannel *c in chans) { NSMutableString *prevTarget = [target mutableCopy]; NSMutableString *prevPass = [pass mutableCopy]; - + c.status = IRCChannelJoining; - + if (NSObjectIsNotEmpty(target)) { [target appendString:@","]; } - + [target appendString:c.name]; - + if (NSObjectIsNotEmpty(c.password)) { if (NSObjectIsNotEmpty(pass)) { [pass appendString:@","]; } - + [pass appendString:c.password]; } - + NSStringEncoding enc = self.conn.encoding; - + if (enc == 0x0000) { enc = NSUTF8StringEncoding; } - + NSData *targetData = [target dataUsingEncoding:enc]; NSData *passData = [pass dataUsingEncoding:enc]; - + if ((targetData.length + passData.length) > TXMaximumIRCBodyLength) { if (NSObjectIsEmpty(prevTarget)) { if (NSObjectIsEmpty(prevPass)) { @@ -1001,7 +1002,7 @@ - (void)quickJoin:(NSArray *)chans } else { [self send:IRCCommandIndexJoin, prevTarget, prevPass, nil]; } - + [target setString:c.name]; [pass setString:c.password]; } else { @@ -1010,13 +1011,13 @@ - (void)quickJoin:(NSArray *)chans } else { [self joinChannel:c password:c.password]; } - + [target setString:NSStringEmptyPlaceholder]; [pass setString:NSStringEmptyPlaceholder]; } } } - + if (NSObjectIsNotEmpty(target)) { if (NSObjectIsEmpty(pass)) { [self send:IRCCommandIndexJoin, target, nil]; @@ -1034,7 +1035,7 @@ - (void)updateAutoJoinStatus - (void)performAutoJoin { NSMutableArray *ary = [NSMutableArray array]; - + for (IRCChannel *c in self.channels) { if (c.isChannel && c.config.autoJoin) { if (c.isActive == NO) { @@ -1042,50 +1043,50 @@ - (void)performAutoJoin } } } - + [self joinChannels:ary]; - + [self performSelector:@selector(updateAutoJoinStatus) withObject:nil afterDelay:5.0]; } - (void)joinChannels:(NSArray *)chans { NSMutableArray *ary = [NSMutableArray array]; - + BOOL pass = YES; - + for (IRCChannel *c in chans) { BOOL hasPass = NSObjectIsNotEmpty(c.password); - + if (pass) { pass = hasPass; - + [ary safeAddObject:c]; } else { if (hasPass) { [self quickJoin:ary]; - + [ary removeAllObjects]; - + pass = hasPass; } - + [ary safeAddObject:c]; } - + if (ary.count >= [TPCPreferences autojoinMaxChannelJoins]) { [self quickJoin:ary]; - + [ary removeAllObjects]; - + pass = YES; } } - + if (NSObjectIsNotEmpty(ary)) { [self quickJoin:ary]; } - + [self.world reloadTree]; } @@ -1097,7 +1098,7 @@ - (void)joinChannels:(NSArray *)chans - (void)startTrialPeriodTimer { if (self.trialPeriodTimer.isActive) return; - + [self.trialPeriodTimer start:_trialPeriodInterval]; } @@ -1110,7 +1111,7 @@ - (void)onTrialPeriodTimer:(id)sender { if (self.isLoggedIn) { self.disconnectType = IRCTrialPeriodDisconnectMode; - + [self quit]; } } @@ -1129,10 +1130,10 @@ - (BOOL)encryptOutgoingMessage:(NSString **)message channel:(IRCChannel *)chan NSString *newstr = [CSFWBlowfish encodeData:*message key:chan.config.encryptionKey encoding:self.config.encoding]; - + if ([newstr length] < 5) { [self printDebugInformation:TXTLS(@"BlowfishEncryptionFailed") channel:chan]; - + return NO; } else { *message = newstr; @@ -1141,7 +1142,7 @@ - (BOOL)encryptOutgoingMessage:(NSString **)message channel:(IRCChannel *)chan } } } - + return YES; } @@ -1154,7 +1155,7 @@ - (void)decryptIncomingMessage:(NSString **)message channel:(IRCChannel *)chan NSString *newstr = [CSFWBlowfish decodeData:*message key:chan.config.encryptionKey encoding:self.config.encoding]; - + if (NSObjectIsNotEmpty(newstr)) { *message = newstr; } @@ -1167,152 +1168,152 @@ - (void)decryptIncomingMessage:(NSString **)message channel:(IRCChannel *)chan #pragma mark - #pragma mark Plugins and Scripts --(void)executeTextualCmdScript:(NSDictionary *)details +-(void)executeTextualCmdScript:(NSDictionary *)details { if ([details containsKey:@"path"] == NO) { return; } - + NSString *scriptPath = [details valueForKey:@"path"]; - + #ifdef TXUserScriptsFolderAvailable BOOL MLNonsandboxedScript = NO; - + NSString *userScriptsPath = [TPCPreferences whereScriptsUnsupervisedPath]; - + if (NSObjectIsNotEmpty(userScriptsPath)) { if ([scriptPath contains:userScriptsPath]) { MLNonsandboxedScript = YES; } } #endif - + if ([scriptPath hasSuffix:@".scpt"]) { /* /////////////////////////////////////////////////////// */ /* Event Descriptor */ /* /////////////////////////////////////////////////////// */ - + NSAppleEventDescriptor *firstParameter = [NSAppleEventDescriptor descriptorWithString:details[@"input"]]; NSAppleEventDescriptor *parameters = [NSAppleEventDescriptor listDescriptor]; - + [parameters insertDescriptor:firstParameter atIndex:1]; - + ProcessSerialNumber psn = { 0, kCurrentProcess }; - + NSAppleEventDescriptor *target = [NSAppleEventDescriptor descriptorWithDescriptorType:typeProcessSerialNumber bytes:&psn length:sizeof(ProcessSerialNumber)]; - + NSAppleEventDescriptor *handler = [NSAppleEventDescriptor descriptorWithString:@"textualcmd"]; NSAppleEventDescriptor *event = [NSAppleEventDescriptor appleEventWithEventClass:kASAppleScriptSuite eventID:kASSubroutineEvent targetDescriptor:target returnID:kAutoGenerateReturnID transactionID:kAnyTransactionID]; - + [event setParamDescriptor:handler forKeyword:keyASSubroutineName]; [event setParamDescriptor:parameters forKeyword:keyDirectObject]; - + /* /////////////////////////////////////////////////////// */ /* Execute Event — Mountain Lion, Non-sandboxed Script */ /* /////////////////////////////////////////////////////// */ - + #ifdef TXUserScriptsFolderAvailable if (MLNonsandboxedScript) { if ([TPCPreferences featureAvailableToOSXMountainLion]) { NSError *aserror = [NSError new]; - + NSUserAppleScriptTask *applescript = [[NSUserAppleScriptTask alloc] initWithURL:[NSURL fileURLWithPath:scriptPath] error:&aserror]; - + if (PointerIsEmpty(applescript)) { NSLog(TXTLS(@"ScriptExecutionFailure"), [aserror localizedDescription]); } else { [applescript executeWithAppleEvent:event completionHandler:^(NSAppleEventDescriptor *result, NSError *error) { - + if (PointerIsEmpty(result)) { NSLog(TXTLS(@"ScriptExecutionFailure"), [error localizedDescription]); - } else { + } else { NSString *finalResult = [result stringValue].trim; - + if (NSObjectIsNotEmpty(finalResult)) { [self.world.iomt inputText:finalResult command:IRCCommandIndexPrivmsg]; } } }]; } - + } - + return; } #endif - + /* /////////////////////////////////////////////////////// */ /* Execute Event — All Other */ /* /////////////////////////////////////////////////////// */ - + NSDictionary *errors = @{}; - + NSAppleScript *appleScript = [[NSAppleScript alloc] initWithContentsOfURL:[NSURL fileURLWithPath:scriptPath] error:&errors]; - + if (appleScript) { NSAppleEventDescriptor *result = [appleScript executeAppleEvent:event error:&errors]; - + if (errors && PointerIsEmpty(result)) { NSLog(TXTLS(@"ScriptExecutionFailure"), errors); - } else { + } else { NSString *finalResult = [result stringValue].trim; - + if (NSObjectIsNotEmpty(finalResult)) { [self.world.iomt inputText:finalResult command:IRCCommandIndexPrivmsg]; } } } else { - NSLog(TXTLS(@"ScriptExecutionFailure"), errors); + NSLog(TXTLS(@"ScriptExecutionFailure"), errors); } - + } else { /* /////////////////////////////////////////////////////// */ /* Execute Shell Script */ /* /////////////////////////////////////////////////////// */ - + NSMutableArray *args = [NSMutableArray array]; - + NSString *input = [details valueForKey:@"input"]; - + for (NSString *i in [input componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]) { [args addObject:i]; } - + NSTask *scriptTask = [NSTask new]; NSPipe *outputPipe = [NSPipe pipe]; - + if ([_NSFileManager() isExecutableFileAtPath:scriptPath] == NO) { NSArray *chmodArguments = @[@"+x", scriptPath]; - + NSTask *chmod = [NSTask launchedTaskWithLaunchPath:@"/bin/chmod" arguments:chmodArguments]; - + [chmod waitUntilExit]; } - + [scriptTask setStandardOutput:outputPipe]; [scriptTask setLaunchPath:scriptPath]; [scriptTask setArguments:args]; - + NSFileHandle *filehandle = [outputPipe fileHandleForReading]; - + [scriptTask launch]; [scriptTask waitUntilExit]; - + NSData *outputData = [filehandle readDataToEndOfFile]; - + NSString *outputString = [NSString stringWithData:outputData encoding:NSUTF8StringEncoding]; - + if (NSObjectIsNotEmpty(outputString)) { [self.world.iomt inputText:outputString command:IRCCommandIndexPrivmsg]; } - + } } @@ -1320,12 +1321,12 @@ - (void)processBundlesUserMessage:(NSArray *)info { NSString *command = NSStringEmptyPlaceholder; NSString *message = [info safeObjectAtIndex:0]; - + if ([info count] == 2) { command = [info safeObjectAtIndex:1]; command = [command uppercaseString]; } - + [NSBundle sendUserInputDataToBundles:self.world message:message command:command client:self]; } @@ -1344,56 +1345,56 @@ - (BOOL)inputText:(id)str command:(NSString *)command return NO; } } - + id sel = self.world.selected; - + if (PointerIsEmpty(sel)) { return NO; } - + if ([str isKindOfClass:[NSString class]]) { str = [NSAttributedString emptyStringWithBase:str]; } - + NSArray *lines = [str performSelector:@selector(splitIntoLines)]; - + for (__strong NSAttributedString *s in lines) { if (NSObjectIsEmpty(s)) { continue; } - + NSRange chopRange = NSMakeRange(1, (s.string.length - 1)); - + if ([sel isClient]) { if ([s.string hasPrefix:@"/"]) { s = [s attributedSubstringFromRange:chopRange]; } - + [self sendCommand:s]; } else { IRCChannel *channel = (IRCChannel *)sel; - + if ([s.string hasPrefix:@"/"] && [s.string hasPrefix:@"//"] == NO) { s = [s attributedSubstringFromRange:chopRange]; - + [self sendCommand:s]; } else { if ([s.string hasPrefix:@"/"]) { s = [s attributedSubstringFromRange:chopRange]; } - + [self sendText:s command:command channel:channel]; } } } - + return YES; } - (void)sendPrivmsgToSelectedChannel:(NSString *)message { NSAttributedString *new = [NSAttributedString emptyStringWithBase:message]; - + [self sendText:new command:IRCCommandIndexPrivmsg channel:[self.world selectedChannelOn:self]]; } @@ -1402,9 +1403,9 @@ - (void)sendText:(NSAttributedString *)str command:(NSString *)command channel:( if (NSObjectIsEmpty(str)) { return; } - + TVCLogLineType type; - + if ([command isEqualToString:IRCCommandIndexNotice]) { type = TVCLogLineNoticeType; } else if ([command isEqualToString:IRCCommandIndexAction]) { @@ -1412,42 +1413,42 @@ - (void)sendText:(NSAttributedString *)str command:(NSString *)command channel:( } else { type = TVCLogLinePrivateMessageType; } - + if ([self.world.bundlesForUserInput containsKey:command]) { [self.invokeInBackgroundThread processBundlesUserMessage:@[str.string, (id)nil]]; } - + NSArray *lines = [str performSelector:@selector(splitIntoLines)]; - + for (NSAttributedString *line in lines) { if (NSObjectIsEmpty(line)) { continue; } - + NSMutableAttributedString *str = [line mutableCopy]; - + while (NSObjectIsNotEmpty(str)) { NSString *newstr = [str attributedStringToASCIIFormatting:&str lineType:type channel:channel.name hostmask:self.myHost]; - + [self printBoth:channel type:type nick:self.myNick text:newstr identified:YES]; - + if ([self encryptOutgoingMessage:&newstr channel:channel] == NO) { continue; } - + NSString *cmd = command; - + if (type == TVCLogLineActionType) { cmd = IRCCommandIndexPrivmsg; - + newstr = [NSString stringWithFormat:@"%c%@ %@%c", 0x01, IRCCommandIndexAction, newstr, 0x01]; } else if (type == TVCLogLinePrivateMessageType) { [channel detectOutgoingConversation:newstr]; } - + [self send:cmd, channel.name, newstr, nil]; } } @@ -1458,28 +1459,28 @@ - (void)sendCTCPQuery:(NSString *)target command:(NSString *)command text:(NSStr if (NSObjectIsEmpty(command)) { return; } - + NSString *trail; - + if (NSObjectIsNotEmpty(text)) { trail = [NSString stringWithFormat:@"%c%@ %@%c", 0x01, command, text, 0x01]; } else { trail = [NSString stringWithFormat:@"%c%@%c", 0x01, command, 0x01]; } - + [self send:IRCCommandIndexPrivmsg, target, trail, nil]; } - (void)sendCTCPReply:(NSString *)target command:(NSString *)command text:(NSString *)text { NSString *trail; - + if (NSObjectIsNotEmpty(text)) { trail = [NSString stringWithFormat:@"%c%@ %@%c", 0x01, command, text, 0x01]; } else { trail = [NSString stringWithFormat:@"%c%@%c", 0x01, command, 0x01]; } - + [self send:IRCCommandIndexNotice, target, trail, nil]; } @@ -1496,7 +1497,7 @@ - (BOOL)sendCommand:(id)str - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString *)targetChannelName { NSMutableAttributedString *s = [NSMutableAttributedString alloc]; - + if ([str isKindOfClass:[NSString class]]) { s = [s initWithString:str]; } else { @@ -1504,17 +1505,17 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString s = [s initWithAttributedString:str]; } } - + NSString *cmd = [s.getToken.string uppercaseString]; - + if (NSObjectIsEmpty(cmd)) return NO; if (NSObjectIsEmpty(str)) return NO; - + IRCClient *u = [self.world selectedClient]; IRCChannel *c = [self.world selectedChannel]; - + IRCChannel *selChannel = nil; - + if ([cmd isEqualToString:IRCCommandIndexMode] && ([s.string hasPrefix:@"+"] || [s.string hasPrefix:@"-"]) == NO) { // Do not complete for /mode #chname ... } else if (completeTarget && targetChannelName) { @@ -1522,65 +1523,65 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString } else if (completeTarget && u == self && c) { selChannel = c; } - + BOOL cutColon = NO; - + if ([s.string hasPrefix:@"/"]) { cutColon = YES; - + [s deleteCharactersInRange:NSMakeRange(0, 1)]; } - + switch ([TPCPreferences indexOfIRCommand:cmd]) { case 3: // Command: AWAY { NSString *msg = s.string; - + if (NSObjectIsEmpty(s) && cutColon == NO) { if (self.isAway == NO) { msg = TXTLS(@"IRCAwayCommandDefaultReason"); } } - + if ([TPCPreferences awayAllConnections]) { for (IRCClient *u in [self.world clients]) { if (u.isConnected == NO) continue; - + [u.client send:cmd, msg, nil]; } } else { if (self.isConnected == NO) return NO; - + [self send:cmd, msg, nil]; } - + return YES; break; } case 5: // Command: INVITE { /* invite nick[ nick[ ...]] [channel] */ - + if (NSObjectIsEmpty(s)) { return NO; } - + NSMutableArray *nicks = [NSMutableArray arrayWithArray:[s.mutableString componentsSeparatedByString:NSStringWhitespacePlaceholder]]; - + if ([nicks count] && [nicks.lastObject isChannelName]) { targetChannelName = [nicks lastObject]; - + [nicks removeLastObject]; } else if (c) { targetChannelName = c.name; } else { return NO; } - + for (NSString *nick in nicks) { [self send:cmd, nick, targetChannelName, nil]; } - + return YES; break; } @@ -1593,16 +1594,16 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString if (NSObjectIsEmpty(s)) { return NO; } - + targetChannelName = s.getToken.string; - + if ([targetChannelName isChannelName] == NO && [targetChannelName isEqualToString:@"0"] == NO) { targetChannelName = [@"#" stringByAppendingString:targetChannelName]; } } - + [self send:IRCCommandIndexJoin, targetChannelName, s.string, nil]; - + return YES; break; } @@ -1614,39 +1615,39 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString if (NSObjectIsEmpty(s)) { return NO; } - + targetChannelName = s.getToken.string; } - + NSString *peer = s.getToken.string; - + if (peer) { NSString *reason = [s.string trim]; - + if (NSObjectIsEmpty(reason)) { reason = [TPCPreferences defaultKickMessage]; } - + [self send:cmd, targetChannelName, peer, reason, nil]; } - + return YES; break; } case 9: // Command: KILL { NSString *peer = s.getToken.string; - + if (peer) { NSString *reason = [s.string trim]; - + if (NSObjectIsEmpty(reason)) { reason = [TPCPreferences IRCopDefaultKillMessage]; } - + [self send:IRCCommandIndexKill, peer, reason, nil]; } - + return YES; break; } @@ -1655,28 +1656,28 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString if (PointerIsEmpty(self.channelListDialog)) { [self createChannelListDialog]; } - + [self send:IRCCommandIndexList, s.string, nil]; - + return YES; break; } case 13: // Command: NICK { NSString *newnick = s.getToken.string; - + if ([TPCPreferences nickAllConnections]) { for (IRCClient *u in [self.world clients]) { if ([u isConnected] == NO) continue; - + [u.client changeNick:newnick]; } } else { if (self.isConnected == NO) return NO; - + [self changeNick:newnick]; } - + return YES; break; } @@ -1692,31 +1693,31 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString { BOOL opMsg = NO; BOOL secretMsg = NO; - + if ([cmd isEqualToString:IRCCommandIndexMsg]) { cmd = IRCCommandIndexPrivmsg; } else if ([cmd isEqualToString:IRCCommandIndexOmsg]) { opMsg = YES; - + cmd = IRCCommandIndexPrivmsg; } else if ([cmd isEqualToString:IRCCommandIndexOnotice]) { opMsg = YES; - + cmd = IRCCommandIndexNotice; } else if ([cmd isEqualToString:IRCCommandIndexSme]) { secretMsg = YES; - + cmd = IRCCommandIndexMe; } else if ([cmd isEqualToString:IRCCommandIndexSmsg]) { secretMsg = YES; - + cmd = IRCCommandIndexPrivmsg; - } - - if ([cmd isEqualToString:IRCCommandIndexPrivmsg] || - [cmd isEqualToString:IRCCommandIndexNotice] || + } + + if ([cmd isEqualToString:IRCCommandIndexPrivmsg] || + [cmd isEqualToString:IRCCommandIndexNotice] || [cmd isEqualToString:IRCCommandIndexAction]) { - + if (opMsg) { if (selChannel && selChannel.isChannel && [s.string isChannelName] == NO) { targetChannelName = selChannel.name; @@ -1728,81 +1729,81 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString } } else if ([cmd isEqualToString:IRCCommandIndexMe]) { cmd = IRCCommandIndexAction; - + if (selChannel) { targetChannelName = selChannel.name; } else { targetChannelName = s.getToken.string; } } - + if ([cmd isEqualToString:IRCCommandIndexPrivmsg] || [cmd isEqualToString:IRCCommandIndexNotice]) { - + if ([s.string hasPrefix:@"\x01"]) { cmd = (([cmd isEqualToString:IRCCommandIndexPrivmsg]) ? IRCCommandIndexCtcp : IRCCommandIndexCtcpreply); - + [s deleteCharactersInRange:NSMakeRange(0, 1)]; - + NSRange r = [s.string rangeOfString:@"\x01"]; - + if (NSDissimilarObjects(r.location, NSNotFound)) { NSInteger len = (s.length - r.location); - + if (len > 0) { [s deleteCharactersInRange:NSMakeRange(r.location, len)]; } } } } - + if ([cmd isEqualToString:IRCCommandIndexCtcp]) { NSMutableAttributedString *t = s.mutableCopy; - + NSString *subCommand = [t.getToken.string uppercaseString]; - + if ([subCommand isEqualToString:IRCCommandIndexAction]) { cmd = IRCCommandIndexAction; - + s = t; - + targetChannelName = s.getToken.string; } else { NSString *subCommand = [s.getToken.string uppercaseString]; - + if (NSObjectIsNotEmpty(subCommand)) { targetChannelName = s.getToken.string; - + if ([subCommand isEqualToString:IRCCommandIndexPing]) { [self sendCTCPPing:targetChannelName]; } else { [self sendCTCPQuery:targetChannelName command:subCommand text:s.string]; } } - + return YES; } } - + if ([cmd isEqualToString:IRCCommandIndexCtcpreply]) { targetChannelName = s.getToken.string; - + NSString *subCommand = s.getToken.string; - + [self sendCTCPReply:targetChannelName command:subCommand text:s.string]; - + return YES; } - - if ([cmd isEqualToString:IRCCommandIndexPrivmsg] || - [cmd isEqualToString:IRCCommandIndexNotice] || + + if ([cmd isEqualToString:IRCCommandIndexPrivmsg] || + [cmd isEqualToString:IRCCommandIndexNotice] || [cmd isEqualToString:IRCCommandIndexAction]) { - + if (NSObjectIsEmpty(s)) return NO; if (NSObjectIsEmpty(targetChannelName)) return NO; - + TVCLogLineType type; - + if ([cmd isEqualToString:IRCCommandIndexNotice]) { type = TVCLogLineNoticeType; } else if ([cmd isEqualToString:IRCCommandIndexAction]) { @@ -1810,30 +1811,30 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString } else { type = TVCLogLinePrivateMessageType; } - + while (NSObjectIsNotEmpty(s)) { NSArray *targets = [targetChannelName componentsSeparatedByString:@","]; - + NSString *t = [s attributedStringToASCIIFormatting:&s lineType:type channel:targetChannelName hostmask:self.myHost]; - + for (__strong NSString *chname in targets) { if (NSObjectIsEmpty(chname)) { continue; } - + BOOL opPrefix = NO; - + if ([chname hasPrefix:@"@"]) { opPrefix = YES; - + chname = [chname safeSubstringFromIndex:1]; } - + IRCChannel *c = [self findChannel:chname]; - + if (PointerIsEmpty(c) && secretMsg == NO && [chname isChannelName] == NO) { if (type == TVCLogLineNoticeType) { c = (id)self; @@ -1841,38 +1842,38 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString c = [self.world createTalk:chname client:self]; } } - + if (c) { [self printBoth:c type:type nick:self.myNick text:t identified:YES]; - + if ([self encryptOutgoingMessage:&t channel:c] == NO) { continue; } } - + if ([chname isChannelName]) { if (opMsg || opPrefix) { chname = [@"@" stringByAppendingString:chname]; } } - + NSString *localCmd = cmd; - + if ([localCmd isEqualToString:IRCCommandIndexAction]) { localCmd = IRCCommandIndexPrivmsg; - + t = [NSString stringWithFormat:@"\x01%@ %@\x01", IRCCommandIndexAction, t]; } - + [self send:localCmd, chname, t, nil]; - + if (c && [TPCPreferences giveFocusOnMessage]) { [self.world select:c]; } } } - } - + } + return YES; break; } @@ -1883,29 +1884,29 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString targetChannelName = selChannel.name; } else if (selChannel && selChannel.isTalk && [s.string isChannelName] == NO) { [self.world destroyChannel:selChannel]; - + return YES; } else { targetChannelName = s.getToken.string; } - + if (targetChannelName) { NSString *reason = [s.string trim]; - + if (NSObjectIsEmpty(s) && cutColon == NO) { reason = [self.config leavingComment]; } - + [self partUnlistedChannel:targetChannelName withComment:reason]; } - + return YES; break; } case 20: // Command: QUIT { [self quit:s.string.trim]; - + return YES; break; } @@ -1917,81 +1918,81 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString } else { targetChannelName = s.getToken.string; } - + if (targetChannelName) { NSString *topic = [s attributedStringToASCIIFormatting]; - + if (NSObjectIsEmpty(topic)) { topic = nil; } - + IRCChannel *c = [self findChannel:targetChannelName]; - + if ([self encryptOutgoingMessage:&topic channel:c] == YES) { [self send:IRCCommandIndexTopic, targetChannelName, topic, nil]; } } - + return YES; break; } case 23: // Command: WHO { self.inWhoInfoRun = YES; - + [self send:IRCCommandIndexWho, s.string, nil]; - + return YES; break; } case 24: // Command: WHOIS { NSString *peer = s.string; - + if (NSObjectIsEmpty(peer)) { IRCChannel *c = self.world.selectedChannel; - + if (c.isTalk) { peer = c.name; } else { return NO; } } - + if ([s.string contains:NSStringWhitespacePlaceholder]) { [self sendLine:[NSString stringWithFormat:@"%@ %@", IRCCommandIndexWhois, peer]]; } else { [self send:IRCCommandIndexWhois, peer, peer, nil]; } - + return YES; break; } case 32: // Command: CTCP - { + { targetChannelName = s.getToken.string; - + if (NSObjectIsNotEmpty(targetChannelName)) { NSString *subCommand = [s.getToken.string uppercaseString]; - + if ([subCommand isEqualToString:IRCCommandIndexPing]) { [self sendCTCPPing:targetChannelName]; } else { [self sendCTCPQuery:targetChannelName command:subCommand text:s.string]; } } - + return YES; break; } case 33: // Command: CTCPREPLY { targetChannelName = s.getToken.string; - + NSString *subCommand = s.getToken.string; - + [self sendCTCPReply:targetChannelName command:subCommand text:s.string]; - + return YES; break; } @@ -2000,12 +2001,12 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString { if (c) { NSString *peer = s.getToken.string; - + if (peer) { IRCUser *user = [c findMember:peer]; - + NSString *host = ((user) ? [user banMask] : peer); - + if ([cmd isEqualToString:IRCCommandIndexBan]) { [self sendCommand:[NSString stringWithFormat:@"MODE +b %@", host] completeTarget:YES target:c.name]; } else { @@ -2013,7 +2014,7 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString } } } - + return YES; break; } @@ -2030,7 +2031,7 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString if ([cmd isEqualToString:IRCCommandIndexM]) { cmd = IRCCommandIndexMode; } - + if ([cmd isEqualToString:IRCCommandIndexMode]) { if (selChannel && selChannel.isChannel && [s.string isModeChannelName] == NO) { targetChannelName = selChannel.name; @@ -2046,56 +2047,56 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString } else { targetChannelName = s.getToken.string; } - + NSString *sign; - + if ([cmd hasPrefix:@"DE"] || [cmd hasPrefix:@"UN"]) { sign = @"-"; - + cmd = [cmd safeSubstringFromIndex:2]; } else { sign = @"+"; } - + NSArray *params = [s.string componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - + if (NSObjectIsEmpty(params)) { return YES; } else { NSMutableString *ms = [NSMutableString stringWithString:sign]; - + NSString *modeCharStr; - + modeCharStr = [cmd safeSubstringToIndex:1]; modeCharStr = [modeCharStr lowercaseString]; - + for (NSInteger i = (params.count - 1); i >= 0; --i) { [ms appendString:modeCharStr]; } - + [ms appendString:NSStringWhitespacePlaceholder]; [ms appendString:s.string]; - + [s setAttributedString:[NSAttributedString emptyStringWithBase:ms]]; } } - + NSMutableString *line = [NSMutableString string]; - + [line appendString:IRCCommandIndexMode]; - + if (NSObjectIsNotEmpty(targetChannelName)) { [line appendString:NSStringWhitespacePlaceholder]; [line appendString:targetChannelName]; } - + if (NSObjectIsNotEmpty(s)) { [line appendString:NSStringWhitespacePlaceholder]; [line appendString:s.string]; } - + [self sendLine:line]; - + return YES; break; } @@ -2103,21 +2104,21 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString { if (c) { [self.world clearContentsOfChannel:c inClient:self]; - + [c setDockUnreadCount:0]; [c setTreeUnreadCount:0]; [c setKeywordCount:0]; } else if (u) { [self.world clearContentsOfClient:self]; - + [u setDockUnreadCount:0]; [u setTreeUnreadCount:0]; [u setKeywordCount:0]; } - + [self.world updateIcon]; [self.world reloadTree]; - + return YES; break; } @@ -2125,15 +2126,15 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString case 77: // Command: REMOVE { NSString *nick = s.getToken.string; - + if (NSObjectIsNotEmpty(nick)) { c = [self findChannel:nick]; } - + if (c) { [self.world destroyChannel:c]; } - + return YES; break; } @@ -2143,15 +2144,15 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString { if (c) { NSString *pass = nil; - + if ([c.mode modeIsDefined:@"k"]) { pass = [c.mode modeInfoFor:@"k"].param; } - + [self partChannel:c]; [self forceJoinChannel:c.name password:pass]; } - + return YES; break; } @@ -2162,21 +2163,21 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString [self.world.menuController showServerPropertyDialog:self ignore:@"--"]; } else { NSString *n = s.getToken.string; - + IRCUser *u = [c findMember:n]; - + if (PointerIsEmpty(u)) { [self.world.menuController showServerPropertyDialog:self ignore:n]; - + return YES; } - + NSString *hostmask = [u banMask]; - + IRCAddressBook *g = [IRCAddressBook new]; - + g.hostmask = hostmask; - + g.ignorePublicMsg = YES; g.ignorePrivateMsg = YES; g.ignoreHighlights = YES; @@ -2185,42 +2186,42 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString g.ignoreCTCP = YES; g.ignoreJPQE = YES; g.notifyJoins = NO; - + [g processHostMaskRegex]; - + if ([cmd isEqualToString:IRCCommandIndexIgnore]) { BOOL found = NO; - + for (IRCAddressBook *e in self.config.ignores) { if ([g.hostmask isEqualToString:e.hostmask]) { found = YES; - + break; } } - + if (found == NO) { [self.config.ignores safeAddObject:g]; - + [self.world save]; } } else { NSMutableArray *ignores = self.config.ignores; - + for (NSInteger i = (ignores.count - 1); i >= 0; --i) { IRCAddressBook *e = [ignores safeObjectAtIndex:i]; - + if ([g.hostmask isEqualToString:e.hostmask]) { [ignores safeRemoveObjectAtIndex:i]; - + [self.world save]; - + break; } } } } - + return YES; break; } @@ -2228,47 +2229,47 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString case 60: // Command: QUOTE { [self sendLine:s.string]; - + return YES; break; } case 59: // Command: QUERY { NSString *nick = s.getToken.string; - + if (NSObjectIsEmpty(nick)) { if (c && c.isTalk) { [self.world destroyChannel:c]; } } else { IRCChannel *c = [self findChannelOrCreate:nick useTalk:YES]; - + [self.world select:c]; } - + return YES; break; } case 62: // Command: TIMER - { + { NSInteger interval = [s.getToken.string integerValue]; - + if (interval > 0) { TLOTimerCommand *cmd = [TLOTimerCommand new]; - + if ([s.string hasPrefix:@"/"]) { [s deleteCharactersInRange:NSMakeRange(0, 1)]; } - + cmd.input = s.string; cmd.cid = ((c) ? c.uid : -1); cmd.time = (CFAbsoluteTimeGetCurrent() + interval); - + [self addCommandToCommandQueue:cmd]; } else { [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineErrorReplyType text:TXTLS(@"IRCTimerCommandRequiresInteger")]; } - + return YES; break; } @@ -2276,22 +2277,22 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString { if (c) { NSInteger tc = 0; - + for (IRCUser *m in c.members) { if (m.totalWeight > 0) { NSString *text = TXTFLS(@"IRCWeightsCommandResultRow", m.nick, m.incomingWeight, m.outgoingWeight, m.totalWeight); - + tc++; - + [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:text]; } } - + if (tc == 0) { [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:TXTLS(@"IRCWeightsCommandNoResults")]; } } - + return YES; break; } @@ -2300,16 +2301,16 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString { if ([s.string isEqualNoCase:@"raw on"]) { self.rawModeEnabled = YES; - + [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:TXTLS(@"IRCRawModeIsEnabled")]; } else if ([s.string isEqualNoCase:@"raw off"]) { - self.rawModeEnabled = NO; - + self.rawModeEnabled = NO; + [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:TXTLS(@"IRCRawModeIsDisabled")]; } else { [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:s.string]; } - + return YES; break; } @@ -2317,10 +2318,10 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString { if ([TPCPreferences clearAllOnlyOnActiveServer]) { [self.world clearContentsOfClient:self]; - + for (IRCChannel *c in self.channels) { [self.world clearContentsOfChannel:c inClient:self]; - + [c setDockUnreadCount:0]; [c setTreeUnreadCount:0]; [c setKeywordCount:0]; @@ -2328,116 +2329,116 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString } else { for (IRCClient *u in [self.world clients]) { [self.world clearContentsOfClient:u]; - + for (IRCChannel *c in [u channels]) { [self.world clearContentsOfChannel:c inClient:u]; - + [c setDockUnreadCount:0]; [c setTreeUnreadCount:0]; [c setKeywordCount:0]; } } } - + [self.world updateIcon]; [self.world reloadTree]; [self.world markAllAsRead]; - + return YES; break; } case 72: // Command: AMSG { [s insertAttributedString:[NSAttributedString emptyStringWithBase:@"MSG "] atIndex:0]; - + if ([TPCPreferences amsgAllConnections]) { for (IRCClient *u in [self.world clients]) { if ([u isConnected] == NO) continue; - + for (IRCChannel *c in [u channels]) { c.isUnread = YES; - + [u.client sendCommand:s completeTarget:YES target:c.name]; } } } else { if (self.isConnected == NO) return NO; - + for (IRCChannel *c in self.channels) { c.isUnread = YES; - + [self sendCommand:s completeTarget:YES target:c.name]; } } - + [self reloadTree]; - + return YES; break; } case 73: // Command: AME { [s insertAttributedString:[NSAttributedString emptyStringWithBase:@"ME "] atIndex:0]; - + if ([TPCPreferences amsgAllConnections]) { for (IRCClient *u in [self.world clients]) { if ([u isConnected] == NO) continue; - + for (IRCChannel *c in [u channels]) { c.isUnread = YES; - + [u.client sendCommand:s completeTarget:YES target:c.name]; } } } else { if (self.isConnected == NO) return NO; - + for (IRCChannel *c in self.channels) { c.isUnread = YES; - + [u.client sendCommand:s completeTarget:YES target:c.name]; } } - + [self reloadTree]; - + return YES; break; } case 78: // Command: KB - case 79: // Command: KICKBAN + case 79: // Command: KICKBAN { if (c) { NSString *peer = s.getToken.string; - + if (peer) { NSString *reason = [s.string trim]; - + IRCUser *user = [c findMember:peer]; - + NSString *host = ((user) ? [user banMask] : peer); - + if (NSObjectIsEmpty(reason)) { reason = [TPCPreferences defaultKickMessage]; } - + [self send:IRCCommandIndexMode, c.name, @"+b", host, nil]; [self send:IRCCommandIndexKick, c.name, user.nick, reason, nil]; } } - + return YES; break; } case 81: // Command: ICBADGE { if ([s.string contains:NSStringWhitespacePlaceholder] == NO) return NO; - + NSArray *data = [s.string componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - - [TVCDockIcon drawWithHilightCount:[data integerAtIndex:0] + + [TVCDockIcon drawWithHilightCount:[data integerAtIndex:0] messageCount:[data integerAtIndex:1]]; - + return YES; break; } @@ -2446,7 +2447,7 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString if (NSObjectIsNotEmpty(s)) { [self.world createConnection:s.string chan:nil]; } - + return YES; break; } @@ -2455,11 +2456,11 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString if (NSObjectIsNotEmpty(s)) { [self.config setHost:s.getToken.string]; } - + if (self.isConnected) [self quit]; - + [self performSelector:@selector(connect) withObject:nil afterDelay:2.0]; - + return YES; break; } @@ -2468,19 +2469,19 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString NSString *ref = [TPCPreferences gitBuildReference]; NSString *name = [TPCPreferences applicationName]; NSString *vers = [TPCPreferences textualInfoPlist][@"CFBundleVersion"]; - + NSString *text = [NSString stringWithFormat:TXTLS(@"IRCCTCPVersionInfo"), name, vers, ((NSObjectIsEmpty(ref)) ? TXTLS(@"Unknown") : ref), [TPCPreferences textualInfoPlist][@"TXBundleBuildCodeName"]]; - + if (c.isChannel == NO && c.isTalk == NO) { [self printDebugInformationToConsole:text]; } else { text = TXTFLS(@"IRCCTCPVersionTitle", text); - + [self sendPrivmsgToSelectedChannel:text]; } - + return YES; break; } @@ -2490,10 +2491,10 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:TXTLS(@"SoundIsAlreadyMuted")]; } else { [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:TXTLS(@"SoundIsNowMuted")]; - + [self.world setSoundMuted:YES]; } - + return YES; break; } @@ -2501,26 +2502,26 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString { if (self.world.soundMuted) { [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:TXTLS(@"SoundIsNoLongerMuted")]; - + [self.world setSoundMuted:NO]; } else { [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:TXTLS(@"SoundIsNotMuted")]; } - + return YES; break; } case 76: // Command: UNLOAD_PLUGINS { [NSBundle.invokeInBackgroundThread deallocBundlesFromMemory:self.world]; - + return YES; break; } case 91: // Command: LOAD_PLUGINS { [NSBundle.invokeInBackgroundThread loadBundlesIntoMemory:self.world]; - + return YES; break; } @@ -2528,15 +2529,15 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString case 95: // Command: MYLAG { self.lastLagCheck = CFAbsoluteTimeGetCurrent(); - + if ([cmd isEqualNoCase:IRCCommandIndexMylag]) { self.sendLagcheckToChannel = YES; } - + [self sendCTCPQuery:self.myNick command:IRCCommandIndexLagcheck text:[NSString stringWithDouble:self.lastLagCheck]]; - + [self printDebugInformation:TXTLS(@"LagCheckRequestSentMessage")]; - + return YES; break; } @@ -2545,34 +2546,34 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString case 98: // Command: GZLINE { NSString *peer = s.getToken.string; - + if ([peer hasPrefix:@"-"]) { [self send:cmd, peer, s.string, nil]; } else { NSString *time = s.getToken.string; NSString *reason = s.string; - + if (peer) { reason = [reason trim]; - + if (NSObjectIsEmpty(reason)) { reason = [TPCPreferences IRCopDefaultGlineMessage]; - + if ([reason contains:NSStringWhitespacePlaceholder]) { NSInteger spacePos = [reason stringPosition:NSStringWhitespacePlaceholder]; - + if (NSObjectIsEmpty(time)) { time = [reason safeSubstringToIndex:spacePos]; } - + reason = [reason safeSubstringAfterIndex:spacePos]; } } - + [self send:cmd, peer, time, reason, nil]; } } - + return YES; break; } @@ -2580,48 +2581,48 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString case 100: // Command: TEMPSHUN { NSString *peer = s.getToken.string; - + if ([peer hasPrefix:@"-"]) { [self send:cmd, peer, s.string, nil]; } else { if (peer) { if ([cmd isEqualToString:IRCCommandIndexTempshun]) { NSString *reason = s.getToken.string.trim; - + if (NSObjectIsEmpty(reason)) { reason = [TPCPreferences IRCopDefaultShunMessage]; - + if ([reason contains:NSStringWhitespacePlaceholder]) { NSInteger spacePos = [reason stringPosition:NSStringWhitespacePlaceholder]; - + reason = [reason safeSubstringAfterIndex:spacePos]; } } - + [self send:cmd, peer, reason, nil]; } else { NSString *time = s.getToken.string; NSString *reason = s.string.trim; - + if (NSObjectIsEmpty(reason)) { reason = [TPCPreferences IRCopDefaultShunMessage]; - + if ([reason contains:NSStringWhitespacePlaceholder]) { NSInteger spacePos = [reason stringPosition:NSStringWhitespacePlaceholder]; - + if (NSObjectIsEmpty(time)) { time = [reason safeSubstringToIndex:spacePos]; } - + reason = [reason safeSubstringAfterIndex:spacePos]; } } - + [self send:cmd, peer, time, reason, nil]; } } } - + return YES; break; } @@ -2630,43 +2631,43 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString { if (NSObjectIsNotEmpty(self.acceptedCaps)) { NSString *caps = [self.acceptedCaps componentsJoinedByString:@", "]; - + [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:TXTFLS(@"IRCCapCurrentlyEnbaled", caps)]; } else { [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:TXTLS(@"IRCCapCurrentlyEnabledNone")]; } - + return YES; break; } case 104: // Command: CCBADGE { NSString *chan = s.getToken.string; - + if (NSObjectIsEmpty(chan)) { return NO; } - + NSInteger count = [s.getToken.string integerValue]; - + IRCChannel *c = [self findChannel:chan]; - + if (PointerIsNotEmpty(c)) { [c setTreeUnreadCount:count]; - + NSString *hlt = s.getToken.string; - + if (NSObjectIsNotEmpty(hlt)) { if ([hlt isEqualToString:@"-h"]) { [c setIsKeyword:YES]; - + [c setKeywordCount:1]; } } - + [self.world reloadTree]; } - + return YES; break; } @@ -2681,12 +2682,19 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString return YES; break; } - default: - { - NSString *command = [cmd lowercaseString]; - - NSArray *extensions = @[@".scpt", @".py", @".pyc", @".rb", @".pl", @".sh", @".bash", NSStringEmptyPlaceholder]; - + case 106: // Command: SSLCONTEXT + { + [self.conn.conn openSSLCertificateTrustDialog]; + + return YES; + break; + } + default: + { + NSString *command = [cmd lowercaseString]; + + NSArray *extensions = @[@".scpt", @".py", @".pyc", @".rb", @".pl", @".sh", @".bash", NSStringEmptyPlaceholder]; + #ifdef TXUserScriptsFolderAvailable NSArray *scriptPaths = @[ NSStringNilValueSubstitute([TPCPreferences whereScriptsLocalPath]), @@ -2699,41 +2707,41 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString NSStringNilValueSubstitute([TPCPreferences whereScriptsPath]) ]; #endif - + NSString *scriptPath = [NSString string]; - + BOOL scriptFound = NO; - + for (NSString *path in scriptPaths) { if (NSObjectIsEmpty(path)) { continue; } - + if (scriptFound == YES) { break; } - + for (NSString *i in extensions) { NSString *filename = [NSString stringWithFormat:@"%@%@", command, i]; - + scriptPath = [path stringByAppendingPathComponent:filename]; scriptFound = [_NSFileManager() fileExistsAtPath:scriptPath]; - + if (scriptFound == YES) { break; } } } - + BOOL pluginFound = BOOLValueFromObject([self.world.bundlesForUserInput objectForKey:cmd]); - + if (pluginFound && scriptFound) { NSLog(TXTLS(@"PluginCommandClashErrorMessage") ,cmd); } else { if (pluginFound) { [self.invokeInBackgroundThread processBundlesUserMessage: @[[NSString stringWithString:s.string], cmd]]; - + return YES; } else { if (scriptFound) { @@ -2743,42 +2751,42 @@ - (BOOL)sendCommand:(id)str completeTarget:(BOOL)completeTarget target:(NSString @"input": s.string, @"completeTarget": @(completeTarget), @"target": NSStringNilValueSubstitute(targetChannelName)}; - + [self.invokeInBackgroundThread executeTextualCmdScript:inputInfo]; - + return YES; } } } - + if (cutColon) { [s insertAttributedString:[NSAttributedString emptyStringWithBase:@":"] atIndex:0]; } - + if ([s length]) { [s insertAttributedString:[NSAttributedString emptyStringWithBase:NSStringWhitespacePlaceholder] atIndex:0]; } - + [s insertAttributedString:[NSAttributedString emptyStringWithBase:cmd] atIndex:0]; - + [self sendLine:s.string]; - + return YES; break; } } - + return NO; } - (void)sendLine:(NSString *)str { [self.conn sendLine:str]; - + if (self.rawModeEnabled) { NSLog(@" << %@", str); } - + self.world.messagesSent++; self.world.bandwidthOut += [str length]; } @@ -2786,36 +2794,36 @@ - (void)sendLine:(NSString *)str - (void)send:(NSString *)str, ... { NSMutableArray *ary = [NSMutableArray array]; - + id obj; - + va_list args; va_start(args, str); - + while ((obj = va_arg(args, id))) { [ary safeAddObject:obj]; } - + va_end(args); - + NSMutableString *s = [NSMutableString stringWithString:str]; - + NSInteger count = ary.count; - + for (NSInteger i = 0; i < count; i++) { NSString *e = [ary safeObjectAtIndex:i]; - + [s appendString:NSStringWhitespacePlaceholder]; - + if (i == (count - 1) && (NSObjectIsEmpty(e) || [e hasPrefix:@":"] || [e contains:NSStringWhitespacePlaceholder])) { - + [s appendString:@":"]; } - + [s appendString:e]; } - + [self sendLine:s]; } @@ -2829,50 +2837,50 @@ - (IRCChannel *)findChannel:(NSString *)name return c; } } - + return nil; } - (IRCChannel *)findChannelOrCreate:(NSString *)name { IRCChannel *c = [self findChannel:name]; - + if (PointerIsEmpty(c)) { return [self findChannelOrCreate:name useTalk:NO]; } - + return c; } - (IRCChannel *)findChannelOrCreate:(NSString *)name useTalk:(BOOL)doTalk { IRCChannel *c = [self findChannel:name]; - + if (PointerIsEmpty(c)) { if (doTalk) { return [self.world createTalk:name client:self]; } else { IRCChannelConfig *seed = [IRCChannelConfig new]; - + seed.name = name; - + return [self.world createChannel:seed client:self reload:YES adjust:YES]; } } - + return c; } - (NSInteger)indexOfTalkChannel { NSInteger i = 0; - + for (IRCChannel *e in self.channels) { if (e.isTalk) return i; - + ++i; } - + return -1; } @@ -2882,32 +2890,32 @@ - (NSInteger)indexOfTalkChannel - (void)processCommandsInCommandQueue { CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); - + while (self.commandQueue.count) { TLOTimerCommand *m = [self.commandQueue safeObjectAtIndex:0]; - + if (m.time <= now) { NSString *target = nil; - + IRCChannel *c = [self.world findChannelByClientId:self.uid channelId:m.cid]; - + if (c) { target = c.name; } - + [self sendCommand:m.input completeTarget:YES target:target]; - + [self.commandQueue safeRemoveObjectAtIndex:0]; } else { break; } } - + if (self.commandQueue.count) { TLOTimerCommand *m = [self.commandQueue safeObjectAtIndex:0]; - + CFAbsoluteTime delta = (m.time - CFAbsoluteTimeGetCurrent()); - + [self.commandQueueTimer start:delta]; } else { [self.commandQueueTimer stop]; @@ -2917,25 +2925,25 @@ - (void)processCommandsInCommandQueue - (void)addCommandToCommandQueue:(TLOTimerCommand *)m { BOOL added = NO; - + NSInteger i = 0; - + for (TLOTimerCommand *c in self.commandQueue) { if (m.time < c.time) { added = YES; - + [self.commandQueue safeInsertObject:m atIndex:i]; - + break; } - + ++i; } - + if (added == NO) { [self.commandQueue safeAddObject:m]; } - + if (i == 0) { [self processCommandsInCommandQueue]; } @@ -2973,20 +2981,20 @@ - (BOOL)notifyText:(TXNotificationType)type lineType:(TVCLogLineType)ltype targe if ([self.myNick isEqual:nick]) { return NO; } - + if ([self outputRuleMatchedInMessage:text inChannel:target withLineType:ltype] == YES) { return NO; } - + IRCChannel *channel = nil; - + NSString *chname = nil; - + if (target) { if ([target isKindOfClass:[IRCChannel class]]) { channel = (IRCChannel *)target; chname = channel.name; - + if (type == TXNotificationHighlightType) { if (channel.config.ignoreHighlights) { return YES; @@ -2998,36 +3006,36 @@ - (BOOL)notifyText:(TXNotificationType)type lineType:(TVCLogLineType)ltype targe chname = (NSString *)target; } } - + if (NSObjectIsEmpty(chname)) { chname = self.name; } - + [TLOSoundPlayer play:[TPCPreferences soundForEvent:type] isMuted:self.world.soundMuted]; - + if ([TPCPreferences growlEnabledForEvent:type] == NO) return YES; if ([TPCPreferences stopGrowlOnActive] && [self.world.window isOnCurrentWorkspace]) return YES; if ([TPCPreferences disableWhileAwayForEvent:type] == YES && self.isAway == YES) return YES; - + NSDictionary *info = nil; - + NSString *title = chname; NSString *desc; - + if (ltype == TVCLogLineActionType || ltype == TVCLogLineActionNoHighlightType) { desc = [NSString stringWithFormat:@"• %@: %@", nick, text]; } else { desc = [NSString stringWithFormat:@"<%@> %@", nick, text]; } - + if (channel) { info = @{@"client": @(self.uid), @"channel": @(channel.uid)}; } else { info = @{@"client": @(self.uid)}; } - + [self.world notifyOnGrowl:type title:title desc:desc userInfo:info]; - + return YES; } @@ -3041,28 +3049,28 @@ - (BOOL)notifyEvent:(TXNotificationType)type lineType:(TVCLogLineType)ltype targ if ([self outputRuleMatchedInMessage:text inChannel:target withLineType:ltype] == YES) { return NO; } - + [TLOSoundPlayer play:[TPCPreferences soundForEvent:type] isMuted:self.world.soundMuted]; - + if ([TPCPreferences growlEnabledForEvent:type] == NO) return YES; if ([TPCPreferences stopGrowlOnActive] && [self.world.window isOnCurrentWorkspace]) return YES; if ([TPCPreferences disableWhileAwayForEvent:type] == YES && self.isAway == YES) return YES; - + IRCChannel *channel = nil; - + if (target) { if ([target isKindOfClass:[IRCChannel class]]) { channel = (IRCChannel *)target; - + if (channel.config.growl == NO) { return YES; } } } - + NSString *title = NSStringEmptyPlaceholder; NSString *desc = NSStringEmptyPlaceholder; - + switch (type) { case TXNotificationConnectType: title = self.name; break; case TXNotificationDisconnectType: title = self.name; break; @@ -3070,32 +3078,32 @@ - (BOOL)notifyEvent:(TXNotificationType)type lineType:(TVCLogLineType)ltype targ case TXNotificationKickType: { title = channel.name; - + desc = TXTFLS(@"NotificationKickedMessageDescription", nick, text); - + break; } case TXNotificationInviteType: { title = self.name; - + desc = TXTFLS(@"NotificationInvitedMessageDescriptioni", nick, text); - + break; } default: return YES; } - + NSDictionary *info = nil; - + if (channel) { info = @{@"client": @(self.uid), @"channel": @(channel.uid)}; } else { info = @{@"client": @(self.uid)}; } - + [self.world notifyOnGrowl:type title:title desc:desc userInfo:info]; - + return YES; } @@ -3105,25 +3113,25 @@ - (BOOL)notifyEvent:(TXNotificationType)type lineType:(TVCLogLineType)ltype targ - (void)setKeywordState:(id)t { BOOL isActiveWindow = [self.world.window isOnCurrentWorkspace]; - + if ([t isKindOfClass:[IRCChannel class]]) { if ([t isChannel] == YES || [t isTalk] == YES) { if (NSDissimilarObjects(self.world.selected, t) || isActiveWindow == NO) { [t setKeywordCount:([t keywordCount] + 1)]; - + [self.world updateIcon]; } } } - + if ([t isUnread] || (isActiveWindow && self.world.selected == t)) { return; } - + [t setIsKeyword:YES]; - + [self reloadTree]; - + if (isActiveWindow == NO) { [NSApp requestUserAttention:NSInformationalRequest]; } @@ -3132,53 +3140,53 @@ - (void)setKeywordState:(id)t - (void)setNewTalkState:(id)t { BOOL isActiveWindow = [self.world.window isOnCurrentWorkspace]; - + if ([t isUnread] || (isActiveWindow && self.world.selected == t)) { return; } - + [t setIsNewTalk:YES]; - + [self reloadTree]; - + if (isActiveWindow == NO) { [NSApp requestUserAttention:NSInformationalRequest]; } - + [self.world updateIcon]; } - (void)setUnreadState:(id)t { BOOL isActiveWindow = [self.world.window isOnCurrentWorkspace]; - + if ([t isKindOfClass:[IRCChannel class]]) { if ([TPCPreferences countPublicMessagesInIconBadge] == NO) { if ([t isTalk] == YES && [t isClient] == NO) { if (NSDissimilarObjects(self.world.selected, t) || isActiveWindow == NO) { [t setDockUnreadCount:([t dockUnreadCount] + 1)]; - + [self.world updateIcon]; } } } else { if (NSDissimilarObjects(self.world.selected, t) || isActiveWindow == NO) { [t setDockUnreadCount:([t dockUnreadCount] + 1)]; - + [self.world updateIcon]; - } + } } } - + if (isActiveWindow == NO || (NSDissimilarObjects(self.world.selected, t) && isActiveWindow)) { [t setTreeUnreadCount:([t treeUnreadCount] + 1)]; } - + if (isActiveWindow && self.world.selected == t) { return; } else { [t setIsUnread:YES]; - + [self reloadTree]; } } @@ -3209,39 +3217,39 @@ - (BOOL)printBoth:(id)chan type:(TVCLogLineType)type nick:(NSString *)nick text: - (NSString *)formatNick:(NSString *)nick channel:(IRCChannel *)channel { NSString *format = [TPCPreferences themeNickFormat]; - + if (NSObjectIsNotEmpty(self.world.viewTheme.other.nicknameFormat)) { format = self.world.viewTheme.other.nicknameFormat; } - + if (NSObjectIsEmpty(format)) { format = TXLogLineUndefinedNicknameFormat; } - + if ([format contains:@"%n"]) { format = [format stringByReplacingOccurrencesOfString:@"%n" withString:nick]; } - + if ([format contains:@"%@"]) { if (channel && channel.isClient == NO && channel.isChannel) { IRCUser *m = [channel findMember:nick]; - + if (m) { NSString *mark = [NSString stringWithChar:m.mark]; - + if ([mark isEqualToString:NSStringWhitespacePlaceholder] || NSObjectIsEmpty(mark)) { format = [format stringByReplacingOccurrencesOfString:@"%@" withString:NSStringEmptyPlaceholder]; } else { format = [format stringByReplacingOccurrencesOfString:@"%@" withString:mark]; } } else { - format = [format stringByReplacingOccurrencesOfString:@"%@" withString:NSStringEmptyPlaceholder]; + format = [format stringByReplacingOccurrencesOfString:@"%@" withString:NSStringEmptyPlaceholder]; } } else { - format = [format stringByReplacingOccurrencesOfString:@"%@" withString:NSStringEmptyPlaceholder]; + format = [format stringByReplacingOccurrencesOfString:@"%@" withString:NSStringEmptyPlaceholder]; } } - + return format; } @@ -3258,36 +3266,36 @@ - (BOOL)printChannel:(id)chan type:(TVCLogLineType)type text:(NSString *)text re - (BOOL)printAndLog:(TVCLogLine *)line withHTML:(BOOL)rawHTML { BOOL result = [self.log print:line withHTML:rawHTML]; - + if (self.isConnected == NO) { return NO; } - + if ([TPCPreferences logTranscript] && rawHTML == NO) { if (PointerIsEmpty(self.logFile)) { self.logFile = [TLOFileLogger new]; self.logFile.client = self; } - + NSString *comp = [NSString stringWithFormat:@"%@", [NSDate.date dateWithCalendarFormat:@"%Y%m%d%H%M%S" timeZone:nil]]; - + if (self.logDate) { if ([self.logDate isEqualToString:comp] == NO) { self.logDate = comp; - + [self.logFile reopenIfNeeded]; } } else { self.logDate = comp; } - + NSString *logstr = [self.log renderedBodyForTranscriptLog:line]; - + if (NSObjectIsNotEmpty(logstr)) { [self.logFile writeLine:logstr]; } } - + return result; } @@ -3304,23 +3312,23 @@ - (BOOL)printRawHTMLToCurrentChannelWithoutTime:(NSString *)text receivedAt:(NSD - (BOOL)printRawHTMLToCurrentChannel:(NSString *)text withTimestamp:(BOOL)showTime receivedAt:(NSDate *)receivedAt { TVCLogLine *c = [TVCLogLine new]; - + IRCChannel *channel = [self.world selectedChannelOn:self]; - + c.body = text; c.lineType = TVCLogLineReplyType; c.memberType = TVCLogMemberNormalType; - + if (showTime) { NSString *time = TXFormattedTimestampWithOverride(receivedAt, [TPCPreferences themeTimestampFormat], self.world.viewTheme.other.timestampFormat); - + if (NSObjectIsNotEmpty(time)) { time = [time stringByAppendingString:NSStringWhitespacePlaceholder]; } - + c.time = time; } - + if (channel) { return [channel print:c withHTML:YES]; } else { @@ -3333,27 +3341,27 @@ - (BOOL)printChannel:(id)chan type:(TVCLogLineType)type nick:(NSString *)nick te if ([self outputRuleMatchedInMessage:text inChannel:chan withLineType:type] == YES) { return NO; } - + NSString *nickStr = nil; NSString *time = TXFormattedTimestampWithOverride(receivedAt, [TPCPreferences themeTimestampFormat], self.world.viewTheme.other.timestampFormat); - + IRCChannel *channel = nil; - + TVCLogMemberType memberType = TVCLogMemberNormalType; - + NSInteger colorNumber = 0; - + NSArray *keywords = nil; NSArray *excludeWords = nil; - + TVCLogLine *c = [TVCLogLine new]; - + if (nick && [nick isEqualToString:self.myNick]) { memberType = TVCLogMemberLocalUserType; } - + if ([chan isKindOfClass:[IRCChannel class]]) { channel = chan; } else if ([chan isKindOfClass:[NSString class]]) { @@ -3361,38 +3369,38 @@ - (BOOL)printChannel:(id)chan type:(TVCLogLineType)type nick:(NSString *)nick te return NO; } } - + if (type == TVCLogLinePrivateMessageType || type == TVCLogLineActionType) { if (NSDissimilarObjects(memberType, TVCLogMemberLocalUserType)) { if (channel && channel.config.ignoreHighlights == NO) { keywords = [TPCPreferences keywords]; excludeWords = [TPCPreferences excludeWords]; - + if (NSDissimilarObjects([TPCPreferences keywordMatchingMethod], TXNicknameHighlightRegularExpressionMatchType)) { - + if ([TPCPreferences keywordCurrentNick]) { NSMutableArray *ary = [keywords mutableCopy]; - + [ary safeInsertObject:self.myNick atIndex:0]; - + keywords = ary; } } } } } - + if (type == TVCLogLineActionNoHighlightType) { type = TVCLogLineActionType; } else if (type == TVCLogLinePrivateMessageNoHighlightType) { type = TVCLogLinePrivateMessageType; } - + if (NSObjectIsNotEmpty(time)) { time = [time stringByAppendingString:NSStringWhitespacePlaceholder]; } - + if (NSObjectIsNotEmpty(nick)) { if (type == TVCLogLineActionType) { nickStr = [NSString stringWithFormat:TXLogLineActionNicknameFormat, nick]; @@ -3402,33 +3410,33 @@ - (BOOL)printChannel:(id)chan type:(TVCLogLineType)type nick:(NSString *)nick te nickStr = [self formatNick:nick channel:channel]; } } - + if (nick && channel && (type == TVCLogLinePrivateMessageType || type == TVCLogLineActionType)) { IRCUser *user = [channel findMember:nick]; - + if (user) { colorNumber = user.colorNumber; } } - + c.time = time; c.nick = nickStr; c.body = text; - + c.lineType = type; c.memberType = memberType; c.nickInfo = nick; c.identified = identified; c.nickColorNumber = colorNumber; - + c.keywords = keywords; c.excludeWords = excludeWords; - + if (channel) { if ([TPCPreferences autoAddScrollbackMark]) { if (NSDissimilarObjects(channel, self.world.selectedChannel) || [self.world.window isOnCurrentWorkspace] == NO) { - + if (channel.isUnread == NO) { if (type == TVCLogLinePrivateMessageType || type == TVCLogLineActionType || type == TVCLogLineNoticeType) { [channel.log unmark]; @@ -3437,7 +3445,7 @@ - (BOOL)printChannel:(id)chan type:(TVCLogLineType)type nick:(NSString *)nick te } } } - + return [channel print:c]; } else { if ([TPCPreferences logTranscript]) { @@ -3458,7 +3466,7 @@ - (void)printSystem:(id)channel text:(NSString *)text receivedAt:(NSDate *)recei [self printChannel:channel type:TVCLogLineSystemType text:text receivedAt:receivedAt]; } -- (void)printSystemBoth:(id)channel text:(NSString *)text +- (void)printSystemBoth:(id)channel text:(NSString *)text { [self printSystemBoth:channel text:text receivedAt:[NSDate date]]; } @@ -3501,7 +3509,7 @@ - (void)printErrorReply:(IRCMessage *)m - (void)printErrorReply:(IRCMessage *)m channel:(IRCChannel *)channel { NSString *text = TXTFLS(@"IRCHadRawError", m.numericReply, [m sequence]); - + [self printBoth:channel type:TVCLogLineErrorReplyType text:text receivedAt:m.receivedAt]; } @@ -3549,32 +3557,32 @@ - (NSString *)label - (void)receivePrivmsgAndNotice:(IRCMessage *)m { NSString *text = [m paramAt:1]; - + BOOL identified = NO; - + if (self.identifyCTCP && ([text hasPrefix:@"+\x01"] || [text hasPrefix:@"-\x01"])) { identified = [text hasPrefix:@"+"]; - + text = [text safeSubstringFromIndex:1]; } else if (self.identifyMsg && ([text hasPrefix:@"+"] || [text hasPrefix:@"-"])) { identified = [text hasPrefix:@"+"]; - + text = [text safeSubstringFromIndex:1]; } - + if ([text hasPrefix:@"\x01"]) { text = [text safeSubstringFromIndex:1]; - + NSInteger n = [text stringPosition:@"\x01"]; - + if (n >= 0) { text = [text safeSubstringToIndex:n]; } - + if ([m.command isEqualToString:IRCCommandIndexPrivmsg]) { if ([[text uppercaseString] hasPrefix:@"ACTION "]) { text = [text safeSubstringFromIndex:7]; - + [self receiveText:m command:IRCCommandIndexAction text:text identified:identified]; } else { [self receiveCTCPQuery:m text:text]; @@ -3591,27 +3599,27 @@ - (void)receiveText:(IRCMessage *)m command:(NSString *)cmd text:(NSString *)tex { NSString *anick = m.sender.nick; NSString *target = [m paramAt:0]; - + TVCLogLineType type = TVCLogLinePrivateMessageType; - + if ([cmd isEqualToString:IRCCommandIndexNotice]) { type = TVCLogLineNoticeType; } else if ([cmd isEqualToString:IRCCommandIndexAction]) { type = TVCLogLineActionType; } - + if ([target hasPrefix:@"@"]) { target = [target safeSubstringFromIndex:1]; } - - IRCAddressBook *ignoreChecks = [self checkIgnoreAgainstHostmask:m.sender.raw + + IRCAddressBook *ignoreChecks = [self checkIgnoreAgainstHostmask:m.sender.raw withMatches:@[@"ignoreHighlights", @"ignorePMHighlights", - @"ignoreNotices", - @"ignorePublicMsg", + @"ignoreNotices", + @"ignorePublicMsg", @"ignorePrivateMsg"]]; - - + + if ([target isChannelName]) { if ([ignoreChecks ignoreHighlights] == YES) { if (type == TVCLogLineActionType) { @@ -3620,7 +3628,7 @@ - (void)receiveText:(IRCMessage *)m command:(NSString *)cmd text:(NSString *)tex type = TVCLogLinePrivateMessageNoHighlightType; } } - + if (type == TVCLogLineNoticeType) { if ([ignoreChecks ignoreNotices] == YES) { return; @@ -3630,43 +3638,43 @@ - (void)receiveText:(IRCMessage *)m command:(NSString *)cmd text:(NSString *)tex return; } } - + IRCChannel *c = [self findChannel:target]; - + if (PointerIsEmpty(c)) { return; } - + [self decryptIncomingMessage:&text channel:c]; - - if (type == TVCLogLineNoticeType) { + + if (type == TVCLogLineNoticeType) { [self printBoth:c type:type nick:anick text:text identified:identified receivedAt:m.receivedAt]; - + [self notifyText:TXNotificationChannelNoticeType lineType:type target:c nick:anick text:text]; } else { BOOL highlight = [self printBoth:c type:type nick:anick text:text identified:identified receivedAt:m.receivedAt]; BOOL postevent = NO; - + if (highlight) { postevent = [self notifyText:TXNotificationHighlightType lineType:type target:c nick:anick text:text]; - + if (postevent) { [self setKeywordState:c]; } } else { postevent = [self notifyText:TXNotificationChannelMessageType lineType:type target:c nick:anick text:text]; } - + if (postevent && (highlight || c.config.growl)) { [self setUnreadState:c]; } - + if (c) { IRCUser *sender = [c findMember:anick]; - + if (sender) { NSString *trimmedMyNick = [self.myNick stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"_"]]; - + if ([text stringPositionIgnoringCase:trimmedMyNick] >= 0) { [sender outgoingConversation]; } else { @@ -3677,7 +3685,7 @@ - (void)receiveText:(IRCMessage *)m command:(NSString *)cmd text:(NSString *)tex } } else { BOOL targetOurself = [target isEqualNoCase:self.myNick]; - + if ([ignoreChecks ignorePMHighlights] == YES) { if (type == TVCLogLineActionType) { type = TVCLogLineActionNoHighlightType; @@ -3685,53 +3693,53 @@ - (void)receiveText:(IRCMessage *)m command:(NSString *)cmd text:(NSString *)tex type = TVCLogLinePrivateMessageNoHighlightType; } } - + if (targetOurself && [ignoreChecks ignorePrivateMsg]) { return; } - + if (NSObjectIsEmpty(anick)) { [self printBoth:nil type:type text:text receivedAt:m.receivedAt]; } else if ([anick isNickname] == NO) { if (type == TVCLogLineNoticeType) { if (self.hasIRCopAccess) { - if ([text hasPrefix:@"*** Notice -- Client connecting"] || - [text hasPrefix:@"*** Notice -- Client exiting"] || - [text hasPrefix:@"*** You are connected to"] || - [text hasPrefix:@"Forbidding Q-lined nick"] || + if ([text hasPrefix:@"*** Notice -- Client connecting"] || + [text hasPrefix:@"*** Notice -- Client exiting"] || + [text hasPrefix:@"*** You are connected to"] || + [text hasPrefix:@"Forbidding Q-lined nick"] || [text hasPrefix:@"Exiting ssl client"]) { - + [self printBoth:nil type:type text:text receivedAt:m.receivedAt]; - + BOOL processData = NO; - + NSInteger match_math = 0; - + if ([text hasPrefix:@"*** Notice -- Client connecting at"]) { processData = YES; } else if ([text hasPrefix:@"*** Notice -- Client connecting on port"]) { processData = YES; - + match_math = 1; } - - if (processData) { + + if (processData) { NSString *host = nil; NSString *snick = nil; - + NSArray *chunks = [text componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - + host = [chunks safeObjectAtIndex:(8 + match_math)]; snick = [chunks safeObjectAtIndex:(7 + match_math)]; - + host = [host safeSubstringFromIndex:1]; host = [host safeSubstringToIndex:(host.length - 1)]; - + ignoreChecks = [self checkIgnoreAgainstHostmask:[snick stringByAppendingFormat:@"!%@", host] withMatches:@[@"notifyJoins"]]; - - [self handleUserTrackingNotification:ignoreChecks - nickname:snick + + [self handleUserTrackingNotification:ignoreChecks + nickname:snick hostmask:host langitem:@"UserTrackingHostmaskConnected"]; } @@ -3739,17 +3747,17 @@ - (void)receiveText:(IRCMessage *)m command:(NSString *)cmd text:(NSString *)tex if ([TPCPreferences handleServerNotices]) { if ([TPCPreferences handleIRCopAlerts] && [text containsIgnoringCase:[TPCPreferences IRCopAlertMatch]]) { IRCChannel *c = [self.world selectedChannelOn:self]; - + [self setUnreadState:c]; - + [self printBoth:c type:TVCLogLineNoticeType text:text receivedAt:m.receivedAt]; } else { IRCChannel *c = [self findChannelOrCreate:TXTLS(@"ServerNoticeTreeItemTitle") useTalk:YES]; - + c.isUnread = YES; - + [self setUnreadState:c]; - + [self printBoth:c type:type text:text receivedAt:m.receivedAt]; } } else { @@ -3764,43 +3772,43 @@ - (void)receiveText:(IRCMessage *)m command:(NSString *)cmd text:(NSString *)tex } } else { IRCChannel *c; - + if (targetOurself) { c = [self findChannel:anick]; } else { c = [self findChannel:target]; } - + [self decryptIncomingMessage:&text channel:c]; - + BOOL newTalk = NO; - + if (PointerIsEmpty(c) && NSDissimilarObjects(type, TVCLogLineNoticeType)) { if (targetOurself) { c = [self.world createTalk:anick client:self]; } else { c = [self.world createTalk:target client:self]; } - + newTalk = YES; } - + if (type == TVCLogLineNoticeType) { if ([ignoreChecks ignoreNotices] == YES) { return; } - + if ([TPCPreferences locationToSendNotices] == TXNoticeSendCurrentChannelType) { c = [self.world selectedChannelOn:self]; } - + [self printBoth:c type:type nick:anick text:text identified:identified receivedAt:m.receivedAt]; - + if ([anick isEqualNoCase:@"NickServ"]) { if ([text hasPrefix:@"This nickname is registered"]) { if (NSObjectIsNotEmpty(self.config.nickPassword) && self.isIdentifiedWithSASL == NO) { self.serverHasNickServ = YES; - + [self send:IRCCommandIndexPrivmsg, @"NickServ", [NSString stringWithFormat:@"IDENTIFY %@", self.config.nickPassword], nil]; } @@ -3808,7 +3816,7 @@ - (void)receiveText:(IRCMessage *)m command:(NSString *)cmd text:(NSString *)tex if ([self.config.server hasSuffix:@"dal.net"]) { if (NSObjectIsNotEmpty(self.config.nickPassword)) { self.serverHasNickServ = YES; - + [self send:IRCCommandIndexPrivmsg, @"NickServ@services.dal.net", [NSString stringWithFormat:@"IDENTIFY %@", self.config.nickPassword], nil]; } } @@ -3818,10 +3826,10 @@ - (void)receiveText:(IRCMessage *)m command:(NSString *)cmd text:(NSString *)tex [text hasPrefix:@"You are already identified"] || [text hasSuffix:@"you are now recognized."] || [text hasPrefix:@"Password accepted for"]) { - + if (self.autojoinInitialized == NO && self.serverHasNickServ) { self.autojoinInitialized = YES; - + [self performAutoJoin]; } } @@ -3830,26 +3838,26 @@ - (void)receiveText:(IRCMessage *)m command:(NSString *)cmd text:(NSString *)tex } } } - + if (targetOurself) { [self setUnreadState:c]; - + [self notifyText:TXNotificationQueryNoticeType lineType:type target:c nick:anick text:text]; } } else { BOOL highlight = [self printBoth:c type:type nick:anick text:text identified:identified receivedAt:m.receivedAt]; BOOL postevent = NO; - + if (highlight) { postevent = [self notifyText:TXNotificationHighlightType lineType:type target:c nick:anick text:text]; - + if (postevent) { [self setKeywordState:c]; } } else if (targetOurself) { if (newTalk) { postevent = [self notifyText:TXNotificationNewQueryType lineType:type target:c nick:anick text:text]; - + if (postevent) { [self setNewTalkState:c]; } @@ -3857,13 +3865,13 @@ - (void)receiveText:(IRCMessage *)m command:(NSString *)cmd text:(NSString *)tex postevent = [self notifyText:TXNotificationQueryMessageType lineType:type target:c nick:anick text:text]; } } - + if (postevent) { [self setUnreadState:c]; } - + NSString *hostTopic = m.sender.raw; - + if ([hostTopic isEqualNoCase:c.topic] == NO) { [c setTopic:hostTopic]; [c.log setTopic:hostTopic]; @@ -3876,57 +3884,57 @@ - (void)receiveText:(IRCMessage *)m command:(NSString *)cmd text:(NSString *)tex - (void)receiveCTCPQuery:(IRCMessage *)m text:(NSString *)text { NSString *nick = m.sender.nick; - + NSMutableString *s = text.mutableCopy; - + NSString *command = s.getToken.uppercaseString; - + if ([TPCPreferences replyToCTCPRequests] == NO) { [self printDebugInformationToConsole:TXTFLS(@"IRCCTCPRequestIgnored", command, nick)]; - + return; } - - IRCAddressBook *ignoreChecks = [self checkIgnoreAgainstHostmask:m.sender.raw + + IRCAddressBook *ignoreChecks = [self checkIgnoreAgainstHostmask:m.sender.raw withMatches:@[@"ignoreCTCP"]]; - + if ([ignoreChecks ignoreCTCP] == YES) { return; } - + if ([command isEqualToString:IRCCommandIndexDcc]) { [self printDebugInformationToConsole:TXTLS(@"DCCRequestErrorMessage")]; } else { IRCChannel *target = nil; - + if ([TPCPreferences locationToSendNotices] == TXNoticeSendCurrentChannelType) { target = [self.world selectedChannelOn:self]; } - + NSString *text = TXTFLS(@"IRCRecievedCTCPRequest", command, nick); - + if ([command isEqualToString:IRCCommandIndexLagcheck] == NO) { [self printBoth:target type:TVCLogLineCTCPType text:text receivedAt:m.receivedAt]; } - + if ([command isEqualToString:IRCCommandIndexPing]) { [self sendCTCPReply:nick command:command text:s]; } else if ([command isEqualToString:IRCCommandIndexTime]) { [self sendCTCPReply:nick command:command text:[[NSDate date] descriptionWithLocale:[NSLocale currentLocale]]]; } else if ([command isEqualToString:IRCCommandIndexVersion]) { NSString *fakever = [TPCPreferences masqueradeCTCPVersion]; - + if (NSObjectIsNotEmpty(fakever)) { [self sendCTCPReply:nick command:command text:fakever]; } else { NSString *ref = [TPCPreferences gitBuildReference]; NSString *name = [TPCPreferences applicationName]; NSString *vers = [TPCPreferences textualInfoPlist][@"CFBundleVersion"]; - + NSString *text = [NSString stringWithFormat:TXTLS(@"IRCCTCPVersionInfo"), name, vers, ((NSObjectIsEmpty(ref)) ? TXTLS(@"Unknown") : ref), [TPCPreferences textualInfoPlist][@"TXBundleBuildCodeName"]]; - + [self sendCTCPReply:nick command:command text:text]; } } else if ([command isEqualToString:IRCCommandIndexUserinfo]) { @@ -3937,15 +3945,15 @@ - (void)receiveCTCPQuery:(IRCMessage *)m text:(NSString *)text if (self.lastLagCheck == 0) { [self printDebugInformationToConsole:TXTFLS(@"IRCCTCPRequestIgnored", command, nick)]; } - + TXNSDouble time = CFAbsoluteTimeGetCurrent(); - + if (time >= self.lastLagCheck) { TXNSDouble delta = (time - self.lastLagCheck); NSString *rating; - if (delta <= 0.09) { rating = TXTLS(@"LagCheckRequestReplyRating_01"); + if (delta <= 0.09) { rating = TXTLS(@"LagCheckRequestReplyRating_01"); } else if (delta >= 0.1 && delta < 0.2) { rating = TXTLS(@"LagCheckRequestReplyRating_02"); } else if (delta >= 0.2 && delta < 0.5) { rating = TXTLS(@"LagCheckRequestReplyRating_03"); } else if (delta >= 0.5 && delta < 1.0) { rating = TXTLS(@"LagCheckRequestReplyRating_04"); @@ -3954,20 +3962,20 @@ - (void)receiveCTCPQuery:(IRCMessage *)m text:(NSString *)text } else if (delta >= 5.0 && delta < 10.0) { rating = TXTLS(@"LagCheckRequestReplyRating_07"); } else if (delta >= 10.0 && delta < 30.0) { rating = TXTLS(@"LagCheckRequestReplyRating_08"); } else if (delta >= 30.0) { rating = TXTLS(@"LagCheckRequestReplyRating_09"); } - + text = TXTFLS(@"LagCheckRequestReplyMessage", self.config.server, delta, rating); } else { text = TXTLS(@"LagCheckRequestUnknownReply"); } - + if (self.sendLagcheckToChannel) { [self sendPrivmsgToSelectedChannel:text]; - + self.sendLagcheckToChannel = NO; } else { [self printDebugInformation:text]; } - + self.lastLagCheck = 0; } } @@ -3976,51 +3984,51 @@ - (void)receiveCTCPQuery:(IRCMessage *)m text:(NSString *)text - (void)receiveCTCPReply:(IRCMessage *)m text:(NSString *)text { NSString *nick = m.sender.nick; - + NSMutableString *s = text.mutableCopy; - + NSString *command = s.getToken.uppercaseString; - - IRCAddressBook *ignoreChecks = [self checkIgnoreAgainstHostmask:m.sender.raw + + IRCAddressBook *ignoreChecks = [self checkIgnoreAgainstHostmask:m.sender.raw withMatches:@[@"ignoreCTCP"]]; - + if ([ignoreChecks ignoreCTCP] == YES) { return; } - + IRCChannel *c = nil; - + if ([TPCPreferences locationToSendNotices] == TXNoticeSendCurrentChannelType) { c = [self.world selectedChannelOn:self]; } - + if ([command isEqualToString:IRCCommandIndexPing]) { uint64_t delta = (mach_absolute_time() - [s longLongValue]); - + mach_timebase_info_data_t info; mach_timebase_info(&info); - + TXNSDouble nano = (1e-9 * ((TXNSDouble)info.numer / (TXNSDouble)info.denom)); TXNSDouble seconds = ((TXNSDouble)delta * nano); - + text = TXTFLS(@"IRCRecievedCTCPPingReply", nick, command, seconds); } else { text = TXTFLS(@"IRCRecievedCTCPReply", nick, command, s); } - + [self printBoth:c type:TVCLogLineCTCPType text:text receivedAt:m.receivedAt]; } -- (void)requestUserHosts:(IRCChannel *)c +- (void)requestUserHosts:(IRCChannel *)c { if ([c.name isChannelName]) { [c setIsModeInit:YES]; - + [self send:IRCCommandIndexMode, c.name, nil]; - + if (self.userhostInNames == NO) { // We can skip requesting WHO, we already have this information. - + [self send:IRCCommandIndexWho, c.name, nil, nil]; } } @@ -4030,78 +4038,78 @@ - (void)receiveJoin:(IRCMessage *)m { NSString *nick = m.sender.nick; NSString *chname = [m paramAt:0]; - + BOOL njoin = NO; BOOL myself = [nick isEqualNoCase:self.myNick]; - + if ([chname hasSuffix:@"\x07o"]) { njoin = YES; - + chname = [chname safeSubstringToIndex:(chname.length - 2)]; } - + IRCChannel *c = [self findChannelOrCreate:chname]; - + if (myself) { [c activate]; - + [self reloadTree]; - + self.myHost = m.sender.raw; - + if (self.autojoinInitialized == NO && [self.autoJoinTimer isActive] == NO) { [self.world select:c]; [self.world.serverList expandItem:c]; } - + if (NSObjectIsNotEmpty(c.config.encryptionKey)) { [c.client printDebugInformation:TXTLS(@"BlowfishEncryptionStarted") channel:c]; } } - + if (PointerIsEmpty([c findMember:nick])) { IRCUser *u = [IRCUser new]; - + u.o = njoin; u.nick = nick; u.username = m.sender.user; u.address = m.sender.address; u.supportInfo = self.isupport; - + [c addMember:u]; } - - IRCAddressBook *ignoreChecks = [self checkIgnoreAgainstHostmask:m.sender.raw + + IRCAddressBook *ignoreChecks = [self checkIgnoreAgainstHostmask:m.sender.raw withMatches:@[@"ignoreJPQE", @"notifyJoins"]]; - + if ([ignoreChecks ignoreJPQE] == YES && myself == NO) { return; } - + if (self.hasIRCopAccess == NO) { if ([ignoreChecks notifyJoins] == YES) { NSString *tracker = [ignoreChecks trackingNickname]; - + BOOL ison = [self.trackedUsers boolForKey:tracker]; - - if (ison == NO) { - [self handleUserTrackingNotification:ignoreChecks - nickname:m.sender.nick - hostmask:[m.sender.raw hostmaskFromRawString] + + if (ison == NO) { + [self handleUserTrackingNotification:ignoreChecks + nickname:m.sender.nick + hostmask:[m.sender.raw hostmaskFromRawString] langitem:@"UserTrackingHostmaskNowAvailable"]; - + [self.trackedUsers setBool:YES forKey:tracker]; } } } - + if ([TPCPreferences showJoinLeave]) { if (c.config.ignoreJPQActivity) { return; } - + NSString *text = TXTFLS(@"IRCUserJoinedChannel", nick, m.sender.user, m.sender.address); - + [self printBoth:c type:TVCLogLineJoinType text:text receivedAt:m.receivedAt]; } } @@ -4111,36 +4119,36 @@ - (void)receivePart:(IRCMessage *)m NSString *nick = m.sender.nick; NSString *chname = [m paramAt:0]; NSString *comment = [m paramAt:1].trim; - + IRCChannel *c = [self findChannel:chname]; - + if (c) { if ([nick isEqualNoCase:self.myNick]) { [c deactivate]; - + [self reloadTree]; } - + [c removeMember:nick]; - + if ([TPCPreferences showJoinLeave]) { if (c.config.ignoreJPQActivity) { return; } - - IRCAddressBook *ignoreChecks = [self checkIgnoreAgainstHostmask:m.sender.raw + + IRCAddressBook *ignoreChecks = [self checkIgnoreAgainstHostmask:m.sender.raw withMatches:@[@"ignoreJPQE"]]; - + if ([ignoreChecks ignoreJPQE] == YES) { return; } - + NSString *message = TXTFLS(@"IRCUserPartedChannel", nick, m.sender.user, m.sender.address); - + if (NSObjectIsNotEmpty(comment)) { message = [message stringByAppendingFormat:@" (%@)", comment]; } - + [self printBoth:c type:TVCLogLinePartType text:message receivedAt:m.receivedAt]; } } @@ -4152,39 +4160,39 @@ - (void)receiveKick:(IRCMessage *)m NSString *chname = [m paramAt:0]; NSString *target = [m paramAt:1]; NSString *comment = [m paramAt:2].trim; - + IRCChannel *c = [self findChannel:chname]; - + if (c) { [c removeMember:target]; - + if ([TPCPreferences showJoinLeave]) { if (c.config.ignoreJPQActivity) { return; } - - IRCAddressBook *ignoreChecks = [self checkIgnoreAgainstHostmask:m.sender.raw + + IRCAddressBook *ignoreChecks = [self checkIgnoreAgainstHostmask:m.sender.raw withMatches:@[@"ignoreJPQE"]]; - + if ([ignoreChecks ignoreJPQE] == YES) { return; } - + NSString *message = TXTFLS(@"IRCUserKickedFromChannel", nick, target, comment); - + [self printBoth:c type:TVCLogLineKickType text:message receivedAt:m.receivedAt]; } - + if ([target isEqualNoCase:self.myNick]) { [c deactivate]; - + [self reloadTree]; - + [self notifyEvent:TXNotificationKickType lineType:TVCLogLineKickType target:c nick:nick text:comment]; - + if ([TPCPreferences rejoinOnKick] && c.errLastJoin == NO) { [self printDebugInformation:TXTLS(@"IRCChannelPreparingRejoinAttempt") channel:c]; - + [self performSelector:@selector(_joinKickedChannel:) withObject:c afterDelay:3.0]; } } @@ -4195,57 +4203,57 @@ - (void)receiveQuit:(IRCMessage *)m { NSString *nick = m.sender.nick; NSString *comment = [m paramAt:0].trim; - + BOOL myself = [nick isEqualNoCase:self.myNick]; - - IRCAddressBook *ignoreChecks = [self checkIgnoreAgainstHostmask:m.sender.raw + + IRCAddressBook *ignoreChecks = [self checkIgnoreAgainstHostmask:m.sender.raw withMatches:@[@"ignoreJPQE"]]; - + NSString *text = TXTFLS(@"IRCUserDisconnected", nick, m.sender.user, m.sender.address); - + if (NSObjectIsNotEmpty(comment)) { - if ([TLORegularExpression string:comment + if ([TLORegularExpression string:comment isMatchedByRegex:@"^((([a-zA-Z0-9-_\\.\\*]+)\\.([a-zA-Z0-9-_]+)) (([a-zA-Z0-9-_\\.\\*]+)\\.([a-zA-Z0-9-_]+)))$"]) { - + comment = TXTFLS(@"IRCServerHadNetsplitQuitMessage", comment); } - + text = [text stringByAppendingFormat:@" (%@)", comment]; } - + for (IRCChannel *c in self.channels) { if ([c findMember:nick]) { if ([TPCPreferences showJoinLeave] && c.config.ignoreJPQActivity == NO && [ignoreChecks ignoreJPQE] == NO) { [self printChannel:c type:TVCLogLineQuitType text:text receivedAt:m.receivedAt]; } - + [c removeMember:nick]; - + if (myself) { [c deactivate]; } } } - + if (myself == NO) { if ([nick isEqualNoCase:self.config.nick]) { [self changeNick:self.config.nick]; } } - + [self.world reloadTree]; - + if (self.hasIRCopAccess == NO) { if ([ignoreChecks notifyJoins] == YES) { NSString *tracker = [ignoreChecks trackingNickname]; - + BOOL ison = [self.trackedUsers boolForKey:tracker]; - - if (ison) { + + if (ison) { [self.trackedUsers setBool:NO forKey:tracker]; - - [self handleUserTrackingNotification:ignoreChecks - nickname:m.sender.nick + + [self handleUserTrackingNotification:ignoreChecks + nickname:m.sender.nick hostmask:[m.sender.raw hostmaskFromRawString] langitem:@"UserTrackingHostmaskNoLongerAvailable"]; } @@ -4256,7 +4264,7 @@ - (void)receiveQuit:(IRCMessage *)m - (void)receiveKill:(IRCMessage *)m { NSString *target = [m paramAt:0]; - + for (IRCChannel *c in self.channels) { if ([c findMember:target]) { [c removeMember:target]; @@ -4267,68 +4275,68 @@ - (void)receiveKill:(IRCMessage *)m - (void)receiveNick:(IRCMessage *)m { IRCAddressBook *ignoreChecks; - + NSString *nick = m.sender.nick; NSString *toNick = [m paramAt:0]; - + if ([nick isEqualToString:toNick]) { return; } - + BOOL myself = [nick isEqualNoCase:self.myNick]; - + if (myself) { self.myNick = toNick; } else { - ignoreChecks = [self checkIgnoreAgainstHostmask:m.sender.raw + ignoreChecks = [self checkIgnoreAgainstHostmask:m.sender.raw withMatches:@[@"ignoreJPQE"]]; - + if (self.hasIRCopAccess == NO) { if ([ignoreChecks notifyJoins] == YES) { NSString *tracker = [ignoreChecks trackingNickname]; - + BOOL ison = [self.trackedUsers boolForKey:tracker]; - - if (ison) { - [self handleUserTrackingNotification:ignoreChecks - nickname:m.sender.nick + + if (ison) { + [self handleUserTrackingNotification:ignoreChecks + nickname:m.sender.nick hostmask:[m.sender.raw hostmaskFromRawString] langitem:@"UserTrackingHostmaskNoLongerAvailable"]; - } else { - [self handleUserTrackingNotification:ignoreChecks - nickname:m.sender.nick + } else { + [self handleUserTrackingNotification:ignoreChecks + nickname:m.sender.nick hostmask:[m.sender.raw hostmaskFromRawString] langitem:@"UserTrackingHostmaskNowAvailable"]; } - + [self.trackedUsers setBool:BOOLReverseValue(ison) forKey:tracker]; } } } - + for (IRCChannel *c in self.channels) { - if ([c findMember:nick]) { + if ([c findMember:nick]) { if ((myself == NO && [ignoreChecks ignoreJPQE] == NO) || myself == YES) { NSString *text = TXTFLS(@"IRCUserChangedNickname", nick, toNick); - + [self printChannel:c type:TVCLogLineNickType text:text receivedAt:m.receivedAt]; } - + [c renameMember:nick to:toNick]; } } - + IRCChannel *c = [self findChannel:nick]; - + if (c) { IRCChannel *t = [self findChannel:toNick]; - + if (t) { [self.world destroyChannel:t]; } - + c.name = toNick; - + [self reloadTree]; } } @@ -4338,27 +4346,27 @@ - (void)receiveMode:(IRCMessage *)m NSString *nick = m.sender.nick; NSString *target = [m paramAt:0]; NSString *modeStr = [m sequence:1]; - + if ([target isChannelName]) { IRCChannel *c = [self findChannel:target]; - + if (c) { NSArray *info = [c.mode update:modeStr]; - + BOOL performWho = NO; - + for (IRCModeInfo *h in info) { [c changeMember:h.param mode:h.mode value:h.plus]; - + if (h.plus == NO && self.multiPrefix == NO) { performWho = YES; } } - + if (performWho) { [self send:IRCCommandIndexWho, c.name, nil, nil]; } - + [self printBoth:c type:TVCLogLineModeType text:TXTFLS(@"IRCModeSet", nick, modeStr) receivedAt:m.receivedAt]; } } else { @@ -4371,15 +4379,15 @@ - (void)receiveTopic:(IRCMessage *)m NSString *nick = m.sender.nick; NSString *chname = [m paramAt:0]; NSString *topic = [m paramAt:1]; - + IRCChannel *c = [self findChannel:chname]; - + [self decryptIncomingMessage:&topic channel:c]; - + if (c) { [c setTopic:topic]; [c.log setTopic:topic]; - + [self printBoth:c type:TVCLogLineTopicType text:TXTFLS(@"IRCChannelTopicChanged", nick, topic) receivedAt:m.receivedAt]; } } @@ -4388,13 +4396,13 @@ - (void)receiveInvite:(IRCMessage *)m { NSString *nick = m.sender.nick; NSString *chname = [m paramAt:1]; - + NSString *text = TXTFLS(@"IRCUserInvitedYouToJoinChannel", nick, m.sender.user, m.sender.address, chname); - + [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineInviteType text:text receivedAt:m.receivedAt]; - + [self notifyEvent:TXNotificationInviteType lineType:TVCLogLineInviteType target:nil nick:nick text:chname]; - + if ([TPCPreferences autoJoinOnInvite]) { [self joinUnlistedChannel:chname]; } @@ -4408,14 +4416,14 @@ - (void)receiveError:(IRCMessage *)m #pragma mark - #pragma mark Server CAP -- (void)sendNextCap +- (void)sendNextCap { if (self.capPaused == NO) { if (self.pendingCaps && [self.pendingCaps count]) { NSString *cap = [self.pendingCaps lastObject]; - + [self send:IRCCommandIndexCap, @"REQ", cap, nil]; - + [self.pendingCaps removeLastObject]; } else { [self send:IRCCommandIndexCap, @"END", nil]; @@ -4423,19 +4431,19 @@ - (void)sendNextCap } } -- (void)pauseCap +- (void)pauseCap { self.capPaused++; } -- (void)resumeCap +- (void)resumeCap { self.capPaused--; - + [self sendNextCap]; } -- (BOOL)isCapAvailable:(NSString*)cap +- (BOOL)isCapAvailable:(NSString*)cap { return ([cap isEqualNoCase:@"identify-msg"] || [cap isEqualNoCase:@"identify-ctcp"] || @@ -4445,12 +4453,12 @@ - (BOOL)isCapAvailable:(NSString*)cap ([cap isEqualNoCase:@"sasl"] && NSObjectIsNotEmpty(self.config.nickPassword))); } -- (void)cap:(NSString*)cap result:(BOOL)supported +- (void)cap:(NSString*)cap result:(BOOL)supported { if (supported) { if ([cap isEqualNoCase:@"sasl"]) { self.inSASLRequest = YES; - + [self pauseCap]; [self send:IRCCommandIndexAuthenticate, @"PLAIN", nil]; } else if ([cap isEqualNoCase:@"userhost-in-names"]) { @@ -4468,19 +4476,19 @@ - (void)cap:(NSString*)cap result:(BOOL)supported - (void)receiveCapacityOrAuthenticationRequest:(IRCMessage *)m { /* Implementation based off Colloquy's own. */ - + NSString *command = [m command]; NSString *star = [m paramAt:0]; NSString *base = [m paramAt:1]; NSString *action = [m sequence:2]; - + star = [star trim]; action = [action trim]; - + if ([command isEqualNoCase:IRCCommandIndexCap]) { if ([base isEqualNoCase:@"LS"]) { NSArray *caps = [action componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - + for (NSString *cap in caps) { if ([self isCapAvailable:cap]) { [self.pendingCaps addObject:cap]; @@ -4488,39 +4496,39 @@ - (void)receiveCapacityOrAuthenticationRequest:(IRCMessage *)m } } else if ([base isEqualNoCase:@"ACK"]) { NSArray *caps = [action componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - + for (NSString *cap in caps) { [self.acceptedCaps addObject:cap]; - + [self cap:cap result:YES]; } } else if ([base isEqualNoCase:@"NAK"]) { NSArray *caps = [action componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - + for (NSString *cap in caps) { [self cap:cap result:NO]; } } - + [self sendNextCap]; } else { if ([star isEqualToString:@"+"]) { NSData *usernameData = [self.config.nick dataUsingEncoding:self.config.encoding allowLossyConversion:YES]; - + NSMutableData *authenticateData = [usernameData mutableCopy]; - + [authenticateData appendBytes:"\0" length:1]; [authenticateData appendData:usernameData]; [authenticateData appendBytes:"\0" length:1]; [authenticateData appendData:[self.config.nickPassword dataUsingEncoding:self.config.encoding allowLossyConversion:YES]]; - + NSString *authString = [authenticateData base64EncodingWithLineLength:400]; NSArray *authStrings = [authString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; - + for (NSString *string in authStrings) { [self send:IRCCommandIndexAuthenticate, string, nil]; } - + if (NSObjectIsEmpty(authStrings) || [(NSString *)[authStrings lastObject] length] == 400) { [self send:IRCCommandIndexAuthenticate, @"+", nil]; } @@ -4538,71 +4546,71 @@ - (void)receiveInit:(IRCMessage *)m [self startPongTimer]; [self stopRetryTimer]; [self stopAutoJoinTimer]; - + self.sendLagcheckToChannel = self.serverHasNickServ = NO; self.isLoggedIn = self.conn.loggedIn = self.inFirstISONRun = YES; self.isAway = self.isConnecting = self.hasIRCopAccess = NO; - + self.tryingNickNumber = -1; - + self.serverHostname = m.sender.raw; self.myNick = [m paramAt:0]; - + [self notifyEvent:TXNotificationConnectType lineType:TVCLogLineSystemType]; - + for (__strong NSString *s in self.config.loginCommands) { if ([s hasPrefix:@"/"]) { s = [s safeSubstringFromIndex:1]; } - + [self sendCommand:s completeTarget:NO target:nil]; } - + for (IRCChannel *c in self.channels) { if (c.isTalk) { [c activate]; - + IRCUser *m; - + m = [IRCUser new]; m.supportInfo = self.isupport; m.nick = self.myNick; [c addMember:m]; - + m = [IRCUser new]; m.supportInfo = self.isupport; m.nick = c.name; [c addMember:m]; } } - + [self reloadTree]; [self populateISONTrackedUsersList:self.config.ignores]; - + #ifdef IS_TRIAL_BINARY [self startTrialPeriodTimer]; #endif - + [self startAutoJoinTimer]; } - (void)receiveNumericReply:(IRCMessage *)m { - NSInteger n = m.numericReply; - - if (400 <= n && n < 600 && - NSDissimilarObjects(n, 403) && + NSInteger n = m.numericReply; + + if (400 <= n && n < 600 && + NSDissimilarObjects(n, 403) && NSDissimilarObjects(n, 422)) { - + return [self receiveErrorNumericReply:m]; } - + switch (n) { - case 1: + case 1: { [self receiveInit:m]; [self printReply:m]; - + break; } case 2 ... 4: @@ -4610,19 +4618,19 @@ - (void)receiveNumericReply:(IRCMessage *)m if ([m.sender.nick isNickname] == NO) { [self.config setServer:m.sender.nick]; } - + [self printReply:m]; - + break; } case 5: { [self.isupport update:[m sequence:1] client:self]; - + [self.config setNetwork:TXTFLS(@"IRCServerNetworkName", self.isupport.networkName)]; - + [self.world updateTitle]; - + break; } case 10: @@ -4632,82 +4640,82 @@ - (void)receiveNumericReply:(IRCMessage *)m case 265 ... 266: { [self printReply:m]; - + break; } case 372: case 375: - case 376: - case 422: + case 376: + case 422: { if ([TPCPreferences displayServerMOTD]) { [self printReply:m]; } - + break; } case 221: { NSString *modeStr = [m paramAt:1]; - + if ([modeStr isEqualToString:@"+"]) return; - + [self printBoth:nil type:TVCLogLineReplyType text:TXTFLS(@"IRCUserHasModes", modeStr) receivedAt:m.receivedAt]; - + break; } case 290: { NSString *kind = [[m paramAt:1] lowercaseString]; - + if ([kind isEqualToString:@"identify-msg"]) { self.identifyMsg = YES; } else if ([kind isEqualToString:@"identify-ctcp"]) { self.identifyCTCP = YES; } - + [self printReply:m]; - + break; } case 301: { NSString *nick = [m paramAt:1]; NSString *comment = [m paramAt:2]; - + IRCChannel *c = [self findChannel:nick]; IRCChannel *sc = [self.world selectedChannelOn:self]; - + NSString *text = TXTFLS(@"IRCUserIsAway", nick, comment); - + if (c) { [self printBoth:(id)nick type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; } - + if (self.whoisChannel && [self.whoisChannel isEqualTo:c] == NO) { [self printBoth:self.whoisChannel type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; - } else { + } else { if ([sc isEqualTo:c] == NO) { [self printBoth:sc type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; } } - + break; } - case 305: + case 305: { self.isAway = NO; - + [self printUnknownReply:m]; - + break; } - case 306: + case 306: { self.isAway = YES; - + [self printUnknownReply:m]; - + break; } case 307: @@ -4719,25 +4727,25 @@ - (void)receiveNumericReply:(IRCMessage *)m case 671: { NSString *text = [NSString stringWithFormat:@"%@ %@", [m paramAt:1], [m paramAt:2]]; - + if (self.whoisChannel) { [self printBoth:self.whoisChannel type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; - } else { + } else { [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; } - + break; } case 338: { NSString *text = [NSString stringWithFormat:@"%@ %@ %@", [m paramAt:1], [m sequence:3], [m paramAt:2]]; - + if (self.whoisChannel) { [self printBoth:self.whoisChannel type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; - } else { + } else { [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; } - + break; } case 311: @@ -4747,27 +4755,27 @@ - (void)receiveNumericReply:(IRCMessage *)m NSString *username = [m paramAt:2]; NSString *address = [m paramAt:3]; NSString *realname = [m paramAt:5]; - + NSString *text = nil; - + self.inWhoWasRun = (m.numericReply == 314); - + if ([realname hasPrefix:@":"]) { realname = [realname safeSubstringFromIndex:1]; } - + if (self.inWhoWasRun) { text = TXTFLS(@"IRCUserWhowasHostmask", nick, username, address, realname); } else { text = TXTFLS(@"IRCUserWhoisHostmask", nick, username, address, realname); - } - + } + if (self.whoisChannel) { [self printBoth:self.whoisChannel type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; - } else { + } else { [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; } - + break; } case 312: @@ -4775,142 +4783,142 @@ - (void)receiveNumericReply:(IRCMessage *)m NSString *nick = [m paramAt:1]; NSString *server = [m paramAt:2]; NSString *serverInfo = [m paramAt:3]; - + NSString *text = nil; - + if (self.inWhoWasRun) { text = TXTFLS(@"IRCUserWhowasConnectedFrom", nick, server, [dateTimeFormatter stringFromDate:[NSDate dateWithNaturalLanguageString:serverInfo]]); } else { text = TXTFLS(@"IRCUserWhoisConnectedFrom", nick, server, serverInfo); } - + if (self.whoisChannel) { [self printBoth:self.whoisChannel type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; - } else { + } else { [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; } - + break; } case 317: { NSString *nick = [m paramAt:1]; - + NSInteger idleStr = [m paramAt:2].doubleValue; NSInteger signOnStr = [m paramAt:3].doubleValue; - + NSString *idleTime = TXReadableTime(idleStr); NSString *dateFromString = [dateTimeFormatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:signOnStr]]; - + NSString *text = TXTFLS(@"IRCUserWhoisUptime", nick, dateFromString, idleTime); - + if (self.whoisChannel) { [self printBoth:self.whoisChannel type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; - } else { + } else { [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; } - + break; } case 319: { NSString *nick = [m paramAt:1]; NSString *trail = [m paramAt:2].trim; - + NSString *text = TXTFLS(@"IRCUserWhoisChannels", nick, trail); - + if (self.whoisChannel) { [self printBoth:self.whoisChannel type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; - } else { + } else { [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; } - + break; } case 318: { self.whoisChannel = nil; - + break; } case 324: { NSString *chname = [m paramAt:1]; NSString *modeStr; - + modeStr = [m sequence:2]; modeStr = [modeStr stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - + if ([modeStr isEqualToString:@"+"]) return; - + IRCChannel *c = [self findChannel:chname]; - + if (c.isModeInit == NO || NSObjectIsEmpty([c.mode allModes])) { if (c && c.isActive) { [c.mode clear]; [c.mode update:modeStr]; - + c.isModeInit = YES; } - + [self printBoth:c type:TVCLogLineModeType text:TXTFLS(@"IRCChannelHasModes", modeStr) receivedAt:m.receivedAt]; } - + break; } case 332: { NSString *chname = [m paramAt:1]; NSString *topic = [m paramAt:2]; - + IRCChannel *c = [self findChannel:chname]; - + [self decryptIncomingMessage:&topic channel:c]; - + if (c && c.isActive) { [c setTopic:topic]; [c.log setTopic:topic]; - + [self printBoth:c type:TVCLogLineTopicType text:TXTFLS(@"IRCChannelHasTopic", topic) receivedAt:m.receivedAt]; } - + break; } - case 333: + case 333: { NSString *chname = [m paramAt:1]; NSString *setter = [m paramAt:2]; NSString *timeStr = [m paramAt:3]; - + long long timeNum = [timeStr longLongValue]; - + NSRange r = [setter rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"!@"]]; - + if (NSDissimilarObjects(r.location, NSNotFound)) { setter = [setter safeSubstringToIndex:r.location]; } - + IRCChannel *c = [self findChannel:chname]; - + if (c) { - NSString *text = [NSString stringWithFormat:TXTLS(@"IRCChannelHasTopicAuthor"), setter, + NSString *text = [NSString stringWithFormat:TXTLS(@"IRCChannelHasTopicAuthor"), setter, [dateTimeFormatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:timeNum]]]; - + [self printBoth:c type:TVCLogLineTopicType text:text receivedAt:m.receivedAt]; } - + break; } case 341: { NSString *nick = [m paramAt:1]; NSString *chname = [m paramAt:2]; - + IRCChannel *c = [self findChannel:chname]; - + [self printBoth:c type:TVCLogLineReplyType text:TXTFLS(@"IRCUserInvitedToJoinChannel", nick, chname) receivedAt:m.receivedAt]; - + break; } case 303: @@ -4919,104 +4927,104 @@ - (void)receiveNumericReply:(IRCMessage *)m [self printUnknownReply:m]; } else { NSArray *users = [[m sequence] componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - + for (NSString *name in self.trackedUsers) { NSString *langkey = nil; - + BOOL ison = [self.trackedUsers boolForKey:name]; - + if (ison) { if ([users containsObjectIgnoringCase:name] == NO) { if (self.inFirstISONRun == NO) { langkey = @"UserTrackingNicknameNoLongerAvailable"; } - + [self.trackedUsers setBool:NO forKey:name]; } } else { if ([users containsObjectIgnoringCase:name]) { langkey = ((self.inFirstISONRun) ? @"UserTrackingNicknameIsAvailable" : @"UserTrackingNicknameNowAvailable"); - + [self.trackedUsers setBool:YES forKey:name]; } } - + if (NSObjectIsNotEmpty(langkey)) { for (IRCAddressBook *g in self.config.ignores) { NSString *trname = [g trackingNickname]; - + if ([trname isEqualNoCase:name]) { [self handleUserTrackingNotification:g nickname:name hostmask:name langitem:langkey]; } } } } - + if (self.inFirstISONRun) { self.inFirstISONRun = NO; } } - + break; } case 315: { NSString *chname = [m paramAt:1]; - + IRCChannel *c = [self findChannel:chname]; - + if (c && c.isModeInit) { [c setIsModeInit:NO]; - } - + } + if (self.inWhoInfoRun) { [self printUnknownReply:m]; - + self.inWhoInfoRun = NO; } - + break; } - case 352: + case 352: { NSString *chname = [m paramAt:1]; - + IRCChannel *c = [self findChannel:chname]; - + if (c) { NSString *nick = [m paramAt:5]; NSString *hostmask = [m paramAt:3]; NSString *username = [m paramAt:2]; NSString *fields = [m paramAt:6]; - + BOOL isIRCOp = NO; - + // fields = G|H *| chanprefixes // strip G or H (away status) fields = [fields substringFromIndex:1]; - + if ([fields hasPrefix:@"*"]) { // The nick is an oper fields = [fields substringFromIndex:1]; - + isIRCOp = YES; } - + IRCUser *u = [c findMember:nick]; - + if (PointerIsEmpty(u)) { IRCUser *u = [IRCUser new]; - + u.nick = nick; u.isIRCOp = isIRCOp; u.supportInfo = self.isupport; } - + NSInteger i; - + for (i = 0; i < fields.length; i++) { NSString *prefix = [fields safeSubstringWithRange:NSMakeRange(i, 1)]; - + if ([prefix isEqualTo:self.isupport.userModeQPrefix]) { u.q = YES; } else if ([prefix isEqualTo:self.isupport.userModeAPrefix]) { @@ -5031,44 +5039,44 @@ - (void)receiveNumericReply:(IRCMessage *)m break; } } - + if (NSObjectIsEmpty(u.address)) { [u setAddress:hostmask]; [u setUsername:username]; } - + [c updateOrAddMember:u]; [c reloadMemberList]; - } - + } + if (self.inWhoInfoRun) { - [self printUnknownReply:m]; + [self printUnknownReply:m]; } - + break; } case 353: { NSString *chname = [m paramAt:2]; NSString *trail = [m paramAt:3]; - + IRCChannel *c = [self findChannel:chname]; - + if (c) { NSArray *ary = [trail componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - + for (__strong NSString *nick in ary) { nick = [nick trim]; - + if (NSObjectIsEmpty(nick)) continue; - + IRCUser *m = [IRCUser new]; - + NSInteger i; - + for (i = 0; i < nick.length; i++) { NSString *prefix = [nick safeSubstringWithRange:NSMakeRange(i, 1)]; - + if ([prefix isEqualTo:self.isupport.userModeQPrefix]) { m.q = YES; } else if ([prefix isEqualTo:self.isupport.userModeAPrefix]) { @@ -5083,79 +5091,79 @@ - (void)receiveNumericReply:(IRCMessage *)m break; } } - + nick = [nick substringFromIndex:i]; - + m.nick = [nick nicknameFromHostmask]; m.username = [nick identFromHostmask]; m.address = [nick hostFromHostmask]; - + m.supportInfo = self.isupport; m.isMyself = [nick isEqualNoCase:self.myNick]; - + [c addMember:m reload:NO]; - + if (m.isMyself) { c.isOp = (m.q || m.a | m.o); c.isHalfOp = (m.h || c.isOp); } } - + [c reloadMemberList]; - } - + } + break; } case 366: { NSString *chname = [m paramAt:1]; - + IRCChannel *c = [self findChannel:chname]; - + if (c) { if ([c numberOfMembers] <= 1 && c.isOp) { NSString *m = c.config.mode; - + if (NSObjectIsNotEmpty(m)) { NSString *line = [NSString stringWithFormat:@"%@ %@ %@", IRCCommandIndexMode, chname, m]; - + [self sendLine:line]; } - + c.isModeInit = YES; } - + if ([c numberOfMembers] <= 1 && [chname isModeChannelName] && c.isOp) { NSString *topic = c.storedTopic; - + if (NSObjectIsEmpty(topic)) { topic = c.config.topic; } - + if (NSObjectIsNotEmpty(topic)) { if ([self encryptOutgoingMessage:&topic channel:c] == YES) { [self send:IRCCommandIndexTopic, chname, topic, nil]; } } } - + if ([TPCPreferences processChannelModes]) { [self requestUserHosts:c]; } } - + break; } case 320: { NSString *text = [NSString stringWithFormat:@"%@ %@", [m paramAt:1], [m sequence:2]]; - + if (self.whoisChannel) { [self printBoth:self.whoisChannel type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; - } else { + } else { [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; } - + break; } case 321: @@ -5163,7 +5171,7 @@ - (void)receiveNumericReply:(IRCMessage *)m if (self.channelListDialog) { [self.channelListDialog clear]; } - + break; } case 322: @@ -5171,14 +5179,14 @@ - (void)receiveNumericReply:(IRCMessage *)m NSString *chname = [m paramAt:1]; NSString *countStr = [m paramAt:2]; NSString *topic = [m sequence:3]; - + if (self.channelListDialog) { [self.channelListDialog addChannel:chname count:[countStr integerValue] topic:topic]; } - + break; } - case 323: + case 323: case 329: case 368: case 347: @@ -5190,95 +5198,95 @@ - (void)receiveNumericReply:(IRCMessage *)m case 330: { NSString *text = [NSString stringWithFormat:@"%@ %@ %@", [m paramAt:1], [m sequence:3], [m paramAt:2]]; - + if (self.whoisChannel) { [self printBoth:self.whoisChannel type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; - } else { + } else { [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; } - + break; } case 367: { NSString *mask = [m paramAt:2]; NSString *owner = [m paramAt:3]; - + long long seton = [[m paramAt:4] longLongValue]; - + if (self.chanBanListSheet) { [self.chanBanListSheet addBan:mask tset:[dateTimeFormatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:seton]] setby:owner]; } - + break; } case 346: { NSString *mask = [m paramAt:2]; NSString *owner = [m paramAt:3]; - + long long seton = [[m paramAt:4] longLongValue]; - + if (self.inviteExceptionSheet) { [self.inviteExceptionSheet addException:mask tset:[dateTimeFormatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:seton]] setby:owner]; } - + break; } case 348: { NSString *mask = [m paramAt:2]; NSString *owner = [m paramAt:3]; - + long long seton = [[m paramAt:4] longLongValue]; - + if (self.banExceptionSheet) { [self.banExceptionSheet addException:mask tset:[dateTimeFormatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:seton]] setby:owner]; } - + break; } case 381: { if (self.hasIRCopAccess == NO) { - /* If we are already an IRCOp, then we do not need to see this line again. + /* If we are already an IRCOp, then we do not need to see this line again. We will assume that if we are seeing it again, then it is the result of a user opening two connections to a single bouncer session. */ - + [self printBoth:nil type:TVCLogLineReplyType text:TXTFLS(@"IRCUserIsNowIRCOperator", m.sender.nick) receivedAt:m.receivedAt]; - + self.hasIRCopAccess = YES; } - + break; } case 328: { NSString *chname = [m paramAt:1]; NSString *website = [m paramAt:2]; - + IRCChannel *c = [self findChannel:chname]; - + if (c && website) { [self printBoth:c type:TVCLogLineWebsiteType text:TXTFLS(@"IRCChannelHasWebsite", website) receivedAt:m.receivedAt]; } - + break; } case 369: { self.inWhoWasRun = NO; - + [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:[m sequence] receivedAt:m.receivedAt]; - + break; } case 900: { self.isIdentifiedWithSASL = YES; - + [self printBoth:self type:TVCLogLineReplyType text:[m sequence:3] receivedAt:m.receivedAt]; - + break; } case 903: @@ -5287,18 +5295,18 @@ - (void)receiveNumericReply:(IRCMessage *)m case 906: case 907: { - if (n == 903) { // success + if (n == 903) { // success [self printBoth:self type:TVCLogLineNoticeType text:[m sequence:1] receivedAt:m.receivedAt]; } else { [self printReply:m]; } - + if (self.inSASLRequest) { self.inSASLRequest = NO; - + [self resumeCap]; } - + break; } default: @@ -5306,9 +5314,9 @@ - (void)receiveNumericReply:(IRCMessage *)m if ([self.world.bundlesForServerInput containsKey:[NSString stringWithInteger:m.numericReply]]) { break; } - + [self printUnknownReply:m]; - + break; } } @@ -5317,53 +5325,53 @@ - (void)receiveNumericReply:(IRCMessage *)m - (void)receiveErrorNumericReply:(IRCMessage *)m { NSInteger n = m.numericReply; - + switch (n) { - case 401: + case 401: { IRCChannel *c = [self findChannel:[m paramAt:1]]; - + if (c && c.isActive) { [self printErrorReply:m channel:c]; - + return; } else { [self printErrorReply:m]; } - + return; break; } - case 433: - case 437: + case 433: + case 437: { if (self.isLoggedIn) break; - + [self receiveNickCollisionError:m]; - + return; break; } - case 402: + case 402: { NSString *text = TXTFLS(@"IRCHadRawError", m.numericReply, [m sequence:1]); - + [self printBoth:[self.world selectedChannelOn:self] type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; - + return; break; } - case 404: + case 404: { NSString *chname = [m paramAt:1]; NSString *text = TXTFLS(@"IRCHadRawError", m.numericReply, [m sequence:2]); - + [self printBoth:[self findChannel:chname] type:TVCLogLineReplyType text:text receivedAt:m.receivedAt]; - + return; break; } - case 405: + case 405: case 471: case 473: case 474: @@ -5372,13 +5380,13 @@ - (void)receiveErrorNumericReply:(IRCMessage *)m case 485: { IRCChannel *c = [self findChannel:[m paramAt:1]]; - + if (c) { c.errLastJoin = YES; } } } - + [self printErrorReply:m]; } @@ -5386,12 +5394,12 @@ - (void)receiveNickCollisionError:(IRCMessage *)m { if (self.config.altNicks.count && self.isLoggedIn == NO) { ++self.tryingNickNumber; - + NSArray *altNicks = self.config.altNicks; - + if (self.tryingNickNumber < altNicks.count) { NSString *nick = [altNicks safeObjectAtIndex:self.tryingNickNumber]; - + [self send:IRCCommandIndexNick, nick, nil]; } else { [self tryAnotherNick]; @@ -5405,36 +5413,36 @@ - (void)tryAnotherNick { if (self.sentNick.length >= self.isupport.nickLen) { NSString *nick = [self.sentNick safeSubstringToIndex:self.isupport.nickLen]; - + BOOL found = NO; - + for (NSInteger i = (nick.length - 1); i >= 0; --i) { UniChar c = [nick characterAtIndex:i]; - + if (NSDissimilarObjects(c, '_')) { found = YES; - + NSString *head = [nick safeSubstringToIndex:i]; - + NSMutableString *s = [head mutableCopy]; - + for (NSInteger i = (self.isupport.nickLen - s.length); i > 0; --i) { [s appendString:@"_"]; } - + self.sentNick = s; - + break; } } - + if (found == NO) { self.sentNick = @"0"; } } else { self.sentNick = [self.sentNick stringByAppendingString:@"_"]; } - + [self send:IRCCommandIndexNick, self.sentNick, nil]; } @@ -5444,153 +5452,159 @@ - (void)tryAnotherNick - (void)changeStateOff { if (self.isLoggedIn == NO && self.isConnecting == NO) return; - + BOOL prevConnected = self.isConnected; - + [self.acceptedCaps removeAllObjects]; self.capPaused = 0; - + self.userhostInNames = NO; self.multiPrefix = NO; self.identifyMsg = NO; self.identifyCTCP = NO; - + self.conn = nil; - + for (IRCChannel *c in self.channels) { c.status = IRCChannelParted; } - + [self clearCommandQueue]; [self stopRetryTimer]; [self stopISONTimer]; - + if (self.reconnectEnabled) { [self startReconnectTimer]; } - + self.sendLagcheckToChannel = self.isIdentifiedWithSASL = NO; self.isConnecting = self.isConnected = self.isLoggedIn = self.isQuitting = NO; self.hasIRCopAccess = self.serverHasNickServ = self.autojoinInitialized = NO; - + self.myNick = NSStringEmptyPlaceholder; self.sentNick = NSStringEmptyPlaceholder; - + self.tryingNickNumber = -1; - + NSString *disconnectTXTLString = nil; - + switch (self.disconnectType) { - case IRCDisconnectNormalMode: disconnectTXTLString = @"IRCDisconnectedFromServer"; break; - case IRCSleepModeDisconnectMode: disconnectTXTLString = @"IRCDisconnectedBySleepMode"; break; - case IRCTrialPeriodDisconnectMode: disconnectTXTLString = @"IRCDisconnectedByTrialPeriodTimer"; break; + case IRCDisconnectNormalMode: disconnectTXTLString = @"IRCDisconnectedFromServer"; break; + case IRCSleepModeDisconnectMode: disconnectTXTLString = @"IRCDisconnectedBySleepMode"; break; + case IRCTrialPeriodDisconnectMode: disconnectTXTLString = @"IRCDisconnectedByTrialPeriodTimer"; break; + case IRCBadSSLCertificateDisconnectMode: disconnectTXTLString = @"IRCDisconnectedByBadSSLCertificate"; break; default: break; } - + if (disconnectTXTLString) { for (IRCChannel *c in self.channels) { if (c.isActive) { [c deactivate]; - + [self printSystem:c text:TXTLS(disconnectTXTLString)]; } } - + [self printSystemBoth:nil text:TXTLS(disconnectTXTLString)]; - + if (prevConnected) { [self notifyEvent:TXNotificationDisconnectType lineType:TVCLogLineSystemType]; } } - + #ifdef IS_TRIAL_BINARY [self stopTrialPeriodTimer]; #endif - + [self reloadTree]; - + self.isAway = NO; } - (void)ircConnectionDidConnect:(IRCConnection *)sender { [self startRetryTimer]; - - if (NSDissimilarObjects(self.connectType, IRCBadSSLCertificateReconnectMode)) { - [self printSystemBoth:nil text:TXTLS(@"IRCConnectedToServer")]; - } - + [self printSystemBoth:nil text:TXTLS(@"IRCConnectedToServer")]; + self.isLoggedIn = NO; self.isConnected = self.reconnectEnabled = YES; - + self.encoding = self.config.encoding; - + if (NSObjectIsEmpty(self.inputNick)) { self.inputNick = self.config.nick; } - + self.sentNick = self.inputNick; self.myNick = self.inputNick; - + [self.isupport reset]; - + NSInteger modeParam = ((self.config.invisibleMode) ? 8 : 0); - + NSString *user = self.config.username; NSString *realName = self.config.realName; - + if (NSObjectIsEmpty(user)) { user = self.config.nick; } - + if (NSObjectIsEmpty(realName)) { realName = self.config.nick; } - + [self send:IRCCommandIndexCap, @"LS", nil]; - + if (NSObjectIsNotEmpty(self.config.password)) { [self send:IRCCommandIndexPass, self.config.password, nil]; } - + [self send:IRCCommandIndexNick, self.sentNick, nil]; - + if (self.config.bouncerMode) { // Fuck psybnc — use ZNC [self send:IRCCommandIndexUser, user, [NSString stringWithDouble:modeParam], @"*", [@":" stringByAppendingString:realName], nil]; } else { [self send:IRCCommandIndexUser, user, [NSString stringWithDouble:modeParam], @"*", realName, nil]; } - + [self.world reloadTree]; } + +- (void)ircBadSSLCertificateDisconnectCallback:(TLOPopupPromptReturnType)returnCode +{ + NSString *supKeyFull = [TXPopupPromptSuppressionPrefix stringByAppendingFormat:@"cert_trust_error.@", self.config.guid];; + + if (returnCode == TLOPopupPromptReturnPrimaryType) { + [_NSUserDefaults() setBool:YES forKey:supKeyFull]; + + self.config.isTrustedConnection = YES; + + [self connect:IRCBadSSLCertificateReconnectMode]; + } +} + - (void)ircConnectionDidDisconnect:(IRCConnection *)sender { if (self.disconnectType == IRCBadSSLCertificateDisconnectMode) { - NSString *supkeyHead = TXPopupPromptSuppressionPrefix; NSString *supkeyBack = [NSString stringWithFormat:@"cert_trust_error.@", self.config.guid]; - + if (self.config.isTrustedConnection == NO) { - BOOL status = [TLOPopupPrompts dialogWindowWithQuestion:TXTLS(@"SocketBadSSLCertificateErrorMessage") - title:TXTLS(@"SocketBadSSLCertificateErrorTitle") - defaultButton:TXTLS(@"TrustButton") - alternateButton:TXTLS(@"CancelButton") - otherButton:nil - suppressionKey:supkeyBack - suppressionText:@"-"]; - - [_NSUserDefaults() setBool:status forKey:[supkeyHead stringByAppendingString:supkeyBack]]; - - if (status) { - self.config.isTrustedConnection = status; - - [self connect:IRCBadSSLCertificateReconnectMode]; - - return; - } + TLOPopupPrompts *prompt = [TLOPopupPrompts new]; + + [prompt sheetWindowWithQuestion:self.world.window + target:self + action:@selector(ircBadSSLCertificateDisconnectCallback:) + body:TXTLS(@"SocketBadSSLCertificateErrorMessage") + title:TXTLS(@"SocketBadSSLCertificateErrorTitle") + defaultButton:TXTLS(@"TrustButton") + alternateButton:TXTLS(@"CancelButton") + otherButton:nil + suppressionKey:supkeyBack + suppressionText:@"-"]; } } - + [self changeStateOff]; } @@ -5602,42 +5616,42 @@ - (void)ircConnectionDidError:(NSString *)error - (void)ircConnectionDidReceive:(NSData *)data { self.lastMessageReceived = [NSDate epochTime]; - + NSString *s = [NSString stringWithData:data encoding:self.encoding]; - + if (PointerIsEmpty(s)) { s = [NSString stringWithData:data encoding:self.config.fallbackEncoding]; - + if (PointerIsEmpty(s)) { s = [NSString stringWithData:data encoding:NSUTF8StringEncoding]; - + if (PointerIsEmpty(s)) { NSLog(@"NSData decode failure. (%@)", data); - + return; } } } - + self.world.messagesReceived++; self.world.bandwidthIn += [s length]; - + if (self.rawModeEnabled) { NSLog(@" >> %@", s); } - + if ([TPCPreferences removeAllFormatting]) { s = [s stripEffects]; } - + IRCMessage *m = [[IRCMessage alloc] initWithLine:s]; - + NSString *cmd = m.command; - - if (m.numericReply > 0) { + + if (m.numericReply > 0) { [self receiveNumericReply:m]; } else { - switch ([TPCPreferences indexOfIRCommand:cmd]) { + switch ([TPCPreferences indexOfIRCommand:cmd]) { case 4: // Command: ERROR { [self receiveError:m]; @@ -5707,16 +5721,16 @@ - (void)ircConnectionDidReceive:(NSData *)data case 89: // Command: ADCHAT { [m.params safeInsertObject:m.sender.nick atIndex:0]; - + NSString *text = [m.params safeObjectAtIndex:1]; - + [m.params safeRemoveObjectAtIndex:1]; [m.params safeInsertObject:[NSString stringWithFormat:@"[%@]: %@", m.command, text] atIndex:1]; - + m.command = IRCCommandIndexNotice; - + [self receivePrivmsgAndNotice:m]; - + break; } case 101: // Command: AUTHENTICATE @@ -5727,11 +5741,11 @@ - (void)ircConnectionDidReceive:(NSData *)data } } } - + if ([self.world.bundlesForServerInput containsKey:cmd]) { [self.invokeInBackgroundThread processBundlesServerMessage:m]; } - + [self.world updateTitle]; } @@ -5745,7 +5759,7 @@ - (void)ircConnectionWillSend:(NSString *)line + (void)load { if (NSDissimilarObjects(self, [IRCClient class])) return; - + @autoreleasepool { dateTimeFormatter = [NSDateFormatter new]; [dateTimeFormatter setDateStyle:NSDateFormatterLongStyle]; diff --git a/Classes/Library/External Libraries/Sockets/GCDAsyncSocketExtensions.m b/Classes/Library/External Libraries/Sockets/GCDAsyncSocketExtensions.m index 49656e770f..526f36b28e 100755 --- a/Classes/Library/External Libraries/Sockets/GCDAsyncSocketExtensions.m +++ b/Classes/Library/External Libraries/Sockets/GCDAsyncSocketExtensions.m @@ -3,6 +3,8 @@ #import "TextualApplication.h" +#import + #define TXkCFStreamErrorDomainSSL @"kCFStreamErrorDomainSSL" @implementation GCDAsyncSocket (GCDsyncSocketExtensions) @@ -15,12 +17,12 @@ + (id)socketWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq socket + (void)useSSLWithConnection:(id)socket delegate:(id)theDelegate { IRCClient *client = [theDelegate performSelector:@selector(delegate)]; - + NSMutableDictionary *settings = [NSMutableDictionary dictionary]; - + settings[CFItemRefToID(kCFStreamSSLLevel)] = CFItemRefToID(kCFStreamSocketSecurityLevelNegotiatedSSL); settings[CFItemRefToID(kCFStreamSSLPeerName)] = CFItemRefToID(kCFNull); - + if (client.config.isTrustedConnection) { settings[CFItemRefToID(kCFStreamSSLIsServer)] = CFItemRefToID(kCFBooleanFalse); settings[CFItemRefToID(kCFStreamSSLAllowsAnyRoot)] = CFItemRefToID(kCFBooleanTrue); @@ -28,7 +30,7 @@ + (void)useSSLWithConnection:(id)socket delegate:(id)theDelegate settings[CFItemRefToID(kCFStreamSSLAllowsExpiredCertificates)] = CFItemRefToID(kCFBooleanTrue); settings[CFItemRefToID(kCFStreamSSLValidatesCertificateChain)] = CFItemRefToID(kCFBooleanFalse); } - + [socket startTLS:settings]; } @@ -36,44 +38,87 @@ + (BOOL)badSSLCertErrorFound:(NSError *)error { NSInteger code = [error code]; NSString *domain = [error domain]; - + if ([domain isEqualToString:TXkCFStreamErrorDomainSSL]) { - NSArray *errorCodes = @[@(errSSLBadCert), - @(errSSLNoRootCert), - @(errSSLCertExpired), - @(errSSLPeerBadCert), - @(errSSLPeerCertRevoked), - @(errSSLPeerCertExpired), - @(errSSLPeerCertUnknown), - @(errSSLUnknownRootCert), - @(errSSLCertNotYetValid), - @(errSSLXCertChainInvalid), - @(errSSLPeerUnsupportedCert), - @(errSSLPeerUnknownCA), - @(errSSLHostNameMismatch)]; - + NSArray *errorCodes = @[@(errSSLBadCert), + @(errSSLNoRootCert), + @(errSSLCertExpired), + @(errSSLPeerBadCert), + @(errSSLPeerCertRevoked), + @(errSSLPeerCertExpired), + @(errSSLPeerCertUnknown), + @(errSSLUnknownRootCert), + @(errSSLCertNotYetValid), + @(errSSLXCertChainInvalid), + @(errSSLPeerUnsupportedCert), + @(errSSLPeerUnknownCA), + @(errSSLHostNameMismatch)]; + NSNumber *errorCode = @(code); - + return [errorCodes containsObject:errorCode]; } - + return NO; } + (NSString *)posixErrorStringFromErrno:(NSInteger)code { const char *error = strerror((int)code); - + if (error) { return @(error); } - + return nil; } +- (void)requestSSLTrustFor:(NSWindow *)docWindow + modalDelegate:(id)adelegate + didEndSelector:(SEL)didEndSelector + contextInfo:(void *)contextInfo + defaultButton:(NSString *)defaultButton + alternateButton:(NSString *)alternateButton +{ + SecTrustRef trust = [self sslCertificateTrustInformation]; + + DLog(@"SSL Trust Ref: %@", trust); + + if (PointerIsNotEmpty(trust)) { + SFCertificatePanel *panel = [SFCertificatePanel sharedCertificatePanel]; + + [panel setDefaultButtonTitle:defaultButton]; + [panel setAlternateButtonTitle:alternateButton]; + + [panel beginSheetForWindow:docWindow + modalDelegate:adelegate + didEndSelector:didEndSelector + contextInfo:contextInfo + trust:trust + showGroup:NO]; + } +} + +- (SecTrustRef)sslCertificateTrustInformation /* @private */ +{ + __block SecTrustRef trust; + + dispatch_block_t block = ^{ + OSStatus status = SSLCopyPeerTrust(self.sslContext, &trust); + + DLog(@"SSL Context: %@\nTrust Ref: %@\nCopy Status: %i", self.sslContext, trust, status); + +#pragma unused(status) + }; + + [self performBlock:block]; + + return trust; +} + @end -@implementation AsyncSocket (RLMAsyncSocketExtensions) +@implementation AsyncSocket (RLMAsyncSocketExtensions) + (id)socketWithDelegate:(id)delegate { @@ -83,30 +128,30 @@ + (id)socketWithDelegate:(id)delegate - (void)useSystemSocksProxy { CFDictionaryRef settings = SCDynamicStoreCopyProxies(NULL); - + CFReadStreamSetProperty(theReadStream, kCFStreamPropertySOCKSProxy, settings); CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertySOCKSProxy, settings); - + CFRelease(settings); } -- (void)useSocksProxyVersion:(NSInteger)version - host:(NSString *)host - port:(NSInteger)port - user:(NSString *)user +- (void)useSocksProxyVersion:(NSInteger)version + host:(NSString *)host + port:(NSInteger)port + user:(NSString *)user password:(NSString *)password { NSMutableDictionary *settings = [NSMutableDictionary dictionary]; - + if (version == 4) { settings[CFItemRefToID(kCFStreamPropertySOCKSVersion)] = CFItemRefToID(kCFStreamSocketSOCKSVersion4); } else { settings[CFItemRefToID(kCFStreamPropertySOCKSVersion)] = CFItemRefToID(kCFStreamSocketSOCKSVersion5); } - + settings[CFItemRefToID(kCFStreamPropertySOCKSProxyHost)] = host; [settings setInteger:port forKey:CFItemRefToID(kCFStreamPropertySOCKSProxyPort)]; - + if (NSObjectIsNotEmpty(user)) settings[CFItemRefToID(kCFStreamPropertySOCKSUser)] = user; if (NSObjectIsNotEmpty(password)) settings[CFItemRefToID(kCFStreamPropertySOCKSPassword)] = password; diff --git a/Classes/Library/TLOPopupPrompts.m b/Classes/Library/TLOPopupPrompts.m index 443918102d..fb5b669abc 100755 --- a/Classes/Library/TLOPopupPrompts.m +++ b/Classes/Library/TLOPopupPrompts.m @@ -22,21 +22,29 @@ + (void)sheetWindowWithQuestionCallback:(NSAlert *)alert returnCode:(NSInteger)r if (NSObjectIsNotEmpty(suppressionKey)) { NSButton *button = [alert suppressionButton]; - + [_NSUserDefaults() setBool:[button state] forKey:suppressionKey]; } - + if ([targetClass isKindOfClass:[self class]]) { return; } - - objc_msgSend(targetClass, targetAction, @(returnCode)); + + TLOPopupPromptReturnType returnValue = TLOPopupPromptReturnPrimaryType; + + if (returnCode == NSAlertSecondButtonReturn) { + returnValue = TLOPopupPromptReturnSecondaryType; + } else if (returnCode == NSAlertOtherReturn || returnCode == NSAlertThirdButtonReturn) { + returnValue = TLOPopupPromptReturnOtherType; + } + + objc_msgSend(targetClass, targetAction, returnValue); } - (void)sheetWindowWithQuestion:(NSWindow *)window target:(id)targetClass action:(SEL)actionSelector - body:(NSString *)bodyText + body:(NSString *)bodyText title:(NSString *)titleText defaultButton:(NSString *)buttonDefault alternateButton:(NSString *)buttonAlternate @@ -45,19 +53,21 @@ - (void)sheetWindowWithQuestion:(NSWindow *)window suppressionText:(NSString *)suppressText { BOOL useSupression = NO; - + NSString *__suppressionKey = NSStringEmptyPlaceholder; - + if (NSObjectIsNotEmpty(suppressKey)) { __suppressionKey = [TXPopupPromptSuppressionPrefix stringByAppendingString:suppressKey]; - - useSupression = YES; - - if ([_NSUserDefaults() boolForKey:__suppressionKey] == YES && [suppressText isEqualToString:@"-"] == NO) { - return; + + if ([suppressText isEqualToString:@"-"] == NO) { + useSupression = YES; + + if ([_NSUserDefaults() boolForKey:__suppressionKey] == YES) { + return; + } } } - + if (useSupression) { if (NSObjectIsEmpty(suppressText)) { suppressText = TXTLS(@"PromptSuppressionButtonDefaultTitle"); @@ -65,29 +75,29 @@ - (void)sheetWindowWithQuestion:(NSWindow *)window } else { suppressText = nil; } - + NSAlert *alert = [NSAlert new]; - + [alert setAlertStyle:NSInformationalAlertStyle]; - + [alert setMessageText:titleText]; [alert setInformativeText:bodyText]; [alert addButtonWithTitle:buttonDefault]; [alert addButtonWithTitle:buttonAlternate]; [alert addButtonWithTitle:otherButton]; [alert setShowsSuppressionButton:useSupression]; - + [[alert suppressionButton] setTitle:suppressText]; NSArray *context = @[__suppressionKey, targetClass, - NSStringFromSelector(actionSelector)]; + NSStringFromSelector(actionSelector)]; [alert beginSheetModalForWindow:window modalDelegate:[self class] didEndSelector:@selector(sheetWindowWithQuestionCallback:returnCode:contextInfo:) contextInfo:(void *)CFBridgingRetain(context)]; } -+ (void)popupPromptNULLSelector:(NSInteger)returnCode ++ (void)popupPromptNilSelector:(TLOPopupPromptReturnType)returnCode { return; } @@ -95,7 +105,7 @@ + (void)popupPromptNULLSelector:(NSInteger)returnCode #pragma mark - #pragma mark Alert Dialogs -+ (BOOL)dialogWindowWithQuestion:(NSString *)bodyText ++ (BOOL)dialogWindowWithQuestion:(NSString *)bodyText title:(NSString *)titleText defaultButton:(NSString *)buttonDefault alternateButton:(NSString *)buttonAlternate @@ -104,72 +114,72 @@ + (BOOL)dialogWindowWithQuestion:(NSString *)bodyText suppressionText:(NSString *)suppressText { BOOL useSupression = NO; - + NSString *__suppressKey = NSStringEmptyPlaceholder; - + if (NSObjectIsNotEmpty(suppressKey) && [suppressText isEqualToString:@"-"] == NO) { __suppressKey = [TXPopupPromptSuppressionPrefix stringByAppendingString:suppressKey]; useSupression = YES; - + if ([_NSUserDefaults() boolForKey:__suppressKey] == YES) { return YES; } } - + NSAlert *alert = [NSAlert alertWithMessageText:titleText defaultButton:buttonDefault alternateButton:buttonAlternate otherButton:otherButton informativeTextWithFormat:bodyText]; - + NSButton *button = [alert suppressionButton]; - + [alert setShowsSuppressionButton:useSupression]; - + if (useSupression) { if (NSObjectIsEmpty(suppressText)) { suppressText = TXTLS(@"PromptSuppressionButtonDefaultTitle"); } - + [button setTitle:suppressText]; } - + if ([alert runModal] == NSAlertDefaultReturn) { if (useSupression) { [_NSUserDefaults() setBool:[button state] forKey:__suppressKey]; } - + return YES; } else { return NO; } } -+ (NSString *)dialogWindowWithInput:(NSString *)bodyText ++ (NSString *)dialogWindowWithInput:(NSString *)bodyText title:(NSString *)titleText defaultButton:(NSString *)buttonDefault alternateButton:(NSString *)buttonAlternate defaultInput:(NSString *)defaultValue { TVCInputPromptDialog *dialog = [TVCInputPromptDialog new]; - + [dialog alertWithMessageText:titleText defaultButton:buttonDefault alternateButton:buttonAlternate informativeText:bodyText defaultUserInput:defaultValue]; - + [dialog runModal]; - + NSInteger button = [dialog buttonClicked]; - + NSString *result = [dialog promptValue]; - + if (NSObjectIsNotEmpty(result) && button == NSAlertDefaultReturn) { return result; } - + return nil; } diff --git a/Classes/Library/TLOSocketClient.m b/Classes/Library/TLOSocketClient.m index 3003fdcd1f..be7b8cb7c3 100755 --- a/Classes/Library/TLOSocketClient.m +++ b/Classes/Library/TLOSocketClient.m @@ -13,7 +13,7 @@ - (id)init if ((self = [super init])) { self.buffer = [NSMutableData new]; } - + return self; } @@ -30,7 +30,7 @@ - (void)destroyDispatchQueue dispatch_release(self.dispatchQueue); self.dispatchQueue = NULL; } - + if (self.socketQueue) { dispatch_release(self.socketQueue); self.socketQueue = NULL; @@ -43,7 +43,7 @@ - (void)createDispatchQueue if ([self useNewSocketEngine]) { NSString *dqname = [NSString stringWithUUID]; NSString *sqname = [NSString stringWithUUID]; - + self.socketQueue = dispatch_queue_create([sqname UTF8String], NULL); self.dispatchQueue = dispatch_queue_create([dqname UTF8String], NULL); } @@ -55,9 +55,9 @@ - (void)dealloc [self.conn setDelegate:nil]; [self.conn disconnect]; } - + [self destroyDispatchQueue]; - + self.delegate = nil; } @@ -65,47 +65,47 @@ - (void)open { [self createDispatchQueue]; [self close]; - + [self.buffer setLength:0]; - + NSError *connError = nil; - + if ([self useNewSocketEngine]) { self.conn = [GCDAsyncSocket socketWithDelegate:self delegateQueue:self.dispatchQueue socketQueue:self.socketQueue]; - + IRCClient *clin = [self.delegate delegate]; - + [self.conn setPreferIPv4OverIPv6:BOOLReverseValue(clin.config.prefersIPv6)]; } else { self.conn = [AsyncSocket socketWithDelegate:self]; } - + if ([self.conn connectToHost:self.host onPort:self.port withTimeout:(-1) error:&connError] == NO) { NSLog(@"Silently ignoring connection error: %@", [connError localizedDescription]); } - + self.active = YES; self.connecting = YES; self.connected = NO; - + self.sendQueueSize = 0; } - (void)close { if (PointerIsEmpty(self.conn)) return; - + [self.conn setDelegate:nil]; [self.conn disconnect]; - + [self destroyDispatchQueue]; - + self.active = NO; self.connecting = NO; self.connected = NO; - + self.sendQueueSize = 0; } @@ -113,43 +113,43 @@ - (NSData *)readLine { NSInteger len = [self.buffer length]; if (len < 1) return nil; - + const char *bytes = [self.buffer bytes]; char *p = memchr(bytes, _LF, len); - + if (p == NULL) return nil; - + NSInteger n = (p - bytes); - + if (n > 0) { char prev = *(p - 1); - + if (prev == _CR) { --n; } } - + NSMutableData *result = self.buffer; - + ++p; - + if (p < (bytes + len)) { self.buffer = [[NSMutableData alloc] initWithBytes:p length:((bytes + len) - p)]; } else { self.buffer = [NSMutableData new]; } - + [result setLength:n]; - + return result; } - (void)write:(NSData *)data { if (self.connected == NO) return; - + ++self.sendQueueSize; - + [self.conn writeData:data withTimeout:(-1) tag:0]; [self.conn readDataWithTimeout:(-1) tag:0]; } @@ -159,31 +159,31 @@ - (BOOL)onSocketWillConnect:(id)sock if (self.useSystemSocks) { [self.conn useSystemSocksProxy]; } else if (self.useSocks) { - [self.conn useSocksProxyVersion:self.socksVersion - host:self.proxyHost - port:self.proxyPort - user:self.proxyUser - password:self.proxyPassword]; + [self.conn useSocksProxyVersion:self.socksVersion + host:self.proxyHost + port:self.proxyPort + user:self.proxyUser + password:self.proxyPassword]; } else if (self.useSSL) { [GCDAsyncSocket useSSLWithConnection:self.conn delegate:self.delegate]; } - + return YES; } - (void)onSocket:(id)sock didConnectToHost:(NSString *)ahost port:(UInt16)aport { - [self.conn readDataWithTimeout:(-1) tag:0]; - + [self.conn readDataWithTimeout:(-1) tag:0]; + self.connecting = NO; self.connected = YES; - + if ([self.delegate respondsToSelector:@selector(tcpClientDidConnect:)]) { [self.delegate tcpClientDidConnect:self]; } - + IRCClient *clin = [self.delegate delegate]; - + if (clin.rawModeEnabled) { NSLog(@"Debug Information:"); NSLog(@" Connected Host: %@", [sock connectedHost]); @@ -195,10 +195,10 @@ - (void)onSocket:(id)sock didConnectToHost:(NSString *)ahost port:(UInt16)aport - (void)onSocketDidDisconnect:(id)sock { [self close]; - + if ([self.delegate respondsToSelector:@selector(tcpClientDidDisconnect:)]) { [self.delegate tcpClientDidDisconnect:self]; - } + } } - (void)onSocket:(id)sender willDisconnectWithError:(NSError *)error @@ -208,25 +208,25 @@ - (void)onSocket:(id)sender willDisconnectWithError:(NSError *)error } else { NSString *msg = nil; NSString *domain = [error domain]; - + if ([GCDAsyncSocket badSSLCertErrorFound:error]) { IRCClient *client = [self.delegate performSelector:@selector(delegate)]; - + client.disconnectType = IRCBadSSLCertificateDisconnectMode; } else { if ([domain isEqualToString:NSPOSIXErrorDomain]) { msg = [GCDAsyncSocket posixErrorStringFromErrno:[error code]]; - } - + } + if (NSObjectIsEmpty(msg)) { msg = [error localizedDescription]; } - + if ([self.delegate respondsToSelector:@selector(tcpClient:error:)]) { [self.delegate tcpClient:self error:msg]; } } - + if ([self useNewSocketEngine]) { [self onSocketDidDisconnect:sender]; } @@ -238,24 +238,24 @@ - (void)onSocket:(id)sock didReadData:(NSData *)data withTag:(long)tag if (PointerIsEmpty(self.delegate)) { return; } - + [self.buffer appendData:data]; - + if ([self.delegate respondsToSelector:@selector(tcpClientDidReceiveData:)]) { [self.delegate tcpClientDidReceiveData:self]; } - - [self.conn readDataWithTimeout:(-1) tag:0]; + + [self.conn readDataWithTimeout:(-1) tag:0]; } - (void)onSocket:(id)sock didWriteDataWithTag:(long)tag { --self.sendQueueSize; - + if (PointerIsEmpty(self.delegate)) { return; } - + if ([self.delegate respondsToSelector:@selector(tcpClientDidSendData:)]) { [self.delegate tcpClientDidSendData:self]; } @@ -282,4 +282,49 @@ - (void)socket:(id)sock didWriteDataWithTag:(long)tag [self.iomt onSocket:sock didWriteDataWithTag:tag]; } +#pragma mark - +#pragma mark SSL Certificate Trust Message + +- (void)openSSLCertificateTrustDialog +{ + [self openSSLCertificateTrustDialog:nil]; +} + +- (void)openSSLCertificateTrustDialog:(NSString *)suppressKey; +{ + if ([self useNewSocketEngine]) { + BOOL saveTrust = NSObjectIsNotEmpty(suppressKey); + + TXMasterController *master = [TPCPreferences masterController]; + + NSString *defaultButton = TXTLS(@"ContinueButton"); + NSString *alternateButton = TXTLS(@"CancelButton"); + + if (saveTrust == NO) { + defaultButton = TXTLS(@"CloseButton"); + alternateButton = nil; + } + + [self.conn requestSSLTrustFor:master.window + modalDelegate:self + didEndSelector:@selector(SSLCertificateTrustDialogDidEnd:returnCode:contextInfo:) + contextInfo:(__bridge void *)(suppressKey) + defaultButton:defaultButton + alternateButton:alternateButton]; + } +} + +- (void)SSLCertificateTrustDialogDidEnd:(NSWindow *)sheet + returnCode:(NSInteger)returnCode + contextInfo:(void *)contextInfo +{ + NSString *suppressKey = (__bridge NSString *)(contextInfo); + + if (NSObjectIsNotEmpty(suppressKey)) { + if (returnCode == NSFileHandlingPanelOKButton) { + [_NSUserDefaults() setBool:YES forKey:suppressKey]; + } + } +} + @end \ No newline at end of file diff --git a/Classes/Preferences/TPCPreferences.m b/Classes/Preferences/TPCPreferences.m index 66152f6737..af3ec1df73 100755 --- a/Classes/Preferences/TPCPreferences.m +++ b/Classes/Preferences/TPCPreferences.m @@ -140,6 +140,7 @@ + (void)populateCommandIndex commandIndex[IRCCommandIndexShun] = @"99"; commandIndex[IRCCommandIndexSme] = @"92"; commandIndex[IRCCommandIndexSmsg] = @"93"; + commandIndex[IRCCommandIndexSslcontext] = @"106"; commandIndex[IRCCommandIndexT] = @"61"; commandIndex[IRCCommandIndexTempshun] = @"100"; commandIndex[IRCCommandIndexTime] = @"34"; @@ -1087,11 +1088,9 @@ + (void)observeValueForKeyPath:(NSString *)key ofObject:(id)object change:(NSDic #pragma mark - #pragma mark Initialization -+ (void)defaultIRCClientSheetCallback:(NSNumber *)returnCode ++ (void)defaultIRCClientSheetCallback:(TLOPopupPromptReturnType)returnCode { - NSInteger _returnCode = [returnCode integerValue]; - - if (_returnCode == NSAlertFirstButtonReturn) { + if (returnCode == TLOPopupPromptReturnPrimaryType) { NSString *bundleID = [TPCPreferences applicationBundleIdentifier]; OSStatus changeResult; diff --git a/Classes/Views/Channel View/TVCLogController.m b/Classes/Views/Channel View/TVCLogController.m index 5b8d6127eb..db95bc530f 100755 --- a/Classes/Views/Channel View/TVCLogController.m +++ b/Classes/Views/Channel View/TVCLogController.m @@ -83,6 +83,8 @@ - (void)setUp self.view.keyDelegate = self; self.view.resizeDelegate = self; self.view.autoresizingMask = (NSViewWidthSizable | NSViewHeightSizable); + + [self.view setShouldUpdateWhileOffscreen:NO]; [self loadAlternateHTML:[self initialDocument:nil]]; diff --git a/Main Project (Textual).xcodeproj/project.pbxproj b/Main Project (Textual).xcodeproj/project.pbxproj index c199b5f85f..1b864dca7e 100644 --- a/Main Project (Textual).xcodeproj/project.pbxproj +++ b/Main Project (Textual).xcodeproj/project.pbxproj @@ -46,6 +46,7 @@ 4C28F441159200C900CD2CDE /* FormattingColor_Rainbow.tif in Resources */ = {isa = PBXBuildFile; fileRef = 4C28F43F159200C900CD2CDE /* FormattingColor_Rainbow.tif */; }; 4C28F442159200C900CD2CDE /* FormattingColor_Rainbow@2x.tif in Resources */ = {isa = PBXBuildFile; fileRef = 4C28F440159200C900CD2CDE /* FormattingColor_Rainbow@2x.tif */; }; 4C28F4461592019900CD2CDE /* colloquyRoomTabRegular@2x.tif in Resources */ = {isa = PBXBuildFile; fileRef = 4C28F4441592019900CD2CDE /* colloquyRoomTabRegular@2x.tif */; }; + 4C2B8A3015B3DADA000F91B5 /* SecurityInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C2B8A2F15B3DADA000F91B5 /* SecurityInterface.framework */; }; 4C3D0B791594908B00567623 /* TPCPreferencesMigrationAssistant.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C3D0B781594908B00567623 /* TPCPreferencesMigrationAssistant.m */; }; 4C3D0B7C159490C800567623 /* TPCPreferencesMigrationAssistant.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C3D0B7B159490C800567623 /* TPCPreferencesMigrationAssistant.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4C3ED27A15A0E0E40063880C /* DarkServerListViewSelectedQueryUser.tif in Resources */ = {isa = PBXBuildFile; fileRef = 4C3ED27915A0E0E40063880C /* DarkServerListViewSelectedQueryUser.tif */; }; @@ -461,6 +462,7 @@ 4C28F43F159200C900CD2CDE /* FormattingColor_Rainbow.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = FormattingColor_Rainbow.tif; path = "Resources/Images/IRC Formatting Colors/FormattingColor_Rainbow.tif"; sourceTree = SOURCE_ROOT; }; 4C28F440159200C900CD2CDE /* FormattingColor_Rainbow@2x.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = "FormattingColor_Rainbow@2x.tif"; path = "Resources/Images/IRC Formatting Colors/FormattingColor_Rainbow@2x.tif"; sourceTree = SOURCE_ROOT; }; 4C28F4441592019900CD2CDE /* colloquyRoomTabRegular@2x.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = "colloquyRoomTabRegular@2x.tif"; path = "Resources/Images/Status Badges/colloquyRoomTabRegular@2x.tif"; sourceTree = SOURCE_ROOT; }; + 4C2B8A2F15B3DADA000F91B5 /* SecurityInterface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SecurityInterface.framework; path = ../../../../System/Library/Frameworks/SecurityInterface.framework; sourceTree = ""; }; 4C3D0B781594908B00567623 /* TPCPreferencesMigrationAssistant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TPCPreferencesMigrationAssistant.m; path = "Migration Assistant/TPCPreferencesMigrationAssistant.m"; sourceTree = ""; }; 4C3D0B7B159490C800567623 /* TPCPreferencesMigrationAssistant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPCPreferencesMigrationAssistant.h; sourceTree = ""; }; 4C3ED27915A0E0E40063880C /* DarkServerListViewSelectedQueryUser.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = DarkServerListViewSelectedQueryUser.tif; sourceTree = ""; }; @@ -805,6 +807,7 @@ 4C46CCB71580468900846B64 /* libcrypto.dylib in Frameworks */, 4C46CCB81580468900846B64 /* libicucore.dylib in Frameworks */, 4C46CCC91580469E00846B64 /* Security.framework in Frameworks */, + 4C2B8A3015B3DADA000F91B5 /* SecurityInterface.framework in Frameworks */, 4C46CCCA1580469E00846B64 /* SystemConfiguration.framework in Frameworks */, 4C46CCCB1580469E00846B64 /* WebKit.framework in Frameworks */, ); @@ -1099,6 +1102,7 @@ 4C46CCBE1580469E00846B64 /* Foundation.framework */, 4C46CCBF1580469E00846B64 /* IOKit.framework */, 4C46CCC01580469E00846B64 /* Security.framework */, + 4C2B8A2F15B3DADA000F91B5 /* SecurityInterface.framework */, 4C46CCC11580469E00846B64 /* SystemConfiguration.framework */, 4C46CCC21580469E00846B64 /* WebKit.framework */, ); @@ -1806,7 +1810,7 @@ 4C1DC5491580420500A47BC9 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0440; + LastUpgradeCheck = 0450; ORGANIZATIONNAME = "Codeux Software"; }; buildConfigurationList = 4C1DC54C1580420500A47BC9 /* Build configuration list for PBXProject "Main Project (Textual)" */; @@ -2307,6 +2311,7 @@ 4C01B9FE1580C592007E2DAF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; }; @@ -2315,6 +2320,7 @@ 4C01B9FF1580C592007E2DAF /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; }; @@ -2323,6 +2329,7 @@ 4C01BA001580C592007E2DAF /* Release (Trial) */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; }; @@ -2331,6 +2338,7 @@ 4C01BA011580C592007E2DAF /* Release + No Sandbox */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; }; @@ -2339,6 +2347,7 @@ 4C01BA021580C592007E2DAF /* App Store Release */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; }; @@ -2347,6 +2356,7 @@ 4C01BA0615810DD8007E2DAF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; TEXTUAL_BUNDLE_ID = com.codeux.irc.textual; @@ -2357,6 +2367,7 @@ 4C01BA0715810DD8007E2DAF /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; TEXTUAL_BUNDLE_ID = com.codeux.irc.textual; @@ -2367,6 +2378,7 @@ 4C01BA0815810DD8007E2DAF /* Release (Trial) */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; TEXTUAL_BUNDLE_ID = com.codeux.irc.textual.trial; @@ -2377,6 +2389,7 @@ 4C01BA0915810DD8007E2DAF /* Release + No Sandbox */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; TEXTUAL_BUNDLE_ID = com.codeux.irc.textual; @@ -2387,6 +2400,7 @@ 4C01BA0A15810DD8007E2DAF /* App Store Release */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; TEXTUAL_BUNDLE_ID = com.codeux.irc.textual; @@ -2407,7 +2421,6 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_ENTITLEMENTS = Textual.entitlements; CODE_SIGN_IDENTITY = "3rd Party Mac Developer Application"; - COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; FRAMEWORK_SEARCH_PATHS = "\"$(SRCROOT)/Frameworks/**\""; @@ -2458,7 +2471,6 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_ENTITLEMENTS = Textual.entitlements; CODE_SIGN_IDENTITY = "3rd Party Mac Developer Application"; - COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; FRAMEWORK_SEARCH_PATHS = "\"$(SRCROOT)/Frameworks/**\""; @@ -2499,6 +2511,7 @@ 4C1DC5711580420500A47BC9 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)/Frameworks/Blowfish Encryption\"", @@ -2515,6 +2528,7 @@ 4C1DC5721580420500A47BC9 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)/Frameworks/Blowfish Encryption\"", @@ -2540,7 +2554,6 @@ CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = NO; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "3rd Party Mac Developer Application"; - COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; FRAMEWORK_SEARCH_PATHS = "\"$(SRCROOT)/Frameworks/**\""; @@ -2581,6 +2594,7 @@ 4CCF2F8F1580497B006FFE21 /* Release + No Sandbox */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)/Frameworks/Blowfish Encryption\"", @@ -2607,7 +2621,6 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_ENTITLEMENTS = Textual.entitlements; CODE_SIGN_IDENTITY = "3rd Party Mac Developer Application"; - COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; FRAMEWORK_SEARCH_PATHS = "\"$(SRCROOT)/Frameworks/**\""; @@ -2648,6 +2661,7 @@ 4CCF2F9115804987006FFE21 /* Release (Trial) */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)/Frameworks/Blowfish Encryption\"", @@ -2674,7 +2688,6 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_ENTITLEMENTS = Textual.entitlements; CODE_SIGN_IDENTITY = "3rd Party Mac Developer Application"; - COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; FRAMEWORK_SEARCH_PATHS = "\"$(SRCROOT)/Frameworks/**\""; @@ -2715,6 +2728,7 @@ 4CCF2F97158049A8006FFE21 /* App Store Release */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)/Frameworks/Blowfish Encryption\"", @@ -2731,6 +2745,7 @@ 4CCF301215804DCE006FFE21 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; }; @@ -2739,6 +2754,7 @@ 4CCF301315804DCE006FFE21 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; }; @@ -2747,6 +2763,7 @@ 4CCF301415804DCE006FFE21 /* Release (Trial) */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; }; @@ -2755,6 +2772,7 @@ 4CCF301515804DCE006FFE21 /* Release + No Sandbox */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; }; @@ -2763,6 +2781,7 @@ 4CCF301615804DCE006FFE21 /* App Store Release */ = { isa = XCBuildConfiguration; buildSettings = { + COMBINE_HIDPI_IMAGES = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; }; diff --git a/Main Project (Textual).xcodeproj/xcshareddata/xcschemes/Textual (App Store Release).xcscheme b/Main Project (Textual).xcodeproj/xcshareddata/xcschemes/Textual (App Store Release).xcscheme index 93a9bb0029..241022ef86 100644 --- a/Main Project (Textual).xcodeproj/xcshareddata/xcschemes/Textual (App Store Release).xcscheme +++ b/Main Project (Textual).xcodeproj/xcshareddata/xcschemes/Textual (App Store Release).xcscheme @@ -1,6 +1,6 @@ TXBundleBuildCodeName Turtle Soup TXBundleBuildNumber - 11809 + 11856 TXBundleBuildReference - 2.1.1-234-gf411b9f-debug,llvm4.0 + 2.1.1-235-gd034fd3-debug,llvm4.0 diff --git a/Resources/Localization/English.lproj/BasicLanguage.strings b/Resources/Localization/English.lproj/BasicLanguage.strings index 04513abc8b..9a1d977447 100644 --- a/Resources/Localization/English.lproj/BasicLanguage.strings +++ b/Resources/Localization/English.lproj/BasicLanguage.strings @@ -20,6 +20,8 @@ "ChannelListNetworkName" = "Channel List for \"%@\""; +"CloseButton" = "Close"; + "CmdWShortcutCloseQueryType" = "Close Query"; "CmdWShortcutCloseWindowType" = "Close Window"; "CmdWShortcutDisconnectServerType" = "Disconnect from %@"; @@ -64,6 +66,7 @@ "IRCChannelTopicChanged" = "\002%1$@\002 changed the topic to \002%2$@\002"; "IRCConnectedToServer" = "Connection to host completed."; "IRCDisconnectedBySleepMode" = "Disconnect for Sleep Mode"; +"IRCDisconnectedByBadSSLCertificate" = "Disconnected from server because of an untrusted SSL certificate."; "IRCDisconnectedByTimeout" = "%i minutes have elapsed since last response from this connection. Disconnecting due to timeout."; "IRCDisconnectedByTrialPeriodTimer" = "Disconnected from server due to trial period limit. Please consider purchasing Textual to continue or simply reconnect."; "IRCDisconnectedFromServer" = "Disconnected"; diff --git a/Textual.entitlements b/Textual.entitlements index db431b51c2..4d48922f76 100644 --- a/Textual.entitlements +++ b/Textual.entitlements @@ -6,12 +6,10 @@ com.apple.security.files.bookmarks.app-scope - com.apple.security.network.client - com.apple.security.files.user-selected.read-write NSFileProtectionComplete - com.apple.security.temporary-exception.files.home-relative-path.read-write - /Library/Containers/com.codeux.irc.textual/ + com.apple.security.network.client + com.apple.security.temporary-exception.apple-events com.apple.systemevents @@ -23,5 +21,7 @@ com.operasoftware.opera com.growl.growlhelperapp + com.apple.security.temporary-exception.files.home-relative-path.read-write + /Library/Containers/com.codeux.irc.textual/