Is there a better way to implement this? - objective-c

I have an object that has properties for a person's address. For convenience, I have written a method to generate an NSString with the person's full address. My implementation is:
/**
Returns the full address in US format of the Addressable object.
*/
- (NSString *)fullAddress {
NSMutableString *ret = [NSMutableString string];
if (self.company) {
[ret appendFormat:#"%#\n", self.company];
}
if (self.firstName) {
[ret appendFormat:#"%#", self.firstName];
}
if (self.firstName && self.lastName) {
[ret appendString:#" "];
}
if (self.lastName) {
[ret appendFormat:#"%#", self.firstName];
}
if (self.firstName || self.lastName) {
[ret appendString:#"\n"];
}
if (self.address) {
[ret appendFormat:#"%#\n", self.address];
}
if (self.addressLine2 && ![self.addressLine2 isEqualToString:#""]) {
[ret appendFormat:#"%#\n", self.addressLine2];
}
if (self.addressLine3 && ![self.addressLine3 isEqualToString:#""]) {
[ret appendFormat:#"%#\n", self.addressLine3];
}
if (self.city) {
[ret appendString:self.city];
}
if (self.city && self.state) {
[ret appendString:#", "];
}
if (self.state) {
[ret appendString:self.state];
}
if (self.zip) {
[ret appendFormat:#" %#", self.zip];
}
return ret;
}
This feels clumsy to me. Is there a better way to do this?

I don't think so.
You could loop through the properties, but since you're not appending a consistent string, you're not going to save yourself any trouble by doing so.

Related

NSPredicate - predicateWithFormat insecure

I have a predicate for query in core data base but i don't know what is the correct way to validate its params?
- (void) queryToDatabaseWithStoreId:(NSInteger) storeId {
[NSPredicate predicateWithFormat:#"store.storeId = %d", storeId];
}
My question is how can i validate storeId param or what i need to use for that vulnerability to dissapear?
And if i have a list:
- (void) queryToDataBaseWithListStore:(NSArray<Store *> *) storeList {
[NSPredicate predicateWithFormat:#"store.storeId IN %#", [storeList valueForObject:#"storeId"]];
}
https://developer.apple.com/library/archive/documentation/Security/Conceptual/SecureCodingGuide/Articles/ValidatingInput.html#//apple_ref/doc/uid/TP40007246-SW3
I need avoid that:
The following commonly-used functions and methods are subject to format-string attacks:
Standard C
printf and other functions listed on the printf(3) manual page
sscanf and other functions listed on the scanf(3) manual page
syslog and vsyslog
Carbon
AEBuildDesc and vAEBuildDesc
AEBuildParameters and vAEBuildParameters
AEBuildAppleEvent and vAEBuildAppleEvent
Core Foundation
CFStringCreateWithFormat
CFStringCreateWithFormatAndArguments
CFStringAppendFormat
CFStringAppendFormatAndArguments
Cocoa
stringWithFormat:, initWithFormat:, and other NSString methods that take formatted strings as arguments
appendFormat: in the NSMutableString class
alertWithMessageText:defaultButton:alternateButton:otherButton:informativeTextWithFormat: in NSAlert
predicateWithFormat:, predicateWithFormat:arguments:, and predicateWithFormat:argumentArray: in NSPredicate
raise:format: and raise:format:arguments: in NSException
NSRunAlertPanel and other AppKit functions that create or return panels or sheets
What is the best way to avoid this attack?
I have programmed this class but i don't know if it is enough.
#implementation StringUtils
+ (BOOL) isEmpty:(id) text {
if ([text isKindOfClass:[NSNull class]]) {
return YES;
} else {
if (text) {
if ([text isKindOfClass:[NSString class]]) {
NSString *textStr = [NSString stringWithFormat:#"%#", text];
return [textStr isEqualToString:#""];
}
return YES;
} else {
return YES;
}
}
}
+ (NSString *) validateField:(id) text {
NSInteger numErrors = 0;
NSString *pattern = #"[^A-Za-z0-9-]+";
NSError *error = nil;
NSString *textValidated = #"";
if ([text isKindOfClass:[NSNumber class]]) {
textValidated = [text stringValue];
} else if ([text isKindOfClass:[NSString class]]) {
textValidated = text;
} else {
#try {
textValidated = [text stringValue];
} #catch (NSException *exception) {
numErrors=+1;
}
}
//Only numbers && chars && -
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error];
NSRange textRange = NSMakeRange(0, textValidated.length);
NSRange matchRange = [regex rangeOfFirstMatchInString:textValidated options:NSMatchingReportProgress range:textRange];
if (matchRange.location != NSNotFound) {
numErrors+=1;
}
//Not empty string
if ([StringUtils isEmpty:textValidated]) {
numErrors+=1;
}
if (numErrors == 0) {
return textValidated;
}
return #"";
}
+ (NSArray *) validateArrayFields:(NSArray *) list {
NSInteger *numErrors = 0;
for (id obj in list) {
if ([StringUtils isEmpty:[StringUtils validateField:obj]]) {
numErrors+=1;
}
}
if (numErrors == 0) {
return list;
}
return [[NSArray alloc] init];
}
#end
For use normal:
[NSPredicate predicateWithFormat:#"store.storeId = %#", [StringUtils validateField:storeId]];
For use with array:
[NSPredicate predicateWithFormat:#"store.storeId IN %#", [StringUtils validateArrayFields:storeId]];

Does anyone know two NSString_s with the same hashes?

I want to test some cases in my app with strings which have the same hash, and I can't find it =(
I've found two strings with the same MD5. here But their hash are different. And googling didn't help me =(
NSString(MD5) category
Little story about NSDictionary
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSString *string1 = [self fileContentWithName:#"message1" encoding:NSUnicodeStringEncoding];
NSString *string2 = [self fileContentWithName:#"message2" encoding:NSUnicodeStringEncoding];
if (string1 != nil) {
if (string1.hash == string2.hash) {
NSLog(#"Hashes are the same");
} else {
if ([[string1 MD5Hash] isEqualToString:[string2 MD5Hash]]) {
NSLog(#"MD5 hases are equalfor:");
NSLog(#"lenght = %3ld - %#", string1.length, string1);
NSLog(#"lenght = %3ld - %#", string2.length, string2);
if ([string1 isEqualToString:string2]) {
NSLog(#"Strings are equal too");
} else {
NSLog(#"But strings are not equal");
}
}
}
}
}
#pragma mark -
- (NSString*)fileContentWithName:(NSString*)name encoding:(NSStringEncoding)enc
{
NSString *txtFilePath1 = [[NSBundle mainBundle] pathForResource:name ofType:#"bin"];
NSError *error = nil;
NSString *txtFileContents1 = [NSString stringWithContentsOfFile:txtFilePath1 encoding:enc error:&error];
return txtFileContents1;
}

insertObject: atIndex: - index 3 beyond bounds for empty array

I create an array based on a dictionaries key's:
factsBuiltArray = [NSMutableArray arrayWithCapacity: 6];
if ([statusDict count] == 10) {
for (NSString *key in [statusDict allKeys]) {
if ([key isEqualToString: #"currenciesAndConversions"]) {
[factsBuiltArray insertObject:key atIndex: 0];
}
else if ([key isEqualToString: #"languageAndTranslations"]) {
[factsBuiltArray insertObject:key atIndex: 1];
}
else if ([key isEqualToString: #"plugSize"]) {
[factsBuiltArray insertObject:key atIndex: 2];
}
else if ([key isEqualToString: #"timezone"]) {
[factsBuiltArray insertObject:key atIndex: 3]; // crashes over here
}
else if ([key isEqualToString: #"population"]) {
[factsBuiltArray insertObject:key atIndex: 4];
}
else if ([key isEqualToString: #"wikipedia"]) {
[factsBuiltArray insertObject:key atIndex: 5];
}
}
}
The crash log is:
*** -[__NSArrayM insertObject:atIndex:]: index 3 beyond bounds for empty array
Why does inserting an object to an array that is specified with a capacity of 6 make it crash? Very confusing!
The capacity is merely how many objects a container class can hold. Inserting at an index requires that index to be a valid placement for the new object based on the total number of objects contained in the container (not the total number of objects that CAN be contained).
If your array's values are index dependent (which is seems like perhaps a different architecture or data structure would be better) then you can ensure that every index is filled by prepopulating the array with NSNulls. This would require you to check for NSNulls when reading from the array later on though which would likely be extra work, hence why this is probably not the best approach. In any case, you can change your code to the following to fix your crash.
factsBuiltArray = [NSMutableArray arrayWithCapacity: 6];
for (NSUInter i = 0; i < 6; i++) {
[factsBuiltArray addObject:[NSNull null]];
}
if ([statusDict count] == 10) {
for (NSString *key in [statusDict allKeys]) {
if ([key isEqualToString: #"currenciesAndConversions"]) {
[factsBuiltArray replaceObjectAtIndex:0 withObject:key];
}
else if ([key isEqualToString: #"languageAndTranslations"]) {
[factsBuiltArray replaceObjectAtIndex:1 withObject:key];
}
else if ([key isEqualToString: #"plugSize"]) {
[factsBuiltArray replaceObjectAtIndex:2 withObject:key];
}
else if ([key isEqualToString: #"timezone"]) {
[factsBuiltArray replaceObjectAtIndex:3 withObject:key];
}
else if ([key isEqualToString: #"population"]) {
[factsBuiltArray replaceObjectAtIndex:4 withObject:key];
}
else if ([key isEqualToString: #"wikipedia"]) {
[factsBuiltArray replaceObjectAtIndex:5 withObject:key];
}
}
}

Encoding NSAttributedString Throws Error

Based on the accepted answer to this question I wrote the following code:
NSData* somedata;
somedata=[NSKeyedArchiver archivedDataWithRootObject:ts];
where ts is an NSAttributedString that is populated with some text and some attributes (colors, in this case).
When I execute this code, I receive this error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFType encodeWithCoder:]: unrecognized selector sent to instance 0x6eb5b90'
I'm new to the NSCoder arena, but the answer to the aforementioned question made it seem like this is all I have to do. Is it? Did I miss something?
EDIT:
The unrecognized selector in this case is being sent to a color attribute in the NSAttributedString. When I initialize the string like so:
NSAttributedString *ts = [[NSAttributedString alloc] initWithString:text attributes:self.currentAttributeDictionary];
The dictionary is built like so:
self.currentAttributeDictionary=[NSDictionary dictionaryWithObjectsAndKeys:
[self.currentColor CGColor],(NSString*)kCTForegroundColorAttributeName,
nil];
And an NSLog of the dictionary yields this:
New dictionary is: {
CTForegroundColor = "<CGColor 0x6eb5b90> [<CGColorSpace 0x6e968c0> (kCGColorSpaceDeviceRGB)] ( 1 1 0 1 )";}
The address of the CGColor, above, matches the address in the error message.
While UIColor conforms to NSCoding, it is (unlike most such classes) not toll-free bridged to CGColorRef. Your dictionary is attempting to encode its contents, and CGColorRef doesn't know how to encode itself.
Presuming that you don't want to encode a UIColor instead (since these sound like Core Text attributes), you are going to have to handle serialization of the CGColorRef yourself. See, for example, this question for some useful thoughts.
It should be noted, btw, since I don't know where the archived data is going, that if you want to unarchive the data on OS X that colors again become a headache at the AppKit/UIKit level: NSColor and UIColor are not directly compatible, so you would still need to go via CGColorRef, stashing the color space information as appropriate.
As requested, here's the code I used to accomplish what i needed to accomplish. It's been a year since I looked at this code, and it was written more to understand what was going on than for great coding practices or for any sort of efficiency. However, it did work, and it worked great!
I defined a category of NSAttributedString code is below.
Example use:
-(void)code:(id)sender {
self.testData=[textView.attributedString customEncode];
NSLog(#"%#",self.testData);
}
-(void)recover:(id)sender {
NSAttributedString* tString=[NSMutableAttributedString customDecode:self.testData];
NSLog(#"Recover pressed: %#",tString);
textView.attributedString=tString;
}
And here's the underlying code:
#import "NSAttributedString+Extras.h"
#import <CoreText/CoreText.h>
#implementation NSAttributedString (Extras)
-(NSData*)customEncode {
__block NSMutableArray* archivableAttributes=[[NSMutableArray alloc]init];
[self enumerateAttributesInRange:NSMakeRange(0, [self length]) options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
NSLog(#"range: %d %d",range.location, range.length);
NSLog(#"dict: %#",attrs);
NSLog(#"keys: %#", [attrs allKeys]);
NSLog(#"values: %#", [attrs allValues]);
NSMutableDictionary* tDict=[[NSMutableDictionary alloc]init];
[tDict setObject:[NSNumber numberWithInt:range.location] forKey:#"location"];
[tDict setObject:[NSNumber numberWithInt:range.length] forKey:#"length"];
for (NSString* tKey in [attrs allKeys]) {
if ([tKey isEqualToString:#"CTUnderlineColor"]) {
[tDict setObject:[NSAttributedString arrayFromCGColorComponents:((CGColorRef)[attrs objectForKey:#"CTUnderlineColor"])] forKey:#"CTUnderlineColor"];
}
if ([tKey isEqualToString:#"NSUnderline"]) {
NSNumber* underline=[attrs objectForKey:#"NSUnderline"];
[tDict setObject:underline forKey:#"NSUnderline"];
}
if ([tKey isEqualToString:#"CTForegroundColor"]) {
[tDict setObject:[NSAttributedString arrayFromCGColorComponents:((CGColorRef)[attrs objectForKey:#"CTForegroundColor"])] forKey:#"CTForegroundColor"];
}
if ([tKey isEqualToString:#"NSFont"]) {
CTFontRef font=((CTFontRef)[attrs objectForKey:#"NSFont"]);
NSDictionary* fontDict=[NSDictionary
dictionaryWithObjects:
[NSArray arrayWithObjects:(NSString*)CTFontCopyPostScriptName(font),[NSNumber numberWithFloat:CTFontGetSize(font)], nil]
forKeys:
[NSArray arrayWithObjects:#"fontName", #"fontSize", nil]];
[tDict setObject:fontDict forKey:#"NSFont"];
}
}
[archivableAttributes addObject:tDict];
}];
NSMutableDictionary* archiveNSMString=[NSMutableDictionary
dictionaryWithObjects: [NSArray arrayWithObjects:[self string],archivableAttributes,nil]
forKeys:[NSArray arrayWithObjects:#"string",#"attributes",nil]];
NSLog(#"archivableAttributes array: %#",archiveNSMString);
NSData* tData=[NSKeyedArchiver archivedDataWithRootObject:archiveNSMString];
NSLog(#"tdata: %#",tData);
return tData;
}
+(NSAttributedString*)customDecode:(NSData *)data {
NSMutableAttributedString* tString;
NSMutableDictionary* tDict=[NSKeyedUnarchiver unarchiveObjectWithData:data];
NSArray* attrs;
CTFontRef font=NULL;
CGColorRef color=NULL;
NSNumber* underlineProp=[NSNumber numberWithInt:0];
CGColorRef underlineColor=NULL;
NSLog(#"decoded dictionary: %#",tDict);
if ([[tDict allKeys]containsObject:#"string"]) {
tString=[[NSMutableAttributedString alloc]initWithString:((NSString*)[tDict objectForKey:#"string"])];
}
else {
tString=[[NSMutableAttributedString alloc]initWithString:#""];
}
if ([[tDict allKeys]containsObject:#"attributes"]) {
attrs=[tDict objectForKey:#"attributes"];
}
else {
attrs=nil;
}
for (NSDictionary* attDict in attrs) {
int location=-1;
int length=-1;
NSRange insertRange=NSMakeRange(-1, 0);
if ([[attDict allKeys]containsObject:#"location"]) {
location=[[attDict objectForKey:#"location"]intValue];
}
if ([[attDict allKeys]containsObject:#"length"]) {
length=[[attDict objectForKey:#"length"]intValue];
}
if (location!=-1&&length!=-1) {
insertRange=NSMakeRange(location, length);
}
if ([[attDict allKeys]containsObject:#"NSUnderline"]) {
underlineProp=[attDict objectForKey:#"NSUnderline"];
}
if ([[attDict allKeys]containsObject:#"CTUnderlineColor"]) {
underlineColor=[NSAttributedString cgColorRefFromArray:[attDict objectForKey:#"CTUnderlineColor"]];
}
if ([[attDict allKeys]containsObject:#"CTForegroundColor"]) {
color=[NSAttributedString cgColorRefFromArray:[attDict objectForKey:#"CTForegroundColor"]];
}
if ([[attDict allKeys]containsObject:#"NSFont"]) {
NSString* name=nil;
float size=-1;
NSDictionary* fontDict=[attDict objectForKey:#"NSFont"];
if ([[fontDict allKeys]containsObject:#"fontName"]) {
name=[fontDict objectForKey:#"fontName"];
}
if ([[fontDict allKeys]containsObject:#"fontSize"]) {
size=[[fontDict objectForKey:#"fontSize"]floatValue];
}
if (name!=nil&&size!=-1) {
font=CTFontCreateWithName((CFStringRef)name, size, NULL);
}
}
if (insertRange.location!=-1) {
if (color!=NULL) {
[tString addAttribute:(NSString*)kCTForegroundColorAttributeName value:(id)color range:insertRange];
}
if (font!=NULL) {
[tString addAttribute:(NSString*)kCTFontAttributeName value:(id)font range:insertRange];
}
if ([underlineProp intValue]!=0&&underlineColor!=NULL) {
[tString addAttribute:(NSString*)kCTUnderlineColorAttributeName value:(id)underlineColor range:insertRange];
[tString addAttribute:(NSString*)kCTUnderlineStyleAttributeName value:(id)underlineProp range:insertRange];
}
}
}
[tString enumerateAttributesInRange:NSMakeRange(0, [tString length]) options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
NSLog(#"range: %d %d",range.location, range.length);
NSLog(#"dict: %#",attrs);
NSLog(#"keys: %#", [attrs allKeys]);
NSLog(#"values: %#", [attrs allValues]);
}];
return [[NSAttributedString alloc]initWithAttributedString:tString];
}
+(NSArray*)arrayFromCGColorComponents:(CGColorRef)color {
int numComponents=CGColorGetNumberOfComponents(color);
CGFloat* components=CGColorGetComponents(color);
NSMutableArray* retval=[[NSMutableArray alloc]init];
for(int i=0;i<numComponents;i++) {
[retval addObject:[NSNumber numberWithFloat:components[i]]];
}
return [NSArray arrayWithArray:retval];
}
+(CGColorRef)cgColorRefFromArray:(NSArray*)theArray {
CGFloat* array=malloc(sizeof(CGFloat)*[theArray count]);
for (int i=0; i<[theArray count]; i++) {
array[i]=[[theArray objectAtIndex:i]floatValue];
}
CGColorSpaceRef theSpace;
if ([theArray count]==2) {
theSpace=CGColorSpaceCreateDeviceGray();
}
else {
theSpace=CGColorSpaceCreateDeviceRGB();
}
return CGColorCreate(theSpace, array);
}
#end

Send email to multiple recipients with SKPSMTPMessage?

I need to send email in background, so I have to use the library named: SMTP. And the main class I used is: SKPSMTPMessage. The problem is "ccEmail", when I add more than 2 recipients, it can't send email. (that takes too long time to go to delegate methods). It works well with recipient <= 2.
smtpEmail.ccEmail = #"xyz#gmail.com, xyz1#gmail.com, xyz2#gmail.com";
Anyone knows this, please help me. Thanks you so much !
There is my changes in the parseBuffer function:
case kSKPSMTPWaitingFromReply:
{
if ([tmpLine hasPrefix:#"250 "]) {
if (!multipleRcptTo) {
NSMutableString *multipleRcptToString = [NSMutableString string];
[multipleRcptToString appendString:[self formatAddresses:toEmail]];
[multipleRcptToString appendString:[self formatAddresses:ccEmail]];
[multipleRcptToString appendString:[self formatAddresses:bccEmail]];
multipleRcptTo = [[multipleRcptToString componentsSeparatedByString:#"\r\n"] mutableCopy];
[multipleRcptTo removeLastObject];
}
if ([multipleRcptTo count] > 0) {
NSString *rcptTo = [NSString stringWithFormat:#"%#\r\n", [multipleRcptTo objectAtIndex:0]];
[multipleRcptTo removeObjectAtIndex:0];
//DEBUGLOG(#"C: %#", rcptTo);
if (CFWriteStreamWriteFully((CFWriteStreamRef)outputStream, (const uint8_t *)[rcptTo UTF8String], [rcptTo lengthOfBytesUsingEncoding:NSUTF8StringEncoding]) < 0)
{
error = [outputStream streamError];
encounteredError = YES;
}
else
{
[self startShortWatchdog];
}
}
if ([multipleRcptTo count] == 0) {
sendState = kSKPSMTPWaitingToReply;
}
}
break;
}
and add this into header:
NSMutableArray *multipleRcptTo;
EDIT : Also change below method as multipleRcptTo is used as NSMutableString which is local declaration :
- (NSString *)formatAddresses:(NSString *)addresses {
NSCharacterSet *splitSet = [NSCharacterSet characterSetWithCharactersInString:#";,"];
NSMutableString *multipleRcpt = [NSMutableString string];
if ((addresses != nil) && (![addresses isEqualToString:#""])) {
if( [addresses rangeOfString:#";"].location != NSNotFound || [addresses rangeOfString:#","].location != NSNotFound ) {
NSArray *addressParts = [addresses componentsSeparatedByCharactersInSet:splitSet];
for( NSString *address in addressParts ) {
[multipleRcpt appendString:[self formatAnAddress:address]];
}
}
else {
[multipleRcpt appendString:[self formatAnAddress:addresses]];
}
}
return(multipleRcpt);
}
SKPSMTPMessage sends to the SMTP address all at once, and must send one by one.