Changeset 3515

Show
Ignore:
Timestamp:
12/31/06 21:49:52 (2 years ago)
Author:
timothy
Message:

Auto detect for valid UTF-8 characters in server and chat messages. You can now have a chat room set to some other encoding and if someone talks in UTF-8, it will still display correctly. #188

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/Additions/NSAttributedStringAdditions.m

    r3457 r3515  
    244244                        end = i; 
    245245                        j = ++i; 
    246                         for( ; i < length && bytes[i] != '\006' ; i++ ) ; 
     246 
     247                        for( ; i < length && bytes[i] != '\006' ; i++ ); 
    247248                        if( i >= length ) break; 
    248249                        if( i == j ) continue; 
     250 
    249251                        if( bytes[j++] == 'E' ) { 
    250252                                NSString *encodingStr = [[NSString allocWithZone:nil] initWithBytes:&(bytes[j]) length:(i-j) encoding:NSASCIIStringEncoding]; 
     
    294296                                if( newEncoding && newEncoding != currentEncoding ) { 
    295297                                        if( ( end - start ) > 0 ) { 
    296                                                 NSData *subdata = [data subdataWithRange:NSMakeRange(start, end-start)]; 
     298                                                NSData *subdata = [data subdataWithRange:NSMakeRange( start, ( end - start ) )]; 
    297299                                                if( currentEncoding != NSUTF8StringEncoding ) { 
    298300                                                        NSString *tempStr = [[NSString allocWithZone:nil] initWithData:subdata encoding:currentEncoding]; 
    299                                                         // I'd check for nil, except only UTF-8 could fail since the others all 
    300                                                         // define values for all bytes 0-255 
    301                                                         subdata = [tempStr dataUsingEncoding:NSUTF8StringEncoding]; 
     301                                                        NSData *tempData = [tempStr dataUsingEncoding:NSUTF8StringEncoding]; 
     302                                                        if( tempData ) subdata = tempData; 
     303                                                        [tempStr release]; 
    302304                                                } 
    303305 
     
    314316        if( [newData length] > 0 || currentEncoding != encoding ) { 
    315317                if( start < length ) { 
    316                         NSData *subdata = [data subdataWithRange:NSMakeRange(start, length-start)]; 
     318                        NSData *subdata = [data subdataWithRange:NSMakeRange( start, ( length - start) )]; 
    317319                        if( currentEncoding != NSUTF8StringEncoding ) { 
    318320                                NSString *tempStr = [[NSString allocWithZone:nil] initWithData:subdata encoding:currentEncoding]; 
    319                                 subdata = [tempStr dataUsingEncoding:NSUTF8StringEncoding]; 
     321                                NSData *tempData = [tempStr dataUsingEncoding:NSUTF8StringEncoding]; 
     322                                if( tempData ) subdata = tempData; 
     323                                [tempStr release]; 
    320324                        } 
    321325 
     
    327331        } 
    328332 
    329         NSString *message = [[[NSString allocWithZone:nil] initWithData:data encoding:encoding] autorelease]; 
     333        BOOL validUTF8 = isValidUTF8( [data bytes], [data length] ); 
     334        NSString *message = [[[NSString allocWithZone:nil] initWithBytes:[data bytes] length:[data length] encoding:( validUTF8 ? NSUTF8StringEncoding : encoding )] autorelease]; 
    330335        if( ! message ) { 
    331336                [self release]; 
  • trunk/Additions/NSStringAdditions.h

    r3500 r3515  
     1BOOL isValidUTF8( const char *s, unsigned len ); 
     2 
    13@interface NSString (NSStringAdditions) 
    24+ (NSString *) locallyUniqueString; 
  • trunk/Additions/NSStringAdditions.m

    r3500 r3515  
    11#import "NSStringAdditions.h" 
    22#include <sys/time.h> 
     3 
     4#define is7Bit(ch) (((ch) & 0x80) == 0) 
     5#define isUTF8Tupel(ch) (((ch) & 0xE0) == 0xC0) 
     6#define isUTF8LongTupel(ch) (((ch) & 0xFE) == 0xC0) 
     7#define isUTF8Triple(ch) (((ch) & 0xF0) == 0xE0) 
     8#define isUTF8LongTriple(ch1,ch2) (((ch1) & 0xFF) == 0xE0 && ((ch2) & 0xE0) == 0x80) 
     9#define isUTF8Quartet(ch) (((ch) & 0xF8) == 0xF0) 
     10#define isUTF8LongQuartet(ch1,ch2) (((ch1) & 0xFF) == 0xF0 && ((ch2) & 0xF0) == 0x80) 
     11#define isUTF8Quintet(ch) (((ch) & 0xFC) == 0xF8) 
     12#define isUTF8LongQuintet(ch1,ch2) (((ch1) & 0xFF) == 0xF8 && ((ch2) & 0xF8) == 0x80) 
     13#define isUTF8Sextet(ch) (((ch) & 0xFE) == 0xFC) 
     14#define isUTF8LongSextet(ch1,ch2) (((ch1) & 0xFF) == 0xFC && ((ch2) & 0xFC) == 0x80) 
     15#define isUTF8Cont(ch) (((ch) & 0xC0) == 0x80) 
     16 
     17BOOL isValidUTF8( const char *s, unsigned len ) { 
     18        for( unsigned i = 0; i < len; ++i ) { 
     19                const unsigned char ch = s[i]; 
     20 
     21                if( is7Bit( ch ) ) 
     22                        continue; 
     23 
     24                if( isUTF8Tupel( ch ) ) { 
     25                        if( len - i < 1 ) // too short 
     26                                return NO; 
     27                        if( isUTF8LongTupel( ch ) ) // not minimally encoded 
     28                                return NO; 
     29                        if( ! isUTF8Cont( s[i + 1] ) ) 
     30                                return NO; 
     31                        i += 1; 
     32                } else if( isUTF8Triple( ch ) ) { 
     33                        if( len - i < 2 ) // too short 
     34                                return NO; 
     35                        if( isUTF8LongTriple( ch, s[i + 1] ) ) // not minimally encoded 
     36                                return NO; 
     37                        if( ! isUTF8Cont( s[i + 2] ) ) 
     38                                return NO; 
     39                        i += 2; 
     40                } else if( isUTF8Quartet( ch ) ) { 
     41                        if( len - i < 3 ) // too short 
     42                                return NO; 
     43                        if( isUTF8LongQuartet( ch, s[i + 1] ) ) // not minimally encoded 
     44                                return NO; 
     45                        if( ! isUTF8Cont( s[i + 2] ) || ! isUTF8Cont( s[i + 3] ) ) 
     46                                return NO; 
     47                        i += 3; 
     48                } else if( isUTF8Quintet( ch ) ) { 
     49                        if( len - i < 4 ) // too short 
     50                                return NO; 
     51                        if( isUTF8LongQuintet( ch, s[i + 1] ) ) // not minimally encoded 
     52                                return NO; 
     53                        if( ! isUTF8Cont( s[i + 2] ) || ! isUTF8Cont( s[i + 3] ) || ! isUTF8Cont( s[i + 4] ) ) 
     54                                return NO; 
     55                        i += 4; 
     56                } else if( isUTF8Sextet( ch ) ) { 
     57                        if( len - i < 5 ) // too short 
     58                                return NO; 
     59                        if( isUTF8LongSextet( ch, s[i + 1] ) ) // not minimally encoded 
     60                                return NO; 
     61                        if( ! isUTF8Cont( s[i + 2] ) || ! isUTF8Cont( s[i + 3] ) || ! isUTF8Cont( s[i + 4] ) || ! isUTF8Cont( s[i + 5] ) ) 
     62                                return NO; 
     63                        i += 5; 
     64                } else return NO; 
     65        } 
     66 
     67        return YES; 
     68} 
     69 
     70#undef is7Bit 
     71#undef isUTF8Tupel 
     72#undef isUTF8LongTupel 
     73#undef isUTF8Triple 
     74#undef isUTF8LongTriple 
     75#undef isUTF8Quartet 
     76#undef isUTF8LongQuartet 
     77#undef isUTF8Quintet 
     78#undef isUTF8LongQuintet 
     79#undef isUTF8Sextet 
     80#undef isUTF8LongSextet 
     81#undef isUTF8Cont 
    382 
    483@implementation NSString (NSStringAdditions) 
  • trunk/Chat Core.exp

    r3512 r3515  
    11.objc_class_name_MVChatConnection 
     2.objc_class_name_MVChatPluginManager 
    23.objc_class_name_MVChatRoom 
    34.objc_class_name_MVChatUser 
    45.objc_class_name_MVChatUserWatchRule 
     6.objc_class_name_MVDownloadFileTransfer 
    57.objc_class_name_MVFileTransfer 
    6 .objc_class_name_MVDownloadFileTransfer 
    78.objc_class_name_MVUploadFileTransfer 
    8 .objc_class_name_MVChatPluginManager 
    9 _MVChatConnectionWillConnectNotification 
     9_MVChatConnectionChatRoomListUpdatedNotification 
    1010_MVChatConnectionDidConnectNotification 
     11_MVChatConnectionDidDisconnectNotification 
    1112_MVChatConnectionDidNotConnectNotification 
    12 _MVChatConnectionWillDisconnectNotification 
    13 _MVChatConnectionDidDisconnectNotification 
     13_MVChatConnectionErrorDomain 
    1414_MVChatConnectionErrorNotification 
     15_MVChatConnectionGotBeepNotification 
     16_MVChatConnectionGotPrivateMessageNotification 
     17_MVChatConnectionGotRawMessageNotification 
     18_MVChatConnectionNeedCertificatePasswordNotification 
    1519_MVChatConnectionNeedNicknamePasswordNotification 
    16 _MVChatConnectionNeedCertificatePasswordNotification 
    1720_MVChatConnectionNeedPublicKeyVerificationNotification 
    18 _MVChatConnectionGotBeepNotification 
    19 _MVChatConnectionGotRawMessageNotification 
    20 _MVChatConnectionGotPrivateMessageNotification 
    21 _MVChatConnectionChatRoomListUpdatedNotification 
    22 _MVChatConnectionSelfAwayStatusChangedNotification 
    23 _MVChatConnectionWatchedUserOnlineNotification 
    24 _MVChatConnectionWatchedUserOfflineNotification 
    2521_MVChatConnectionNicknameAcceptedNotification 
    2622_MVChatConnectionNicknameRejectedNotification 
     23_MVChatConnectionSelfAwayStatusChangedNotification 
     24_MVChatConnectionSubcodeReplyNotification 
    2725_MVChatConnectionSubcodeRequestNotification 
    28 _MVChatConnectionSubcodeReplyNotification 
    29 _MVChatConnectionErrorDomain 
     26_MVChatConnectionWatchedUserOfflineNotification 
     27_MVChatConnectionWatchedUserOnlineNotification 
     28_MVChatConnectionWillConnectNotification 
     29_MVChatConnectionWillDisconnectNotification 
     30_MVChatPluginManagerDidReloadPluginsNotification 
     31_MVChatPluginManagerWillReloadPluginsNotification 
     32_MVChatRoomAttributeUpdatedNotification 
     33_MVChatRoomBannedUsersSyncedNotification 
     34_MVChatRoomGotMessageNotification 
     35_MVChatRoomInvitedNotification 
    3036_MVChatRoomJoinedNotification 
    31 _MVChatRoomPartedNotification 
    3237_MVChatRoomKickedNotification 
    33 _MVChatRoomInvitedNotification 
    34 _MVChatRoomMemberQuietedFeature 
    35 _MVChatRoomMemberVoicedFeature 
     38_MVChatRoomMemberAdministratorFeature 
     39_MVChatRoomMemberFounderFeature 
    3640_MVChatRoomMemberHalfOperatorFeature 
    3741_MVChatRoomMemberOperatorFeature 
    38 _MVChatRoomMemberAdministratorFeature 
    39 _MVChatRoomMemberFounderFeature 
     42_MVChatRoomMemberQuietedFeature 
    4043_MVChatRoomMemberUsersSyncedNotification 
    41 _MVChatRoomBannedUsersSyncedNotification 
     44_MVChatRoomMemberVoicedFeature 
     45_MVChatRoomModesChangedNotification 
     46_MVChatRoomPartedNotification 
     47_MVChatRoomTopicChangedNotification 
     48_MVChatRoomUserBanRemovedNotification 
     49_MVChatRoomUserBannedNotification 
    4250_MVChatRoomUserJoinedNotification 
     51_MVChatRoomUserKickedNotification 
     52_MVChatRoomUserModeChangedNotification 
    4353_MVChatRoomUserPartedNotification 
    44 _MVChatRoomUserKickedNotification 
    45 _MVChatRoomUserBannedNotification 
    46 _MVChatRoomUserBanRemovedNotification 
    47 _MVChatRoomUserModeChangedNotification 
    48 _MVChatRoomGotMessageNotification 
    49 _MVChatRoomTopicChangedNotification 
    50 _MVChatRoomModesChangedNotification 
    51 _MVChatRoomAttributeUpdatedNotification 
     54_MVChatUserAttributeUpdatedNotification 
     55_MVChatUserAwayStatusMessageChangedNotification 
     56_MVChatUserBanAuthorAttribute 
     57_MVChatUserBanDateAttribute 
     58_MVChatUserBanServerAttribute 
     59_MVChatUserClientInfoAttribute 
     60_MVChatUserDeviceInfoAttribute 
     61_MVChatUserDigitalSignatureAttribute 
     62_MVChatUserExtensionAttribute 
     63_MVChatUserGeoLocationAttribute 
     64_MVChatUserIdleTimeUpdatedNotification 
     65_MVChatUserInformationUpdatedNotification 
    5266_MVChatUserKnownRoomsAttribute 
     67_MVChatUserLocalTimeAttribute 
     68_MVChatUserModeChangedNotification 
     69_MVChatUserMoodAttribute 
     70_MVChatUserNicknameChangedNotification 
    5371_MVChatUserPictureAttribute 
    5472_MVChatUserPingAttribute 
    55 _MVChatUserLocalTimeAttribute 
    56 _MVChatUserClientInfoAttribute 
     73_MVChatUserPreferredContactMethodsAttribute 
     74_MVChatUserPreferredLanguageAttribute 
     75_MVChatUserPublicKeyAttribute 
     76_MVChatUserServerDigitalSignatureAttribute 
     77_MVChatUserServerPublicKeyAttribute 
     78_MVChatUserServiceAttribute 
     79_MVChatUserStatusChangedNotification 
     80_MVChatUserStatusMessageAttribute 
     81_MVChatUserTimezoneAttribute 
    5782_MVChatUserVCardAttribute 
    58 _MVChatUserServiceAttribute 
    59 _MVChatUserMoodAttribute 
    60 _MVChatUserStatusMessageAttribute 
    61 _MVChatUserPreferredLanguageAttribute 
    62 _MVChatUserPreferredContactMethodsAttribute 
    63 _MVChatUserTimezoneAttribute 
    64 _MVChatUserGeoLocationAttribute 
    65 _MVChatUserDeviceInfoAttribute 
    66 _MVChatUserExtensionAttribute 
    67 _MVChatUserPublicKeyAttribute 
    68 _MVChatUserServerPublicKeyAttribute 
    69 _MVChatUserDigitalSignatureAttribute 
    70 _MVChatUserBanServerAttribute 
    71 _MVChatUserBanAuthorAttribute 
    72 _MVChatUserBanDateAttribute 
    73 _MVChatUserServerDigitalSignatureAttribute 
    74 _MVChatUserNicknameChangedNotification 
    75 _MVChatUserStatusChangedNotification 
    76 _MVChatUserAwayStatusMessageChangedNotification 
    77 _MVChatUserIdleTimeUpdatedNotification 
    78 _MVChatUserModeChangedNotification 
    79 _MVChatUserInformationUpdatedNotification 
    80 _MVChatUserAttributeUpdatedNotification 
    8183_MVChatUserWatchRuleMatchedNotification 
    8284_MVChatUserWatchRuleRemovedMatchedUserNotification 
    8385_MVDownloadFileTransferOfferNotification 
     86_MVFileTransferErrorDomain 
     87_MVFileTransferErrorOccurredNotification 
     88_MVFileTransferFinishedNotification 
    8489_MVFileTransferStartedNotification 
    85 _MVFileTransferFinishedNotification 
    86 _MVFileTransferErrorOccurredNotification 
    87 _MVFileTransferErrorDomain 
    88 _MVChatPluginManagerWillReloadPluginsNotification 
    89 _MVChatPluginManagerDidReloadPluginsNotification 
     90_NSChatCTCPTwoFormatType 
    9091_NSChatWindowsIRCFormatType 
    91 _NSChatCTCPTwoFormatType 
     92_isValidUTF8 
  • trunk/Chat Core/MVIRCChatConnection.h

    r3498 r3515  
    6767- (void) _resetSupportedFeatures; 
    6868 
     69- (NSString *) _newStringWithBytes:(const char *) bytes length:(unsigned) length; 
    6970- (NSString *) _stringFromPossibleData:(id) input; 
    7071@end 
  • trunk/Chat Core/MVIRCChatConnection.m

    r3507 r3515  
    2626#define JVMaximumISONCommandLength 450 
    2727#define JVMaximumMembersForWhoRequest 100 
     28#define JVFallbackEncoding NSISOLatin1StringEncoding 
    2829 
    2930static const NSStringEncoding supportedEncodings[] = { 
     
    746747 
    747748- (void) socket:(AsyncSocket *) sock didReadData:(NSData *) data withTag:(long) tag { 
    748         NSString *rawString = [[NSString allocWithZone:nil] initWithData:data encoding:[self encoding]]; 
    749         if( ! rawString ) rawString = [[NSString allocWithZone:nil] initWithData:data encoding:NSISOLatin1StringEncoding]; 
     749        NSString *rawString = [self _newStringWithBytes:[data bytes] length:[data length]]; 
    750750 
    751751        const char *line = (const char *)[data bytes]; 
     
    827827                                currentParameter = line; 
    828828                                while( notEndOfLine() && *line != ' ' ) line++; 
    829                                 param = [[NSString allocWithZone:nil] initWithBytes:currentParameter length:(line - currentParameter) encoding:[self encoding]]; 
    830                                 if( ! param ) param = [[NSString allocWithZone:nil] initWithBytes:currentParameter length:(line - currentParameter) encoding:NSISOLatin1StringEncoding]; 
     829                                param = [self _newStringWithBytes:currentParameter length:(line - currentParameter)]; 
    831830                                checkAndMarkIfDone(); 
    832831                                if( ! done ) line++; 
     
    855854 
    856855                if( [self respondsToSelector:selector] ) { 
    857                         NSString *senderString = nil; 
    858                         if( sender && senderLength ) { 
    859                                 senderString = [[NSString allocWithZone:nil] initWithBytes:sender length:senderLength encoding:[self encoding]]; 
    860                                 if( ! senderString ) senderString = [[NSString allocWithZone:nil] initWithBytes:sender length:senderLength encoding:NSISOLatin1StringEncoding]; 
    861                         } 
     856                        NSString *senderString = [self _newStringWithBytes:sender length:senderLength]; 
    862857 
    863858                        MVChatUser *chatUser = nil; 
     
    867862                                chatUser = [self chatUserWithUniqueIdentifier:senderString]; 
    868863                                if( ! [chatUser address] && host && hostLength ) { 
    869                                         NSString *hostString = [[NSString allocWithZone:nil] initWithBytes:host length:hostLength encoding:[self encoding]]; 
    870                                         if( ! hostString ) hostString = [[NSString allocWithZone:nil] initWithBytes:host length:hostLength encoding:NSISOLatin1StringEncoding]; 
     864                                        NSString *hostString = [self _newStringWithBytes:host length:hostLength]; 
    871865                                        [chatUser _setAddress:hostString]; 
    872866                                        [hostString release]; 
     
    874868 
    875869                                if( ! [chatUser username] ) { 
    876                                         NSString *userString = [[NSString allocWithZone:nil] initWithBytes:user length:userLength encoding:[self encoding]]; 
    877                                         if( ! userString ) userString = [[NSString allocWithZone:nil] initWithBytes:user length:userLength encoding:NSISOLatin1StringEncoding]; 
     870                                        NSString *userString = [self _newStringWithBytes:user length:userLength]; 
    878871                                        [chatUser _setUsername:userString]; 
    879872                                        [userString release]; 
     
    12541247#pragma mark - 
    12551248 
     1249- (NSString *) _newStringWithBytes:(const char *) bytes length:(unsigned) length { 
     1250        if( bytes && length ) { 
     1251                BOOL validUTF8 = isValidUTF8( bytes, length ); 
     1252                NSStringEncoding encoding = ( [self encoding] != NSUTF8StringEncoding ? [self encoding] : JVFallbackEncoding ); 
     1253                NSString *ret = [[NSString allocWithZone:nil] initWithBytes:bytes length:length encoding:( validUTF8 ? NSUTF8StringEncoding : encoding )]; 
     1254                if( ! ret && encoding != JVFallbackEncoding ) ret = [[NSString allocWithZone:nil] initWithBytes:bytes length:length encoding:JVFallbackEncoding]; 
     1255                return ret; 
     1256        } 
     1257 
     1258        if( ! length ) return @""; 
     1259        return nil; 
     1260} 
     1261 
    12561262- (NSString *) _stringFromPossibleData:(id) input { 
    1257         if( [input isKindOfClass:[NSData class]] ) { 
    1258                 NSString *ret = [[[NSString allocWithZone:nil] initWithData:input encoding:[self encoding]] autorelease]; 
    1259                 if( ret ) return ret; 
    1260                 return [[[NSString allocWithZone:nil] initWithData:input encoding:NSISOLatin1StringEncoding] autorelease]; 
    1261         } 
     1263        if( [input isKindOfClass:[NSData class]] ) 
     1264                return [[self _newStringWithBytes:[input bytes] length:[input length]] autorelease]; 
    12621265        return input; 
    12631266} 
     
    15051508                                [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:MVChatConnectionGotPrivateMessageNotification object:sender userInfo:[NSDictionary dictionaryWithObjectsAndKeys:msgData, @"message", [NSString locallyUniqueString], @"identifier", [NSNumber numberWithBool:YES], @"notice", nil]]; 
    15061509                                if( [[sender nickname] isEqualToString:@"NickServ"] ) { 
    1507                                         NSString *msg = [[NSString allocWithZone:nil] initWithData:msgData encoding:[self encoding]]; 
    1508                                         if( ! msg ) msg = [[NSString allocWithZone:nil] initWithData:msgData encoding:NSISOLatin1StringEncoding]; 
     1510                                        NSString *msg = [self _newStringWithBytes:[msgData bytes] length:[msgData length]]; 
     1511 
    15091512                                        if( [msg rangeOfString:@"NickServ"].location != NSNotFound && [msg rangeOfString:@"IDENTIFY"].location != NSNotFound ) { 
    15101513                                                if( ! [self nicknamePassword] ) { 
     
    15161519                                                [[self localUser] _setIdentified:NO]; 
    15171520                                        } 
     1521 
    15181522                                        [msg release]; 
    15191523                                } 
     
    15341538 
    15351539        while( line != end && *line != ' ' ) line++; 
    1536         NSString *command = [[NSString allocWithZone:nil] initWithBytes:current length:(line - current) encoding:[self encoding]]; 
    1537         if( ! command ) command = [[NSString allocWithZone:nil] initWithBytes:current length:(line - current) encoding:NSISOLatin1StringEncoding]; 
    1538  
     1540 
     1541        NSString *command = [self _newStringWithBytes:current length:(line - current)]; 
    15391542        NSMutableData *arguments = nil; 
    15401543        if( line != end ) { 
     
    15961599                        if( [arguments length] < 100 ) [sender sendSubcodeReply:command withArguments:arguments]; 
    15971600                } else if( [command caseInsensitiveCompare:@"DCC"] == NSOrderedSame ) { 
    1598                         NSString *msg = [[NSString allocWithZone:nil] initWithData:arguments encoding:[self encoding]]; 
    1599                         if( ! msg ) msg = [[NSString allocWithZone:nil] initWithData:arguments encoding:NSISOLatin1StringEncoding]; 
    1600  
     1601                        NSString *msg = [self _newStringWithBytes:[arguments bytes] length:[arguments length]]; 
    16011602                        NSString *subCommand = nil; 
    16021603                        NSString *fileName = nil;