From 8d85ca13df1e9d0e127e44c86b75f7248c159d16 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 14 Oct 2015 09:21:32 -0400 Subject: [PATCH 01/69] (fix) numerous EAS fixes when connections are dropped before the EAS client receives the response (#3058, #2849) --- ActiveSync/SOGoActiveSyncDispatcher+Sync.m | 387 +++++++++++++++++---- ActiveSync/SOGoActiveSyncDispatcher.h | 6 +- ActiveSync/SOGoActiveSyncDispatcher.m | 18 +- ActiveSync/SOGoSyncCacheObject.h | 1 + ActiveSync/SOGoSyncCacheObject.m | 35 +- NEWS | 1 + SoObjects/Mailer/SOGoMailFolder.m | 2 +- SoObjects/SOGo/SOGoCacheGCSObject.h | 2 + SoObjects/SOGo/SOGoCacheGCSObject.m | 11 + 9 files changed, 369 insertions(+), 94 deletions(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m index c43afa6b8..2683633d1 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m +++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m @@ -112,6 +112,30 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. @implementation SOGoActiveSyncDispatcher (Sync) +- (void) _setOrUnsetSyncInProgress: (BOOL) set + invalidate: (BOOL) invalidate +{ + SOGoCacheGCSObject *o; + + o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil useCache: NO]; + [o setObjectType: ActiveSyncGlobalCacheObject]; + [o setTableUrl: [self folderTableURL]]; + [o reloadIfNeeded]; + + if (set) + if (invalidate) + [[o properties] setObject: [NSCalendarDate date] forKey: @"InvalidateSyncInProgress"]; + else + [[o properties] setObject: [NSCalendarDate date] forKey: @"SyncInProgress"]; + else + { + [[o properties] removeObjectForKey: @"SyncInProgress"]; + [[o properties] removeObjectForKey: @"InvalidateSyncInProgress"]; + } + + [o save]; +} + - (void) _setFolderMetadata: (NSDictionary *) theFolderMetadata forKey: (NSString *) theFolderKey { @@ -405,7 +429,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [sogoObject saveComponent: o]; [syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId]; - } break; case ActiveSyncEventFolder: @@ -416,14 +439,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [sogoObject saveComponent: o]; [syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId]; - } break; case ActiveSyncMailFolder: default: { NSDictionary *result; - NSString *modseq; + NSNumber *modseq; [sogoObject takeActiveSyncValues: allChanges inContext: context]; @@ -431,11 +453,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. modseq = [[[result objectForKey: @"RawResponse"] objectForKey: @"fetch"] objectForKey: @"modseq"]; if (modseq) - [syncCache setObject: modseq forKey: serverId]; + [syncCache setObject: [modseq stringValue] forKey: serverId]; } } - [self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]]; + [self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]]; [theBuffer appendString: @""]; @@ -578,36 +600,43 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. withFilterType: (NSCalendarDate *) theFilterType inBuffer: (NSMutableString *) theBuffer lastServerKey: (NSString **) theLastServerKey - { NSMutableDictionary *folderMetadata, *dateCache, *syncCache; NSString *davCollectionTagToStore; NSAutoreleasePool *pool; NSMutableString *s; - BOOL more_available; + BOOL cleanup_needed, more_available; int i, max; s = [NSMutableString string]; - - more_available = NO; + cleanup_needed = more_available = NO; folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]]; - // If this is a new sync operation, DateCache and SyncCache needs to be deleted + // If this is a new sync operation, DateCache and SyncCache need to be deleted if ([theSyncKey isEqualToString: @"-1"]) { [folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"SyncCache"]; [folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"DateCache"]; } + else if ([folderMetadata objectForKey: @"SyncKey"] && !([theSyncKey isEqualToString: [folderMetadata objectForKey: @"SyncKey"]])) + { + // The syncKey received from the client doesn't match the syncKey we have in cache - client might have missed a response. + // We need to cleanup this mess. + [self logWithFormat: @"Cache cleanup needed for device %@ - user: %@", [context objectForKey: @"DeviceId"], [[context activeUser] login]]; + cleanup_needed = YES; + } syncCache = [folderMetadata objectForKey: @"SyncCache"]; dateCache = [folderMetadata objectForKey: @"DateCache"]; if ((theFolderType == ActiveSyncMailFolder || theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder) && - !([folderMetadata objectForKey: @"MoreAvailable"]) && // previous sync operation reached the windowSize or maximumSyncReponseSize - !([theSyncKey isEqualToString: @"-1"]) && // new sync operation - theFilterType) + (cleanup_needed || + ( !([folderMetadata objectForKey: @"MoreAvailable"]) && // previous sync operation reached the windowSize or maximumSyncReponseSize + !([folderMetadata objectForKey: @"InitialLoadSequence"]))) && + theFilterType + ) { NSArray *allKeys; NSString *key; @@ -623,14 +652,36 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if ([[dateCache objectForKey:key] compare: theFilterType] == NSOrderedAscending) { - [s appendString: @""]; - [s appendFormat: @"%@", key]; - [s appendString: @""]; + if ([syncCache objectForKey:key]) + { + if (debugOn) + [self logWithFormat: @"EAS - SoftDelete %@", key]; - [syncCache removeObjectForKey: key]; - [dateCache removeObjectForKey: key]; + [s appendString: @""]; + [s appendFormat: @"%@", key]; + [s appendString: @""]; + + [syncCache removeObjectForKey: key]; + //[dateCache removeObjectForKey: key]; - softdelete_count++; + softdelete_count++; + } + else if (cleanup_needed) + { + if (debugOn) + [self logWithFormat: @"EAS - SoftDelete cleanup %@", key]; + + // With this we make sure that a SoftDelete is set again on next sync. + [syncCache setObject: @"0" forKey: key]; + } + else + { + if (debugOn) + [self logWithFormat: @"EAS - SoftDelete final delete %@", key]; + + // Now we are save to remove the dateCache entry. + [dateCache removeObjectForKey: key]; + } } if (softdelete_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize)) @@ -657,8 +708,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // if ([theSyncKey isEqualToString: [theCollection davCollectionTag]] && !([s length])) return; - - more_available = NO; davCollectionTagToStore = [theCollection davCollectionTag]; @@ -687,7 +736,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. initialLoadInProgress = NO; if ([theSyncKey isEqualToString: @"-1"]) - [folderMetadata setObject: davCollectionTagToStore forKey: @"InitialLoadSequence"]; + [folderMetadata setObject: davCollectionTagToStore forKey: @"InitialLoadSequence"]; if ([folderMetadata objectForKey: @"InitialLoadSequence"]) { @@ -865,11 +914,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOGoMailObject *mailObject; NSArray *allMessages, *a; + NSString *lastSequence, *firstUidAdded; int j, k, return_count, highestmodseq; BOOL found_in_cache, initialLoadInProgress; - initialLoadInProgress = NO; + initialLoadInProgress = NO; + found_in_cache = NO; + lastSequence = nil; + firstUidAdded = nil; if ([theSyncKey isEqualToString: @"-1"]) { @@ -900,7 +953,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. for (i = 0; i < max; i++) { [allCacheObjects addObject: [SOGoSyncCacheObject syncCacheObjectWithUID: [[[allMessages objectAtIndex: i] allKeys] lastObject] - sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]]; + sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]]; } sortedBySequence = [[NSMutableArray alloc] initWithDictionary: syncCache]; @@ -909,15 +962,82 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [allCacheObjects sortUsingSelector: @selector(compareSequence:)]; - //NSLog(@"sortedBySequence (%d) - lastObject: %@", [sortedBySequence count], [sortedBySequence lastObject]); - //NSLog(@"allCacheObjects (%d) - lastObject: %@", [allCacheObjects count], [allCacheObjects lastObject]); + if (debugOn) + { + [self logWithFormat: @"EAS - sortedBySequence (%d) - lastObject: %@", [sortedBySequence count], [sortedBySequence lastObject]]; + [self logWithFormat: @"EAS - allCacheObjects (%d) - lastObject: %@", [allCacheObjects count], [allCacheObjects lastObject]]; + } lastCacheObject = [sortedBySequence lastObject]; + + // + // Cleanup the mess + // + if (cleanup_needed) + { + NSMutableArray *sortedByUID; + int uidnextFromCache; + + sortedByUID = [[NSMutableArray alloc] initWithDictionary: syncCache]; + [sortedByUID sortUsingSelector: @selector(compareUID:)]; + + // Get the uid from SyncKey in cache. The uid is the first uid added to cache by the last sync request. + a = [[folderMetadata objectForKey: @"SyncKey"] componentsSeparatedByString: @"-"]; + uidnextFromCache = [[a objectAtIndex: 0] intValue]; + + if (debugOn) + [self logWithFormat: @"EAS - Cache cleanup: from uid: %d to uid: %d", uidnextFromCache, [[[sortedByUID lastObject] uid] intValue]]; + + // Remove all entries from cache beginning with the first uid added by the last sync request. + for (j = uidnextFromCache; j <= [[[sortedByUID lastObject] uid] intValue]; j++) + { + if (debugOn) + [self logWithFormat: @"EAS - Cache cleanup: ADD %d", j]; + + [syncCache removeObjectForKey: [NSString stringWithFormat:@"%d", j]]; + [dateCache removeObjectForKey: [NSString stringWithFormat:@"%d", j]]; + } + + RELEASE(sortedByUID); + + for (j = 0; j < [allCacheObjects count]; j++) + { + // Update the modseq in cache, sence othersie, it would be identical to the modseq from server + //and we would skip the cache when generating the response. + if ([syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]] && ![[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]]) + { + if (debugOn) + [self logWithFormat: @"EAS - Cache cleanup: CHANGE %@", [[allCacheObjects objectAtIndex: j] uid]]; + + [syncCache setObject: @"0" forKey:[[allCacheObjects objectAtIndex: j] uid]]; + } + else if ([[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]]) + { + if (debugOn) + [self logWithFormat: @"EAS - Cache cleanup: DELETE %@", [[allCacheObjects objectAtIndex: j] uid]]; + + // For deletes we have to recreate a cache entry to have the included in the response. + [syncCache setObject: @"0" forKey:[[allCacheObjects objectAtIndex: j] uid]]; + } + } + } - if ([folderMetadata objectForKey: @"MoreAvailable"] && lastCacheObject) + if (!cleanup_needed && + [folderMetadata objectForKey: @"MoreAvailable"] && + lastCacheObject && + !([[lastCacheObject sequence] isEqual: @"0"])) // Sequence 0 is set during cache cleanup. { for (j = 0; j < [allCacheObjects count]; j++) { + if (([[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]] && [syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]]) || + ([[allCacheObjects objectAtIndex: j] sequence] && ![syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]])) + { + // We need to continue with adds or deletes from here. + found_in_cache = YES; + j--; + break; + } + if ([[lastCacheObject uid] isEqual: [[allCacheObjects objectAtIndex: j] uid]]) { // Found out where we're at, let's continue from there... @@ -932,12 +1052,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if (found_in_cache) k = j+1; else - { - k = 0; - j = 0; - } - - //NSLog(@"found in cache: %d k = %d", found_in_cache, k); + j = k = 0; + + if (debugOn) + [self logWithFormat: @"EAS - found in cache: %d k = %d", found_in_cache, k]; return_count = 0; @@ -948,23 +1066,38 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Check for the WindowSize and slice accordingly if (return_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize)) { - NSString *lastSequence; more_available = YES; + if (!firstUidAdded) + { + a = [davCollectionTagToStore componentsSeparatedByString: @"-"]; + firstUidAdded = [a objectAtIndex: 0]; + RETAIN(firstUidAdded); + } lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? [NSString stringWithFormat:@"%d", highestmodseq] : [aCacheObject sequence]); - *theLastServerKey = [[NSString alloc] initWithFormat: @"%@-%@", [aCacheObject uid], lastSequence]; - //NSLog(@"Reached windowSize - lastUID will be: %@", *theLastServerKey); + //RETAIN(lastSequence); + *theLastServerKey = [[NSString alloc] initWithFormat: @"%@-%@", firstUidAdded, lastSequence]; + + if (debugOn) + [self logWithFormat: @"EAS - Reached windowSize - lastUID will be: %@", *theLastServerKey]; + DESTROY(pool); break; } aCacheObject = [allCacheObjects objectAtIndex: k]; - // If found in cache, it's either a Change or a Delete + if (debugOn) + [self logWithFormat: @"EAS - Dealing with cacheObject: %@", aCacheObject]; + + // If found in cache, it's either a Change or a Delete operation. if ([syncCache objectForKey: [aCacheObject uid]]) { if ([[aCacheObject sequence] isEqual: [NSNull null]]) { + if (debugOn) + [self logWithFormat: @"EAS - DELETE!"]; + // Deleted [s appendString: @""]; [s appendFormat: @"%@", [aCacheObject uid]]; @@ -982,9 +1115,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mailObject = [theCollection lookupName: [aCacheObject uid] inContext: context acquire: 0]; - + if (![[aCacheObject sequence] isEqual: [syncCache objectForKey: [aCacheObject uid]]]) { + if (debugOn) + [self logWithFormat: @"EAS - CHANGE!"]; + [s appendString: @""]; [s appendFormat: @"%@", [aCacheObject uid]]; [s appendString: @""]; @@ -1000,6 +1136,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } else { + if (debugOn) + [self logWithFormat: @"EAS - ADD!"]; + // Added if (![[aCacheObject sequence] isEqual: [NSNull null]]) { @@ -1032,11 +1171,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [syncCache setObject: [aCacheObject sequence] forKey: [aCacheObject uid]]; [dateCache setObject: [NSCalendarDate date] forKey: [aCacheObject uid]]; + + // Save the frist UID we add. We will use it for the synckey late. + if (!firstUidAdded) + { + firstUidAdded = [aCacheObject uid]; + RETAIN(firstUidAdded); + if (debugOn) + [self logWithFormat: @"EAS - first uid added %@", firstUidAdded]; + } + return_count++; } else { - //NSLog(@"skipping old deleted UID: %@", [aCacheObject uid]); + if (debugOn) + [self logWithFormat: @"EAS - skipping old deleted UID: %@", [aCacheObject uid]]; } } @@ -1045,16 +1195,27 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if (more_available) { - [folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"]; + //[folderMetadata setObject: lastSequence forKey: @"MoreAvailable"]; + [folderMetadata setObject: [NSNumber numberWithInt: YES] forKey: @"MoreAvailable"]; [folderMetadata setObject: *theLastServerKey forKey: @"SyncKey"]; + //RELEASE(lastSequence); } else { [folderMetadata removeObjectForKey: @"MoreAvailable"]; - [folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"]; + + if (firstUidAdded) + { + a = [davCollectionTagToStore componentsSeparatedByString: @"-"]; + [folderMetadata setObject: [[NSString alloc] initWithFormat: @"%@-%@", firstUidAdded, [a objectAtIndex: 1]] forKey: @"SyncKey"]; + } + else + [folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"]; } [self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]]; + + RELEASE(firstUidAdded); RELEASE(*theLastServerKey); } // default: @@ -1153,15 +1314,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. changeDetected: (BOOL *) changeDetected maxSyncResponseSize: (int) theMaxSyncResponseSize { - NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *mimeSupport, *lastServerKey, *syncKeyInCache; - SOGoMicrosoftActiveSyncFolderType folderType; - id collection, value; - - NSMutableString *changeBuffer, *commandsBuffer; - BOOL getChanges, first_sync; - unsigned int windowSize, v, status; + NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *mimeSupport, *lastServerKey, *syncKeyInCache, *folderKey; NSMutableDictionary *folderMetadata, *folderOptions; - + NSMutableString *changeBuffer, *commandsBuffer; + id collection, value; + + SOGoMicrosoftActiveSyncFolderType folderType; + unsigned int windowSize, v, status; + BOOL getChanges, first_sync; + changeBuffer = [NSMutableString string]; commandsBuffer = [NSMutableString string]; @@ -1184,7 +1345,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //[theBuffer appendString: @""]; return; } - + + // + // First check if we have any concurrent Sync requests going on for this device. + // If we do and we are still within our maximumSyncInterval, we let our EAS + // device know to retry. + // + folderKey = [self _getNameInCache: collection withType: folderType]; + folderMetadata = [self _folderMetadataForKey: folderKey]; + // We check for a window size, default to 100 if not specfied or out of bounds windowSize = [[[(id)[theDocumentElement getElementsByTagName: @"WindowSize"] lastObject] textValue] intValue]; @@ -1209,8 +1378,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. first_sync = NO; - folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]]; - if ([syncKey isEqualToString: @"0"]) { davCollectionTag = @"-1"; @@ -1265,7 +1432,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. { folderOptions = [[NSDictionary alloc] initWithObjectsAndKeys: mimeSupport, @"MIMESupport", bodyPreferenceType, @"BodyPreferenceType", nil]; [folderMetadata setObject: folderOptions forKey: @"FolderOptions"]; - [self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: collection withType: folderType]]; + [self _setFolderMetadata: folderMetadata forKey: folderKey]; } } @@ -1311,7 +1478,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. lastServerKey: &lastServerKey]; } - folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]]; + folderMetadata = [self _folderMetadataForKey: folderKey]; // If we got any changes or if we have applied any commands // let's regenerate our SyncKey based on the collection tag. @@ -1473,16 +1640,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. NSArray *allCollections; NSData *d; - int i, j, defaultInterval, heartbeatInterval, internalInterval, maxSyncResponseSize; + int i, j, defaultInterval, heartbeatInterval, internalInterval, maxSyncResponseSize, total_sleep; BOOL changeDetected; - changeDetected = NO; - - maxSyncResponseSize = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncResponseSize]; - // We initialize our output buffer output = [[NSMutableString alloc] init]; + defaults = [SOGoSystemDefaults sharedSystemDefaults]; + defaultInterval = [defaults maximumSyncInterval]; + [output appendString: @""]; [output appendString: @""]; [output appendString: @""]; @@ -1497,12 +1663,53 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [output appendString: @""]; d = [[output dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; [theResponse setContent: d]; + RELEASE(output); return; } + + if ([[self globalMetadataForDevice] objectForKey: @"SyncInProgress"]) + { + NSCalendarDate *syncStartDate; + + // An other sync is going on right now. Lets break it and wait + // until the other process clears it up. + [self _setOrUnsetSyncInProgress: YES invalidate: YES]; + total_sleep = 0; + + syncStartDate = [[self globalMetadataForDevice] objectForKey: @"SyncInProgress"]; + + while ([[self globalMetadataForDevice] objectForKey: @"SyncInProgress"]) + { + // Don't go into a heartbeat loop. + heartbeatInterval = 0; + + // We waited too long. Return a fatal error to the client. + if (abs([syncStartDate timeIntervalSinceNow]) > defaultInterval) + { + if (debugOn) + [self logWithFormat: @"EAS - We waited too long. syncStartDate: %@ %@", syncStartDate, [self globalMetadataForDevice]]; + + [theResponse setStatus: 503]; + [self _setOrUnsetSyncInProgress: NO invalidate: NO]; + RELEASE(output); + return; + } + + if (debugOn) + [self logWithFormat: @"EAS - globalMetadataForDevice %@", [self globalMetadataForDevice]]; + + [self logWithFormat: @"Sync in progress for device %@ (login: %@). Lock will expire in %d seconds", [context objectForKey: @"DeviceId"], [[context activeUser] login], defaultInterval-total_sleep]; + sleep(5); + total_sleep += 5; + } + } + + // No lock or broke the existing one, lets grab it and proceed + [self _setOrUnsetSyncInProgress: YES invalidate: NO]; - defaults = [SOGoSystemDefaults sharedSystemDefaults]; + changeDetected = NO; + maxSyncResponseSize = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncResponseSize]; heartbeatInterval = [[[(id)[theDocumentElement getElementsByTagName: @"HeartbeatInterval"] lastObject] textValue] intValue]; - defaultInterval = [defaults maximumSyncInterval]; internalInterval = [defaults internalSyncInterval]; // If the request doesn't contain "HeartbeatInterval" there is no reason to delay the response. @@ -1512,11 +1719,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // We check to see if our heartbeat interval falls into the supported ranges. if (heartbeatInterval > defaultInterval || heartbeatInterval < 1) { + int limit; // Interval is too long, inform the client. heartbeatInterval = defaultInterval; - // Outlook doesn't like this... - //[output appendFormat: @"%d", defaultInterval]; + // When Status = 14, the Wait interval is specified in minutes while + // defaultInterval is specifed in seconds. Adjust accordinlgy. + limit = defaultInterval/60; + if (limit < 1) limit = 1; + if (limit > 59) limit = 59; + //[output appendFormat: @"%d", limit]; //[output appendFormat: @"%d", 14]; } @@ -1532,7 +1744,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. for (j = 0; j < [allCollections count]; j++) { aCollection = [allCollections objectAtIndex: j]; - + [self processSyncCollection: aCollection inBuffer: s changeDetected: &changeDetected @@ -1544,13 +1756,46 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if (changeDetected) { - [self logWithFormat: @"Change detected, we push the content."]; + // Don't return a response if an other Sync is waiting and this is a heartbeat request. + if ([[self globalMetadataForDevice] objectForKey: @"InvalidateSyncInProgress"] && heartbeatInterval > 1) + { + if (debugOn) + [self logWithFormat: @"EAS - Heartbeat stopped - discard response %@", [self globalMetadataForDevice]]; + + [theResponse setStatus: 503]; + [self _setOrUnsetSyncInProgress: NO invalidate: NO]; + RELEASE(output); + return; + } + + [self logWithFormat: @"Change detected during Sync, we push the content."]; break; } else if (heartbeatInterval > 1) { - [self logWithFormat: @"Sleeping %d seconds while detecting changes...", internalInterval]; - sleep(internalInterval); + total_sleep = 0; + + while (total_sleep < internalInterval) + { + // We check if we must break the current synchronization since an other Sync + // has just arrived. + if ([[self globalMetadataForDevice] objectForKey: @"InvalidateSyncInProgress"]) + { + if (debugOn) + [self logWithFormat: @"EAS - Heartbeat stopped %@", [self globalMetadataForDevice]]; + + // Make sure we end the heardbeat-loop. + heartbeatInterval = internalInterval = 1; + + break; + } + else + { + [self logWithFormat: @"Sleeping %d seconds while detecting changes in Sync...", internalInterval-total_sleep]; + sleep(5); + total_sleep += 5; + } + } } else { @@ -1558,8 +1803,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } } - - // Only send a response if there are changes or MS-ASProtocolVersion is either 2.5 or 12.0 oterwise send an empty response. + // + // Only send a response if there are changes or MS-ASProtocolVersion is either 2.5 or 12.0, + // otherwise send an empty response. + // if (changeDetected || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"] || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"12.0"]) { // We always return the last generated response. @@ -1577,6 +1824,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Avoid overloading the autorelease pool here, as Sync command can // generate fairly large responses. RELEASE(output); + + [self _setOrUnsetSyncInProgress: NO invalidate: NO]; } @end diff --git a/ActiveSync/SOGoActiveSyncDispatcher.h b/ActiveSync/SOGoActiveSyncDispatcher.h index affe9e5b9..ca3628b82 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.h +++ b/ActiveSync/SOGoActiveSyncDispatcher.h @@ -1,6 +1,6 @@ /* -Copyright (c) 2014, Inverse inc. +Copyright (c) 2014-2015, Inverse inc. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "SOGoActiveSyncConstants.h" @class NSException; +@class NSMutableDictionary; @class NSURL; @interface SOGoActiveSyncDispatcher : NSObject @@ -39,8 +40,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. NSURL *folderTableURL; NSDictionary *imapFolderGUIDS; id context; + BOOL debugOn; } +- (NSMutableDictionary *) globalMetadataForDevice; + - (id) collectionFromId: (NSString *) theCollectionId type: (SOGoMicrosoftActiveSyncFolderType) theFolderType; diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 7fd80aab2..1d6f2bfbb 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -143,8 +143,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. @implementation SOGoActiveSyncDispatcher -static BOOL debugOn = NO; - - (id) init { [super init]; @@ -171,16 +169,16 @@ static BOOL debugOn = NO; [o setTableUrl: [self folderTableURL]]; [o reloadIfNeeded]; - [[o properties] removeAllObjects]; - [[o properties] addEntriesFromDictionary: [NSDictionary dictionaryWithObject: theSyncKey forKey: @"FolderSyncKey"]]; + [[o properties] setObject: theSyncKey + forKey: @"FolderSyncKey"]; [o save]; } -- (NSMutableDictionary *) _globalMetadataForDevice +- (NSMutableDictionary *) globalMetadataForDevice { SOGoCacheGCSObject *o; - o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil]; + o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil useCache: NO]; [o setObjectType: ActiveSyncGlobalCacheObject]; [o setTableUrl: [self folderTableURL]]; [o reloadIfNeeded]; @@ -204,7 +202,7 @@ static BOOL debugOn = NO; if (theFilter) { o = [SOGoCacheGCSObject objectWithName: [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], theCollectionId] inContainer: nil]; - [o setObjectType: ActiveSyncGlobalCacheObject]; + [o setObjectType: ActiveSyncFolderCacheObject]; [o setTableUrl: [self folderTableURL]]; [o reloadIfNeeded]; @@ -716,7 +714,7 @@ static BOOL debugOn = NO; BOOL first_sync; sm = [SoSecurityManager sharedSecurityManager]; - metadata = [self _globalMetadataForDevice]; + metadata = [self globalMetadataForDevice]; syncKey = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue]; s = [NSMutableString string]; @@ -2033,13 +2031,13 @@ static BOOL debugOn = NO; if ([foldersWithChanges count]) { - [self logWithFormat: @"Change detected, we push the content."]; + [self logWithFormat: @"Change detected using Ping, we let the EAS client know to send a Sync."]; status = 2; break; } else { - [self logWithFormat: @"Sleeping %d seconds while detecting changes...", internalInterval]; + [self logWithFormat: @"Sleeping %d seconds while detecting changes in Ping...", internalInterval]; sleep(internalInterval); } } diff --git a/ActiveSync/SOGoSyncCacheObject.h b/ActiveSync/SOGoSyncCacheObject.h index c6ba61068..a86d1067b 100644 --- a/ActiveSync/SOGoSyncCacheObject.h +++ b/ActiveSync/SOGoSyncCacheObject.h @@ -37,6 +37,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. @interface SOGoSyncCacheObject : NSObject { + @public id _uid; id _sequence; } diff --git a/ActiveSync/SOGoSyncCacheObject.m b/ActiveSync/SOGoSyncCacheObject.m index 63c545b34..79761a343 100644 --- a/ActiveSync/SOGoSyncCacheObject.m +++ b/ActiveSync/SOGoSyncCacheObject.m @@ -32,9 +32,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import #import +#import + +static Class NSNullK; @implementation SOGoSyncCacheObject ++ (void) initialize +{ + NSNullK = [NSNull class]; +} + - (id) init { if ((self = [super init])) @@ -46,14 +54,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. return self; } -+ (id) syncCacheObjectWithUID: (id) theUID sequence: (id) theSequence; ++ (id) syncCacheObjectWithUID: (id) theUID sequence: (id) theSequence { id o; o = [[self alloc] init]; - - [o setUID: theUID]; - [o setSequence: theSequence]; + + [o setUID: [NSNumber numberWithInt: [theUID intValue]]]; + [o setSequence: ([theSequence isKindOfClass: NSNullK] ? theSequence : [NSNumber numberWithInt: [theSequence intValue]])]; return [o autorelease]; } @@ -67,7 +75,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - (id) uid { - return _uid; + return [_uid description]; } - (void) setUID: (id) theUID @@ -77,7 +85,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - (id) sequence { - return _sequence; + return ([_sequence isKindOfClass: NSNullK] ? _sequence : [_sequence description]); } - (void) setSequence: (id) theSequence @@ -88,7 +96,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - (NSComparisonResult) compareUID: (SOGoSyncCacheObject *) theSyncCacheObject { - return [[self uid] compare: [theSyncCacheObject uid]]; + return [self->_uid compare: theSyncCacheObject->_uid]; } // @@ -97,21 +105,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // - (NSComparisonResult) compareSequence: (SOGoSyncCacheObject *) theSyncCacheObject { - if ([[self sequence] isEqual: [NSNull null]] && - [[theSyncCacheObject sequence] isEqual: [NSNull null]]) + if ([self->_sequence isEqual: [NSNull null]] && + [theSyncCacheObject->_sequence isEqual: [NSNull null]]) return [self compareUID: theSyncCacheObject]; - if (![[self sequence] isEqual: [NSNull null]] && [[theSyncCacheObject sequence] isEqual: [NSNull null]]) + if (![self->_sequence isEqual: [NSNull null]] && [theSyncCacheObject->_sequence isEqual: [NSNull null]]) return NSOrderedDescending; - if ([[self sequence] isEqual: [NSNull null]] && ![[theSyncCacheObject sequence] isEqual: [NSNull null]]) + if ([self->_sequence isEqual: [NSNull null]] && ![theSyncCacheObject->_sequence isEqual: [NSNull null]]) return NSOrderedAscending; // Must check this here, to avoid comparing NSNull objects - if ([[self sequence] compare: [theSyncCacheObject sequence]] == NSOrderedSame) + if ([self->_sequence compare: theSyncCacheObject->_sequence] == NSOrderedSame) return [self compareUID: theSyncCacheObject]; - return [[self sequence] compare: [theSyncCacheObject sequence]]; + return [self->_sequence compare: theSyncCacheObject->_sequence]; } - (NSString *) description @@ -120,3 +128,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } @end + diff --git a/NEWS b/NEWS index 25ac09c6f..dacaf978b 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,7 @@ New features Enhancements Bug fixes + - numerous EAS fixes when connections are dropped before the EAS client receives the response (#3058, #2849) 2.3.2 (2015-09-16) ------------------ diff --git a/SoObjects/Mailer/SOGoMailFolder.m b/SoObjects/Mailer/SOGoMailFolder.m index 63f0bdb02..11912896d 100644 --- a/SoObjects/Mailer/SOGoMailFolder.m +++ b/SoObjects/Mailer/SOGoMailFolder.m @@ -2077,7 +2077,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) // * 5 FETCH (UID 559 MODSEQ (17)) // * 6 FETCH (UID 560 MODSEQ (18)) // * 7 FETCH (UID 561 MODSEQ (19)) - +// // // fetchUIDsOfVanishedItems .. // diff --git a/SoObjects/SOGo/SOGoCacheGCSObject.h b/SoObjects/SOGo/SOGoCacheGCSObject.h index b456c1775..1f6969d16 100644 --- a/SoObjects/SOGo/SOGoCacheGCSObject.h +++ b/SoObjects/SOGo/SOGoCacheGCSObject.h @@ -56,6 +56,8 @@ typedef enum { - (void) reloadIfNeeded; - (void) save; ++ (id) objectWithName: (NSString *) key inContainer: (id) theContainer useCache: (BOOL) useCache; + /* accessors */ - (NSMutableString *) path; /* full filename */ diff --git a/SoObjects/SOGo/SOGoCacheGCSObject.m b/SoObjects/SOGo/SOGoCacheGCSObject.m index a2251173a..7956ecefd 100644 --- a/SoObjects/SOGo/SOGoCacheGCSObject.m +++ b/SoObjects/SOGo/SOGoCacheGCSObject.m @@ -103,11 +103,22 @@ static EOAttribute *textColumn = nil; } + (id) objectWithName: (NSString *) key inContainer: (id) theContainer +{ + return [self objectWithName: key + inContainer: theContainer + useCache: YES]; +} + ++ (id) objectWithName: (NSString *) key inContainer: (id) theContainer useCache: (BOOL) useCache { SOGoCache *cache; id o; cache = [SOGoCache sharedCache]; + + if (!useCache) + [cache unregisterObjectWithName: key inContainer: theContainer]; + o = [cache objectNamed: key inContainer: theContainer]; if (!o) From 7f92aca0bd1ffc91767755f1a10621205f428cda Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 14 Oct 2015 09:27:34 -0400 Subject: [PATCH 02/69] (fix) handle references correctly over EAS (#3365) --- ActiveSync/SOGoMailObject+ActiveSync.m | 8 +++++++- NEWS | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ActiveSync/SOGoMailObject+ActiveSync.m b/ActiveSync/SOGoMailObject+ActiveSync.m index 6b2e1b03a..3dbc74828 100644 --- a/ActiveSync/SOGoMailObject+ActiveSync.m +++ b/ActiveSync/SOGoMailObject+ActiveSync.m @@ -1071,9 +1071,15 @@ struct GlobalObjectId { if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.0"] || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.1"]) { + id value; NSString *reference; - reference = [[[[self mailHeaders] objectForKey: @"references"] componentsSeparatedByString: @" "] objectAtIndex: 0]; + value = [[self mailHeaders] objectForKey: @"references"]; + + if ([value isKindOfClass: [NSArray class]]) + reference = [[[value objectAtIndex: 0] componentsSeparatedByString: @" "] objectAtIndex: 0]; + else + reference = [[value componentsSeparatedByString: @" "] objectAtIndex: 0]; if ([reference length] > 0) [s appendFormat: @"%@", [[reference dataUsingEncoding: NSUTF8StringEncoding] activeSyncRepresentationInContext: context]]; diff --git a/NEWS b/NEWS index dacaf978b..ecd03ee5c 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ Enhancements Bug fixes - numerous EAS fixes when connections are dropped before the EAS client receives the response (#3058, #2849) + - correctly handle the References header over EAS (#3365) 2.3.2 (2015-09-16) ------------------ From a54cc22da8c690c7ec4b9c03b99349be7b326262 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 14 Oct 2015 09:29:52 -0400 Subject: [PATCH 03/69] (fix) we no longer always rewrite emails for Outlook when using EAS --- ActiveSync/SOGoMailObject+ActiveSync.m | 2 +- NEWS | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ActiveSync/SOGoMailObject+ActiveSync.m b/ActiveSync/SOGoMailObject+ActiveSync.m index 3dbc74828..cfee21a81 100644 --- a/ActiveSync/SOGoMailObject+ActiveSync.m +++ b/ActiveSync/SOGoMailObject+ActiveSync.m @@ -541,7 +541,7 @@ struct GlobalObjectId { // We sanitize the content *ONLY* for Outlook clients and if the content-transfer-encoding is 8bit. Outlook has strange issues // with quoted-printable/base64 encoded text parts. It just doesn't decode them. encoding = [[self lookupInfoForBodyPart: @""] objectForKey: @"encoding"]; - if ([[context objectForKey: @"DeviceType"] isEqualToString: @"WindowsOutlook15"] || ([encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame)) + if (encoding && ([encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame)) d = [self _sanitizedMIMEMessage]; else d = [self content]; diff --git a/NEWS b/NEWS index ecd03ee5c..bfe555813 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ New features Enhancements + - we no longer always entirely rewrite messages for Outlook 2013 when using EAS Bug fixes - numerous EAS fixes when connections are dropped before the EAS client receives the response (#3058, #2849) From beb484da102196a491c6f0c6332e99af02b0ccf4 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 14 Oct 2015 09:57:56 -0400 Subject: [PATCH 04/69] (fix) make sure English is always used when generating Date headers using EAS (#3356) --- ActiveSync/SOGoActiveSyncDispatcher.m | 5 ++++- NEWS | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 1d6f2bfbb..377ebd1c1 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import #import #import +#import #import #import #import @@ -2947,7 +2948,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. { NSString *value; - value = [[NSDate date] descriptionWithCalendarFormat: @"%a, %d %b %Y %H:%M:%S %z" timeZone: [NSTimeZone timeZoneWithName: @"GMT"] locale: nil]; + value = [[NSDate date] descriptionWithCalendarFormat: @"%a, %d %b %Y %H:%M:%S %z" + timeZone: [NSTimeZone timeZoneWithName: @"GMT"] + locale: [[[NSLocale alloc] initWithLocaleIdentifier: @"en_US"] autorelease]]; s = [NSString stringWithFormat: @"Date: %@\n%@", value, [theRequest contentAsString]]; } else diff --git a/NEWS b/NEWS index bfe555813..1af9d69df 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,7 @@ Enhancements Bug fixes - numerous EAS fixes when connections are dropped before the EAS client receives the response (#3058, #2849) - correctly handle the References header over EAS (#3365) + - make sure English is always used when generating Date headers using EAS (#3356) 2.3.2 (2015-09-16) ------------------ From 3702e79a007faddca77c9a5b9a5c0061b598cdf9 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 15 Oct 2015 12:51:30 -0400 Subject: [PATCH 05/69] (fix) don't escape quoted strings during versit generation --- NEWS | 1 + SOPE/NGCards/NSString+NGCards.m | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/NEWS b/NEWS index 1af9d69df..c06fbc82e 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,7 @@ Bug fixes - numerous EAS fixes when connections are dropped before the EAS client receives the response (#3058, #2849) - correctly handle the References header over EAS (#3365) - make sure English is always used when generating Date headers using EAS (#3356) + - don't escape quoted strings during versit generation 2.3.2 (2015-09-16) ------------------ diff --git a/SOPE/NGCards/NSString+NGCards.m b/SOPE/NGCards/NSString+NGCards.m index 9e851ac2e..02c1c3d0b 100644 --- a/SOPE/NGCards/NSString+NGCards.m +++ b/SOPE/NGCards/NSString+NGCards.m @@ -158,16 +158,31 @@ NSMutableString *string; unsigned int len, i; unichar c; + BOOL isQuoted; len = [self length]; string = [NSMutableString stringWithCapacity: len * 1.5]; + isQuoted = NO; for (i = 0; i < len; i++) { c = [self characterAtIndex: i]; + if (isQuoted) + { + if (c == '"') + isQuoted = NO; + + [string appendFormat: @"%C", c]; + continue; + } + switch (c) { + case '"': + isQuoted = YES; + [string appendFormat: @"%C", c]; + break; case '\\': [string appendString: @"\\\\"]; break; From 2b1e06088199c52a2e1f0086329866f6113bd2c7 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 15 Oct 2015 15:31:46 -0400 Subject: [PATCH 06/69] (feat) initial S/MIME support for EAS (#3327) --- ActiveSync/GNUmakefile.preamble | 5 +- ActiveSync/SOGoActiveSyncDispatcher+Sync.m | 6 + ActiveSync/SOGoActiveSyncDispatcher.m | 202 +++++++++++++++++++-- ActiveSync/SOGoMailObject+ActiveSync.m | 53 ++++-- NEWS | 1 + SoObjects/Mailer/SOGoMailObject.m | 16 +- 6 files changed, 247 insertions(+), 36 deletions(-) diff --git a/ActiveSync/GNUmakefile.preamble b/ActiveSync/GNUmakefile.preamble index c302ad8be..545cdf46b 100644 --- a/ActiveSync/GNUmakefile.preamble +++ b/ActiveSync/GNUmakefile.preamble @@ -1 +1,4 @@ -# compilation settings +ifeq ($(HAS_LIBRARY_ssl),yes) +ADDITIONAL_CPPFLAGS += -DHAVE_OPENSSL=1 +BUNDLE_LIBS += -lcrypto +endif diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m index 2683633d1..d2f6448d4 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m +++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m @@ -1407,6 +1407,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // By default, send MIME mails. See #3146 for details. if (!bodyPreferenceType) bodyPreferenceType = @"4"; + + mimeSupport = [[folderMetadata objectForKey: @"FolderOptions"] objectForKey: @"MIMESupport"]; + + if (!mimeSupport) + mimeSupport = @"1"; } else { @@ -1437,6 +1442,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } [context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"]; + [context setObject: mimeSupport forKey: @"MIMESupport"]; // // We process the commands from the request diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 377ebd1c1..f08276c29 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "SOGoActiveSyncDispatcher.h" #import +#import #import #import #import @@ -135,6 +136,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#ifdef HAVE_OPENSSL +#include +#include +#include +#endif + @interface SOGoActiveSyncDispatcher (Sync) - (NSMutableDictionary *) _folderMetadataForKey: (NSString *) theFolderKey; @@ -1262,7 +1269,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - (void) processItemOperations: (id ) theDocumentElement inResponse: (WOResponse *) theResponse { - NSString *fileReference, *realCollectionId, *serverId, *bodyPreferenceType, *collectionId; + NSString *fileReference, *realCollectionId, *serverId, *bodyPreferenceType, *mimeSupport, *collectionId; NSMutableString *s; NSArray *fetchRequests; id aFetch; @@ -1375,6 +1382,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. serverId = [[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue]; bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue]; [context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"]; + mimeSupport = [[(id)[theDocumentElement getElementsByTagName: @"MIMESupport"] lastObject] textValue]; + [context setObject: mimeSupport forKey: @"MIMESupport"]; currentCollection = [self collectionFromId: realCollectionId type: folderType]; @@ -2102,6 +2111,123 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [theResponse setContent: d]; } +// +// +// +#ifdef HAVE_OPENSSL +- (unsigned int) validateCert: (NSString *) theCert +{ + NSData *d; + + const unsigned char *data; + X509_STORE_CTX *ctx; + X509_LOOKUP *lookup; + X509_STORE *store; + X509 *cert; + + BOOL success; + size_t len; + int rc; + + success = NO; + + d = [theCert dataByDecodingBase64]; + data = (unsigned char *)[d bytes]; + len = [d length]; + + cert = d2i_X509(NULL, &data, len); + if (!cert) + { + [self logWithFormat: @"EAS - validateCert failed for device %@: d2i_X509 failed", [context objectForKey: @"DeviceId"]]; + return 17; + } + + store = X509_STORE_new(); + OpenSSL_add_all_algorithms(); + + if (store) + { + lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); + if (lookup) + { + X509_LOOKUP_load_file(lookup, NULL, X509_FILETYPE_DEFAULT); + lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); + if (lookup) + { + X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT); + ERR_clear_error(); + success = YES; + } + } + } + + if (!success) + { + if (store) + { + X509_STORE_free(store); + store = NULL; + } + } + + ctx = X509_STORE_CTX_new(); + if (!ctx) + { + [self logWithFormat: @"EAS - validateCert failed for device %@: X509_STORE_CTX_new failed", [context objectForKey: @"DeviceId"]]; + return 17; + } + + if (X509_STORE_CTX_init(ctx, store, cert, NULL) != 1) + { + [self logWithFormat: @"EAS - validateCert failed for device %@: X509_STORE_CTX_init failed", [context objectForKey: @"DeviceId"]]; + X509_STORE_CTX_free(ctx); + return 17; + } + + rc = X509_verify_cert(ctx); + X509_STORE_CTX_free(ctx); + X509_free(cert); + + if (rc) + { + return 1; + } + else + { + [self logWithFormat: @"EAS - validateCert failed for device %@: err=%d", [context objectForKey: @"DeviceId"], X509_STORE_CTX_get_error(ctx)]; + return 17; + } +} +#else +- (unsigned int) validateCert: (NSString *) theCert +{ + return 17; +} +#endif + +- (void) processValidateCert: (id ) theDocumentElement + inResponse: (WOResponse *) theResponse +{ + NSMutableString *s; + NSString *cert; + NSData *d; + + cert = [[(id)[theDocumentElement getElementsByTagName: @"Certificate"] lastObject] textValue]; + + s = [NSMutableString string]; + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @""]; + [s appendString: @"1"]; + [s appendFormat: @"%d", [self validateCert: cert]]; + [s appendString: @""]; + + d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; + + [theResponse setContent: d]; +} + + // // // @@ -2418,39 +2544,85 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. NGMimeMessageParser *parser; NGMimeMessage *message; NSException *error; - NSData *data; - NGMutableHashMap *map; - NGMimeMessage *messageToSend; - NGMimeMessageGenerator *generator; + NSMutableData *data; + NSData *new_from_header; NSDictionary *identity; NSString *fullName, *email; + + const char *bytes; + int i, len; + BOOL found_header; // We get the mail's data - data = [[[[(id)[theDocumentElement getElementsByTagName: @"MIME"] lastObject] textValue] stringByDecodingBase64] dataUsingEncoding: NSUTF8StringEncoding]; + data = [NSMutableData dataWithData: [[[[(id)[theDocumentElement getElementsByTagName: @"MIME"] lastObject] textValue] stringByDecodingBase64] dataUsingEncoding: NSUTF8StringEncoding]]; // We extract the recipients parser = [[NGMimeMessageParser alloc] init]; message = [parser parsePartFromData: data]; RELEASE(parser); - map = [NGHashMap hashMapWithDictionary: [message headers]]; - identity = [[context activeUser] primaryIdentity]; fullName = [identity objectForKey: @"fullName"]; email = [identity objectForKey: @"email"]; + if ([fullName length]) - [map setObject: [NSString stringWithFormat: @"%@ <%@>", fullName, email] forKey: @"from"]; + new_from_header = [[NSString stringWithFormat: @"From: %@ <%@>\r\n", fullName, email] dataUsingEncoding:NSUTF8StringEncoding]; else - [map setObject: email forKey: @"from"]; + new_from_header = [[NSString stringWithFormat: @"From: %@\r\n", email] dataUsingEncoding:NSUTF8StringEncoding]; - messageToSend = [[[NGMimeMessage alloc] initWithHeader: map] autorelease]; + bytes = [data bytes]; + len = [data length]; + i = 0; + found_header = NO; - [messageToSend setBody: [message body]]; + // Search for the from-header + while (i < len) + { + if (i == 0 && + (*bytes == 'f' || *bytes == 'F') && + (*(bytes+1) == 'r' || *(bytes+1) == 'R') && + (*(bytes+2) == 'o' || *(bytes+2) == 'O') && + (*(bytes+3) == 'm' || *(bytes+3) == 'M') && + (*(bytes+4) == ':')) + { + found_header = YES; + break; + } + + if (((*bytes == '\r') && (*(bytes+1) == '\n')) && + (*(bytes+2) == 'f' || *(bytes+2) == 'F') && + (*(bytes+3) == 'r' || *(bytes+3) == 'R') && + (*(bytes+4) == 'o' || *(bytes+4) == 'O') && + (*(bytes+5) == 'm' || *(bytes+5) == 'M') && + (*(bytes+6) == ':')) + { + found_header = YES; + i = i + 2; // \r\n + break; + } + + bytes++; + i++; + } + + // Update/Add the From header in the MIMEBody of the SendMail request. + // Any other way to modify the mail body would break s/mime emails. + if (found_header) + { + // Change the From header + [data replaceBytesInRange: NSMakeRange(i, [[message headerForKey: @"from"] length]+8) // start of the From header found - length of the parsed from-header-value + 8 (From:+\r\n+1) + withBytes: [new_from_header bytes] + length: [new_from_header length]]; + } + else + { + // Add a From header + [data replaceBytesInRange: NSMakeRange(0, 0) + withBytes: [new_from_header bytes] + length: [new_from_header length]]; + } - generator = [[[NGMimeMessageGenerator alloc] init] autorelease]; - data = [generator generateMimeFromPart: messageToSend]; - error = [self _sendMail: data recipients: [message allRecipients] saveInSentItems: ([(id)[theDocumentElement getElementsByTagName: @"SaveInSentItems"] count] ? YES : NO)]; diff --git a/ActiveSync/SOGoMailObject+ActiveSync.m b/ActiveSync/SOGoMailObject+ActiveSync.m index cfee21a81..763ed20f2 100644 --- a/ActiveSync/SOGoMailObject+ActiveSync.m +++ b/ActiveSync/SOGoMailObject+ActiveSync.m @@ -476,14 +476,16 @@ struct GlobalObjectId { // // - (NSData *) _preferredBodyDataUsingType: (int) theType + mimeSupport: (int) theMimeSupport nativeType: (int *) theNativeType { NSString *type, *subtype, *encoding; NSData *d; + BOOL isSMIME; type = [[[self bodyStructure] valueForKey: @"type"] lowercaseString]; subtype = [[[self bodyStructure] valueForKey: @"subtype"] lowercaseString]; - + isSMIME = NO; d = nil; // We determine the native type @@ -494,8 +496,14 @@ struct GlobalObjectId { else if ([type isEqualToString: @"multipart"]) *theNativeType = 4; + if (([subtype isEqualToString: @"signed"] || [subtype isEqualToString: @"pkcs7-mime"] ) && theMimeSupport > 0) + { + *theNativeType = 4; + isSMIME = YES; + } + // We get the right part based on the preference - if (theType == 1 || theType == 2) + if ((theType == 1 || theType == 2) && !isSMIME) { if ([type isEqualToString: @"text"] && ![subtype isEqualToString: @"calendar"]) { @@ -536,12 +544,12 @@ struct GlobalObjectId { d = [self _preferredBodyDataInMultipartUsingType: theType nativeTypeFound: theNativeType]; } } - else if (theType == 4) + else if (theType == 4 || isSMIME) { // We sanitize the content *ONLY* for Outlook clients and if the content-transfer-encoding is 8bit. Outlook has strange issues // with quoted-printable/base64 encoded text parts. It just doesn't decode them. encoding = [[self lookupInfoForBodyPart: @""] objectForKey: @"encoding"]; - if (encoding && ([encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame)) + if ((encoding && ([encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame)) && !isSMIME) d = [self _sanitizedMIMEMessage]; else d = [self content]; @@ -656,16 +664,18 @@ struct GlobalObjectId { { NSData *d, *globalObjId; NSArray *attachmentKeys; - NSMutableString *s; - - uint32_t v; - NSString *p; - - id value; - iCalCalendar *calendar; + NSString *p, *subtype; + NSMutableString *s; + id value; - int preferredBodyType, nativeBodyType; + int preferredBodyType, mimeSupport, nativeBodyType; + uint32_t v; + + subtype = [[[self bodyStructure] valueForKey: @"subtype"] lowercaseString]; + + preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue]; + mimeSupport = [[context objectForKey: @"MIMESupport"] intValue]; s = [NSMutableString string]; @@ -862,7 +872,12 @@ struct GlobalObjectId { else { // MesssageClass and ContentClass - [s appendFormat: @"%@", @"IPM.Note"]; + if ([subtype isEqualToString: @"signed"]) + [s appendFormat: @"%@", @"IPM.Note.SMIME.MultipartSigned"]; + else if ([subtype isEqualToString: @"pkcs7-mime"]) + [s appendFormat: @"%@", @"IPM.Note.SMIME"]; + else + [s appendFormat: @"%@", @"IPM.Note"]; [s appendFormat: @"%@", @"urn:content-classes:message"]; } @@ -876,10 +891,8 @@ struct GlobalObjectId { [s appendFormat: @"%@", @"65001"]; // Body - namespace 17 - preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue]; - nativeBodyType = 1; - d = [self _preferredBodyDataUsingType: preferredBodyType nativeType: &nativeBodyType]; + d = [self _preferredBodyDataUsingType: preferredBodyType mimeSupport: mimeSupport nativeType: &nativeBodyType]; if (calendar && !d) { @@ -981,9 +994,12 @@ struct GlobalObjectId { { [s appendString: @""]; - // Set the correct type if client requested text/html but we got text/plain + // Set the correct type if client requested text/html but we got text/plain. + // For s/mime mails type is always 4 if mimeSupport is 1 or 2. if (preferredBodyType == 2 && nativeBodyType == 1) [s appendString: @"1"]; + else if (([subtype isEqualToString: @"signed"] || [subtype isEqualToString: @"pkcs7-mime"] ) && mimeSupport > 0) + [s appendString: @"4"]; else [s appendFormat: @"%d", preferredBodyType]; @@ -1001,7 +1017,8 @@ struct GlobalObjectId { // Attachments -namespace 16 attachmentKeys = [self fetchFileAttachmentKeys]; - if ([attachmentKeys count]) + + if ([attachmentKeys count] && !([subtype isEqualToString: @"signed"])) { int i; diff --git a/NEWS b/NEWS index c06fbc82e..2ad60b0da 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,7 @@ ------------------ New features + - initial S/MIME support for EAS (#3327) Enhancements - we no longer always entirely rewrite messages for Outlook 2013 when using EAS diff --git a/SoObjects/Mailer/SOGoMailObject.m b/SoObjects/Mailer/SOGoMailObject.m index 2aabcaa75..4fae67630 100644 --- a/SoObjects/Mailer/SOGoMailObject.m +++ b/SoObjects/Mailer/SOGoMailObject.m @@ -389,6 +389,10 @@ static BOOL debugSoParts = NO; [[[info valueForKey: @"subtype"] lowercaseString] isEqualToString: @"calendar"]) return info; + if ([[[info valueForKey: @"type"] lowercaseString] isEqualToString: @"application"] && + [[[info valueForKey: @"subtype"] lowercaseString] isEqualToString: @"pkcs7-mime"]) + return info; + /* For each path component, eg 1,1,3 @@ -809,7 +813,7 @@ static BOOL debugSoParts = NO; NSMutableDictionary *currentPart; NSString *newPath; NSArray *subparts; - NSString *type; + NSString *type, *subtype; NSUInteger i; type = [[part objectForKey: @"type"] lowercaseString]; @@ -832,7 +836,15 @@ static BOOL debugSoParts = NO; else { if (!path) - path = @"1"; + { + path = @"1"; + + // We set the path to 0 in case of a smime mail if not provided. + subtype = [[part objectForKey: @"subtype"] lowercaseString]; + if ([subtype isEqualToString: @"pkcs7-mime"]) + path = @"0"; + } + [self _fetchFileAttachmentKey: part intoArray: keys withPath: path From 8cb580633d16b13447d85230fd3a0907f4695905 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 15 Oct 2015 15:54:25 -0400 Subject: [PATCH 07/69] (fix) oh well, old crappy distros --- ActiveSync/SOGoActiveSyncDispatcher.m | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index f08276c29..e42750320 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -33,7 +33,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import #import #import +#if GNUSTEP_BASE_MINOR_VERSION >= 21 #import +#endif #import #import #import @@ -3119,10 +3121,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. options: NSCaseInsensitiveSearch].location == NSNotFound) { NSString *value; - +#if GNUSTEP_BASE_MINOR_VERSION < 21 + value = [[NSDate date] descriptionWithCalendarFormat: @"%a, %d %b %Y %H:%M:%S %z" + timeZone: [NSTimeZone timeZoneWithName: @"GMT"] + locale: nil]; +#else value = [[NSDate date] descriptionWithCalendarFormat: @"%a, %d %b %Y %H:%M:%S %z" timeZone: [NSTimeZone timeZoneWithName: @"GMT"] locale: [[[NSLocale alloc] initWithLocaleIdentifier: @"en_US"] autorelease]]; +#endif s = [NSString stringWithFormat: @"Date: %@\n%@", value, [theRequest contentAsString]]; } else From 1716e46210ab1deec5ee346f03816fd189c37f6f Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Tue, 20 Oct 2015 08:48:39 -0400 Subject: [PATCH 08/69] (fix) last fixes for cache coherency --- ActiveSync/SOGoActiveSyncDispatcher+Sync.m | 231 +++++++++++++-------- ActiveSync/SOGoActiveSyncDispatcher.h | 5 + ActiveSync/SOGoActiveSyncDispatcher.m | 2 + 3 files changed, 149 insertions(+), 89 deletions(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m index d2f6448d4..9f617d51d 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m +++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m @@ -112,10 +112,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. @implementation SOGoActiveSyncDispatcher (Sync) -- (void) _setOrUnsetSyncInProgress: (BOOL) set - invalidate: (BOOL) invalidate +- (void) _setOrUnsetSyncRequest: (BOOL) set + collections: (NSArray *) collections { SOGoCacheGCSObject *o; + NSNumber *processIdentifier; + NSString *key; + int i; + + processIdentifier = [NSNumber numberWithInt: [[NSProcessInfo processInfo] processIdentifier]]; o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil useCache: NO]; [o setObjectType: ActiveSyncGlobalCacheObject]; @@ -123,14 +128,27 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [o reloadIfNeeded]; if (set) - if (invalidate) - [[o properties] setObject: [NSCalendarDate date] forKey: @"InvalidateSyncInProgress"]; - else - [[o properties] setObject: [NSCalendarDate date] forKey: @"SyncInProgress"]; + { + RELEASE(syncRequest); + syncRequest = [NSNumber numberWithUnsignedInt: [[NSCalendarDate date] timeIntervalSince1970]]; + RETAIN(syncRequest); + + [[o properties] setObject: syncRequest forKey: @"SyncRequest"]; + + for (i = 0; i < [collections count]; i++) + { + key = [NSString stringWithFormat: @"SyncRequest+%@", [[[(id)[[collections objectAtIndex: i] getElementsByTagName: @"CollectionId"] lastObject] textValue] stringByUnescapingURL]]; + [[o properties] setObject: processIdentifier forKey: key]; + } + } else { - [[o properties] removeObjectForKey: @"SyncInProgress"]; - [[o properties] removeObjectForKey: @"InvalidateSyncInProgress"]; + [[o properties] removeObjectForKey: @"SyncRequest"]; + for (i = 0; i < [collections count]; i++) + { + key = [NSString stringWithFormat: @"SyncRequest+%@", [[[(id)[[collections objectAtIndex: i] getElementsByTagName: @"CollectionId"] lastObject] textValue] stringByUnescapingURL]]; + [[o properties] removeObjectForKey: key]; + } } [o save]; @@ -139,10 +157,28 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - (void) _setFolderMetadata: (NSDictionary *) theFolderMetadata forKey: (NSString *) theFolderKey { + NSNumber *processIdentifier, *processIdentifierInCache; SOGoCacheGCSObject *o; NSDictionary *values; NSString *key; + if ([theFolderKey hasPrefix: @"folder"]) + key = [NSString stringWithFormat: @"SyncRequest+mail/%@", [theFolderKey substringFromIndex: 6]]; + else + key = [NSString stringWithFormat: @"SyncRequest+%@", theFolderKey]; + + processIdentifier = [NSNumber numberWithInt: [[NSProcessInfo processInfo] processIdentifier]]; + processIdentifierInCache = [[self globalMetadataForDevice] objectForKey: key]; + + // Don't update the cache if another request is processing the same collection. + if (!([processIdentifierInCache isEqual: processIdentifier])) + { + if (debugOn) + [self logWithFormat: @"EAS - We lost our lock - discard folder cache update %@ %@ <> %@", key, processIdentifierInCache, processIdentifier]; + + return; + } + key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], theFolderKey]; values = [theFolderMetadata copy]; @@ -428,7 +464,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [o takeActiveSyncValues: allChanges inContext: context]; [sogoObject saveComponent: o]; - [syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId]; + if ([syncCache objectForKey: serverId]) + [syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId]; } break; case ActiveSyncEventFolder: @@ -438,7 +475,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [o takeActiveSyncValues: allChanges inContext: context]; [sogoObject saveComponent: o]; - [syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId]; + if ([syncCache objectForKey: serverId]) + [syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId]; } break; case ActiveSyncMailFolder: @@ -452,7 +490,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. result = [sogoObject fetchParts: [NSArray arrayWithObject: @"MODSEQ"]]; modseq = [[[result objectForKey: @"RawResponse"] objectForKey: @"fetch"] objectForKey: @"modseq"]; - if (modseq) + if (modseq && [syncCache objectForKey: serverId]) [syncCache setObject: [modseq stringValue] forKey: serverId]; } } @@ -600,6 +638,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. withFilterType: (NSCalendarDate *) theFilterType inBuffer: (NSMutableString *) theBuffer lastServerKey: (NSString **) theLastServerKey + defaultInterval: (unsigned int) theDefaultInterval { NSMutableDictionary *folderMetadata, *dateCache, *syncCache; NSString *davCollectionTagToStore; @@ -624,7 +663,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. { // The syncKey received from the client doesn't match the syncKey we have in cache - client might have missed a response. // We need to cleanup this mess. - [self logWithFormat: @"Cache cleanup needed for device %@ - user: %@", [context objectForKey: @"DeviceId"], [[context activeUser] login]]; + [self logWithFormat: @"Cache cleanup needed for device %@ - user: %@ syncKey: %@ cache: %@", [context objectForKey: @"DeviceId"], [[context activeUser] login], theSyncKey, [folderMetadata objectForKey: @"SyncKey"]]; cleanup_needed = YES; } @@ -756,6 +795,53 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Check for the WindowSize max = [allComponents count]; + // + // Cleanup the mess + // + if (cleanup_needed) + { + + for (i = 0; i < max; i++) + { + component = [allComponents objectAtIndex: i]; + deleted = [[component objectForKey: @"c_deleted"] intValue]; + + if (!deleted && ![[component objectForKey: @"c_component"] isEqualToString: component_name]) + continue; + + uid = [[component objectForKey: @"c_name"] sanitizedServerIdWithType: theFolderType]; + + if (deleted) + { + if (debugOn) + [self logWithFormat: @"EAS - Cache cleanup: DELETE %@", uid]; + + // For deletes we have to recreate a cache entry to make sure the delete is sent again. + [syncCache setObject: @"0" forKey: uid]; + } + else + { + if ([syncCache objectForKey: uid] && [[component objectForKey: @"c_creationdate"] intValue] > [theSyncKey intValue]) + { + if (debugOn) + [self logWithFormat: @"EAS - Cache cleanup: ADD %@", uid]; + + // Cleanup the cache to make sure the add is sent again. + [syncCache removeObjectForKey: uid]; + [dateCache removeObjectForKey: uid]; + } + else + { + if (debugOn) + [self logWithFormat: @"EAS - Cache cleanup: CHANGE %@", uid]; + + // Update cache entry to make sure the change is sent again. + [syncCache setObject: @"0" forKey: uid]; + } + } + } + } + return_count = 0; for (i = 0; i < max; i++) @@ -914,15 +1000,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOGoMailObject *mailObject; NSArray *allMessages, *a; - NSString *lastSequence, *firstUidAdded; + NSString *firstUIDAdded; int j, k, return_count, highestmodseq; BOOL found_in_cache, initialLoadInProgress; initialLoadInProgress = NO; found_in_cache = NO; - lastSequence = nil; - firstUidAdded = nil; + firstUIDAdded = nil; if ([theSyncKey isEqualToString: @"-1"]) { @@ -1030,7 +1115,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. for (j = 0; j < [allCacheObjects count]; j++) { if (([[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]] && [syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]]) || - ([[allCacheObjects objectAtIndex: j] sequence] && ![syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]])) + (![[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]] && ![syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]])) { // We need to continue with adds or deletes from here. found_in_cache = YES; @@ -1066,17 +1151,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Check for the WindowSize and slice accordingly if (return_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize)) { + NSString *lastSequence; more_available = YES; - if (!firstUidAdded) + if (!firstUIDAdded) { a = [davCollectionTagToStore componentsSeparatedByString: @"-"]; - firstUidAdded = [a objectAtIndex: 0]; - RETAIN(firstUidAdded); + firstUIDAdded = [a objectAtIndex: 0]; + RETAIN(firstUIDAdded); } lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? [NSString stringWithFormat:@"%d", highestmodseq] : [aCacheObject sequence]); - //RETAIN(lastSequence); - *theLastServerKey = [[NSString alloc] initWithFormat: @"%@-%@", firstUidAdded, lastSequence]; + *theLastServerKey = [[NSString alloc] initWithFormat: @"%@-%@", firstUIDAdded, lastSequence]; if (debugOn) [self logWithFormat: @"EAS - Reached windowSize - lastUID will be: %@", *theLastServerKey]; @@ -1173,12 +1258,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [dateCache setObject: [NSCalendarDate date] forKey: [aCacheObject uid]]; // Save the frist UID we add. We will use it for the synckey late. - if (!firstUidAdded) + if (!firstUIDAdded) { - firstUidAdded = [aCacheObject uid]; - RETAIN(firstUidAdded); - if (debugOn) - [self logWithFormat: @"EAS - first uid added %@", firstUidAdded]; + firstUIDAdded = [aCacheObject uid]; + RETAIN(firstUIDAdded); + if (debugOn) + [self logWithFormat: @"EAS - first uid added %@", firstUIDAdded]; } return_count++; @@ -1195,19 +1280,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if (more_available) { - //[folderMetadata setObject: lastSequence forKey: @"MoreAvailable"]; [folderMetadata setObject: [NSNumber numberWithInt: YES] forKey: @"MoreAvailable"]; [folderMetadata setObject: *theLastServerKey forKey: @"SyncKey"]; - //RELEASE(lastSequence); } else { [folderMetadata removeObjectForKey: @"MoreAvailable"]; - if (firstUidAdded) + if (firstUIDAdded) { a = [davCollectionTagToStore componentsSeparatedByString: @"-"]; - [folderMetadata setObject: [[NSString alloc] initWithFormat: @"%@-%@", firstUidAdded, [a objectAtIndex: 1]] forKey: @"SyncKey"]; + [folderMetadata setObject: [[NSString alloc] initWithFormat: @"%@-%@", firstUIDAdded, [a objectAtIndex: 1]] forKey: @"SyncKey"]; + RELEASE(firstUIDAdded); } else [folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"]; @@ -1215,7 +1299,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]]; - RELEASE(firstUidAdded); RELEASE(*theLastServerKey); } // default: @@ -1481,7 +1564,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. withFolderType: folderType withFilterType: [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]] inBuffer: changeBuffer - lastServerKey: &lastServerKey]; + lastServerKey: &lastServerKey + defaultInterval: [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncInterval]]; } folderMetadata = [self _folderMetadataForKey: folderKey]; @@ -1643,6 +1727,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOGoSystemDefaults *defaults; id aCollection; NSMutableString *output, *s; + NSMutableDictionary *globalMetadata; + NSNumber *syncRequestInCache, *processIdentifier; + NSString *key; NSArray *allCollections; NSData *d; @@ -1654,6 +1741,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. defaults = [SOGoSystemDefaults sharedSystemDefaults]; defaultInterval = [defaults maximumSyncInterval]; + processIdentifier = [NSNumber numberWithInt: [[NSProcessInfo processInfo] processIdentifier]]; + + allCollections = (id)[theDocumentElement getElementsByTagName: @"Collection"]; [output appendString: @""]; [output appendString: @""]; @@ -1673,45 +1763,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. return; } - if ([[self globalMetadataForDevice] objectForKey: @"SyncInProgress"]) - { - NSCalendarDate *syncStartDate; - - // An other sync is going on right now. Lets break it and wait - // until the other process clears it up. - [self _setOrUnsetSyncInProgress: YES invalidate: YES]; - total_sleep = 0; - - syncStartDate = [[self globalMetadataForDevice] objectForKey: @"SyncInProgress"]; - - while ([[self globalMetadataForDevice] objectForKey: @"SyncInProgress"]) - { - // Don't go into a heartbeat loop. - heartbeatInterval = 0; - - // We waited too long. Return a fatal error to the client. - if (abs([syncStartDate timeIntervalSinceNow]) > defaultInterval) - { - if (debugOn) - [self logWithFormat: @"EAS - We waited too long. syncStartDate: %@ %@", syncStartDate, [self globalMetadataForDevice]]; - - [theResponse setStatus: 503]; - [self _setOrUnsetSyncInProgress: NO invalidate: NO]; - RELEASE(output); - return; - } - - if (debugOn) - [self logWithFormat: @"EAS - globalMetadataForDevice %@", [self globalMetadataForDevice]]; - - [self logWithFormat: @"Sync in progress for device %@ (login: %@). Lock will expire in %d seconds", [context objectForKey: @"DeviceId"], [[context activeUser] login], defaultInterval-total_sleep]; - sleep(5); - total_sleep += 5; - } - } - - // No lock or broke the existing one, lets grab it and proceed - [self _setOrUnsetSyncInProgress: YES invalidate: NO]; + // Let other requests know about the collections we are dealing with. + [self _setOrUnsetSyncRequest: YES collections: allCollections]; changeDetected = NO; maxSyncResponseSize = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncResponseSize]; @@ -1739,8 +1792,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } [output appendString: @""]; - - allCollections = (id)[theDocumentElement getElementsByTagName: @"Collection"]; // We enter our loop detection change for (i = 0; i < (heartbeatInterval/internalInterval); i++) @@ -1756,24 +1807,27 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. changeDetected: &changeDetected maxSyncResponseSize: maxSyncResponseSize]; - if (maxSyncResponseSize > 0 && [s length] >= maxSyncResponseSize) + // Don't return a response if another Sync is waiting. + globalMetadata = [self globalMetadataForDevice]; + key = [NSString stringWithFormat: @"SyncRequest+%@", [[[(id)[aCollection getElementsByTagName: @"CollectionId"] lastObject] textValue] stringByUnescapingURL]]; + + if (!([[globalMetadata objectForKey: key] isEqual: processIdentifier])) + { + if (debugOn) + [self logWithFormat: @"EAS - Discard response %@", [self globalMetadataForDevice]]; + + [theResponse setStatus: 503]; + + RELEASE(output); + return; + } + + if ((maxSyncResponseSize > 0 && [s length] >= maxSyncResponseSize)) break; } if (changeDetected) { - // Don't return a response if an other Sync is waiting and this is a heartbeat request. - if ([[self globalMetadataForDevice] objectForKey: @"InvalidateSyncInProgress"] && heartbeatInterval > 1) - { - if (debugOn) - [self logWithFormat: @"EAS - Heartbeat stopped - discard response %@", [self globalMetadataForDevice]]; - - [theResponse setStatus: 503]; - [self _setOrUnsetSyncInProgress: NO invalidate: NO]; - RELEASE(output); - return; - } - [self logWithFormat: @"Change detected during Sync, we push the content."]; break; } @@ -1785,7 +1839,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. { // We check if we must break the current synchronization since an other Sync // has just arrived. - if ([[self globalMetadataForDevice] objectForKey: @"InvalidateSyncInProgress"]) + syncRequestInCache = [[self globalMetadataForDevice] objectForKey: @"SyncRequest"]; + if (!([syncRequest isEqualToNumber: syncRequestInCache])) { if (debugOn) [self logWithFormat: @"EAS - Heartbeat stopped %@", [self globalMetadataForDevice]]; @@ -1830,8 +1885,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Avoid overloading the autorelease pool here, as Sync command can // generate fairly large responses. RELEASE(output); - - [self _setOrUnsetSyncInProgress: NO invalidate: NO]; } @end diff --git a/ActiveSync/SOGoActiveSyncDispatcher.h b/ActiveSync/SOGoActiveSyncDispatcher.h index ca3628b82..76b4acbe7 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.h +++ b/ActiveSync/SOGoActiveSyncDispatcher.h @@ -31,15 +31,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "SOGoActiveSyncConstants.h" +@class NSCalendarDate; @class NSException; @class NSMutableDictionary; @class NSURL; +@class NSNumber; @interface SOGoActiveSyncDispatcher : NSObject { NSURL *folderTableURL; NSDictionary *imapFolderGUIDS; id context; + + NSNumber *syncRequest; + BOOL debugOn; } diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index e42750320..5f41249b5 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -160,6 +160,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. debugOn = [[SOGoSystemDefaults sharedSystemDefaults] easDebugEnabled]; folderTableURL = nil; imapFolderGUIDS = nil; + syncRequest = nil; return self; } @@ -167,6 +168,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. { RELEASE(folderTableURL); RELEASE(imapFolderGUIDS); + RELEASE(syncRequest); [super dealloc]; } From fc9b175f25460b870335c397b03086e347e1af5a Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Tue, 20 Oct 2015 15:33:18 -0400 Subject: [PATCH 09/69] (feat) now possible to choose which folders to sync over EAS --- ActiveSync/SOGoActiveSyncDispatcher.m | 62 ++++++++++++++------------- NEWS | 1 + 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 5f41249b5..31b7cb300 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -707,22 +707,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - (void) processFolderSync: (id ) theDocumentElement inResponse: (WOResponse *) theResponse { - NSString *key, *cKey, *nkey, *name, *serverId, *parentId, *nameInCache, *personalFolderName, *syncKey, *folderType; + NSString *key, *cKey, *nkey, *name, *serverId, *parentId, *nameInCache, *personalFolderName, *syncKey, *folderType, *operation; + NSMutableDictionary *cachedGUIDs, *metadata; + NSMutableArray *folders, *processedFolders; NSDictionary *folderMetadata, *imapGUIDs; NSArray *allFoldersMetadata, *allKeys; - NSMutableDictionary *cachedGUIDs, *metadata; SOGoMailAccounts *accountsFolder; SOGoMailAccount *accountFolder; NSMutableString *s, *commands; SOGoUserFolder *userFolder; - NSMutableArray *folders, *processedFolders; SoSecurityManager *sm; SOGoCacheGCSObject *o; id currentFolder; NSData *d; int status, command_count, i, type, fi, count; - BOOL first_sync; sm = [SoSecurityManager sharedSecurityManager]; @@ -840,32 +839,33 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } else { - if ([cKey rangeOfString: @"vevent" options: NSCaseInsensitiveSearch].location != NSNotFound || - [cKey rangeOfString: @"vtodo" options: NSCaseInsensitiveSearch].location != NSNotFound) - folderType = @"Calendar"; - else - folderType = @"Contacts"; + if ([cKey rangeOfString: @"vevent" options: NSCaseInsensitiveSearch].location != NSNotFound || + [cKey rangeOfString: @"vtodo" options: NSCaseInsensitiveSearch].location != NSNotFound) + folderType = @"Calendar"; + else + folderType = @"Contacts"; - if ([ cKey rangeOfString: @"/"].location != NSNotFound) - currentFolder = [[[[context activeUser] homeFolderInContext: context] lookupName: folderType inContext: context acquire: NO] + if ([ cKey rangeOfString: @"/"].location != NSNotFound) + currentFolder = [[[[context activeUser] homeFolderInContext: context] lookupName: folderType inContext: context acquire: NO] lookupName: [cKey substringFromIndex: [cKey rangeOfString: @"/"].location+1] inContext: context acquire: NO]; - // remove the folder from device if it doesn't exists or it has not the proper permissions - if (!currentFolder || - [sm validatePermission: SoPerm_DeleteObjects - onObject: currentFolder - inContext: context] || - [sm validatePermission: SoPerm_AddDocumentsImagesAndFiles - onObject: currentFolder - inContext: context]) - { - [commands appendFormat: @"%@", [cKey stringByEscapingURL] ]; - command_count++; - [o destroy]; - } - } - } - } + // Remove the folder from device if it doesn't exist, we don't want to sync it, or it doesn't have the proper permissions + if (!currentFolder || + ![currentFolder synchronize] || + [sm validatePermission: SoPerm_DeleteObjects + onObject: currentFolder + inContext: context] || + [sm validatePermission: SoPerm_AddDocumentsImagesAndFiles + onObject: currentFolder + inContext: context]) + { + [commands appendFormat: @"%@", [cKey stringByEscapingURL] ]; + command_count++; + [o destroy]; + } + } + } + } // Handle addition and changes for (i = 0; i < [allFoldersMetadata count]; i++) @@ -965,11 +965,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. folders = [[[[[context activeUser] homeFolderInContext: context] lookupName: @"Calendar" inContext: context acquire: NO] subFolders] mutableCopy]; [folders addObjectsFromArray: [[[[context activeUser] homeFolderInContext: context] lookupName: @"Contacts" inContext: context acquire: NO] subFolders]]; - // Inside this loop we remove all the folder without write/delete permissions + // We remove all the folders that aren't GCS-ones, that we don't want to synchronize and + // the ones without write/delete permissions count = [folders count]-1; for (; count >= 0; count--) { - if ([sm validatePermission: SoPerm_DeleteObjects + if (![[folders objectAtIndex: count] isKindOfClass: [SOGoGCSFolder class]] || + ![[folders objectAtIndex: count] synchronize] || + [sm validatePermission: SoPerm_DeleteObjects onObject: [folders objectAtIndex: count] inContext: context] || [sm validatePermission: SoPerm_AddDocumentsImagesAndFiles @@ -981,7 +984,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } count = [folders count]-1; - NSString *operation; for (fi = 0; fi <= count ; fi++) { diff --git a/NEWS b/NEWS index 2ad60b0da..8a7a7aa20 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,7 @@ New features - initial S/MIME support for EAS (#3327) + - now possible to choose which folders to sync over EAS Enhancements - we no longer always entirely rewrite messages for Outlook 2013 when using EAS From 2c723070c69899055b456119f6e8f42c279d0c57 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 21 Oct 2015 10:54:15 -0400 Subject: [PATCH 10/69] (fix) we now return all cards when we receive an empty addressbook-query REPORT --- NEWS | 1 + SoObjects/Contacts/SOGoFolder+CardDAV.h | 4 +--- SoObjects/Contacts/SOGoFolder+CardDAV.m | 8 +++++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 8a7a7aa20..4b88640e7 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,7 @@ Bug fixes - correctly handle the References header over EAS (#3365) - make sure English is always used when generating Date headers using EAS (#3356) - don't escape quoted strings during versit generation + - we now return all cards when we receive an empty addressbook-query REPORT 2.3.2 (2015-09-16) ------------------ diff --git a/SoObjects/Contacts/SOGoFolder+CardDAV.h b/SoObjects/Contacts/SOGoFolder+CardDAV.h index 2e0e88b0f..4ab4278ce 100644 --- a/SoObjects/Contacts/SOGoFolder+CardDAV.h +++ b/SoObjects/Contacts/SOGoFolder+CardDAV.h @@ -1,8 +1,6 @@ /* NSObject+CardDAV.h - this file is part of SOGo * - * Copyright (C) 2007 Inverse inc. - * - * Author: Ludovic Marcotte + * Copyright (C) 2007-2015 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/SoObjects/Contacts/SOGoFolder+CardDAV.m b/SoObjects/Contacts/SOGoFolder+CardDAV.m index 2644e228f..3c9130670 100644 --- a/SoObjects/Contacts/SOGoFolder+CardDAV.m +++ b/SoObjects/Contacts/SOGoFolder+CardDAV.m @@ -1,8 +1,6 @@ /* NSObject+CardDAV.m - this file is part of SOGo * - * Copyright (C) 2007-2011 Inverse inc. - * - * Author: Ludovic Marcotte + * Copyright (C) 2007-2015 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -187,6 +185,10 @@ [filters addObject: filter]; } + // If no filters are provided, we return everything. + if (![filters count]) + [filters addObject: [NSDictionary dictionaryWithObject: @"." forKey: @"mail"]]; + return filters; } From df211af604793a61065d3a4f38c922ada6ae089c Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Fri, 23 Oct 2015 11:32:04 -0400 Subject: [PATCH 11/69] (fix) fixed br_pt translation + updated transifex (#3355) --- UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings b/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings index af865affe..28f1412cf 100644 --- a/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings +++ b/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings @@ -203,7 +203,7 @@ /* Event+task categories */ "category_none" = "Nenhum"; -"calendar_category_labels" = "Aniversário,Negócios,Ligações,Concorrência,Cliente,Favoritos,Acompanhamento,Presentes,Feriados,Idéias,Meeting,Problemas,Miscelânea,Pessoal,Projetos,Feriado público,Posição,Fornecedores,Viagem,Férias"; +"calendar_category_labels" = "Comemoração,Aniversário,Negócios,Ligações,Concorrência,Cliente,Favoritos,Acompanhamento,Presentes,Feriados,Idéias,Meeting,Problemas,Miscelânea,Pessoal,Projetos,Feriado público,Posição,Fornecedores,Viagem,Férias"; /* Default module */ "Calendar" = "Calendário"; From 1b44218a3565fa69f15c5e1f3b69bbdf6ab9f1d3 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Fri, 23 Oct 2015 12:44:43 -0400 Subject: [PATCH 12/69] (fix) avoid crash when replying to a mail with no recipients (#3359) --- NEWS | 1 + SoObjects/Mailer/SOGoDraftObject.m | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 4b88640e7..9271e0c1a 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,7 @@ Bug fixes - make sure English is always used when generating Date headers using EAS (#3356) - don't escape quoted strings during versit generation - we now return all cards when we receive an empty addressbook-query REPORT + - avoid crash when replying to a mail with no recipients (#3359) 2.3.2 (2015-09-16) ------------------ diff --git a/SoObjects/Mailer/SOGoDraftObject.m b/SoObjects/Mailer/SOGoDraftObject.m index 6967a6262..3874bafe0 100644 --- a/SoObjects/Mailer/SOGoDraftObject.m +++ b/SoObjects/Mailer/SOGoDraftObject.m @@ -699,7 +699,8 @@ static NSString *userAgent = nil; for (count = max - 1; count >= 0; count--) { currentAddress = [addresses objectAtIndex: count]; - if ([currentRecipient + if (![currentAddress baseEMail] || + [currentRecipient caseInsensitiveCompare: [currentAddress baseEMail]] == NSOrderedSame) { From 40a1d5fdbf326d4fbd4c8739d2a6f9b5861b8af0 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Fri, 23 Oct 2015 14:34:19 -0400 Subject: [PATCH 13/69] (fix) inline images sent from SOGo webmail are not displayed in Mozilla Thunderbird (#3271) --- NEWS | 1 + SoObjects/Mailer/SOGoDraftObject.m | 30 ++++++++++++++++++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 9271e0c1a..8366f624e 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,7 @@ Bug fixes - don't escape quoted strings during versit generation - we now return all cards when we receive an empty addressbook-query REPORT - avoid crash when replying to a mail with no recipients (#3359) + - inline images sent from SOGo webmail are not displayed in Mozilla Thunderbird (#3271) 2.3.2 (2015-09-16) ------------------ diff --git a/SoObjects/Mailer/SOGoDraftObject.m b/SoObjects/Mailer/SOGoDraftObject.m index 3874bafe0..11da52e80 100644 --- a/SoObjects/Mailer/SOGoDraftObject.m +++ b/SoObjects/Mailer/SOGoDraftObject.m @@ -179,6 +179,7 @@ static NSString *headerKeys[] = {@"subject", @"to", @"cc", @"bcc", static NGMimeType *MultiMixedType = nil; static NGMimeType *MultiAlternativeType = nil; +static NGMimeType *MultiRelatedType = nil; static NSString *userAgent = nil; + (void) initialize @@ -189,6 +190,9 @@ static NSString *userAgent = nil; MultiAlternativeType = [NGMimeType mimeType: @"multipart" subType: @"alternative"]; [MultiAlternativeType retain]; + MultiRelatedType = [NGMimeType mimeType: @"multipart" subType: @"related"]; + [MultiRelatedType retain]; + userAgent = [NSString stringWithFormat: @"SOGoMail %@", SOGoVersion]; [userAgent retain]; @@ -1679,16 +1683,20 @@ static NSString *userAgent = nil; NGMimeMessage *message; NGMutableHashMap *map; NSString *newText; + BOOL has_inline_images; message = nil; - + has_inline_images = NO; bodyParts = [NSMutableArray array]; if (_extractImages) { newText = [text htmlByExtractingImages: bodyParts]; if ([bodyParts count]) - [self setText: newText]; + { + [self setText: newText]; + has_inline_images = YES; + } } map = [self mimeHeaderMapWithHeaders: _headers @@ -1705,10 +1713,20 @@ static NSString *userAgent = nil; /* no attachments */ message = [self mimeMessageForContentWithHeaderMap: map]; else - /* attachments, create multipart/mixed */ - message = [self mimeMultiPartMessageWithHeaderMap: map - andBodyParts: bodyParts]; - //[self debugWithFormat: @"message: %@", message]; + { + // attachments, create multipart/mixed or multipart/related if + // we have inline image to avoid Thunderbird bug #61815 (https://bugzilla.mozilla.org/show_bug.cgi?id=61815) + if (has_inline_images) + { + [map removeAllObjectsForKey: @"content-type"]; + [map addObject: MultiRelatedType forKey: @"content-type"]; + } + + message = [self mimeMultiPartMessageWithHeaderMap: map + andBodyParts: bodyParts]; + + //[self debugWithFormat: @"message: %@", message]; + } } return message; From 3ba1e33eb66b49c563588f69149400cbe1d65b4a Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Fri, 23 Oct 2015 15:39:49 -0400 Subject: [PATCH 14/69] (fix) perms fixes on scripts --- Scripts/sql-update-1.3.11_to_1.3.12-mysql.sh | 0 Scripts/sql-update-1.3.16_to_1.3.17-mysql.sh | 0 Scripts/sql-update-1.3.3_to_1.3.4-mysql.sh | 0 Scripts/sql-update-1.3.3_to_1.3.4.sh | 0 Scripts/sql-update-20080303.sh | 0 5 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 Scripts/sql-update-1.3.11_to_1.3.12-mysql.sh mode change 100644 => 100755 Scripts/sql-update-1.3.16_to_1.3.17-mysql.sh mode change 100644 => 100755 Scripts/sql-update-1.3.3_to_1.3.4-mysql.sh mode change 100644 => 100755 Scripts/sql-update-1.3.3_to_1.3.4.sh mode change 100644 => 100755 Scripts/sql-update-20080303.sh diff --git a/Scripts/sql-update-1.3.11_to_1.3.12-mysql.sh b/Scripts/sql-update-1.3.11_to_1.3.12-mysql.sh old mode 100644 new mode 100755 diff --git a/Scripts/sql-update-1.3.16_to_1.3.17-mysql.sh b/Scripts/sql-update-1.3.16_to_1.3.17-mysql.sh old mode 100644 new mode 100755 diff --git a/Scripts/sql-update-1.3.3_to_1.3.4-mysql.sh b/Scripts/sql-update-1.3.3_to_1.3.4-mysql.sh old mode 100644 new mode 100755 diff --git a/Scripts/sql-update-1.3.3_to_1.3.4.sh b/Scripts/sql-update-1.3.3_to_1.3.4.sh old mode 100644 new mode 100755 diff --git a/Scripts/sql-update-20080303.sh b/Scripts/sql-update-20080303.sh old mode 100644 new mode 100755 From b378d258cbe732cb583c91b86b4d65516933d8ab Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Fri, 23 Oct 2015 15:54:33 -0400 Subject: [PATCH 15/69] (fix) add unit test from PR #105 --- Tests/Unit/GNUmakefile | 3 +- Tests/Unit/TestNGMailAddressParser.m | 131 +++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 Tests/Unit/TestNGMailAddressParser.m diff --git a/Tests/Unit/GNUmakefile b/Tests/Unit/GNUmakefile index 70f3ff242..b3b666270 100644 --- a/Tests/Unit/GNUmakefile +++ b/Tests/Unit/GNUmakefile @@ -27,7 +27,8 @@ $(TEST_TOOL)_OBJC_FILES += \ TestNSData+Crypto.m \ TestNSString+Crypto.m \ TestNSString+URLEscaping.m \ - TestNSString+Utilities.m + TestNSString+Utilities.m \ + TestNGMailAddressParser.m TEST_TOOL_NAME = $(TEST_TOOL) diff --git a/Tests/Unit/TestNGMailAddressParser.m b/Tests/Unit/TestNGMailAddressParser.m new file mode 100644 index 000000000..9801d509f --- /dev/null +++ b/Tests/Unit/TestNGMailAddressParser.m @@ -0,0 +1,131 @@ +/* TestNGMailAddressParser.m - this file is part of SOGo + * + * Copyright (C) 2015 Inverse inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import + +#import "SOGoTest.h" + +@interface TestNGMailAddressParser : SOGoTest +@end + +@implementation TestNGMailAddressParser + +/* important: this file must be encoded in iso-8859-1, due to issues with the + objc compiler */ +- (void) test_singleEmailParsing_value_ +{ + NSArray *rawAddresses = [NSArray arrayWithObjects: + @"johndown@test.com", // email alone + @"", // email between brackets + @"\"\" ", // doubled +// @"\"johndown@inverse.ca\" ", // with and without br. + @"inoblabla ", // accented full name + @"inoblabla Bla Bl ", // accented and multiword + @"John Down \"Bla Bla\" ", // partly quoted + @"John Down ", // full name + email + @"John, Down ", // full name with comma + email + @"john", // name only, no domain + nil ]; + NSArray *expectedAddresses = [NSArray arrayWithObjects: + @"johndown@test.com", // email alone + @"johndown@test.com", // email between brackets + @"johndown@test.com", // doubled +// @"\"johndown@inverse.ca\" ", // with and without br. + @"johndown@test.com", // accented full name + @"johndown@test.com", // accented + // and multiword + + /* NOTE: the following are wrong but tolerated for now */ + @"johndown@test.com", // partly quoted + @"johndown@test.com", // full name + email + @"johndown@test.com", // full name with comma + email + @"john", // name only, no domain + nil ]; + NSString *rawAddress, *currentExp, *result, *error; + NGMailAddressParser *parser; + NGMailAddress *parsedRecipient; + + int count = 0; + for (count = 0; count < [rawAddresses count]; count++) + { + rawAddress = [rawAddresses objectAtIndex:count]; + currentExp = [expectedAddresses objectAtIndex:count]; + parser = [NGMailAddressParser mailAddressParserWithString: rawAddress]; + parsedRecipient = [parser parse]; + result = [parsedRecipient address]; + error = [NSString + stringWithFormat: @"received '%@' instead of '%@' for '%@'", + result, currentExp, rawAddress]; + testWithMessage([result isEqualToString: currentExp], error); + } +} + +- (void) test_multipleEmailParsing_value_ +{ + NSArray *rawAddresses = [NSArray arrayWithObjects: + @"johndown@test.com", // email alone + @"test1a@test.com, test1b@here.now", + @"\"johndown@inverse.ca\" ", // with and without br. + @"John One , John Two ", // TWO full names + email + @"Three, John , Four, John ", // TWO full names with comma + email + @"john, down", // Two partial names + @"Three A , Three B , Three C ", // Three mails + nil ]; + NSArray *expectedAddresses = [NSArray arrayWithObjects: + [NSArray arrayWithObjects: @"johndown@test.com", nil], // email alone + [NSArray arrayWithObjects: @"test1a@test.com", @"test1b@here.now", nil], // test1 a/b + [NSArray arrayWithObjects: @"johndown@test.com", nil], // with and without br. + [NSArray arrayWithObjects: @"test2a@test.com", @"test2b@here.now", nil], // test2 a/b + [NSArray arrayWithObjects: @"test3a@test.com", @"test3b@here.now", nil], // test3 a/b + [NSArray arrayWithObjects: @"john", @"down", nil], + [NSArray arrayWithObjects: @"threea@test.com", @"threeb@test.com", @"threec@test.com", nil], // test a/b/c + nil ]; + NSString *currentRaw, *currentExp, *result, *error; + NGMailAddressParser *parser = nil; + NSArray *parsedRecipients = nil; + NSArray *expectedRecipients = nil; + NGMailAddress *parsedRecipient = nil; + + int count = 0; + for (count = 0; count < [rawAddresses count]; count++) + { + currentRaw = [rawAddresses objectAtIndex: count]; + expectedRecipients = [expectedAddresses objectAtIndex: count]; + parser = [NGMailAddressParser mailAddressParserWithString: currentRaw]; + parsedRecipients = [parser parseAddressList]; + int innercount; + for (innercount = 0; innercount < [parsedRecipients count]; innercount++) + { + parsedRecipient = [parsedRecipients objectAtIndex:innercount]; + result = [parsedRecipient address]; + currentExp = [expectedRecipients objectAtIndex:innercount]; + error = [NSString + stringWithFormat: @"received '%@' instead of '%@' for '%@'", + result, currentExp, currentRaw]; + testWithMessage([result isEqualToString: currentExp], error); + } + + currentRaw++; + currentExp++; + } +} +@end + From 40cb81fd0b9b6156fb81a9cc0545699663a68416 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Mon, 26 Oct 2015 10:08:00 -0400 Subject: [PATCH 16/69] (fix) prevent postal address showing on single line over EAS (#2614) --- ActiveSync/NGVCard+ActiveSync.m | 84 ++++++++++++++++++++++++++++----- NEWS | 1 + 2 files changed, 72 insertions(+), 13 deletions(-) diff --git a/ActiveSync/NGVCard+ActiveSync.m b/ActiveSync/NGVCard+ActiveSync.m index eb62b9722..fec64524d 100644 --- a/ActiveSync/NGVCard+ActiveSync.m +++ b/ActiveSync/NGVCard+ActiveSync.m @@ -52,7 +52,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. { NSArray *emails, *addresses, *categories, *elements; CardElement *n, *homeAdr, *workAdr; - NSMutableString *s; + NSMutableString *s, *a; NSString *url; id o; @@ -63,7 +63,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if ((o = [n flattenedValueAtIndex: 0 forKey: @""])) [s appendFormat: @"%@", [o activeSyncRepresentationInContext: context]]; - + if ((o = [n flattenedValueAtIndex: 1 forKey: @""])) [s appendFormat: @"%@", [o activeSyncRepresentationInContext: context]]; @@ -146,16 +146,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if ([addresses count]) { homeAdr = [addresses objectAtIndex: 0]; + a = [NSMutableString string]; if ((o = [homeAdr flattenedValueAtIndex: 2 forKey: @""])) - [s appendFormat: @"%@", [o activeSyncRepresentationInContext: context]]; + [a appendString: o]; + + if ((o = [homeAdr flattenedValueAtIndex: 1 forKey: @""]) && [o length]) + [a appendFormat: @"\n%@", o]; + [s appendFormat: @"%@", [a activeSyncRepresentationInContext: context]]; + if ((o = [homeAdr flattenedValueAtIndex: 3 forKey: @""])) [s appendFormat: @"%@", [o activeSyncRepresentationInContext: context]]; if ((o = [homeAdr flattenedValueAtIndex: 4 forKey: @""])) [s appendFormat: @"%@", [o activeSyncRepresentationInContext: context]]; - + if ((o = [homeAdr flattenedValueAtIndex: 5 forKey: @""])) [s appendFormat: @"%@", [o activeSyncRepresentationInContext: context]]; @@ -171,9 +177,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if ([addresses count]) { workAdr = [addresses objectAtIndex: 0]; + a = [NSMutableString string]; if ((o = [workAdr flattenedValueAtIndex: 2 forKey: @""])) - [s appendFormat: @"%@", [o activeSyncRepresentationInContext: context]]; + [a appendString: o]; + + if ((o = [workAdr flattenedValueAtIndex: 1 forKey: @""]) && [o length]) + [a appendFormat: @"\n%@", o]; + + [s appendFormat: @"%@", [a activeSyncRepresentationInContext: context]]; if ((o = [workAdr flattenedValueAtIndex: 3 forKey: @""])) [s appendFormat: @"%@", [o activeSyncRepresentationInContext: context]]; @@ -217,6 +229,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. inContext: (WOContext *) context { CardElement *element; + NSMutableArray *addressLines; id o; // Contact's note @@ -244,10 +257,33 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // BusinessCountry // element = [self elementWithTag: @"adr" ofType: @"work"]; - [element setSingleValue: @"" - atIndex: 1 forKey: @""]; - [element setSingleValue: [theValues objectForKey: @"BusinessStreet"] - atIndex: 2 forKey: @""]; + + if ((o = [theValues objectForKey: @"BusinessStreet"])) + { + addressLines = [NSMutableArray arrayWithArray: [o componentsSeparatedByString: @"\n"]]; + + [element setSingleValue: @"" + atIndex: 1 forKey: @""]; + [element setSingleValue: [addressLines objectAtIndex: 0] + atIndex: 2 forKey: @""]; + + // Extended address line. If there are more than 2 address lines we add them to the extended address line. + if ([addressLines count] > 1) + { + [addressLines removeObjectAtIndex: 0]; + [element setSingleValue: [addressLines componentsJoinedByString: @" "] + atIndex: 1 forKey: @""]; + } + } + else + { + [element setSingleValue: @"" + atIndex: 1 forKey: @""]; + [element setSingleValue: @"" + atIndex: 2 forKey: @""]; + } + + [element setSingleValue: [theValues objectForKey: @"BusinessCity"] atIndex: 3 forKey: @""]; [element setSingleValue: [theValues objectForKey: @"BusinessState"] @@ -267,10 +303,32 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // HomeCountry // element = [self elementWithTag: @"adr" ofType: @"home"]; - [element setSingleValue: @"" - atIndex: 1 forKey: @""]; - [element setSingleValue: [theValues objectForKey: @"HomeStreet"] - atIndex: 2 forKey: @""]; + + if ((o = [theValues objectForKey: @"HomeStreet"])) + { + addressLines = [NSMutableArray arrayWithArray: [o componentsSeparatedByString: @"\n"]]; + + [element setSingleValue: @"" + atIndex: 1 forKey: @""]; + [element setSingleValue: [addressLines objectAtIndex: 0] + atIndex: 2 forKey: @""]; + + // Extended address line. If there are more then 2 address lines we add them to the extended address line. + if ([addressLines count] > 1) + { + [addressLines removeObjectAtIndex: 0]; + [element setSingleValue: [addressLines componentsJoinedByString: @" "] + atIndex: 1 forKey: @""]; + } + } + else + { + [element setSingleValue: @"" + atIndex: 1 forKey: @""]; + [element setSingleValue: @"" + atIndex: 2 forKey: @""]; + } + [element setSingleValue: [theValues objectForKey: @"HomeCity"] atIndex: 3 forKey: @""]; [element setSingleValue: [theValues objectForKey: @"HomeState"] diff --git a/NEWS b/NEWS index 8366f624e..04e05c81f 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,7 @@ Bug fixes - we now return all cards when we receive an empty addressbook-query REPORT - avoid crash when replying to a mail with no recipients (#3359) - inline images sent from SOGo webmail are not displayed in Mozilla Thunderbird (#3271) + - prevent postal address showing on single line over EAS (#2614) 2.3.2 (2015-09-16) ------------------ From b8d77259cdb17c43bac8c7790dd7f9f36e9c58e8 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Mon, 26 Oct 2015 10:15:35 -0400 Subject: [PATCH 17/69] (feat) we now save the supported elements in EAS collections --- ActiveSync/SOGoActiveSyncDispatcher+Sync.m | 29 +++++++++++++++++++++- ActiveSync/SOGoActiveSyncDispatcher.m | 3 +++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m index 9f617d51d..836d9ee79 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m +++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m @@ -192,6 +192,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [[o properties] removeObjectForKey: @"DateCache"]; [[o properties] removeObjectForKey: @"MoreAvailable"]; [[o properties] removeObjectForKey: @"BodyPreferenceType"]; + [[o properties] removeObjectForKey: @"SupportedElements"]; [[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"]; [[o properties] removeObjectForKey: @"InitialLoadSequence"]; @@ -1399,11 +1400,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. { NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *mimeSupport, *lastServerKey, *syncKeyInCache, *folderKey; NSMutableDictionary *folderMetadata, *folderOptions; + NSMutableArray *supportedElements, *supportedElementNames; NSMutableString *changeBuffer, *commandsBuffer; id collection, value; SOGoMicrosoftActiveSyncFolderType folderType; - unsigned int windowSize, v, status; + unsigned int windowSize, v, status, i; BOOL getChanges, first_sync; changeBuffer = [NSMutableString string]; @@ -1466,6 +1468,30 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. davCollectionTag = @"-1"; first_sync = YES; *changeDetected = YES; + + supportedElementNames = [[[NSMutableArray alloc] init] autorelease]; + value = [theDocumentElement getElementsByTagName: @"Supported"]; + + if ([value count]) + { + supportedElements = (id)[[value lastObject] childNodes]; + + if ([supportedElements count]) + { + for (i = 0; i < [supportedElements count]; i++) + { + if ([[supportedElements objectAtIndex: i] nodeType] == DOM_ELEMENT_NODE) + [supportedElementNames addObject: [[supportedElements objectAtIndex: i] tagName]]; + } + } + + [folderMetadata setObject: supportedElementNames forKey: @"SupportedElements"]; + + [self _setFolderMetadata: folderMetadata forKey: folderKey]; + + if (debugOn) + [self logWithFormat: @"EAS - %d %@: supportedElements saved: %@", [supportedElements count], [collection nameInContainer], supportedElementNames]; + } } else if ((![syncKey isEqualToString: @"-1"]) && !([folderMetadata objectForKey: @"SyncCache"])) { @@ -1526,6 +1552,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"]; [context setObject: mimeSupport forKey: @"MIMESupport"]; + [context setObject: [folderMetadata objectForKey: @"SupportedElements"] forKey: @"SupportedElements"]; // // We process the commands from the request diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 31b7cb300..daa1999da 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -953,6 +953,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [[o properties] removeObjectForKey: @"DateCache"]; [[o properties] removeObjectForKey: @"MoreAvailable"]; [[o properties] removeObjectForKey: @"BodyPreferenceType"]; + [[o properties] removeObjectForKey: @"SupportedElements"]; [[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"]; [[o properties] removeObjectForKey: @"InitialLoadSequence"]; [o save]; @@ -1041,6 +1042,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [[o properties] removeObjectForKey: @"DateCache"]; [[o properties] removeObjectForKey: @"MoreAvailable"]; [[o properties] removeObjectForKey: @"BodyPreferenceType"]; + [[o properties] removeObjectForKey: @"SupportedElements"]; [[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"]; [[o properties] removeObjectForKey: @"InitialLoadSequence"]; } @@ -1065,6 +1067,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [[o properties] removeObjectForKey: @"DateCache"]; [[o properties] removeObjectForKey: @"MoreAvailable"]; [[o properties] removeObjectForKey: @"BodyPreferenceType"]; + [[o properties] removeObjectForKey: @"SupportedElements"]; [[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"]; [[o properties] removeObjectForKey: @"InitialLoadSequence"]; } From 1013c473cd72762b6587281d069245ef8daeec16 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Mon, 26 Oct 2015 10:25:11 -0400 Subject: [PATCH 18/69] (feat) support for ghosted elements on contacts over EAS --- ActiveSync/NGVCard+ActiveSync.m | 174 +++++++++++++++++++++++--------- NEWS | 1 + 2 files changed, 125 insertions(+), 50 deletions(-) diff --git a/ActiveSync/NGVCard+ActiveSync.m b/ActiveSync/NGVCard+ActiveSync.m index fec64524d..45fb93142 100644 --- a/ActiveSync/NGVCard+ActiveSync.m +++ b/ActiveSync/NGVCard+ActiveSync.m @@ -48,6 +48,37 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. @implementation NGVCard (ActiveSync) +// +// This function is called for each elements which can be ghosted according to specs. +// https://msdn.microsoft.com/en-us/library/gg650908%28v=exchg.80%29.aspx +// +- (BOOL) _isGhosted: (NSString *) element + inContext: (WOContext *) context +{ + NSArray *supportedElements; + + supportedElements = [context objectForKey: @"SupportedElements"]; + + // If the client does not include a Supported element in the initial Sync command request for + // a folder, then all of the elements that can be ghosted are considered not ghosted. + if (!supportedElements) + return NO; + + // If the client includes an empty Supported element in the initial Sync command request for + // a folder, then all elements that can be ghosted are considered ghosted. + if (![supportedElements count]) + return YES; + + // If the client includes a Supported element that contains child elements in the initial + // Sync command request for a folder, then each child element of that Supported element is + // considered not ghosted. All elements that can be ghosted that are not included as child + // elements of the Supported element are considered ghosted. + if (!([supportedElements indexOfObject: element] == NSNotFound)) + return YES; + + return NO; +} + - (NSString *) activeSyncRepresentationInContext: (WOContext *) context { NSArray *emails, *addresses, *categories, *elements; @@ -239,6 +270,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Categories if ((o = [theValues objectForKey: @"Categories"]) && [o length]) [self setCategories: o]; + else + [[self children] removeObjectsInArray: [self childrenWithTag: @"Categories"]]; // Birthday if ((o = [theValues objectForKey: @"Birthday"])) @@ -246,6 +279,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. o = [o calendarDate]; [self setBday: [o descriptionWithCalendarFormat: @"%Y-%m-%d" timeZone: nil locale: nil]]; } + else if (![self _isGhosted: @"Birthday" inContext: context]) + { + [self setBday: @""]; + } + // // Business address information @@ -258,13 +296,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // element = [self elementWithTag: @"adr" ofType: @"work"]; - if ((o = [theValues objectForKey: @"BusinessStreet"])) + if ((o = [theValues objectForKey: @"BusinessStreet"]) || ![self _isGhosted: @"BusinessStreet" inContext: context]) { addressLines = [NSMutableArray arrayWithArray: [o componentsSeparatedByString: @"\n"]]; [element setSingleValue: @"" atIndex: 1 forKey: @""]; - [element setSingleValue: [addressLines objectAtIndex: 0] + [element setSingleValue: [addressLines count] ? [addressLines objectAtIndex: 0] : @"" atIndex: 2 forKey: @""]; // Extended address line. If there are more than 2 address lines we add them to the extended address line. @@ -275,23 +313,30 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. atIndex: 1 forKey: @""]; } } - else + + if ((o = [theValues objectForKey: @"BusinessCity"]) || ![self _isGhosted: @"BusinessCity" inContext: context]) { - [element setSingleValue: @"" - atIndex: 1 forKey: @""]; - [element setSingleValue: @"" - atIndex: 2 forKey: @""]; + [element setSingleValue: [theValues objectForKey: @"BusinessCity"] + atIndex: 3 forKey: @""]; } + if ((o = [theValues objectForKey: @"BusinessState"]) || ![self _isGhosted: @"BusinessState" inContext: context]) + { + [element setSingleValue: [theValues objectForKey: @"BusinessState"] + atIndex: 4 forKey: @""]; + } - [element setSingleValue: [theValues objectForKey: @"BusinessCity"] - atIndex: 3 forKey: @""]; - [element setSingleValue: [theValues objectForKey: @"BusinessState"] - atIndex: 4 forKey: @""]; - [element setSingleValue: [theValues objectForKey: @"BusinessPostalCode"] - atIndex: 5 forKey: @""]; - [element setSingleValue: [theValues objectForKey: @"BusinessCountry"] - atIndex: 6 forKey: @""]; + if ((o = [theValues objectForKey: @"BusinessPostalCode"]) || ![self _isGhosted: @"BusinessPostalCode" inContext: context]) + { + [element setSingleValue: [theValues objectForKey: @"BusinessPostalCode"] + atIndex: 5 forKey: @""]; + } + + if ((o = [theValues objectForKey: @"BusinessCountry"]) || ![self _isGhosted: @"BusinessCountry" inContext: context]) + { + [element setSingleValue: [theValues objectForKey: @"BusinessCountry"] + atIndex: 6 forKey: @""]; + } // // Home address information @@ -304,13 +349,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // element = [self elementWithTag: @"adr" ofType: @"home"]; - if ((o = [theValues objectForKey: @"HomeStreet"])) + if ((o = [theValues objectForKey: @"HomeStreet"]) || ![self _isGhosted: @"HomeStreet" inContext: context]) { addressLines = [NSMutableArray arrayWithArray: [o componentsSeparatedByString: @"\n"]]; [element setSingleValue: @"" atIndex: 1 forKey: @""]; - [element setSingleValue: [addressLines objectAtIndex: 0] + [element setSingleValue: [addressLines count] ? [addressLines objectAtIndex: 0] : @"" atIndex: 2 forKey: @""]; // Extended address line. If there are more then 2 address lines we add them to the extended address line. @@ -321,39 +366,51 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. atIndex: 1 forKey: @""]; } } - else + + if ((o = [theValues objectForKey: @"HomeCity"]) || ![self _isGhosted: @"HomeCity" inContext: context]) { - [element setSingleValue: @"" - atIndex: 1 forKey: @""]; - [element setSingleValue: @"" - atIndex: 2 forKey: @""]; + [element setSingleValue: [theValues objectForKey: @"HomeCity"] + atIndex: 3 forKey: @""]; } - [element setSingleValue: [theValues objectForKey: @"HomeCity"] - atIndex: 3 forKey: @""]; - [element setSingleValue: [theValues objectForKey: @"HomeState"] - atIndex: 4 forKey: @""]; - [element setSingleValue: [theValues objectForKey: @"HomePostalCode"] - atIndex: 5 forKey: @""]; - [element setSingleValue: [theValues objectForKey: @"HomeCountry"] - atIndex: 6 forKey: @""]; + if ((o = [theValues objectForKey: @"HomeState"]) || ![self _isGhosted: @"HomeState" inContext: context]) + { + [element setSingleValue: [theValues objectForKey: @"HomeState"] + atIndex: 4 forKey: @""]; + } + + if ((o = [theValues objectForKey: @"HomePostalCode"]) || ![self _isGhosted: @"HomePostalCode" inContext: context]) + { + [element setSingleValue: [theValues objectForKey: @"HomePostalCode"] + atIndex: 5 forKey: @""]; + } + + if ((o = [theValues objectForKey: @"HomeCountry"]) || ![self _isGhosted: @"HomeCountry" inContext: context]) + { + [element setSingleValue: [theValues objectForKey: @"HomeCountry"] + atIndex: 6 forKey: @""]; + } // Company's name if ((o = [theValues objectForKey: @"CompanyName"])) [self setOrg: o units: nil]; + else if (![self _isGhosted: @"CompanyName" inContext: context]) + [self setOrg: @"" units: nil]; // Department if ((o = [theValues objectForKey: @"Department"])) [self setOrg: nil units: [NSArray arrayWithObjects:o,nil]]; + else if (![self _isGhosted: @"Department" inContext: context]) + [self setOrg: nil units: [NSArray arrayWithObjects:@"",nil]]; // Email addresses - if ((o = [theValues objectForKey: @"Email1Address"])) + if ((o = [theValues objectForKey: @"Email1Address"]) || ![self _isGhosted: @"Email1Address" inContext: context]) { element = [self elementWithTag: @"email" ofType: @"work"]; [element setSingleValue: [o pureEMailAddress] forKey: @""]; } - if ((o = [theValues objectForKey: @"Email2Address"])) + if ((o = [theValues objectForKey: @"Email2Address"]) || ![self _isGhosted: @"Email2Address" inContext: context]) { element = [self elementWithTag: @"email" ofType: @"home"]; [element setSingleValue: [o pureEMailAddress] forKey: @""]; @@ -361,7 +418,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOGo currently only supports 2 email addresses ... but AS clients might send 3 // FIXME: revise this when the GUI revamp is done in SOGo - if ((o = [theValues objectForKey: @"Email3Address"])) + if ((o = [theValues objectForKey: @"Email3Address"]) || ![self _isGhosted: @"Email3Address" inContext: context]) { element = [self elementWithTag: @"email" ofType: @"three"]; [element setSingleValue: [o pureEMailAddress] forKey: @""]; @@ -371,45 +428,62 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // MiddleName // Suffix (II) // Title (Mr.) - [self setFn: [theValues objectForKey: @"FileAs"]]; + if ((o = [theValues objectForKey: @"FileAs"]) || ![self _isGhosted: @"FileAs" inContext: context]) + [self setFn: [theValues objectForKey: @"FileAs"]]; [self setNWithFamily: [theValues objectForKey: @"LastName"] given: [theValues objectForKey: @"FirstName"] additional: nil prefixes: nil suffixes: nil]; // IM information - [[self uniqueChildWithTag: @"x-aim"] - setSingleValue: [theValues objectForKey: @"IMAddress"] - forKey: @""]; + if ((o = [theValues objectForKey: @"IMAddress"]) || ![self _isGhosted: @"IMAddress" inContext: context]) + [[self uniqueChildWithTag: @"x-aim"] + setSingleValue: [theValues objectForKey: @"IMAddress"] + forKey: @""]; // // Phone numbrrs // - element = [self elementWithTag: @"tel" ofType: @"work"]; - [element setSingleValue: [theValues objectForKey: @"BusinessPhoneNumber"] forKey: @""]; + if ((o = [theValues objectForKey: @"BusinessPhoneNumber"]) || ![self _isGhosted: @"BusinessPhoneNumber" inContext: context]) + { + element = [self elementWithTag: @"tel" ofType: @"work"]; + [element setSingleValue: [theValues objectForKey: @"BusinessPhoneNumber"] forKey: @""]; + } - element = [self elementWithTag: @"tel" ofType: @"home"]; - [element setSingleValue: [theValues objectForKey: @"HomePhoneNumber"] forKey: @""]; + if ((o = [theValues objectForKey: @"HomePhoneNumber"]) || ![self _isGhosted: @"HomePhoneNumber" inContext: context]) + { + element = [self elementWithTag: @"tel" ofType: @"home"]; + [element setSingleValue: [theValues objectForKey: @"HomePhoneNumber"] forKey: @""]; + } - element = [self elementWithTag: @"tel" ofType: @"cell"]; - [element setSingleValue: [theValues objectForKey: @"MobilePhoneNumber"] forKey: @""]; + if ((o = [theValues objectForKey: @"MobilePhoneNumber"]) || ![self _isGhosted: @"MobilePhoneNumber" inContext: context]) + { + element = [self elementWithTag: @"tel" ofType: @"cell"]; + [element setSingleValue: [theValues objectForKey: @"MobilePhoneNumber"] forKey: @""]; + } - element = [self elementWithTag: @"tel" ofType: @"fax"]; - [element setSingleValue: [theValues objectForKey: @"BusinessFaxNumber"] forKey: @""]; + if ((o = [theValues objectForKey: @"BusinessFaxNumber"]) || ![self _isGhosted: @"BusinessFaxNumber" inContext: context]) + { + element = [self elementWithTag: @"tel" ofType: @"fax"]; + [element setSingleValue: [theValues objectForKey: @"BusinessFaxNumber"] forKey: @""]; + } - element = [self elementWithTag: @"tel" ofType: @"pager"]; - [element setSingleValue: [theValues objectForKey: @"PagerNumber"] forKey: @""]; + if ((o = [theValues objectForKey: @"PagerNumber"]) || ![self _isGhosted: @"PagerNumber" inContext: context]) + { + element = [self elementWithTag: @"tel" ofType: @"pager"]; + [element setSingleValue: [theValues objectForKey: @"PagerNumber"] forKey: @""]; + } // Job's title - if ((o = [theValues objectForKey: @"JobTitle"])) + if ((o = [theValues objectForKey: @"JobTitle"]) || ![self _isGhosted: @"JobTitle" inContext: context]) [self setTitle: o]; // WebPage (work) - if ((o = [theValues objectForKey: @"WebPage"])) + if ((o = [theValues objectForKey: @"WebPage"]) || ![self _isGhosted: @"WebPage" inContext: context]) [[self elementWithTag: @"url" ofType: @"work"] setSingleValue: o forKey: @""]; - if ((o = [theValues objectForKey: @"NickName"])) + if ((o = [theValues objectForKey: @"NickName"]) || ![self _isGhosted: @"NickName" inContext: context]) [self setNickname: o]; if ((o = [theValues objectForKey: @"Picture"])) diff --git a/NEWS b/NEWS index 04e05c81f..a1af989d7 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ New features Enhancements - we no longer always entirely rewrite messages for Outlook 2013 when using EAS + - support for ghosted elements on contacts over EAS Bug fixes - numerous EAS fixes when connections are dropped before the EAS client receives the response (#3058, #2849) From b62167dd00851661c9d43b87cc6d66cde7f87f2e Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Tue, 3 Nov 2015 08:27:50 -0500 Subject: [PATCH 19/69] Fix Brazilian (Portuguese) translation Fixes #3355 --- UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings b/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings index 28f1412cf..6e39affc0 100644 --- a/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings +++ b/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings @@ -203,7 +203,7 @@ /* Event+task categories */ "category_none" = "Nenhum"; -"calendar_category_labels" = "Comemoração,Aniversário,Negócios,Ligações,Concorrência,Cliente,Favoritos,Acompanhamento,Presentes,Feriados,Idéias,Meeting,Problemas,Miscelânea,Pessoal,Projetos,Feriado público,Posição,Fornecedores,Viagem,Férias"; +"calendar_category_labels" = "Celebração Anual,Aniversário,Negócios,Ligações,Clientes,Concorrência,Comprador,Favoritos,Acompanhamento,Presentes,Feriados,Idéias,Reunião,Problemas,Miscelânea,Pessoal,Projetos,Feriado,Posição,Fornecedores,Viagem,Férias"; /* Default module */ "Calendar" = "Calendário"; From 6812cb08dcbf08bebfb32fcb34b58b351243337f Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Tue, 3 Nov 2015 08:57:51 -0500 Subject: [PATCH 20/69] Display missing events when printing working hours --- NEWS | 1 + UI/WebServerResources/UIxCalViewPrint.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index a1af989d7..f232dc16b 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,7 @@ Bug fixes - avoid crash when replying to a mail with no recipients (#3359) - inline images sent from SOGo webmail are not displayed in Mozilla Thunderbird (#3271) - prevent postal address showing on single line over EAS (#2614) + - display missing events when printing working hours only 2.3.2 (2015-09-16) ------------------ diff --git a/UI/WebServerResources/UIxCalViewPrint.js b/UI/WebServerResources/UIxCalViewPrint.js index e26396c58..a90fd8803 100644 --- a/UI/WebServerResources/UIxCalViewPrint.js +++ b/UI/WebServerResources/UIxCalViewPrint.js @@ -308,7 +308,7 @@ function _drawCalendarEvents(events, eventsData, columnsData) { if (printHoursCheckBox.checked) { var offset = _computeOffset(parentDiv); - if ((eventRep.start - offset[0]) > 0 && (eventRep.start - offset[0]) < offset[1]) { + if ((eventRep.start - offset[0]) >= 0 && (eventRep.start - offset[0]) <= offset[1]) { var eventCell = newEventDIV(eventRep, calendarEventsData[nbr], offset[0]); var eventInside = eventCell.down(".eventInside"); addColorsOnEvents(eventInside, eventCell); @@ -338,7 +338,7 @@ function _drawCalendarEvents(events, eventsData, columnsData) { var nbr = eventRep.nbr; if (printHoursCheckBox.checked) { var offset = _computeOffset(parentDiv); - if ((eventRep.start - offset[0]) > 0 && (eventRep.start - offset[0]) < offset[1]) { + if ((eventRep.start - offset[0]) >= 0 && (eventRep.start - offset[0]) <= offset[1]) { var eventCell = newEventDIV(eventRep, eventsData[nbr], offset[0]); var eventInside = eventCell.down(".eventInside"); addColorsOnEvents(eventInside, eventCell); From 892bd693d7ee901bc1c7947e0785ff0787fc112a Mon Sep 17 00:00:00 2001 From: Euan Thoms Date: Wed, 4 Nov 2015 03:59:40 +0800 Subject: [PATCH 21/69] Stage 3 of clang compiler warning patches. Conflicts: UI/PreferencesUI/UIxJSONPreferences.m --- SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m | 1 + SoObjects/Mailer/NSDictionary+Mail.m | 1 + SoObjects/Mailer/SOGoMailBaseObject.m | 2 ++ SoObjects/SOGo/SOGoCache.h | 1 + UI/Common/WODirectAction+SOGo.m | 3 +++ UI/Contacts/UIxContactFolderActions.m | 4 ++++ UI/MailPartViewers/UIxMailPartICalActions.m | 1 + UI/MainUI/SOGoMicrosoftActiveSyncActions.m | 3 +++ UI/PreferencesUI/UIxJSONPreferences.m | 5 +++++ 9 files changed, 21 insertions(+) diff --git a/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m b/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m index c9f66a6b1..f5540c466 100644 --- a/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m +++ b/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m @@ -36,6 +36,7 @@ #import #import "iCalRepeatableEntityObject+SOGo.h" +#import "iCalCalendar+SOGo.h" @implementation iCalRepeatableEntityObject (SOGoExtensions) diff --git a/SoObjects/Mailer/NSDictionary+Mail.m b/SoObjects/Mailer/NSDictionary+Mail.m index 74311f292..177ad38ac 100644 --- a/SoObjects/Mailer/NSDictionary+Mail.m +++ b/SoObjects/Mailer/NSDictionary+Mail.m @@ -20,6 +20,7 @@ #import #import +#import #import "NSDictionary+Mail.h" diff --git a/SoObjects/Mailer/SOGoMailBaseObject.m b/SoObjects/Mailer/SOGoMailBaseObject.m index 863491e2e..4948abe7e 100644 --- a/SoObjects/Mailer/SOGoMailBaseObject.m +++ b/SoObjects/Mailer/SOGoMailBaseObject.m @@ -32,9 +32,11 @@ #import #import #import +#import #import #import +#import #import "SOGoMailAccount.h" #import "SOGoMailManager.h" diff --git a/SoObjects/SOGo/SOGoCache.h b/SoObjects/SOGo/SOGoCache.h index 69ef6f942..12e665ea7 100644 --- a/SoObjects/SOGo/SOGoCache.h +++ b/SoObjects/SOGo/SOGoCache.h @@ -21,6 +21,7 @@ #ifndef SOGOCACHE_H #define SOGOCACHE_H +#import #import #include diff --git a/UI/Common/WODirectAction+SOGo.m b/UI/Common/WODirectAction+SOGo.m index 805acbe8c..fada54f17 100644 --- a/UI/Common/WODirectAction+SOGo.m +++ b/UI/Common/WODirectAction+SOGo.m @@ -22,6 +22,7 @@ #import #import +#import #import #import @@ -33,6 +34,8 @@ #import #import +#import + #import "WODirectAction+SOGo.h" @implementation WODirectAction (SOGoExtension) diff --git a/UI/Contacts/UIxContactFolderActions.m b/UI/Contacts/UIxContactFolderActions.m index 974e7d5d0..6a9fb29fd 100644 --- a/UI/Contacts/UIxContactFolderActions.m +++ b/UI/Contacts/UIxContactFolderActions.m @@ -28,10 +28,14 @@ #import #import #import +#define COMPILING_NGOBJWEB 1 /* we want httpRequest for parsing multi-part + form data */ #import +#undef COMPILING_NGOBJWEB #import #import #import +#import #import #import diff --git a/UI/MailPartViewers/UIxMailPartICalActions.m b/UI/MailPartViewers/UIxMailPartICalActions.m index 567412818..9f233515c 100644 --- a/UI/MailPartViewers/UIxMailPartICalActions.m +++ b/UI/MailPartViewers/UIxMailPartICalActions.m @@ -46,6 +46,7 @@ #import #import #import +#import #import #import "UIxMailPartICalActions.h" diff --git a/UI/MainUI/SOGoMicrosoftActiveSyncActions.m b/UI/MainUI/SOGoMicrosoftActiveSyncActions.m index c487d5cce..1cfb8bc2f 100644 --- a/UI/MainUI/SOGoMicrosoftActiveSyncActions.m +++ b/UI/MainUI/SOGoMicrosoftActiveSyncActions.m @@ -30,6 +30,9 @@ #import #import +#import +#import + @interface SOGoMicrosoftActiveSyncActions : WODirectAction @end diff --git a/UI/PreferencesUI/UIxJSONPreferences.m b/UI/PreferencesUI/UIxJSONPreferences.m index 11bd05b36..33a97f708 100644 --- a/UI/PreferencesUI/UIxJSONPreferences.m +++ b/UI/PreferencesUI/UIxJSONPreferences.m @@ -29,6 +29,11 @@ #import #import #import +#import +#import + +#import +#import #import "UIxJSONPreferences.h" From 5b65eaa57259d415ec4d8139899f5722029d79fc Mon Sep 17 00:00:00 2001 From: Euan Thoms Date: Wed, 4 Nov 2015 04:59:27 +0800 Subject: [PATCH 22/69] Stage 4 of clang compiler warning patches. --- SoObjects/SOGo/SOGoCacheGCSFolder.h | 2 ++ SoObjects/SOGo/SOGoCacheObject.h | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SoObjects/SOGo/SOGoCacheGCSFolder.h b/SoObjects/SOGo/SOGoCacheGCSFolder.h index 3495dc0d6..c51dd598f 100644 --- a/SoObjects/SOGo/SOGoCacheGCSFolder.h +++ b/SoObjects/SOGo/SOGoCacheGCSFolder.h @@ -40,6 +40,8 @@ - (NSMutableString *) pathForChild: (NSString *) childName; +- (void) addUserInAcls: (NSString *) user; + - (NSArray *) toOneRelationshipKeys; - (NSArray *) toManyRelationshipKeys; diff --git a/SoObjects/SOGo/SOGoCacheObject.h b/SoObjects/SOGo/SOGoCacheObject.h index d8b01c468..0d0a78492 100644 --- a/SoObjects/SOGo/SOGoCacheObject.h +++ b/SoObjects/SOGo/SOGoCacheObject.h @@ -42,8 +42,6 @@ - (NSCalendarDate *) creationDate; - (NSCalendarDate *) lastModified; -- (NSException *) destroy; - @end #endif /* SOGOCACHEOBJECT_H */ From 1425bb1c7574be44e7a2f1f2f45b7133f08eb44d Mon Sep 17 00:00:00 2001 From: Euan Thoms Date: Wed, 4 Nov 2015 05:28:03 +0800 Subject: [PATCH 23/69] Stage 5 of clang compiler warning patches. --- SoObjects/Appointments/SOGoAppointmentObject.m | 16 ++++++++++------ Tools/SOGoToolRestore.m | 1 + 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index 158a53f6b..6bb1b5ee2 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -45,6 +45,7 @@ #import #import #import +#import #import #import @@ -747,11 +748,14 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent { e = [events objectAtIndex: i]; if ([e recurrenceId]) - for (j = 0; j < [theAttendees count]; j++) - if (shouldAdd) + for (j = 0; j < [theAttendees count]; j++) { + if (shouldAdd) { [e addToAttendees: [theAttendees objectAtIndex: j]]; - else + } + else { [e removeFromAttendees: [theAttendees objectAtIndex: j]]; + } + } } } @@ -1037,7 +1041,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent if ([delegateEmail length]) otherDelegate = [event findAttendeeWithEmail: delegateEmail]; else - otherDelegate = NO; + otherDelegate = nil; /* we handle the addition/deletion of delegate users */ addDelegate = NO; @@ -1075,7 +1079,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent if ([delegateEmail length]) otherDelegate = [event findAttendeeWithEmail: delegateEmail]; else - otherDelegate = NO; + otherDelegate = nil; } } if (addDelegate) @@ -1232,7 +1236,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent if ([delegateEmail length]) otherDelegate = [event findAttendeeWithEmail: delegateEmail]; else - otherDelegate = NO; + otherDelegate = nil; } [self sendEMailUsingTemplateNamed: @"Deletion" diff --git a/Tools/SOGoToolRestore.m b/Tools/SOGoToolRestore.m index 712c680ba..5f1527cc8 100644 --- a/Tools/SOGoToolRestore.m +++ b/Tools/SOGoToolRestore.m @@ -574,6 +574,7 @@ NSLog (@" %@ (%@)", folderKey, [currentFolder objectForKey: @"displayname"]); } + rc = YES; } else { From e173c6d621073e5674893ed09aa35b0d707e014a Mon Sep 17 00:00:00 2001 From: Euan Thoms Date: Wed, 4 Nov 2015 06:13:01 +0800 Subject: [PATCH 24/69] Stage 9 of clang compiler warning patches. --- SoObjects/SOGo/NSData+Crypto.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SoObjects/SOGo/NSData+Crypto.m b/SoObjects/SOGo/NSData+Crypto.m index 5e2f3640a..5c0a03a06 100644 --- a/SoObjects/SOGo/NSData+Crypto.m +++ b/SoObjects/SOGo/NSData+Crypto.m @@ -23,7 +23,7 @@ * Boston, MA 02111-1307, USA. */ -#ifndef __OpenBSD__ +#if !defined(__OpenBSD__) && !defined(__FreeBSD__) #include #endif From 34f626b84c69603e7fd4c2c8301d83309c3d0af3 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 4 Nov 2015 09:21:42 -0500 Subject: [PATCH 25/69] (fix) small fixes around web calendars + sync --- UI/Scheduler/UIxCalendarProperties.m | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/UI/Scheduler/UIxCalendarProperties.m b/UI/Scheduler/UIxCalendarProperties.m index 46c2c8048..771bf8a37 100644 --- a/UI/Scheduler/UIxCalendarProperties.m +++ b/UI/Scheduler/UIxCalendarProperties.m @@ -91,6 +91,9 @@ - (BOOL) synchronizeCalendar { + if ([self isWebCalendar]) + return NO; + return [self mustSynchronize] || [calendar synchronize]; } @@ -101,7 +104,7 @@ - (BOOL) mustSynchronize { - return [[calendar nameInContainer] isEqualToString: @"personal"]; + return ([[calendar nameInContainer] isEqualToString: @"personal"] || [self isWebCalendar]); } - (BOOL) showCalendarAlarms From d3290dbb8ddcc1dd8f3ac0c66d64b5d933d5a0f4 Mon Sep 17 00:00:00 2001 From: Euan Thoms Date: Wed, 4 Nov 2015 03:36:34 +0800 Subject: [PATCH 26/69] Stage 2 of clang compiler warning patches. --- SOPE/NGCards/iCalPerson.m | 2 +- SOPE/NGCards/iCalPerson.m.orig | 299 ++++++ SoObjects/Contacts/SOGoContactSourceFolder.m | 2 +- .../Contacts/SOGoContactSourceFolder.m.orig | 808 +++++++++++++++ SoObjects/SOGo/SOGoCacheGCSFolder.m | 2 +- SoObjects/SOGo/SOGoCacheGCSFolder.m.orig | 486 +++++++++ SoObjects/SOGo/SQLSource.m | 12 +- SoObjects/SOGo/SQLSource.m.orig | 971 ++++++++++++++++++ 8 files changed, 2573 insertions(+), 9 deletions(-) create mode 100644 SOPE/NGCards/iCalPerson.m.orig create mode 100644 SoObjects/Contacts/SOGoContactSourceFolder.m.orig create mode 100644 SoObjects/SOGo/SOGoCacheGCSFolder.m.orig create mode 100644 SoObjects/SOGo/SQLSource.m.orig diff --git a/SOPE/NGCards/iCalPerson.m b/SOPE/NGCards/iCalPerson.m index fbcc92570..f5b908a50 100644 --- a/SOPE/NGCards/iCalPerson.m +++ b/SOPE/NGCards/iCalPerson.m @@ -256,7 +256,7 @@ - (BOOL)isEqual:(id)_other { if(_other == nil) return NO; - if([_other class] != self->isa) + if([_other class] != object_getClass(self)) return NO; if([_other hash] != [self hash]) return NO; diff --git a/SOPE/NGCards/iCalPerson.m.orig b/SOPE/NGCards/iCalPerson.m.orig new file mode 100644 index 000000000..7904fd0f8 --- /dev/null +++ b/SOPE/NGCards/iCalPerson.m.orig @@ -0,0 +1,299 @@ +/* + Copyright (C) 2000-2005 SKYRIX Software AG + + This file is part of SOPE. + + SOPE is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + SOPE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with SOPE; see the file COPYING. If not, write to the + Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +#import + +#import "iCalPerson.h" + +@implementation iCalPerson + ++ (NSString *) descriptionForParticipationStatus: (iCalPersonPartStat) _status +{ + NSString *stat; + + switch (_status) { + case iCalPersonPartStatUndefined: + stat = @""; + break; + case iCalPersonPartStatAccepted: + stat = @"ACCEPTED"; + break; + case iCalPersonPartStatDeclined: + stat = @"DECLINED"; + break; + case iCalPersonPartStatTentative: + stat = @"TENTATIVE"; + break; + case iCalPersonPartStatDelegated: + stat = @"DELEGATED"; + break; + case iCalPersonPartStatCompleted: + stat = @"COMPLETED"; + break; + case iCalPersonPartStatInProcess: + stat = @"IN-PROCESS"; + break; + case iCalPersonPartStatExperimental: + case iCalPersonPartStatOther: +// [NSException raise:NSInternalInconsistencyException +// format:@"Attempt to set meaningless " +// @"participationStatus (%d)!", _status]; + stat = nil; /* keep compiler happy */ + break; + default: + stat = @"NEEDS-ACTION"; + break; + } + + return stat; +} + + +/* accessors */ + +- (void) setCn: (NSString *) _s +{ + [self setValue: 0 ofAttribute: @"cn" to: _s]; +} + +- (NSString *) cn +{ + return [self value: 0 ofAttribute: @"cn"]; +} + +- (NSString *) cnWithoutQuotes +{ + /* remove quotes around a CN */ + NSString *_cn; + + _cn = [self cn]; + if ([_cn length] <= 2) + return _cn; + if ([_cn characterAtIndex:0] != '"') + return _cn; + if (![_cn hasSuffix:@"\""]) + return _cn; + + return [_cn substringWithRange:NSMakeRange(1, [_cn length] - 2)]; +} + +- (void) setEmail: (NSString *)_s +{ + /* iCal.app compatibility: + - "mailto" prefix must be in lowercase; */ + [self setSingleValue: [NSString stringWithFormat: @"mailto:%@", _s] + forKey: @""]; +} + +- (NSString *) email +{ + return [self flattenedValuesForKey: @""]; +} + +- (NSString *) rfc822Email +{ + NSString *_email; + unsigned idx; + + _email = [self email]; + idx = NSMaxRange([_email rangeOfString:@":"]); + + if ((idx > 0) && ([_email length] > idx)) + return [_email substringFromIndex:idx]; + + return _email; +} + +- (void) setRsvp: (NSString *) _s +{ + [self setValue: 0 ofAttribute: @"rsvp" to: _s]; +} + +- (NSString *) rsvp +{ + return [[self value: 0 ofAttribute: @"rsvp"] lowercaseString]; +} + +// - (void)setXuid:(NSString *)_s { +// ASSIGNCOPY(self->xuid, _s); +// } +// - (NSString *)xuid { +// return self->xuid; +// } + +- (void)setRole:(NSString *)_s +{ + [self setValue: 0 ofAttribute: @"role" to: _s]; +} + +- (NSString *) role +{ + return [self value: 0 ofAttribute: @"role"]; +} + +- (void)setPartStat:(NSString *)_s +{ + [self setValue: 0 ofAttribute: @"partstat" to: _s]; +} + +- (NSString *) partStat +{ + return [self value: 0 ofAttribute: @"partstat"]; +} + +- (NSString *) partStatWithDefault +{ + NSString *s; + + s = [self partStat]; + if ([s length] > 0) + return s; + + return @"NEEDS-ACTION"; +} + +- (void) setParticipationStatus: (iCalPersonPartStat) _status +{ + NSString *stat; + + stat = [iCalPerson descriptionForParticipationStatus: _status]; + + if (stat) + [self setPartStat:stat]; +} + +- (iCalPersonPartStat) participationStatus { + NSString *stat; + + stat = [[self partStat] uppercaseString]; + if (![stat length]) + return iCalPersonPartStatUndefined; + else if ([stat isEqualToString:@"NEEDS-ACTION"]) + return iCalPersonPartStatNeedsAction; + else if ([stat isEqualToString:@"ACCEPTED"]) + return iCalPersonPartStatAccepted; + else if ([stat isEqualToString:@"DECLINED"]) + return iCalPersonPartStatDeclined; + else if ([stat isEqualToString:@"TENTATIVE"]) + return iCalPersonPartStatTentative; + else if ([stat isEqualToString:@"DELEGATED"]) + return iCalPersonPartStatDelegated; + else if ([stat isEqualToString:@"COMPLETED"]) + return iCalPersonPartStatCompleted; + else if ([stat isEqualToString:@"IN-PROCESS"]) + return iCalPersonPartStatInProcess; + else if ([stat hasPrefix:@"X-"]) + return iCalPersonPartStatExperimental; + return iCalPersonPartStatOther; +} + +- (void) _setValueOfMailtoAttribute: (NSString *) name + to: (NSString *) value +{ + if ([value length] && ![value hasPrefix: @"\""]) + value = [NSString stringWithFormat: @"\"%@\"", value]; + + [self setValue: 0 ofAttribute: name to: value]; +} + +- (NSString *) _valueOfMailtoAttribute: (NSString *) name +{ + NSString *mailTo; + + mailTo = [self value: 0 ofAttribute: name]; + if ([mailTo hasPrefix: @"\""]) + mailTo + = [mailTo substringWithRange: NSMakeRange (1, [mailTo length] - 2)]; + + return mailTo; +} + +- (void) setDelegatedTo: (NSString *) newDelegate +{ + [self _setValueOfMailtoAttribute: @"delegated-to" to: newDelegate]; +} + +- (NSString *) delegatedTo +{ + return [self _valueOfMailtoAttribute: @"delegated-to"]; +} + +- (void) setDelegatedFrom: (NSString *) newDelegator +{ + [self _setValueOfMailtoAttribute: @"delegated-from" to: newDelegator]; +} + +- (NSString *) delegatedFrom +{ + return [self _valueOfMailtoAttribute: @"delegated-from"]; +} + +- (void) setSentBy: (NSString *) newSentBy +{ + [self _setValueOfMailtoAttribute: @"sent-by" to: newSentBy]; +} + +- (NSString *) sentBy +{ + return [self _valueOfMailtoAttribute: @"sent-by"]; +} + +/* comparison */ + +- (NSUInteger) hash { + if ([self email]) + return [[self email] hash]; + return [super hash]; +} + +- (BOOL)isEqual:(id)_other { + if(_other == nil) + return NO; + if([_other class] != self->isa) + return NO; + if([_other hash] != [self hash]) + return NO; + return [self isEqualToPerson:_other]; +} + +- (BOOL)isEqualToPerson:(iCalPerson *)_other { + if(![self hasSameEmailAddress:_other]) + return NO; + if(!IS_EQUAL([self cn], [_other cn], isEqualToString:)) + return NO; + if(!IS_EQUAL([self rsvp], [_other rsvp], isEqualToString:)) + return NO; + if(!IS_EQUAL([self partStat], [_other partStat], isEqualToString:)) + return NO; + if(!IS_EQUAL([self role], [_other role], isEqualToString:)) + return NO; +// if(!IS_EQUAL([self xuid], [_other xuid], isEqualToString:)) +// return NO; + return YES; +} + +- (BOOL)hasSameEmailAddress:(iCalPerson *)_other { + return IS_EQUAL([[self email] lowercaseString], + [[_other email] lowercaseString], + isEqualToString:); +} + +@end /* iCalPerson */ diff --git a/SoObjects/Contacts/SOGoContactSourceFolder.m b/SoObjects/Contacts/SOGoContactSourceFolder.m index e385d9c09..4de3d9926 100644 --- a/SoObjects/Contacts/SOGoContactSourceFolder.m +++ b/SoObjects/Contacts/SOGoContactSourceFolder.m @@ -657,7 +657,7 @@ BOOL otherIsPersonal; otherIsPersonal = ([otherFolder isKindOfClass: [SOGoContactGCSFolder class]] - || ([otherFolder isKindOfClass: isa] && [otherFolder isPersonalSource])); + || ([otherFolder isKindOfClass: object_getClass(self)] && [otherFolder isPersonalSource])); if (isPersonalSource) { diff --git a/SoObjects/Contacts/SOGoContactSourceFolder.m.orig b/SoObjects/Contacts/SOGoContactSourceFolder.m.orig new file mode 100644 index 000000000..a52ec20b9 --- /dev/null +++ b/SoObjects/Contacts/SOGoContactSourceFolder.m.orig @@ -0,0 +1,808 @@ +/* SOGoContactSourceFolder.m - this file is part of SOGo + * + * Copyright (C) 2006-2015 Inverse inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import +#import +#import +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import "SOGoContactFolders.h" +#import "SOGoContactGCSFolder.h" +#import "SOGoContactLDIFEntry.h" +#import "SOGoContactSourceFolder.h" + +@class WOContext; + +@implementation SOGoContactSourceFolder + ++ (id) folderWithName: (NSString *) aName + andDisplayName: (NSString *) aDisplayName + inContainer: (id) aContainer +{ + id folder; + + folder = [[self alloc] initWithName: aName + andDisplayName: aDisplayName + inContainer: aContainer]; + [folder autorelease]; + + return folder; +} + +- (id) init +{ + if ((self = [super init])) + { + childRecords = [NSMutableDictionary new]; + source = nil; + } + + return self; +} + +- (id) initWithName: (NSString *) newName + andDisplayName: (NSString *) newDisplayName + inContainer: (id) newContainer +{ + if ((self = [self initWithName: newName + inContainer: newContainer])) + { + if (![newDisplayName length]) + newDisplayName = newName; + ASSIGN (displayName, newDisplayName); + } + + return self; +} + +- (void) dealloc +{ + [childRecords release]; + [source release]; + [super dealloc]; +} + +- (void) setSource: (id ) newSource +{ + ASSIGN (source, newSource); +} + +- (id ) source +{ + return source; +} + +- (void) setIsPersonalSource: (BOOL) isPersonal +{ + isPersonalSource = isPersonal; +} + +- (BOOL) isPersonalSource +{ + return isPersonalSource; +} + +- (NSString *) groupDavResourceType +{ + return @"vcard-collection"; +} + +- (NSArray *) davResourceType +{ + NSMutableArray *resourceType; + NSArray *type; + + resourceType = [NSMutableArray arrayWithArray: [super davResourceType]]; + type = [NSArray arrayWithObjects: @"addressbook", XMLNS_CARDDAV, nil]; + [resourceType addObject: type]; + type = [NSArray arrayWithObjects: @"directory", XMLNS_CARDDAV, nil]; + [resourceType addObject: type]; + + return resourceType; +} + +- (id) lookupName: (NSString *) objectName + inContext: (WOContext *) lookupContext + acquire: (BOOL) acquire +{ + NSDictionary *ldifEntry; + SOGoContactLDIFEntry *obj; + NSString *url; + BOOL isNew = NO; + NSArray *baseClasses; + + /* first check attributes directly bound to the application */ + obj = [super lookupName: objectName inContext: lookupContext acquire: NO]; + + if (!obj) + { + ldifEntry = [childRecords objectForKey: objectName]; + if (!ldifEntry) + { + ldifEntry = [source lookupContactEntry: objectName]; + if (ldifEntry) + [childRecords setObject: ldifEntry forKey: objectName]; + else if ([self isValidContentName: objectName]) + { + url = [[[lookupContext request] uri] urlWithoutParameters]; + if ([url hasSuffix: @"AsContact"]) + { + baseClasses = [NSArray arrayWithObjects: @"inetorgperson", + @"mozillaabpersonalpha", nil]; + ldifEntry = [NSMutableDictionary + dictionaryWithObject: baseClasses + forKey: @"objectclass"]; + isNew = YES; + } + } + } + if (ldifEntry) + { + obj = [SOGoContactLDIFEntry contactEntryWithName: objectName + withLDIFEntry: ldifEntry + inContainer: self]; + if (isNew) + [obj setIsNew: YES]; + } + else + obj = [NSException exceptionWithHTTPStatus: 404]; + } + + return obj; +} + +- (NSArray *) toOneRelationshipKeys +{ + NSString *userDomain; + + userDomain = [[context activeUser] domain]; + return [source allEntryIDsVisibleFromDomain: userDomain]; +} + +- (NSException *) saveLDIFEntry: (SOGoContactLDIFEntry *) ldifEntry +{ + return (([ldifEntry isNew]) + ? [source addContactEntry: [ldifEntry ldifRecord] + withID: [ldifEntry nameInContainer]] + : [source updateContactEntry: [ldifEntry ldifRecord]]); +} + +- (NSException *) deleteLDIFEntry: (SOGoContactLDIFEntry *) ldifEntry +{ + return [source removeContactEntryWithID: [ldifEntry nameInContainer]]; +} + +/** + * Normalize keys of dictionary representing a contact. + * @param oldRecord a dictionary with pairs from the source folder (LDAP or SQL) + * @see [SOGoContactGCSFolder _fixupContactRecord] + */ +- (NSDictionary *) _flattenedRecord: (NSDictionary *) oldRecord +{ + NSMutableDictionary *newRecord; + id data; + NSObject *recordSource; + + newRecord = [NSMutableDictionary dictionaryWithCapacity: 8]; + [newRecord setObject: [oldRecord objectForKey: @"c_uid"] + forKey: @"c_uid"]; + + // c_name => id + [newRecord setObject: [oldRecord objectForKey: @"c_name"] + forKey: @"c_name"]; + [newRecord setObject: [oldRecord objectForKey: @"c_name"] + forKey: @"id"]; + + // displayname || c_cn => fn + data = [oldRecord objectForKey: @"displayname"]; + if (!data) + data = [oldRecord objectForKey: @"c_cn"]; + if (data) + [newRecord setObject: data forKey: @"fn"]; + else + data = @""; + [newRecord setObject: data forKey: @"c_cn"]; + + // mail => emails[] + data = [oldRecord objectForKey: @"c_emails"]; + if (data) + { + if ([data isKindOfClass: [NSArray class]]) + { + if ([data count] > 0) + { + NSEnumerator *emails; + NSMutableArray *recordEmails; + NSString *email; + emails = [(NSArray *)data objectEnumerator]; + recordEmails = [NSMutableArray arrayWithCapacity: [data count]]; + while ((email = [emails nextObject])) + { + [recordEmails addObject: [NSDictionary dictionaryWithObject: email forKey: @"value"]]; + } + [newRecord setObject: recordEmails forKey: @"emails"]; + } + } + else if (data) + { + NSDictionary *email; + email = [NSDictionary dictionaryWithObjectsAndKeys: @"pref", @"type", data, @"value", nil]; + [newRecord setObject: [NSArray arrayWithObject: email] forKey: @"emails"]; + } + else + data = @""; + } + else + data = @""; + [newRecord setObject: data forKey: @"c_mail"]; + + data = [oldRecord objectForKey: @"nsaimid"]; + if (![data length]) + data = [oldRecord objectForKey: @"nscpaimscreenname"]; + if (![data length]) + data = @""; + [newRecord setObject: data forKey: @"c_screenname"]; + + // o => org + data = [oldRecord objectForKey: @"o"]; + if (data) + [newRecord setObject: data forKey: @"org"]; + else + data = @""; + [newRecord setObject: data forKey: @"c_o"]; + + // telephonenumber || cellphone || homephone => phones[] + data = [oldRecord objectForKey: @"telephonenumber"]; + if (![data length]) + data = [oldRecord objectForKey: @"cellphone"]; + if (![data length]) + data = [oldRecord objectForKey: @"homephone"]; + if (data) + { + NSDictionary *phonenumber; + phonenumber = [NSDictionary dictionaryWithObjectsAndKeys: @"pref", @"type", data, @"value", nil]; + [newRecord setObject: [NSArray arrayWithObject: phonenumber] forKey: @"phones"]; + } + else + data = @""; + [newRecord setObject: data forKey: @"c_telephonenumber"]; + + // Custom attribute for group-lookups. See LDAPSource.m where + // it's set. + data = [oldRecord objectForKey: @"isGroup"]; + if (data) + { + [newRecord setObject: data forKey: @"isGroup"]; + [newRecord setObject: @"vlist" forKey: @"c_component"]; + } +#warning TODO: create a custom icon for resources + else + { + [newRecord setObject: @"vcard" forKey: @"c_component"]; + } + + // c_info => note + data = [oldRecord objectForKey: @"c_info"]; + if ([data length] > 0) + { + [newRecord setObject: data forKey: @"note"]; + [newRecord setObject: data forKey: @"contactInfo"]; + } + + recordSource = [oldRecord objectForKey: @"source"]; + if ([recordSource conformsToProtocol: @protocol (SOGoDNSource)] && + [[(NSObject *) recordSource MSExchangeHostname] length]) + [newRecord setObject: [NSNumber numberWithInt: 1] forKey: @"isMSExchange"]; + + return newRecord; +} + +- (NSArray *) _flattenedRecords: (NSArray *) records +{ + NSMutableArray *newRecords; + NSEnumerator *oldRecords; + NSDictionary *oldRecord; + + newRecords = [NSMutableArray arrayWithCapacity: [records count]]; + + oldRecords = [records objectEnumerator]; + while ((oldRecord = [oldRecords nextObject])) + [newRecords addObject: [self _flattenedRecord: oldRecord]]; + + return newRecords; +} + +/* This method returns the entry corresponding to the name passed as + parameter. */ +- (NSDictionary *) lookupContactWithName: (NSString *) aName +{ + NSDictionary *record; + + if (aName && [aName length] > 0) + record = [self _flattenedRecord: [source lookupContactEntry: aName]]; + else + record = nil; + + return record; +} + +- (NSArray *) lookupContactsWithFilter: (NSString *) filter + onCriteria: (NSString *) criteria + sortBy: (NSString *) sortKey + ordering: (NSComparisonResult) sortOrdering + inDomain: (NSString *) domain +{ + NSArray *records, *result; + EOSortOrdering *ordering; + + result = nil; + + if (([filter length] > 0 && [criteria isEqualToString: @"name_or_address"]) + || ![source listRequiresDot]) + { + records = [source fetchContactsMatching: filter + inDomain: domain]; + [childRecords setObjects: records + forKeys: [records objectsForKey: @"c_name" + notFoundMarker: nil]]; + records = [self _flattenedRecords: records]; + ordering + = [EOSortOrdering sortOrderingWithKey: sortKey + selector: ((sortOrdering == NSOrderedDescending) + ? EOCompareCaseInsensitiveDescending + : EOCompareCaseInsensitiveAscending)]; + result + = [records sortedArrayUsingKeyOrderArray: + [NSArray arrayWithObject: ordering]]; + } + + return result; +} + +- (NSString *) _deduceObjectNameFromURL: (NSString *) url + fromBaseURL: (NSString *) baseURL +{ + NSRange urlRange; + NSString *name; + + urlRange = [url rangeOfString: baseURL]; + if (urlRange.location != NSNotFound) + { + name = [url substringFromIndex: NSMaxRange (urlRange)]; + if ([name hasPrefix: @"/"]) + name = [name substringFromIndex: 1]; + } + else + name = nil; + + return name; +} + +/* TODO: multiget reorg */ +- (NSString *) _nodeTagForProperty: (NSString *) property +{ + NSString *namespace, *nodeName, *nsRep; + NSRange nsEnd; + + nsEnd = [property rangeOfString: @"}"]; + namespace + = [property substringFromRange: NSMakeRange (1, nsEnd.location - 1)]; + nodeName = [property substringFromIndex: nsEnd.location + 1]; + if ([namespace isEqualToString: XMLNS_CARDDAV]) + nsRep = @"C"; + else + nsRep = @"D"; + + return [NSString stringWithFormat: @"%@:%@", nsRep, nodeName]; +} + +- (NSString *) _nodeTag: (NSString *) property +{ + static NSMutableDictionary *tags = nil; + NSString *nodeTag; + + if (!tags) + tags = [NSMutableDictionary new]; + nodeTag = [tags objectForKey: property]; + if (!nodeTag) + { + nodeTag = [self _nodeTagForProperty: property]; + [tags setObject: nodeTag forKey: property]; + } + + return nodeTag; +} + +- (NSString **) _properties: (NSString **) properties + count: (unsigned int) propertiesCount + ofObject: (NSDictionary *) object +{ + SOGoContactLDIFEntry *ldifEntry; + NSString **currentProperty; + NSString **values, **currentValue; + SEL methodSel; + +// NSLog (@"_properties:ofObject:: %@", [NSDate date]); + + values = NSZoneMalloc (NULL, + (propertiesCount + 1) * sizeof (NSString *)); + *(values + propertiesCount) = nil; + + ldifEntry = [SOGoContactLDIFEntry + contactEntryWithName: [object objectForKey: @"c_name"] + withLDIFEntry: object + inContainer: self]; + currentProperty = properties; + currentValue = values; + while (*currentProperty) + { + methodSel = SOGoSelectorForPropertyGetter (*currentProperty); + if (methodSel && [ldifEntry respondsToSelector: methodSel]) + *currentValue = [[ldifEntry performSelector: methodSel] + stringByEscapingXMLString]; + currentProperty++; + currentValue++; + } + +// NSLog (@"/_properties:ofObject:: %@", [NSDate date]); + + return values; +} + +- (NSArray *) _propstats: (NSString **) properties + count: (unsigned int) propertiesCount + ofObject: (NSDictionary *) object +{ + NSMutableArray *propstats, *properties200, *properties404, *propDict; + NSString **property, **values, **currentValue; + NSString *propertyValue, *nodeTag; + +// NSLog (@"_propstats:ofObject:: %@", [NSDate date]); + + propstats = [NSMutableArray array]; + + properties200 = [NSMutableArray array]; + properties404 = [NSMutableArray array]; + + values = [self _properties: properties count: propertiesCount + ofObject: object]; + currentValue = values; + + property = properties; + while (*property) + { + nodeTag = [self _nodeTag: *property]; + if (*currentValue) + { + propertyValue = [NSString stringWithFormat: @"<%@>%@", + nodeTag, *currentValue, nodeTag]; + propDict = properties200; + } + else + { + propertyValue = [NSString stringWithFormat: @"<%@/>", nodeTag]; + propDict = properties404; + } + [propDict addObject: propertyValue]; + property++; + currentValue++; + } + free (values); + + if ([properties200 count]) + [propstats addObject: [NSDictionary dictionaryWithObjectsAndKeys: + properties200, @"properties", + @"HTTP/1.1 200 OK", @"status", + nil]]; + if ([properties404 count]) + [propstats addObject: [NSDictionary dictionaryWithObjectsAndKeys: + properties404, @"properties", + @"HTTP/1.1 404 Not Found", @"status", + nil]]; +// NSLog (@"/_propstats:ofObject:: %@", [NSDate date]); + + return propstats; +} + +- (void) _appendPropstat: (NSDictionary *) propstat + toBuffer: (NSMutableString *) r +{ + NSArray *properties; + unsigned int count, max; + + [r appendString: @""]; + properties = [propstat objectForKey: @"properties"]; + max = [properties count]; + for (count = 0; count < max; count++) + [r appendString: [properties objectAtIndex: count]]; + [r appendString: @""]; + [r appendString: [propstat objectForKey: @"status"]]; + [r appendString: @""]; +} + +- (void) appendObject: (NSDictionary *) object + properties: (NSString **) properties + count: (unsigned int) propertiesCount + withBaseURL: (NSString *) baseURL + toBuffer: (NSMutableString *) r +{ + NSArray *propstats; + unsigned int count, max; + + [r appendFormat: @""]; + [r appendString: baseURL]; + [r appendString: [[object objectForKey: @"c_name"] stringByEscapingURL]]; + [r appendString: @""]; + + propstats = [self _propstats: properties count: propertiesCount + ofObject: object]; + max = [propstats count]; + for (count = 0; count < max; count++) + [self _appendPropstat: [propstats objectAtIndex: count] + toBuffer: r]; + + [r appendString: @""]; +} + +- (void) appendMissingObjectRef: (NSString *) href + toBuffer: (NSMutableString *) r +{ + [r appendString: @""]; + [r appendString: href]; + [r appendString: @"HTTP/1.1 404 Not Found"]; +} + +- (void) _appendComponentProperties: (NSArray *) properties + matchingURLs: (id ) refs + toResponse: (WOResponse *) response +{ + NSObject *element; + NSString *url, *baseURL, *cname; + NSString **propertiesArray; + NSMutableString *buffer; + NSDictionary *object; + + unsigned int count, max, propertiesCount; + + baseURL = [self davURLAsString]; +#warning review this when fixing http://www.scalableogo.org/bugs/view.php?id=276 + if (![baseURL hasSuffix: @"/"]) + baseURL = [NSString stringWithFormat: @"%@/", baseURL]; + + propertiesArray = [properties asPointersOfObjects]; + propertiesCount = [properties count]; + + max = [refs length]; + buffer = [NSMutableString stringWithCapacity: max*512]; + + for (count = 0; count < max; count++) + { + element = [refs objectAtIndex: count]; + url = [[[element firstChild] nodeValue] stringByUnescapingURL]; + cname = [self _deduceObjectNameFromURL: url fromBaseURL: baseURL]; + object = [source lookupContactEntry: cname]; + if (object) + [self appendObject: object + properties: propertiesArray + count: propertiesCount + withBaseURL: baseURL + toBuffer: buffer]; + else + [self appendMissingObjectRef: url + toBuffer: buffer]; + } + [response appendContentString: buffer]; +// NSLog (@"/adding properties with url"); + + NSZoneFree (NULL, propertiesArray); +} + +- (WOResponse *) performMultigetInContext: (WOContext *) queryContext + inNamespace: (NSString *) namespace +{ + WOResponse *r; + id document; + id documentElement, propElement; + + r = [context response]; + [r prepareDAVResponse]; + [r appendContentString: + [NSString stringWithFormat: @"", namespace]]; + document = [[queryContext request] contentAsDOMDocument]; + documentElement = [document documentElement]; + propElement = [(NGDOMNodeWithChildren *) documentElement + firstElementWithTag: @"prop" + inNamespace: @"DAV:"]; + [self _appendComponentProperties: [(NGDOMNodeWithChildren *) propElement flatPropertyNameOfSubElements] + matchingURLs: [documentElement getElementsByTagName: @"href"] + toResponse: r]; + [r appendContentString:@""]; + + return r; +} + +- (id) davAddressbookMultiget: (id) queryContext +{ + return [self performMultigetInContext: queryContext + inNamespace: XMLNS_CARDDAV]; +} + +- (NSString *) davDisplayName +{ + return displayName; +} + +- (BOOL) isFolderish +{ + return YES; +} + +/* folder type */ + +- (NSString *) folderType +{ + return @"Contact"; +} + +/* sorting */ +- (NSComparisonResult) compare: (id) otherFolder +{ + NSComparisonResult comparison; + BOOL otherIsPersonal; + + otherIsPersonal = ([otherFolder isKindOfClass: [SOGoContactGCSFolder class]] + || ([otherFolder isKindOfClass: isa] && [otherFolder isPersonalSource])); + + if (isPersonalSource) + { + if (otherIsPersonal && ![nameInContainer isEqualToString: @"personal"]) + { + if ([[otherFolder nameInContainer] isEqualToString: @"personal"]) + comparison = NSOrderedDescending; + else + comparison + = [[self displayName] + localizedCaseInsensitiveCompare: [otherFolder displayName]]; + } + else + comparison = NSOrderedAscending; + } + else + { + if (otherIsPersonal) + comparison = NSOrderedDescending; + else + comparison + = [[self displayName] + localizedCaseInsensitiveCompare: [otherFolder displayName]]; + } + + return comparison; +} + +/* common methods */ + +- (NSException *) delete +{ + NSException *error; + + if (isPersonalSource) + { + error = [(SOGoContactFolders *) container + removeLDAPAddressBook: nameInContainer]; + if (!error && [[context request] handledByDefaultHandler]) + [self sendFolderAdvisoryTemplate: @"Removal"]; + } + else + error = [NSException exceptionWithHTTPStatus: 501 /* not implemented */ + reason: @"delete not available on system sources"]; + + return error; +} + +- (void) renameTo: (NSString *) newName +{ + NSException *error; + + if (isPersonalSource) + { + if (![[source displayName] isEqualToString: newName]) + { + error = [(SOGoContactFolders *) container + renameLDAPAddressBook: nameInContainer + withDisplayName: newName]; + if (!error) + [self setDisplayName: newName]; + } + } + /* If public source then method is ignored, maybe we should return an + NSException instead... */ +} + +/* acls */ +- (NSString *) ownerInContext: (WOContext *) noContext +{ + NSString *sourceOwner; + + if (isPersonalSource) + sourceOwner = [[source modifiers] objectAtIndex: 0]; + else + sourceOwner = @"nobody"; + + return sourceOwner; +} + +- (NSArray *) subscriptionRoles +{ + return [NSArray arrayWithObject: SoRole_Authenticated]; +} + +- (NSArray *) aclsForUser: (NSString *) uid +{ + NSArray *acls, *modifiers; + static NSArray *modifierRoles = nil; + + if (!modifierRoles) + modifierRoles = [[NSArray alloc] initWithObjects: @"Owner", + @"ObjectViewer", + @"ObjectEditor", @"ObjectCreator", + @"ObjectEraser", nil]; + + modifiers = [source modifiers]; + if ([modifiers containsObject: uid]) + acls = [modifierRoles copy]; + else + acls = [NSArray new]; + + [acls autorelease]; + + return acls; +} + +@end diff --git a/SoObjects/SOGo/SOGoCacheGCSFolder.m b/SoObjects/SOGo/SOGoCacheGCSFolder.m index b29287fcb..2e483bc32 100644 --- a/SoObjects/SOGo/SOGoCacheGCSFolder.m +++ b/SoObjects/SOGo/SOGoCacheGCSFolder.m @@ -364,7 +364,7 @@ Class SOGoCacheGCSObjectK = Nil; if (record) { if ([[record objectForKey: @"c_type"] intValue] == MAPIFolderCacheObject) - objectClass = isa; + objectClass = object_getClass(self); else objectClass = SOGoCacheGCSObjectK; diff --git a/SoObjects/SOGo/SOGoCacheGCSFolder.m.orig b/SoObjects/SOGo/SOGoCacheGCSFolder.m.orig new file mode 100644 index 000000000..b29287fcb --- /dev/null +++ b/SoObjects/SOGo/SOGoCacheGCSFolder.m.orig @@ -0,0 +1,486 @@ +/* SOGoCacheGCSFolder.m - this file is part of SOGo + * + * Copyright (C) 2012-2014 Inverse inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import +#import + +#import +#import +#import +#import +#import "EOQualifier+SOGoCacheObject.h" +#import "GCSSpecialQueries+SOGoCacheObject.h" + +#import "SOGoCacheGCSFolder.h" + +#undef DEBUG +//#include +//#include +//#include +//#include +//#include +//#include +//#include + +Class SOGoCacheGCSObjectK = Nil; + +@implementation SOGoCacheGCSFolder + ++ (void) initialize +{ + SOGoCacheGCSObjectK = [SOGoCacheGCSObject class]; +} + +- (id) init +{ + if ((self = [super init])) + { + pathPrefix = nil; + } + + return self; +} + +- (id) initWithName: (NSString *) name inContainer: (id) newContainer +{ + if ((self = [super initWithName: name inContainer: newContainer])) + { + objectType = MAPIFolderCacheObject; + aclMessage = [SOGoCacheGCSObject objectWithName: @"permissions" + inContainer: self]; + [aclMessage setObjectType: MAPIInternalCacheObject]; + [aclMessage retain]; + } + + return self; +} + +- (void) dealloc +{ + [aclMessage release]; + [pathPrefix release]; + [super dealloc]; +} + +- (BOOL) isFolderish +{ + return YES; +} + +- (void) setPathPrefix: (NSString *) newPathPrefix +{ + ASSIGN (pathPrefix, newPathPrefix); +} + +- (NSMutableString *) pathForChild: (NSString *) childName +{ + NSMutableString *path; + + path = [self path]; + [path appendFormat: @"/%@", childName]; + + return path; +} + +- (NSMutableString *) path +{ + NSMutableString *path; + + path = [super path]; + if (pathPrefix) + [path insertString: pathPrefix atIndex: 0]; + + return path; +} + +// - (SOGoMAPIDBMessage *) newMessage +// { +// NSString *newFilename; + +// newFilename = [NSString stringWithFormat: @"%@.plist", +// [SOGoObject globallyUniqueObjectId]]; + +// return [SOGoMAPIDBMessage objectWithName: filename inContainer: self]; +// } + +- (NSArray *) childKeysOfType: (SOGoCacheObjectType) type + includeDeleted: (BOOL) includeDeleted + matchingQualifier: (EOQualifier *) qualifier + andSortOrderings: (NSArray *) sortOrderings +{ + NSMutableArray *childKeys; + NSMutableString *sql// , *qualifierClause + ; + NSString *childPathPrefix, *childPath, *childKey; + NSMutableArray *whereClause; + NSArray *records; + NSDictionary *record; + NSUInteger childPathPrefixLen, count, max; + SOGoCacheGCSObject *currentChild; + + /* query construction */ + sql = [NSMutableString stringWithCapacity: 256]; + [sql appendFormat: @"SELECT * FROM %@", [self tableName]]; + + whereClause = [NSMutableArray arrayWithCapacity: 2]; + [whereClause addObject: [NSString stringWithFormat: @"c_parent_path = '%@'", + [self path]]]; + [whereClause addObject: [NSString stringWithFormat: @"c_type = %d", type]]; + if (!includeDeleted) + [whereClause addObject: @"c_deleted = 0"]; + + [sql appendFormat: @" WHERE %@", + [whereClause componentsJoinedByString: @" AND "]]; + + childPathPrefix = [NSString stringWithFormat: @"%@/", [self path]]; + + /* results */ + records = [self performSQLQuery: sql]; + if (records) + { + max = [records count]; + childKeys = [NSMutableArray arrayWithCapacity: max]; + childPathPrefixLen = [childPathPrefix length]; + for (count = 0; count < max; count++) + { + record = [records objectAtIndex: count]; + childPath = [record objectForKey: @"c_path"]; + childKey = [childPath substringFromIndex: childPathPrefixLen]; + if ([childKey rangeOfString: @"/"].location == NSNotFound) + { + if (qualifier) + { + currentChild = [SOGoCacheGCSObject objectWithName: childKey + inContainer: self]; + [currentChild setupFromRecord: record]; + if ([qualifier evaluateSOGoMAPIDBObject: currentChild]) + [childKeys addObject: childKey]; + } + else + [childKeys addObject: childKey]; + } + } + } + else + childKeys = nil; + + return childKeys; +} + +- (NSArray *) toManyRelationshipKeys +{ + return [self childKeysOfType: MAPIFolderCacheObject + includeDeleted: NO + matchingQualifier: nil + andSortOrderings: nil]; +} + +- (NSArray *) toOneRelationshipKeys +{ + return [self childKeysOfType: MAPIMessageCacheObject + includeDeleted: NO + matchingQualifier: nil + andSortOrderings: nil]; +} + +- (void) setNameInContainer: (NSString *) newName +{ + NSMutableString *sql; + NSString *oldPath, *newPath, *path, *parentPath; + NSMutableArray *queries; + NSArray *records; + NSDictionary *record; + NSUInteger count, max; + + /* change the paths in children records */ + if (nameInContainer) + oldPath = [self path]; + + [super setNameInContainer: newName]; + + if (nameInContainer) + { + newPath = [self path]; + + sql = [NSMutableString stringWithFormat: + @"SELECT c_path, c_parent_path FROM %@" + @" WHERE c_path LIKE '%@/%%'", + [self tableName], oldPath]; + records = [self performSQLQuery: sql]; + max = [records count]; + queries = [NSMutableArray arrayWithCapacity: max + 1]; + if (max > 0) + { + for (count = 0; count < max; count++) + { + record = [records objectAtIndex: count]; + path = [record objectForKey: @"c_path"]; + sql = [NSMutableString stringWithFormat: @"UPDATE %@" + @" SET c_path = '%@'", + [self tableName], + [path stringByReplacingPrefix: oldPath + withPrefix: newPath]]; + parentPath = [record objectForKey: @"c_parent_path"]; + if ([parentPath isNotNull]) + [sql appendFormat: @", c_parent_path = '%@'", + [parentPath stringByReplacingPrefix: oldPath + withPrefix: newPath]]; + [sql appendFormat: @" WHERE c_path = '%@'", path]; + [queries addObject: sql]; + } + [self performBatchSQLQueries: queries]; + } + } +} + +- (void) changePathTo: (NSString *) newPath +{ + NSMutableString *sql// , *qualifierClause + ; + NSString *oldPath, *oldPathAsPrefix, *path, *parentPath; + NSMutableArray *queries; + NSArray *records; + NSDictionary *record; + NSUInteger count, max; + + /* change the paths in children records */ + oldPath = [self path]; + oldPathAsPrefix = [NSString stringWithFormat: @"%@/", oldPath]; + + sql = [NSMutableString stringWithFormat: + @"SELECT c_path, c_parent_path FROM %@" + @" WHERE c_path LIKE '%@%%'", + [self tableName], oldPathAsPrefix]; + records = [self performSQLQuery: sql]; + max = [records count]; + queries = [NSMutableArray arrayWithCapacity: max + 1]; + if (max > 0) + { + for (count = 0; count < max; count++) + { + record = [records objectAtIndex: count]; + path = [record objectForKey: @"c_path"]; + sql = [NSMutableString stringWithFormat: @"UPDATE %@" + @" SET c_path = '%@'", + [self tableName], + [path stringByReplacingPrefix: oldPath + withPrefix: newPath]]; + parentPath = [record objectForKey: @"c_parent_path"]; + if ([parentPath isNotNull]) + [sql appendFormat: @", c_parent_path = '%@'", + [parentPath stringByReplacingPrefix: oldPath + withPrefix: newPath]]; + [sql appendFormat: @" WHERE c_path = '%@'", path]; + [queries addObject: sql]; + } + [self performBatchSQLQueries: queries]; + } + + /* change the path in this folder record */ + [super changePathTo: newPath]; +} + +- (void) changePathTo: (NSString *) newPath intoNewContainer: (id) newContainer +{ + [self changePathTo: newPath]; + container = newContainer; + if ([self doesRetainContainer]) + [container retain]; +} + +// - (NSArray *) toOneRelationshipKeysMatchingQualifier: (EOQualifier *) qualifier +// andSortOrderings: (NSArray *) sortOrderings +// { +// NSArray *allKeys; +// NSMutableArray *keys; +// NSUInteger count, max; +// NSString *messageKey; +// SOGoMAPIDBMessage *message; + +// if (sortOrderings) +// [self warnWithFormat: @"sorting is not handled yet"]; + +// allKeys = [self toOneRelationshipKeys]; +// if (qualifier) +// { +// [self logWithFormat: @"%s: getting restricted FAI keys", __PRETTY_FUNCTION__]; +// max = [allKeys count]; +// keys = [NSMutableArray arrayWithCapacity: max]; +// for (count = 0; count < max; count++) +// { +// messageKey = [allKeys objectAtIndex: count]; +// message = [self lookupName: messageKey +// inContext: nil +// acquire: NO]; +// if ([qualifier evaluateMAPIVolatileMessage: message]) +// [keys addObject: messageKey]; +// } +// } +// else +// keys = (NSMutableArray *) allKeys; + +// return keys; +// } + +- (id) lookupName: (NSString *) childName + inContext: (WOContext *) woContext + acquire: (BOOL) acquire +{ + id object; + Class objectClass; + NSString *childPath; + NSDictionary *record; + + childPath = [self pathForChild: childName]; + record = [self lookupRecord: childPath newerThanVersion: -1]; + if (record) + { + if ([[record objectForKey: @"c_type"] intValue] == MAPIFolderCacheObject) + objectClass = isa; + else + objectClass = SOGoCacheGCSObjectK; + + object = [objectClass objectWithName: childName + inContainer: self]; + [object setupFromRecord: record]; + } + else + object = nil; + + return object; +} + +- (id) lookupFolder: (NSString *) folderName + inContext: (WOContext *) woContext +{ + id object; + + object = [SOGoCacheGCSFolder objectWithName: folderName + inContainer: self]; + [object reloadIfNeeded]; + + return object; +} + +// - (id) _fileAttributeForKey: (NSString *) key +// { +// NSDictionary *attributes; + +// attributes = [[NSFileManager defaultManager] +// fileAttributesAtPath: directory +// traverseLink: NO]; + +// return [attributes objectForKey: key]; +// } + +// - (NSCalendarDate *) creationTime +// { +// return [self _fileAttributeForKey: NSFileCreationDate]; +// } + +// - (NSCalendarDate *) lastModificationTime +// { +// return [self _fileAttributeForKey: NSFileModificationDate]; +// } + +/* acl */ +- (NSString *) defaultUserID +{ + return @"default"; +} + +- (NSMutableDictionary *) _aclEntries +{ + NSMutableDictionary *aclEntries; + + [aclMessage reloadIfNeeded]; + aclEntries = [aclMessage properties]; + if (![aclEntries objectForKey: @"users"]) + [aclEntries setObject: [NSMutableArray array] forKey: @"users"]; + if (![aclEntries objectForKey: @"entries"]) + [aclEntries setObject: [NSMutableDictionary dictionary] + forKey: @"entries"]; + + return aclEntries; +} + +- (void) addUserInAcls: (NSString *) user +{ + NSMutableDictionary *acl; + NSMutableArray *users; + + acl = [self _aclEntries]; + users = [acl objectForKey: @"users"]; + [users addObjectUniquely: user]; + [aclMessage save]; +} + +- (void) removeAclsForUsers: (NSArray *) oldUsers +{ + NSDictionary *acl; + NSMutableDictionary *entries; + NSMutableArray *users; + + acl = [self _aclEntries]; + entries = [acl objectForKey: @"entries"]; + [entries removeObjectsForKeys: oldUsers]; + users = [acl objectForKey: @"users"]; + [users removeObjectsInArray: oldUsers]; + [aclMessage save]; +} + +- (NSArray *) aclUsers +{ + return [[self _aclEntries] objectForKey: @"users"]; +} + +- (NSArray *) aclsForUser: (NSString *) uid +{ + NSDictionary *entries; + + entries = [[self _aclEntries] objectForKey: @"entries"]; + + return [entries objectForKey: uid]; +} + +- (void) setRoles: (NSArray *) roles + forUser: (NSString *) uid +{ + NSMutableDictionary *acl; + NSMutableDictionary *entries; + + acl = [self _aclEntries]; + entries = [acl objectForKey: @"entries"]; + [entries setObject: roles forKey: uid]; + [aclMessage save]; +} + +@end diff --git a/SoObjects/SOGo/SQLSource.m b/SoObjects/SOGo/SQLSource.m index aa881b9d3..2e117f54d 100644 --- a/SoObjects/SOGo/SQLSource.m +++ b/SoObjects/SOGo/SQLSource.m @@ -880,7 +880,7 @@ reason = [NSString stringWithFormat: @"method '%@' is not available" @" for class '%@'", NSStringFromSelector (_cmd), - NSStringFromClass (isa)]; + NSStringFromClass (object_getClass(self))]; return [NSException exceptionWithName: @"SQLSourceIOException" reason: reason @@ -893,7 +893,7 @@ reason = [NSString stringWithFormat: @"method '%@' is not available" @" for class '%@'", NSStringFromSelector (_cmd), - NSStringFromClass (isa)]; + NSStringFromClass (object_getClass(self))]; return [NSException exceptionWithName: @"SQLSourceIOException" reason: reason @@ -906,7 +906,7 @@ reason = [NSString stringWithFormat: @"method '%@' is not available" @" for class '%@'", NSStringFromSelector (_cmd), - NSStringFromClass (isa)]; + NSStringFromClass (object_getClass(self))]; return [NSException exceptionWithName: @"SQLSourceIOException" reason: reason @@ -932,7 +932,7 @@ reason = [NSString stringWithFormat: @"method '%@' is not available" @" for class '%@'", NSStringFromSelector (_cmd), - NSStringFromClass (isa)]; + NSStringFromClass (object_getClass(self))]; return [NSException exceptionWithName: @"SQLSourceIOException" reason: reason @@ -947,7 +947,7 @@ reason = [NSString stringWithFormat: @"method '%@' is not available" @" for class '%@'", NSStringFromSelector (_cmd), - NSStringFromClass (isa)]; + NSStringFromClass (object_getClass(self))]; return [NSException exceptionWithName: @"SQLSourceIOException" reason: reason @@ -961,7 +961,7 @@ reason = [NSString stringWithFormat: @"method '%@' is not available" @" for class '%@'", NSStringFromSelector (_cmd), - NSStringFromClass (isa)]; + NSStringFromClass (object_getClass(self))]; return [NSException exceptionWithName: @"SQLSourceIOException" reason: reason diff --git a/SoObjects/SOGo/SQLSource.m.orig b/SoObjects/SOGo/SQLSource.m.orig new file mode 100644 index 000000000..aa881b9d3 --- /dev/null +++ b/SoObjects/SOGo/SQLSource.m.orig @@ -0,0 +1,971 @@ +/* SQLSource.h - this file is part of SOGo + * + * Copyright (C) 2009-2012 Inverse inc. + * + * Authors: Ludovic Marcotte + * Francis Lachapelle + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import +#import +#import +#import +#import + +#import +#import + +#import +#import +#import +#import + +#import + +#import "SOGoConstants.h" +#import "NSString+Utilities.h" +#import "NSString+Crypto.h" + +#import "SQLSource.h" + +/** + * The view MUST contain the following columns: + * + * c_uid - will be used for authentication - it's a username or username@domain.tld) + * c_name - which can be identical to c_uid - will be used to uniquely identify entries) + * c_password - password of the user, can be encoded in {scheme}pass format, or when stored without + * scheme it uses the scheme set in userPasswordAlgorithm. + * Possible algorithms are: plain, md5, crypt-md5, sha, ssha (including 256/512 variants), + * cram-md5, smd5, crypt, crypt-md5 + * c_cn - the user's common name + * mail - the user's mail address + * + * Other columns can be defined - see LDAPSource.m for the complete list. + * + * + * A SQL source can be defined like this: + * + * { + * id = zot; + * type = sql; + * viewURL = "mysql://sogo:sogo@127.0.0.1:5432/sogo/sogo_view"; + * canAuthenticate = YES; + * isAddressBook = YES; + * userPasswordAlgorithm = md5; + * prependPasswordScheme = YES; + * } + * + * If prependPasswordScheme is set to YES, the generated passwords will have the format {scheme}password. + * If it is NO (the default), the password will be written to database without encryption scheme. + * + */ + +@implementation SQLSource + ++ (id) sourceFromUDSource: (NSDictionary *) udSource + inDomain: (NSString *) domain +{ + return [[[self alloc] initFromUDSource: udSource + inDomain: domain] autorelease]; +} + +- (id) init +{ + if ((self = [super init])) + { + _sourceID = nil; + _domainField = nil; + _authenticationFilter = nil; + _loginFields = nil; + _mailFields = nil; + _userPasswordAlgorithm = nil; + _viewURL = nil; + _kindField = nil; + _multipleBookingsField = nil; + _imapHostField = nil; + _sieveHostField = nil; + } + + return self; +} + +- (void) dealloc +{ + [_sourceID release]; + [_authenticationFilter release]; + [_loginFields release]; + [_mailFields release]; + [_userPasswordAlgorithm release]; + [_viewURL release]; + [_kindField release]; + [_multipleBookingsField release]; + [_domainField release]; + [_imapHostField release]; + [_sieveHostField release]; + + [super dealloc]; +} + +- (id) initFromUDSource: (NSDictionary *) udSource + inDomain: (NSString *) sourceDomain +{ + self = [self init]; + + ASSIGN(_sourceID, [udSource objectForKey: @"id"]); + ASSIGN(_authenticationFilter, [udSource objectForKey: @"authenticationFilter"]); + ASSIGN(_loginFields, [udSource objectForKey: @"LoginFieldNames"]); + ASSIGN(_mailFields, [udSource objectForKey: @"MailFieldNames"]); + ASSIGN(_userPasswordAlgorithm, [udSource objectForKey: @"userPasswordAlgorithm"]); + ASSIGN(_imapLoginField, [udSource objectForKey: @"IMAPLoginFieldName"]); + ASSIGN(_imapHostField, [udSource objectForKey: @"IMAPHostFieldName"]); + ASSIGN(_sieveHostField, [udSource objectForKey: @"SieveHostFieldName"]); + ASSIGN(_kindField, [udSource objectForKey: @"KindFieldName"]); + ASSIGN(_multipleBookingsField, [udSource objectForKey: @"MultipleBookingsFieldName"]); + ASSIGN(_domainField, [udSource objectForKey: @"DomainFieldName"]); + if ([udSource objectForKey: @"prependPasswordScheme"]) + _prependPasswordScheme = [[udSource objectForKey: @"prependPasswordScheme"] boolValue]; + else + _prependPasswordScheme = NO; + + if (!_userPasswordAlgorithm) + _userPasswordAlgorithm = @"none"; + + if ([udSource objectForKey: @"viewURL"]) + _viewURL = [[NSURL alloc] initWithString: [udSource objectForKey: @"viewURL"]]; + +#warning this domain code has no effect yet + if ([sourceDomain length]) + ASSIGN (_domain, sourceDomain); + + if (!_viewURL) + { + [self autorelease]; + return nil; + } + + return self; +} + +- (NSString *) domain +{ + return _domain; +} + +- (BOOL) _isPassword: (NSString *) plainPassword + equalTo: (NSString *) encryptedPassword +{ + if (!plainPassword || !encryptedPassword) + return NO; + + return [plainPassword isEqualToCrypted: encryptedPassword + withDefaultScheme: _userPasswordAlgorithm]; +} + +/** + * Encrypts a string using this source password algorithm. + * @param plainPassword the unencrypted password. + * @return a new encrypted string. + * @see _isPassword:equalTo: + */ +- (NSString *) _encryptPassword: (NSString *) plainPassword +{ + NSString *pass; + NSString* result; + + pass = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm]; + + if (pass == nil) + { + [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm]; + return nil; + } + + if (_prependPasswordScheme) + result = [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, pass]; + else + result = pass; + + return result; +} + +// +// SQL sources don't support right now all the password policy +// stuff supported by OpenLDAP (and others). If we want to support +// this for SQL sources, we'll have to implement the same +// kind of logic in this module. +// +- (BOOL) checkLogin: (NSString *) _login + password: (NSString *) _pwd + perr: (SOGoPasswordPolicyError *) _perr + expire: (int *) _expire + grace: (int *) _grace +{ + EOAdaptorChannel *channel; + EOQualifier *qualifier; + GCSChannelManager *cm; + NSException *ex; + NSMutableString *sql; + BOOL rc; + + rc = NO; + + _login = [_login stringByReplacingString: @"'" withString: @"''"]; + cm = [GCSChannelManager defaultChannelManager]; + channel = [cm acquireOpenChannelForURL: _viewURL]; + if (channel) + { + if (_loginFields) + { + NSMutableArray *qualifiers; + NSString *field; + EOQualifier *loginQualifier; + int i; + + qualifiers = [NSMutableArray arrayWithCapacity: [_loginFields count]]; + for (i = 0; i < [_loginFields count]; i++) + { + field = [_loginFields objectAtIndex: i]; + loginQualifier = [[EOKeyValueQualifier alloc] initWithKey: field + operatorSelector: EOQualifierOperatorEqual + value: _login]; + [loginQualifier autorelease]; + [qualifiers addObject: loginQualifier]; + } + qualifier = [[EOOrQualifier alloc] initWithQualifierArray: qualifiers]; + } + else + { + qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"c_uid" + operatorSelector: EOQualifierOperatorEqual + value: _login]; + } + [qualifier autorelease]; + sql = [NSMutableString stringWithFormat: @"SELECT c_password" + @" FROM %@" + @" WHERE ", + [_viewURL gcsTableName]]; + if (_authenticationFilter) + { + qualifier = [[EOAndQualifier alloc] initWithQualifiers: + qualifier, + [EOQualifier qualifierWithQualifierFormat: _authenticationFilter], + nil]; + [qualifier autorelease]; + } + [qualifier _gcsAppendToString: sql]; + + ex = [channel evaluateExpressionX: sql]; + if (!ex) + { + NSDictionary *row; + NSArray *attrs; + NSString *value; + + attrs = [channel describeResults: NO]; + row = [channel fetchAttributes: attrs withZone: NULL]; + value = [row objectForKey: @"c_password"]; + + rc = [self _isPassword: _pwd equalTo: value]; + [channel cancelFetch]; + } + else + [self errorWithFormat: @"could not run SQL '%@': %@", qualifier, ex]; + + [cm releaseChannel: channel]; + } + else + [self errorWithFormat:@"failed to acquire channel for URL: %@", + [_viewURL absoluteString]]; + + return rc; +} + +/** + * Change a user's password. + * @param login the user's login name. + * @param oldPassword the previous password. + * @param newPassword the new password. + * @param perr is not used. + * @return YES if the password was successfully changed. + */ +- (BOOL) changePasswordForLogin: (NSString *) login + oldPassword: (NSString *) oldPassword + newPassword: (NSString *) newPassword + perr: (SOGoPasswordPolicyError *) perr +{ + EOAdaptorChannel *channel; + GCSChannelManager *cm; + NSException *ex; + NSString *sqlstr; + BOOL didChange; + BOOL isOldPwdOk; + + isOldPwdOk = NO; + didChange = NO; + + // Verify current password + isOldPwdOk = [self checkLogin:login password:oldPassword perr:perr expire:0 grace:0]; + + if (isOldPwdOk) + { + // Encrypt new password + NSString *encryptedPassword = [self _encryptPassword: newPassword]; + if(encryptedPassword == nil) + return NO; + + // Save new password + login = [login stringByReplacingString: @"'" withString: @"''"]; + cm = [GCSChannelManager defaultChannelManager]; + channel = [cm acquireOpenChannelForURL: _viewURL]; + if (channel) + { + sqlstr = [NSString stringWithFormat: (@"UPDATE %@" + @" SET c_password = '%@'" + @" WHERE c_uid = '%@'"), + [_viewURL gcsTableName], encryptedPassword, login]; + + ex = [channel evaluateExpressionX: sqlstr]; + if (!ex) + { + didChange = YES; + } + else + { + [self errorWithFormat: @"could not run SQL '%@': %@", sqlstr, ex]; + } + [cm releaseChannel: channel]; + } + } + + return didChange; +} + +- (NSString *) _whereClauseFromArray: (NSArray *) theArray + value: (NSString *) theValue + exact: (BOOL) theBOOL +{ + NSMutableString *s; + int i; + + s = [NSMutableString string]; + + for (i = 0; i < [theArray count]; i++) + { + if (theBOOL) + [s appendFormat: @" OR LOWER(%@) = '%@'", [theArray objectAtIndex: i], theValue]; + else + [s appendFormat: @" OR LOWER(%@) LIKE '%%%@%%'", [theArray objectAtIndex: i], theValue]; + } + + return s; +} + +- (NSDictionary *) _lookupContactEntry: (NSString *) theID + considerEmail: (BOOL) b + inDomain: (NSString *) domain +{ + NSMutableDictionary *response; + NSMutableArray *qualifiers; + NSArray *fieldNames; + EOAdaptorChannel *channel; + EOQualifier *loginQualifier, *domainQualifier, *qualifier; + GCSChannelManager *cm; + NSMutableString *sql; + NSString *value, *field; + NSException *ex; + int i; + + response = nil; + + theID = [theID stringByReplacingString: @"'" withString: @"''"]; + cm = [GCSChannelManager defaultChannelManager]; + channel = [cm acquireOpenChannelForURL: _viewURL]; + if (channel) + { + qualifiers = [NSMutableArray arrayWithCapacity: [_loginFields count] + 1]; + + // Always compare against the c_uid field + loginQualifier = [[EOKeyValueQualifier alloc] initWithKey: @"c_uid" + operatorSelector: EOQualifierOperatorEqual + value: theID]; + [loginQualifier autorelease]; + [qualifiers addObject: loginQualifier]; + + if (_loginFields) + { + for (i = 0; i < [_loginFields count]; i++) + { + field = [_loginFields objectAtIndex: i]; + if ([field caseInsensitiveCompare: @"c_uid"] != NSOrderedSame) + { + loginQualifier = [[EOKeyValueQualifier alloc] initWithKey: field + operatorSelector: EOQualifierOperatorEqual + value: theID]; + [loginQualifier autorelease]; + [qualifiers addObject: loginQualifier]; + } + } + } + + domainQualifier = nil; + if (_domainField && domain) + { + domainQualifier = [[EOKeyValueQualifier alloc] initWithKey: _domainField + operatorSelector: EOQualifierOperatorEqual + value: domain]; + [domainQualifier autorelease]; + } + + if (b) + { + // Always compare againts the mail field + loginQualifier = [[EOKeyValueQualifier alloc] initWithKey: @"mail" + operatorSelector: EOQualifierOperatorEqual + value: [theID lowercaseString]]; + [loginQualifier autorelease]; + [qualifiers addObject: loginQualifier]; + + if (_mailFields) + { + for (i = 0; i < [_mailFields count]; i++) + { + field = [_mailFields objectAtIndex: i]; + if ([field caseInsensitiveCompare: @"mail"] != NSOrderedSame + && ![_loginFields containsObject: field]) + { + loginQualifier = [[EOKeyValueQualifier alloc] initWithKey: field + operatorSelector: EOQualifierOperatorEqual + value: [theID lowercaseString]]; + [loginQualifier autorelease]; + [qualifiers addObject: loginQualifier]; + } + } + } + } + + sql = [NSMutableString stringWithFormat: @"SELECT *" + @" FROM %@" + @" WHERE ", + [_viewURL gcsTableName]]; + qualifier = [[EOOrQualifier alloc] initWithQualifierArray: qualifiers]; + if (domainQualifier) + qualifier = [[EOAndQualifier alloc] initWithQualifiers: domainQualifier, qualifier, nil]; + [qualifier _gcsAppendToString: sql]; + + ex = [channel evaluateExpressionX: sql]; + if (!ex) + { + NSMutableArray *emails; + + response = [[channel fetchAttributes: [channel describeResults: NO] + withZone: NULL] mutableCopy]; + [response autorelease]; + [channel cancelFetch]; + + /* Convert all c_ fields to obtain their ldif equivalent */ + fieldNames = [response allKeys]; + for (i = 0; i < [fieldNames count]; i++) + { + field = [fieldNames objectAtIndex: i]; + if ([field hasPrefix: @"c_"]) + [response setObject: [response objectForKey: field] + forKey: [field substringFromIndex: 2]]; + } + + // FIXME + // We have to do this here since we do not manage modules + // constraints right now over a SQL backend. + [response setObject: [NSNumber numberWithBool: YES] forKey: @"CalendarAccess"]; + [response setObject: [NSNumber numberWithBool: YES] forKey: @"MailAccess"]; + [response setObject: [NSNumber numberWithBool: YES] forKey: @"ActiveSyncAccess"]; + + // We set the domain, if any + value = nil; + if (_domain) + value = _domain; + else if (_domainField) + value = [response objectForKey: _domainField]; + if (![value isNotNull]) + value = @""; + [response setObject: value forKey: @"c_domain"]; + + // We populate all mail fields + emails = [NSMutableArray array]; + + if ([response objectForKey: @"mail"]) + [emails addObject: [response objectForKey: @"mail"]]; + + if (_mailFields && [_mailFields count] > 0) + { + NSString *s; + int i; + + for (i = 0; i < [_mailFields count]; i++) + if ((s = [response objectForKey: [_mailFields objectAtIndex: i]]) && + [[s stringByTrimmingSpaces] length] > 0) + [emails addObject: s]; + } + + [response setObject: emails forKey: @"c_emails"]; + if (_imapHostField) + { + value = [response objectForKey: _imapHostField]; + if ([value isNotNull]) + [response setObject: value forKey: @"c_imaphostname"]; + } + + if (_sieveHostField) + { + value = [response objectForKey: _sieveHostField]; + if ([value isNotNull]) + [response setObject: value forKey: @"c_sievehostname"]; + } + + // We check if the user can authenticate + if (_authenticationFilter) + { + EOQualifier *q_uid, *q_auth; + + sql = [NSMutableString stringWithFormat: @"SELECT c_uid" + @" FROM %@" + @" WHERE ", + [_viewURL gcsTableName]]; + + q_auth = [EOQualifier qualifierWithQualifierFormat: _authenticationFilter]; + + q_uid = [[EOKeyValueQualifier alloc] initWithKey: @"c_uid" + operatorSelector: EOQualifierOperatorEqual + value: theID]; + [q_uid autorelease]; + + qualifier = [[EOAndQualifier alloc] initWithQualifiers: q_uid, q_auth, nil]; + [qualifier autorelease]; + [qualifier _gcsAppendToString: sql]; + + ex = [channel evaluateExpressionX: sql]; + if (!ex) + { + NSDictionary *authResponse; + + authResponse = [channel fetchAttributes: [channel describeResults: NO] withZone: NULL]; + [response setObject: [NSNumber numberWithBool: [authResponse count] > 0] forKey: @"canAuthenticate"]; + [channel cancelFetch]; + } + else + [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex]; + } + else + [response setObject: [NSNumber numberWithBool: YES] forKey: @"canAuthenticate"]; + + // We check if we should use a different login for IMAP + if (_imapLoginField) + { + if ([[response objectForKey: _imapLoginField] isNotNull]) + [response setObject: [response objectForKey: _imapLoginField] forKey: @"c_imaplogin"]; + } + + // We check if it's a resource of not + if (_kindField) + { + if ((value = [response objectForKey: _kindField]) && [value isNotNull]) + { + if ([value caseInsensitiveCompare: @"location"] == NSOrderedSame || + [value caseInsensitiveCompare: @"thing"] == NSOrderedSame || + [value caseInsensitiveCompare: @"group"] == NSOrderedSame) + { + [response setObject: [NSNumber numberWithInt: 1] + forKey: @"isResource"]; + } + } + } + + if (_multipleBookingsField) + { + if ((value = [response objectForKey: _multipleBookingsField])) + { + [response setObject: [NSNumber numberWithInt: [value intValue]] + forKey: @"numberOfSimultaneousBookings"]; + } + } + + [response setObject: self forKey: @"source"]; + } + else + [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex]; + [cm releaseChannel: channel]; + } + else + [self errorWithFormat:@"failed to acquire channel for URL: %@", + [_viewURL absoluteString]]; + + return response; +} + + +- (NSDictionary *) lookupContactEntry: (NSString *) theID +{ + return [self _lookupContactEntry: theID considerEmail: NO inDomain: nil]; +} + +- (NSDictionary *) lookupContactEntryWithUIDorEmail: (NSString *) entryID + inDomain: (NSString *) domain +{ + return [self _lookupContactEntry: entryID considerEmail: YES inDomain: domain]; +} + +/* Returns an EOQualifier of the following form: + * (_domainField = domain OR _domainField = visibleDomain1 [...]) + * Should only be called on SQL sources using _domainField name. + */ +- (EOQualifier *) _visibleDomainsQualifierFromDomain: (NSString *) domain +{ + int i; + EOQualifier *qualifier, *domainQualifier; + NSArray *visibleDomains; + NSMutableArray *qualifiers; + NSString *currentDomain; + + SOGoSystemDefaults *sd; + + /* Return early if no domain or if being called on a 'static' sql source */ + if (!domain || !_domainField) + return nil; + + sd = [SOGoSystemDefaults sharedSystemDefaults]; + visibleDomains = [sd visibleDomainsForDomain: domain]; + qualifier = nil; + + domainQualifier = + [[EOKeyValueQualifier alloc] initWithKey: _domainField + operatorSelector: EOQualifierOperatorEqual + value: domain]; + [domainQualifier autorelease]; + + if ([visibleDomains count]) + { + qualifiers = [NSMutableArray arrayWithCapacity: [visibleDomains count] + 1]; + [qualifiers addObject: domainQualifier]; + for(i = 0; i < [visibleDomains count]; i++) + { + currentDomain = [visibleDomains objectAtIndex: i]; + qualifier = + [[EOKeyValueQualifier alloc] initWithKey: _domainField + operatorSelector: EOQualifierOperatorEqual + value: currentDomain]; + [qualifier autorelease]; + [qualifiers addObject: qualifier]; + } + qualifier = [[EOOrQualifier alloc] initWithQualifierArray: qualifiers]; + [qualifier autorelease]; + } + + return qualifier ? qualifier : domainQualifier; +} + + +- (NSArray *) allEntryIDsVisibleFromDomain: (NSString *) domain +{ + EOAdaptorChannel *channel; + EOQualifier *domainQualifier; + GCSChannelManager *cm; + NSException *ex; + NSMutableArray *results; + NSMutableString *sql; + + results = [NSMutableArray array]; + + cm = [GCSChannelManager defaultChannelManager]; + channel = [cm acquireOpenChannelForURL: _viewURL]; + if (channel) + { + sql = [NSMutableString stringWithFormat: @"SELECT c_uid FROM %@", + [_viewURL gcsTableName]]; + + if (_domainField) + { + if ([domain length]) + { + domainQualifier = + [self _visibleDomainsQualifierFromDomain: domain]; + if (domainQualifier) + { + [sql appendString: @" WHERE "]; + [domainQualifier _gcsAppendToString: sql]; + } + } + else + { + /* Should not happen but avoid returning the whole table + * if a domain should have been defined */ + [sql appendFormat: @" WHERE %@ is NULL", _domainField]; + } + } + + ex = [channel evaluateExpressionX: sql]; + if (!ex) + { + NSDictionary *row; + NSArray *attrs; + NSString *value; + + attrs = [channel describeResults: NO]; + + while ((row = [channel fetchAttributes: attrs withZone: NULL])) + { + value = [row objectForKey: @"c_uid"]; + if (value) + [results addObject: value]; + } + } + else + [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex]; + [cm releaseChannel: channel]; + } + else + [self errorWithFormat:@"failed to acquire channel for URL: %@", + [_viewURL absoluteString]]; + + + return results; +} + +- (NSArray *) allEntryIDs +{ + return [self allEntryIDsVisibleFromDomain: nil]; +} + +- (NSArray *) fetchContactsMatching: (NSString *) filter + inDomain: (NSString *) domain +{ + EOAdaptorChannel *channel; + NSMutableArray *results; + GCSChannelManager *cm; + NSException *ex; + NSMutableString *sql; + NSString *lowerFilter; + + results = [NSMutableArray array]; + + cm = [GCSChannelManager defaultChannelManager]; + channel = [cm acquireOpenChannelForURL: _viewURL]; + if (channel) + { + lowerFilter = [filter lowercaseString]; + lowerFilter = [lowerFilter stringByReplacingString: @"'" withString: @"''"]; + + sql = [NSMutableString stringWithFormat: (@"SELECT *" + @" FROM %@" + @" WHERE" + @" (LOWER(c_cn) LIKE '%%%@%%'" + @" OR LOWER(mail) LIKE '%%%@%%'"), + [_viewURL gcsTableName], + lowerFilter, lowerFilter]; + + if (_mailFields && [_mailFields count] > 0) + { + [sql appendString: [self _whereClauseFromArray: _mailFields value: lowerFilter exact: NO]]; + } + + [sql appendString: @")"]; + + if (_domainField) + { + if ([domain length]) + { + EOQualifier *domainQualifier; + domainQualifier = + [self _visibleDomainsQualifierFromDomain: domain]; + if (domainQualifier) + { + [sql appendFormat: @" AND ("]; + [domainQualifier _gcsAppendToString: sql]; + [sql appendFormat: @")"]; + } + } + else + [sql appendFormat: @" AND %@ IS NULL", _domainField]; + } + + ex = [channel evaluateExpressionX: sql]; + if (!ex) + { + NSDictionary *row; + NSArray *attrs; + + attrs = [channel describeResults: NO]; + + while ((row = [channel fetchAttributes: attrs withZone: NULL])) + { + row = [row mutableCopy]; + [(NSMutableDictionary *) row setObject: self forKey: @"source"]; + [results addObject: row]; + [row release]; + } + } + else + [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex]; + [cm releaseChannel: channel]; + } + else + [self errorWithFormat:@"failed to acquire channel for URL: %@", + [_viewURL absoluteString]]; + + return results; +} + +- (void) setSourceID: (NSString *) newSourceID +{ +} + +- (NSString *) sourceID +{ + return _sourceID; +} + +- (void) setDisplayName: (NSString *) newDisplayName +{ +} + +- (NSString *) displayName +{ + /* This method is only used when supporting user "source" addressbooks, + which is only supported by the LDAP backend for now. */ + return _sourceID; +} + +- (void) setListRequiresDot: (BOOL) newListRequiresDot +{ +} + +- (BOOL) listRequiresDot +{ + /* This method is not implemented for SQLSource. It must enable a mechanism + where using "." is not required to list the content of addressbooks. */ + return YES; +} + +/* card editing */ +- (void) setModifiers: (NSArray *) newModifiers +{ +} + +- (NSArray *) modifiers +{ + /* This method is only used when supporting card editing, + which is only supported by the LDAP backend for now. */ + return nil; +} + +- (NSException *) addContactEntry: (NSDictionary *) roLdifRecord + withID: (NSString *) aId +{ + NSString *reason; + + reason = [NSString stringWithFormat: @"method '%@' is not available" + @" for class '%@'", NSStringFromSelector (_cmd), + NSStringFromClass (isa)]; + + return [NSException exceptionWithName: @"SQLSourceIOException" + reason: reason + userInfo: nil]; +} + +- (NSException *) updateContactEntry: (NSDictionary *) roLdifRecord +{ + NSString *reason; + + reason = [NSString stringWithFormat: @"method '%@' is not available" + @" for class '%@'", NSStringFromSelector (_cmd), + NSStringFromClass (isa)]; + + return [NSException exceptionWithName: @"SQLSourceIOException" + reason: reason + userInfo: nil]; +} + +- (NSException *) removeContactEntryWithID: (NSString *) aId +{ + NSString *reason; + + reason = [NSString stringWithFormat: @"method '%@' is not available" + @" for class '%@'", NSStringFromSelector (_cmd), + NSStringFromClass (isa)]; + + return [NSException exceptionWithName: @"SQLSourceIOException" + reason: reason + userInfo: nil]; +} + +/* user addressbooks */ +- (BOOL) hasUserAddressBooks +{ + return NO; +} + +- (NSArray *) addressBookSourcesForUser: (NSString *) user +{ + return nil; +} + +- (NSException *) addAddressBookSource: (NSString *) newId + withDisplayName: (NSString *) newDisplayName + forUser: (NSString *) user +{ + NSString *reason; + + reason = [NSString stringWithFormat: @"method '%@' is not available" + @" for class '%@'", NSStringFromSelector (_cmd), + NSStringFromClass (isa)]; + + return [NSException exceptionWithName: @"SQLSourceIOException" + reason: reason + userInfo: nil]; +} + +- (NSException *) renameAddressBookSource: (NSString *) newId + withDisplayName: (NSString *) newDisplayName + forUser: (NSString *) user +{ + NSString *reason; + + reason = [NSString stringWithFormat: @"method '%@' is not available" + @" for class '%@'", NSStringFromSelector (_cmd), + NSStringFromClass (isa)]; + + return [NSException exceptionWithName: @"SQLSourceIOException" + reason: reason + userInfo: nil]; +} + +- (NSException *) removeAddressBookSource: (NSString *) newId + forUser: (NSString *) user +{ + NSString *reason; + + reason = [NSString stringWithFormat: @"method '%@' is not available" + @" for class '%@'", NSStringFromSelector (_cmd), + NSStringFromClass (isa)]; + + return [NSException exceptionWithName: @"SQLSourceIOException" + reason: reason + userInfo: nil]; +} + +@end From 0605fdf02a0c8bcaa8b2f365d3aa973b9fd55b08 Mon Sep 17 00:00:00 2001 From: Euan Thoms Date: Wed, 4 Nov 2015 03:40:53 +0800 Subject: [PATCH 27/69] Delete .orig files left over from applying patches to Stage 2. --- SOPE/NGCards/iCalPerson.m.orig | 299 ------ .../Contacts/SOGoContactSourceFolder.m.orig | 808 --------------- SoObjects/SOGo/SOGoCacheGCSFolder.m.orig | 486 --------- SoObjects/SOGo/SQLSource.m.orig | 971 ------------------ 4 files changed, 2564 deletions(-) delete mode 100644 SOPE/NGCards/iCalPerson.m.orig delete mode 100644 SoObjects/Contacts/SOGoContactSourceFolder.m.orig delete mode 100644 SoObjects/SOGo/SOGoCacheGCSFolder.m.orig delete mode 100644 SoObjects/SOGo/SQLSource.m.orig diff --git a/SOPE/NGCards/iCalPerson.m.orig b/SOPE/NGCards/iCalPerson.m.orig deleted file mode 100644 index 7904fd0f8..000000000 --- a/SOPE/NGCards/iCalPerson.m.orig +++ /dev/null @@ -1,299 +0,0 @@ -/* - Copyright (C) 2000-2005 SKYRIX Software AG - - This file is part of SOPE. - - SOPE is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - SOPE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with SOPE; see the file COPYING. If not, write to the - Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ - -#import - -#import "iCalPerson.h" - -@implementation iCalPerson - -+ (NSString *) descriptionForParticipationStatus: (iCalPersonPartStat) _status -{ - NSString *stat; - - switch (_status) { - case iCalPersonPartStatUndefined: - stat = @""; - break; - case iCalPersonPartStatAccepted: - stat = @"ACCEPTED"; - break; - case iCalPersonPartStatDeclined: - stat = @"DECLINED"; - break; - case iCalPersonPartStatTentative: - stat = @"TENTATIVE"; - break; - case iCalPersonPartStatDelegated: - stat = @"DELEGATED"; - break; - case iCalPersonPartStatCompleted: - stat = @"COMPLETED"; - break; - case iCalPersonPartStatInProcess: - stat = @"IN-PROCESS"; - break; - case iCalPersonPartStatExperimental: - case iCalPersonPartStatOther: -// [NSException raise:NSInternalInconsistencyException -// format:@"Attempt to set meaningless " -// @"participationStatus (%d)!", _status]; - stat = nil; /* keep compiler happy */ - break; - default: - stat = @"NEEDS-ACTION"; - break; - } - - return stat; -} - - -/* accessors */ - -- (void) setCn: (NSString *) _s -{ - [self setValue: 0 ofAttribute: @"cn" to: _s]; -} - -- (NSString *) cn -{ - return [self value: 0 ofAttribute: @"cn"]; -} - -- (NSString *) cnWithoutQuotes -{ - /* remove quotes around a CN */ - NSString *_cn; - - _cn = [self cn]; - if ([_cn length] <= 2) - return _cn; - if ([_cn characterAtIndex:0] != '"') - return _cn; - if (![_cn hasSuffix:@"\""]) - return _cn; - - return [_cn substringWithRange:NSMakeRange(1, [_cn length] - 2)]; -} - -- (void) setEmail: (NSString *)_s -{ - /* iCal.app compatibility: - - "mailto" prefix must be in lowercase; */ - [self setSingleValue: [NSString stringWithFormat: @"mailto:%@", _s] - forKey: @""]; -} - -- (NSString *) email -{ - return [self flattenedValuesForKey: @""]; -} - -- (NSString *) rfc822Email -{ - NSString *_email; - unsigned idx; - - _email = [self email]; - idx = NSMaxRange([_email rangeOfString:@":"]); - - if ((idx > 0) && ([_email length] > idx)) - return [_email substringFromIndex:idx]; - - return _email; -} - -- (void) setRsvp: (NSString *) _s -{ - [self setValue: 0 ofAttribute: @"rsvp" to: _s]; -} - -- (NSString *) rsvp -{ - return [[self value: 0 ofAttribute: @"rsvp"] lowercaseString]; -} - -// - (void)setXuid:(NSString *)_s { -// ASSIGNCOPY(self->xuid, _s); -// } -// - (NSString *)xuid { -// return self->xuid; -// } - -- (void)setRole:(NSString *)_s -{ - [self setValue: 0 ofAttribute: @"role" to: _s]; -} - -- (NSString *) role -{ - return [self value: 0 ofAttribute: @"role"]; -} - -- (void)setPartStat:(NSString *)_s -{ - [self setValue: 0 ofAttribute: @"partstat" to: _s]; -} - -- (NSString *) partStat -{ - return [self value: 0 ofAttribute: @"partstat"]; -} - -- (NSString *) partStatWithDefault -{ - NSString *s; - - s = [self partStat]; - if ([s length] > 0) - return s; - - return @"NEEDS-ACTION"; -} - -- (void) setParticipationStatus: (iCalPersonPartStat) _status -{ - NSString *stat; - - stat = [iCalPerson descriptionForParticipationStatus: _status]; - - if (stat) - [self setPartStat:stat]; -} - -- (iCalPersonPartStat) participationStatus { - NSString *stat; - - stat = [[self partStat] uppercaseString]; - if (![stat length]) - return iCalPersonPartStatUndefined; - else if ([stat isEqualToString:@"NEEDS-ACTION"]) - return iCalPersonPartStatNeedsAction; - else if ([stat isEqualToString:@"ACCEPTED"]) - return iCalPersonPartStatAccepted; - else if ([stat isEqualToString:@"DECLINED"]) - return iCalPersonPartStatDeclined; - else if ([stat isEqualToString:@"TENTATIVE"]) - return iCalPersonPartStatTentative; - else if ([stat isEqualToString:@"DELEGATED"]) - return iCalPersonPartStatDelegated; - else if ([stat isEqualToString:@"COMPLETED"]) - return iCalPersonPartStatCompleted; - else if ([stat isEqualToString:@"IN-PROCESS"]) - return iCalPersonPartStatInProcess; - else if ([stat hasPrefix:@"X-"]) - return iCalPersonPartStatExperimental; - return iCalPersonPartStatOther; -} - -- (void) _setValueOfMailtoAttribute: (NSString *) name - to: (NSString *) value -{ - if ([value length] && ![value hasPrefix: @"\""]) - value = [NSString stringWithFormat: @"\"%@\"", value]; - - [self setValue: 0 ofAttribute: name to: value]; -} - -- (NSString *) _valueOfMailtoAttribute: (NSString *) name -{ - NSString *mailTo; - - mailTo = [self value: 0 ofAttribute: name]; - if ([mailTo hasPrefix: @"\""]) - mailTo - = [mailTo substringWithRange: NSMakeRange (1, [mailTo length] - 2)]; - - return mailTo; -} - -- (void) setDelegatedTo: (NSString *) newDelegate -{ - [self _setValueOfMailtoAttribute: @"delegated-to" to: newDelegate]; -} - -- (NSString *) delegatedTo -{ - return [self _valueOfMailtoAttribute: @"delegated-to"]; -} - -- (void) setDelegatedFrom: (NSString *) newDelegator -{ - [self _setValueOfMailtoAttribute: @"delegated-from" to: newDelegator]; -} - -- (NSString *) delegatedFrom -{ - return [self _valueOfMailtoAttribute: @"delegated-from"]; -} - -- (void) setSentBy: (NSString *) newSentBy -{ - [self _setValueOfMailtoAttribute: @"sent-by" to: newSentBy]; -} - -- (NSString *) sentBy -{ - return [self _valueOfMailtoAttribute: @"sent-by"]; -} - -/* comparison */ - -- (NSUInteger) hash { - if ([self email]) - return [[self email] hash]; - return [super hash]; -} - -- (BOOL)isEqual:(id)_other { - if(_other == nil) - return NO; - if([_other class] != self->isa) - return NO; - if([_other hash] != [self hash]) - return NO; - return [self isEqualToPerson:_other]; -} - -- (BOOL)isEqualToPerson:(iCalPerson *)_other { - if(![self hasSameEmailAddress:_other]) - return NO; - if(!IS_EQUAL([self cn], [_other cn], isEqualToString:)) - return NO; - if(!IS_EQUAL([self rsvp], [_other rsvp], isEqualToString:)) - return NO; - if(!IS_EQUAL([self partStat], [_other partStat], isEqualToString:)) - return NO; - if(!IS_EQUAL([self role], [_other role], isEqualToString:)) - return NO; -// if(!IS_EQUAL([self xuid], [_other xuid], isEqualToString:)) -// return NO; - return YES; -} - -- (BOOL)hasSameEmailAddress:(iCalPerson *)_other { - return IS_EQUAL([[self email] lowercaseString], - [[_other email] lowercaseString], - isEqualToString:); -} - -@end /* iCalPerson */ diff --git a/SoObjects/Contacts/SOGoContactSourceFolder.m.orig b/SoObjects/Contacts/SOGoContactSourceFolder.m.orig deleted file mode 100644 index a52ec20b9..000000000 --- a/SoObjects/Contacts/SOGoContactSourceFolder.m.orig +++ /dev/null @@ -1,808 +0,0 @@ -/* SOGoContactSourceFolder.m - this file is part of SOGo - * - * Copyright (C) 2006-2015 Inverse inc. - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#import -#import -#import -#import -#import -#import - -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -#import "SOGoContactFolders.h" -#import "SOGoContactGCSFolder.h" -#import "SOGoContactLDIFEntry.h" -#import "SOGoContactSourceFolder.h" - -@class WOContext; - -@implementation SOGoContactSourceFolder - -+ (id) folderWithName: (NSString *) aName - andDisplayName: (NSString *) aDisplayName - inContainer: (id) aContainer -{ - id folder; - - folder = [[self alloc] initWithName: aName - andDisplayName: aDisplayName - inContainer: aContainer]; - [folder autorelease]; - - return folder; -} - -- (id) init -{ - if ((self = [super init])) - { - childRecords = [NSMutableDictionary new]; - source = nil; - } - - return self; -} - -- (id) initWithName: (NSString *) newName - andDisplayName: (NSString *) newDisplayName - inContainer: (id) newContainer -{ - if ((self = [self initWithName: newName - inContainer: newContainer])) - { - if (![newDisplayName length]) - newDisplayName = newName; - ASSIGN (displayName, newDisplayName); - } - - return self; -} - -- (void) dealloc -{ - [childRecords release]; - [source release]; - [super dealloc]; -} - -- (void) setSource: (id ) newSource -{ - ASSIGN (source, newSource); -} - -- (id ) source -{ - return source; -} - -- (void) setIsPersonalSource: (BOOL) isPersonal -{ - isPersonalSource = isPersonal; -} - -- (BOOL) isPersonalSource -{ - return isPersonalSource; -} - -- (NSString *) groupDavResourceType -{ - return @"vcard-collection"; -} - -- (NSArray *) davResourceType -{ - NSMutableArray *resourceType; - NSArray *type; - - resourceType = [NSMutableArray arrayWithArray: [super davResourceType]]; - type = [NSArray arrayWithObjects: @"addressbook", XMLNS_CARDDAV, nil]; - [resourceType addObject: type]; - type = [NSArray arrayWithObjects: @"directory", XMLNS_CARDDAV, nil]; - [resourceType addObject: type]; - - return resourceType; -} - -- (id) lookupName: (NSString *) objectName - inContext: (WOContext *) lookupContext - acquire: (BOOL) acquire -{ - NSDictionary *ldifEntry; - SOGoContactLDIFEntry *obj; - NSString *url; - BOOL isNew = NO; - NSArray *baseClasses; - - /* first check attributes directly bound to the application */ - obj = [super lookupName: objectName inContext: lookupContext acquire: NO]; - - if (!obj) - { - ldifEntry = [childRecords objectForKey: objectName]; - if (!ldifEntry) - { - ldifEntry = [source lookupContactEntry: objectName]; - if (ldifEntry) - [childRecords setObject: ldifEntry forKey: objectName]; - else if ([self isValidContentName: objectName]) - { - url = [[[lookupContext request] uri] urlWithoutParameters]; - if ([url hasSuffix: @"AsContact"]) - { - baseClasses = [NSArray arrayWithObjects: @"inetorgperson", - @"mozillaabpersonalpha", nil]; - ldifEntry = [NSMutableDictionary - dictionaryWithObject: baseClasses - forKey: @"objectclass"]; - isNew = YES; - } - } - } - if (ldifEntry) - { - obj = [SOGoContactLDIFEntry contactEntryWithName: objectName - withLDIFEntry: ldifEntry - inContainer: self]; - if (isNew) - [obj setIsNew: YES]; - } - else - obj = [NSException exceptionWithHTTPStatus: 404]; - } - - return obj; -} - -- (NSArray *) toOneRelationshipKeys -{ - NSString *userDomain; - - userDomain = [[context activeUser] domain]; - return [source allEntryIDsVisibleFromDomain: userDomain]; -} - -- (NSException *) saveLDIFEntry: (SOGoContactLDIFEntry *) ldifEntry -{ - return (([ldifEntry isNew]) - ? [source addContactEntry: [ldifEntry ldifRecord] - withID: [ldifEntry nameInContainer]] - : [source updateContactEntry: [ldifEntry ldifRecord]]); -} - -- (NSException *) deleteLDIFEntry: (SOGoContactLDIFEntry *) ldifEntry -{ - return [source removeContactEntryWithID: [ldifEntry nameInContainer]]; -} - -/** - * Normalize keys of dictionary representing a contact. - * @param oldRecord a dictionary with pairs from the source folder (LDAP or SQL) - * @see [SOGoContactGCSFolder _fixupContactRecord] - */ -- (NSDictionary *) _flattenedRecord: (NSDictionary *) oldRecord -{ - NSMutableDictionary *newRecord; - id data; - NSObject *recordSource; - - newRecord = [NSMutableDictionary dictionaryWithCapacity: 8]; - [newRecord setObject: [oldRecord objectForKey: @"c_uid"] - forKey: @"c_uid"]; - - // c_name => id - [newRecord setObject: [oldRecord objectForKey: @"c_name"] - forKey: @"c_name"]; - [newRecord setObject: [oldRecord objectForKey: @"c_name"] - forKey: @"id"]; - - // displayname || c_cn => fn - data = [oldRecord objectForKey: @"displayname"]; - if (!data) - data = [oldRecord objectForKey: @"c_cn"]; - if (data) - [newRecord setObject: data forKey: @"fn"]; - else - data = @""; - [newRecord setObject: data forKey: @"c_cn"]; - - // mail => emails[] - data = [oldRecord objectForKey: @"c_emails"]; - if (data) - { - if ([data isKindOfClass: [NSArray class]]) - { - if ([data count] > 0) - { - NSEnumerator *emails; - NSMutableArray *recordEmails; - NSString *email; - emails = [(NSArray *)data objectEnumerator]; - recordEmails = [NSMutableArray arrayWithCapacity: [data count]]; - while ((email = [emails nextObject])) - { - [recordEmails addObject: [NSDictionary dictionaryWithObject: email forKey: @"value"]]; - } - [newRecord setObject: recordEmails forKey: @"emails"]; - } - } - else if (data) - { - NSDictionary *email; - email = [NSDictionary dictionaryWithObjectsAndKeys: @"pref", @"type", data, @"value", nil]; - [newRecord setObject: [NSArray arrayWithObject: email] forKey: @"emails"]; - } - else - data = @""; - } - else - data = @""; - [newRecord setObject: data forKey: @"c_mail"]; - - data = [oldRecord objectForKey: @"nsaimid"]; - if (![data length]) - data = [oldRecord objectForKey: @"nscpaimscreenname"]; - if (![data length]) - data = @""; - [newRecord setObject: data forKey: @"c_screenname"]; - - // o => org - data = [oldRecord objectForKey: @"o"]; - if (data) - [newRecord setObject: data forKey: @"org"]; - else - data = @""; - [newRecord setObject: data forKey: @"c_o"]; - - // telephonenumber || cellphone || homephone => phones[] - data = [oldRecord objectForKey: @"telephonenumber"]; - if (![data length]) - data = [oldRecord objectForKey: @"cellphone"]; - if (![data length]) - data = [oldRecord objectForKey: @"homephone"]; - if (data) - { - NSDictionary *phonenumber; - phonenumber = [NSDictionary dictionaryWithObjectsAndKeys: @"pref", @"type", data, @"value", nil]; - [newRecord setObject: [NSArray arrayWithObject: phonenumber] forKey: @"phones"]; - } - else - data = @""; - [newRecord setObject: data forKey: @"c_telephonenumber"]; - - // Custom attribute for group-lookups. See LDAPSource.m where - // it's set. - data = [oldRecord objectForKey: @"isGroup"]; - if (data) - { - [newRecord setObject: data forKey: @"isGroup"]; - [newRecord setObject: @"vlist" forKey: @"c_component"]; - } -#warning TODO: create a custom icon for resources - else - { - [newRecord setObject: @"vcard" forKey: @"c_component"]; - } - - // c_info => note - data = [oldRecord objectForKey: @"c_info"]; - if ([data length] > 0) - { - [newRecord setObject: data forKey: @"note"]; - [newRecord setObject: data forKey: @"contactInfo"]; - } - - recordSource = [oldRecord objectForKey: @"source"]; - if ([recordSource conformsToProtocol: @protocol (SOGoDNSource)] && - [[(NSObject *) recordSource MSExchangeHostname] length]) - [newRecord setObject: [NSNumber numberWithInt: 1] forKey: @"isMSExchange"]; - - return newRecord; -} - -- (NSArray *) _flattenedRecords: (NSArray *) records -{ - NSMutableArray *newRecords; - NSEnumerator *oldRecords; - NSDictionary *oldRecord; - - newRecords = [NSMutableArray arrayWithCapacity: [records count]]; - - oldRecords = [records objectEnumerator]; - while ((oldRecord = [oldRecords nextObject])) - [newRecords addObject: [self _flattenedRecord: oldRecord]]; - - return newRecords; -} - -/* This method returns the entry corresponding to the name passed as - parameter. */ -- (NSDictionary *) lookupContactWithName: (NSString *) aName -{ - NSDictionary *record; - - if (aName && [aName length] > 0) - record = [self _flattenedRecord: [source lookupContactEntry: aName]]; - else - record = nil; - - return record; -} - -- (NSArray *) lookupContactsWithFilter: (NSString *) filter - onCriteria: (NSString *) criteria - sortBy: (NSString *) sortKey - ordering: (NSComparisonResult) sortOrdering - inDomain: (NSString *) domain -{ - NSArray *records, *result; - EOSortOrdering *ordering; - - result = nil; - - if (([filter length] > 0 && [criteria isEqualToString: @"name_or_address"]) - || ![source listRequiresDot]) - { - records = [source fetchContactsMatching: filter - inDomain: domain]; - [childRecords setObjects: records - forKeys: [records objectsForKey: @"c_name" - notFoundMarker: nil]]; - records = [self _flattenedRecords: records]; - ordering - = [EOSortOrdering sortOrderingWithKey: sortKey - selector: ((sortOrdering == NSOrderedDescending) - ? EOCompareCaseInsensitiveDescending - : EOCompareCaseInsensitiveAscending)]; - result - = [records sortedArrayUsingKeyOrderArray: - [NSArray arrayWithObject: ordering]]; - } - - return result; -} - -- (NSString *) _deduceObjectNameFromURL: (NSString *) url - fromBaseURL: (NSString *) baseURL -{ - NSRange urlRange; - NSString *name; - - urlRange = [url rangeOfString: baseURL]; - if (urlRange.location != NSNotFound) - { - name = [url substringFromIndex: NSMaxRange (urlRange)]; - if ([name hasPrefix: @"/"]) - name = [name substringFromIndex: 1]; - } - else - name = nil; - - return name; -} - -/* TODO: multiget reorg */ -- (NSString *) _nodeTagForProperty: (NSString *) property -{ - NSString *namespace, *nodeName, *nsRep; - NSRange nsEnd; - - nsEnd = [property rangeOfString: @"}"]; - namespace - = [property substringFromRange: NSMakeRange (1, nsEnd.location - 1)]; - nodeName = [property substringFromIndex: nsEnd.location + 1]; - if ([namespace isEqualToString: XMLNS_CARDDAV]) - nsRep = @"C"; - else - nsRep = @"D"; - - return [NSString stringWithFormat: @"%@:%@", nsRep, nodeName]; -} - -- (NSString *) _nodeTag: (NSString *) property -{ - static NSMutableDictionary *tags = nil; - NSString *nodeTag; - - if (!tags) - tags = [NSMutableDictionary new]; - nodeTag = [tags objectForKey: property]; - if (!nodeTag) - { - nodeTag = [self _nodeTagForProperty: property]; - [tags setObject: nodeTag forKey: property]; - } - - return nodeTag; -} - -- (NSString **) _properties: (NSString **) properties - count: (unsigned int) propertiesCount - ofObject: (NSDictionary *) object -{ - SOGoContactLDIFEntry *ldifEntry; - NSString **currentProperty; - NSString **values, **currentValue; - SEL methodSel; - -// NSLog (@"_properties:ofObject:: %@", [NSDate date]); - - values = NSZoneMalloc (NULL, - (propertiesCount + 1) * sizeof (NSString *)); - *(values + propertiesCount) = nil; - - ldifEntry = [SOGoContactLDIFEntry - contactEntryWithName: [object objectForKey: @"c_name"] - withLDIFEntry: object - inContainer: self]; - currentProperty = properties; - currentValue = values; - while (*currentProperty) - { - methodSel = SOGoSelectorForPropertyGetter (*currentProperty); - if (methodSel && [ldifEntry respondsToSelector: methodSel]) - *currentValue = [[ldifEntry performSelector: methodSel] - stringByEscapingXMLString]; - currentProperty++; - currentValue++; - } - -// NSLog (@"/_properties:ofObject:: %@", [NSDate date]); - - return values; -} - -- (NSArray *) _propstats: (NSString **) properties - count: (unsigned int) propertiesCount - ofObject: (NSDictionary *) object -{ - NSMutableArray *propstats, *properties200, *properties404, *propDict; - NSString **property, **values, **currentValue; - NSString *propertyValue, *nodeTag; - -// NSLog (@"_propstats:ofObject:: %@", [NSDate date]); - - propstats = [NSMutableArray array]; - - properties200 = [NSMutableArray array]; - properties404 = [NSMutableArray array]; - - values = [self _properties: properties count: propertiesCount - ofObject: object]; - currentValue = values; - - property = properties; - while (*property) - { - nodeTag = [self _nodeTag: *property]; - if (*currentValue) - { - propertyValue = [NSString stringWithFormat: @"<%@>%@", - nodeTag, *currentValue, nodeTag]; - propDict = properties200; - } - else - { - propertyValue = [NSString stringWithFormat: @"<%@/>", nodeTag]; - propDict = properties404; - } - [propDict addObject: propertyValue]; - property++; - currentValue++; - } - free (values); - - if ([properties200 count]) - [propstats addObject: [NSDictionary dictionaryWithObjectsAndKeys: - properties200, @"properties", - @"HTTP/1.1 200 OK", @"status", - nil]]; - if ([properties404 count]) - [propstats addObject: [NSDictionary dictionaryWithObjectsAndKeys: - properties404, @"properties", - @"HTTP/1.1 404 Not Found", @"status", - nil]]; -// NSLog (@"/_propstats:ofObject:: %@", [NSDate date]); - - return propstats; -} - -- (void) _appendPropstat: (NSDictionary *) propstat - toBuffer: (NSMutableString *) r -{ - NSArray *properties; - unsigned int count, max; - - [r appendString: @""]; - properties = [propstat objectForKey: @"properties"]; - max = [properties count]; - for (count = 0; count < max; count++) - [r appendString: [properties objectAtIndex: count]]; - [r appendString: @""]; - [r appendString: [propstat objectForKey: @"status"]]; - [r appendString: @""]; -} - -- (void) appendObject: (NSDictionary *) object - properties: (NSString **) properties - count: (unsigned int) propertiesCount - withBaseURL: (NSString *) baseURL - toBuffer: (NSMutableString *) r -{ - NSArray *propstats; - unsigned int count, max; - - [r appendFormat: @""]; - [r appendString: baseURL]; - [r appendString: [[object objectForKey: @"c_name"] stringByEscapingURL]]; - [r appendString: @""]; - - propstats = [self _propstats: properties count: propertiesCount - ofObject: object]; - max = [propstats count]; - for (count = 0; count < max; count++) - [self _appendPropstat: [propstats objectAtIndex: count] - toBuffer: r]; - - [r appendString: @""]; -} - -- (void) appendMissingObjectRef: (NSString *) href - toBuffer: (NSMutableString *) r -{ - [r appendString: @""]; - [r appendString: href]; - [r appendString: @"HTTP/1.1 404 Not Found"]; -} - -- (void) _appendComponentProperties: (NSArray *) properties - matchingURLs: (id ) refs - toResponse: (WOResponse *) response -{ - NSObject *element; - NSString *url, *baseURL, *cname; - NSString **propertiesArray; - NSMutableString *buffer; - NSDictionary *object; - - unsigned int count, max, propertiesCount; - - baseURL = [self davURLAsString]; -#warning review this when fixing http://www.scalableogo.org/bugs/view.php?id=276 - if (![baseURL hasSuffix: @"/"]) - baseURL = [NSString stringWithFormat: @"%@/", baseURL]; - - propertiesArray = [properties asPointersOfObjects]; - propertiesCount = [properties count]; - - max = [refs length]; - buffer = [NSMutableString stringWithCapacity: max*512]; - - for (count = 0; count < max; count++) - { - element = [refs objectAtIndex: count]; - url = [[[element firstChild] nodeValue] stringByUnescapingURL]; - cname = [self _deduceObjectNameFromURL: url fromBaseURL: baseURL]; - object = [source lookupContactEntry: cname]; - if (object) - [self appendObject: object - properties: propertiesArray - count: propertiesCount - withBaseURL: baseURL - toBuffer: buffer]; - else - [self appendMissingObjectRef: url - toBuffer: buffer]; - } - [response appendContentString: buffer]; -// NSLog (@"/adding properties with url"); - - NSZoneFree (NULL, propertiesArray); -} - -- (WOResponse *) performMultigetInContext: (WOContext *) queryContext - inNamespace: (NSString *) namespace -{ - WOResponse *r; - id document; - id documentElement, propElement; - - r = [context response]; - [r prepareDAVResponse]; - [r appendContentString: - [NSString stringWithFormat: @"", namespace]]; - document = [[queryContext request] contentAsDOMDocument]; - documentElement = [document documentElement]; - propElement = [(NGDOMNodeWithChildren *) documentElement - firstElementWithTag: @"prop" - inNamespace: @"DAV:"]; - [self _appendComponentProperties: [(NGDOMNodeWithChildren *) propElement flatPropertyNameOfSubElements] - matchingURLs: [documentElement getElementsByTagName: @"href"] - toResponse: r]; - [r appendContentString:@""]; - - return r; -} - -- (id) davAddressbookMultiget: (id) queryContext -{ - return [self performMultigetInContext: queryContext - inNamespace: XMLNS_CARDDAV]; -} - -- (NSString *) davDisplayName -{ - return displayName; -} - -- (BOOL) isFolderish -{ - return YES; -} - -/* folder type */ - -- (NSString *) folderType -{ - return @"Contact"; -} - -/* sorting */ -- (NSComparisonResult) compare: (id) otherFolder -{ - NSComparisonResult comparison; - BOOL otherIsPersonal; - - otherIsPersonal = ([otherFolder isKindOfClass: [SOGoContactGCSFolder class]] - || ([otherFolder isKindOfClass: isa] && [otherFolder isPersonalSource])); - - if (isPersonalSource) - { - if (otherIsPersonal && ![nameInContainer isEqualToString: @"personal"]) - { - if ([[otherFolder nameInContainer] isEqualToString: @"personal"]) - comparison = NSOrderedDescending; - else - comparison - = [[self displayName] - localizedCaseInsensitiveCompare: [otherFolder displayName]]; - } - else - comparison = NSOrderedAscending; - } - else - { - if (otherIsPersonal) - comparison = NSOrderedDescending; - else - comparison - = [[self displayName] - localizedCaseInsensitiveCompare: [otherFolder displayName]]; - } - - return comparison; -} - -/* common methods */ - -- (NSException *) delete -{ - NSException *error; - - if (isPersonalSource) - { - error = [(SOGoContactFolders *) container - removeLDAPAddressBook: nameInContainer]; - if (!error && [[context request] handledByDefaultHandler]) - [self sendFolderAdvisoryTemplate: @"Removal"]; - } - else - error = [NSException exceptionWithHTTPStatus: 501 /* not implemented */ - reason: @"delete not available on system sources"]; - - return error; -} - -- (void) renameTo: (NSString *) newName -{ - NSException *error; - - if (isPersonalSource) - { - if (![[source displayName] isEqualToString: newName]) - { - error = [(SOGoContactFolders *) container - renameLDAPAddressBook: nameInContainer - withDisplayName: newName]; - if (!error) - [self setDisplayName: newName]; - } - } - /* If public source then method is ignored, maybe we should return an - NSException instead... */ -} - -/* acls */ -- (NSString *) ownerInContext: (WOContext *) noContext -{ - NSString *sourceOwner; - - if (isPersonalSource) - sourceOwner = [[source modifiers] objectAtIndex: 0]; - else - sourceOwner = @"nobody"; - - return sourceOwner; -} - -- (NSArray *) subscriptionRoles -{ - return [NSArray arrayWithObject: SoRole_Authenticated]; -} - -- (NSArray *) aclsForUser: (NSString *) uid -{ - NSArray *acls, *modifiers; - static NSArray *modifierRoles = nil; - - if (!modifierRoles) - modifierRoles = [[NSArray alloc] initWithObjects: @"Owner", - @"ObjectViewer", - @"ObjectEditor", @"ObjectCreator", - @"ObjectEraser", nil]; - - modifiers = [source modifiers]; - if ([modifiers containsObject: uid]) - acls = [modifierRoles copy]; - else - acls = [NSArray new]; - - [acls autorelease]; - - return acls; -} - -@end diff --git a/SoObjects/SOGo/SOGoCacheGCSFolder.m.orig b/SoObjects/SOGo/SOGoCacheGCSFolder.m.orig deleted file mode 100644 index b29287fcb..000000000 --- a/SoObjects/SOGo/SOGoCacheGCSFolder.m.orig +++ /dev/null @@ -1,486 +0,0 @@ -/* SOGoCacheGCSFolder.m - this file is part of SOGo - * - * Copyright (C) 2012-2014 Inverse inc. - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#import -#import -#import -#import -#import -#import -#import -#import - -#import -#import -#import -#import - -#import -#import -#import -#import -#import "EOQualifier+SOGoCacheObject.h" -#import "GCSSpecialQueries+SOGoCacheObject.h" - -#import "SOGoCacheGCSFolder.h" - -#undef DEBUG -//#include -//#include -//#include -//#include -//#include -//#include -//#include - -Class SOGoCacheGCSObjectK = Nil; - -@implementation SOGoCacheGCSFolder - -+ (void) initialize -{ - SOGoCacheGCSObjectK = [SOGoCacheGCSObject class]; -} - -- (id) init -{ - if ((self = [super init])) - { - pathPrefix = nil; - } - - return self; -} - -- (id) initWithName: (NSString *) name inContainer: (id) newContainer -{ - if ((self = [super initWithName: name inContainer: newContainer])) - { - objectType = MAPIFolderCacheObject; - aclMessage = [SOGoCacheGCSObject objectWithName: @"permissions" - inContainer: self]; - [aclMessage setObjectType: MAPIInternalCacheObject]; - [aclMessage retain]; - } - - return self; -} - -- (void) dealloc -{ - [aclMessage release]; - [pathPrefix release]; - [super dealloc]; -} - -- (BOOL) isFolderish -{ - return YES; -} - -- (void) setPathPrefix: (NSString *) newPathPrefix -{ - ASSIGN (pathPrefix, newPathPrefix); -} - -- (NSMutableString *) pathForChild: (NSString *) childName -{ - NSMutableString *path; - - path = [self path]; - [path appendFormat: @"/%@", childName]; - - return path; -} - -- (NSMutableString *) path -{ - NSMutableString *path; - - path = [super path]; - if (pathPrefix) - [path insertString: pathPrefix atIndex: 0]; - - return path; -} - -// - (SOGoMAPIDBMessage *) newMessage -// { -// NSString *newFilename; - -// newFilename = [NSString stringWithFormat: @"%@.plist", -// [SOGoObject globallyUniqueObjectId]]; - -// return [SOGoMAPIDBMessage objectWithName: filename inContainer: self]; -// } - -- (NSArray *) childKeysOfType: (SOGoCacheObjectType) type - includeDeleted: (BOOL) includeDeleted - matchingQualifier: (EOQualifier *) qualifier - andSortOrderings: (NSArray *) sortOrderings -{ - NSMutableArray *childKeys; - NSMutableString *sql// , *qualifierClause - ; - NSString *childPathPrefix, *childPath, *childKey; - NSMutableArray *whereClause; - NSArray *records; - NSDictionary *record; - NSUInteger childPathPrefixLen, count, max; - SOGoCacheGCSObject *currentChild; - - /* query construction */ - sql = [NSMutableString stringWithCapacity: 256]; - [sql appendFormat: @"SELECT * FROM %@", [self tableName]]; - - whereClause = [NSMutableArray arrayWithCapacity: 2]; - [whereClause addObject: [NSString stringWithFormat: @"c_parent_path = '%@'", - [self path]]]; - [whereClause addObject: [NSString stringWithFormat: @"c_type = %d", type]]; - if (!includeDeleted) - [whereClause addObject: @"c_deleted = 0"]; - - [sql appendFormat: @" WHERE %@", - [whereClause componentsJoinedByString: @" AND "]]; - - childPathPrefix = [NSString stringWithFormat: @"%@/", [self path]]; - - /* results */ - records = [self performSQLQuery: sql]; - if (records) - { - max = [records count]; - childKeys = [NSMutableArray arrayWithCapacity: max]; - childPathPrefixLen = [childPathPrefix length]; - for (count = 0; count < max; count++) - { - record = [records objectAtIndex: count]; - childPath = [record objectForKey: @"c_path"]; - childKey = [childPath substringFromIndex: childPathPrefixLen]; - if ([childKey rangeOfString: @"/"].location == NSNotFound) - { - if (qualifier) - { - currentChild = [SOGoCacheGCSObject objectWithName: childKey - inContainer: self]; - [currentChild setupFromRecord: record]; - if ([qualifier evaluateSOGoMAPIDBObject: currentChild]) - [childKeys addObject: childKey]; - } - else - [childKeys addObject: childKey]; - } - } - } - else - childKeys = nil; - - return childKeys; -} - -- (NSArray *) toManyRelationshipKeys -{ - return [self childKeysOfType: MAPIFolderCacheObject - includeDeleted: NO - matchingQualifier: nil - andSortOrderings: nil]; -} - -- (NSArray *) toOneRelationshipKeys -{ - return [self childKeysOfType: MAPIMessageCacheObject - includeDeleted: NO - matchingQualifier: nil - andSortOrderings: nil]; -} - -- (void) setNameInContainer: (NSString *) newName -{ - NSMutableString *sql; - NSString *oldPath, *newPath, *path, *parentPath; - NSMutableArray *queries; - NSArray *records; - NSDictionary *record; - NSUInteger count, max; - - /* change the paths in children records */ - if (nameInContainer) - oldPath = [self path]; - - [super setNameInContainer: newName]; - - if (nameInContainer) - { - newPath = [self path]; - - sql = [NSMutableString stringWithFormat: - @"SELECT c_path, c_parent_path FROM %@" - @" WHERE c_path LIKE '%@/%%'", - [self tableName], oldPath]; - records = [self performSQLQuery: sql]; - max = [records count]; - queries = [NSMutableArray arrayWithCapacity: max + 1]; - if (max > 0) - { - for (count = 0; count < max; count++) - { - record = [records objectAtIndex: count]; - path = [record objectForKey: @"c_path"]; - sql = [NSMutableString stringWithFormat: @"UPDATE %@" - @" SET c_path = '%@'", - [self tableName], - [path stringByReplacingPrefix: oldPath - withPrefix: newPath]]; - parentPath = [record objectForKey: @"c_parent_path"]; - if ([parentPath isNotNull]) - [sql appendFormat: @", c_parent_path = '%@'", - [parentPath stringByReplacingPrefix: oldPath - withPrefix: newPath]]; - [sql appendFormat: @" WHERE c_path = '%@'", path]; - [queries addObject: sql]; - } - [self performBatchSQLQueries: queries]; - } - } -} - -- (void) changePathTo: (NSString *) newPath -{ - NSMutableString *sql// , *qualifierClause - ; - NSString *oldPath, *oldPathAsPrefix, *path, *parentPath; - NSMutableArray *queries; - NSArray *records; - NSDictionary *record; - NSUInteger count, max; - - /* change the paths in children records */ - oldPath = [self path]; - oldPathAsPrefix = [NSString stringWithFormat: @"%@/", oldPath]; - - sql = [NSMutableString stringWithFormat: - @"SELECT c_path, c_parent_path FROM %@" - @" WHERE c_path LIKE '%@%%'", - [self tableName], oldPathAsPrefix]; - records = [self performSQLQuery: sql]; - max = [records count]; - queries = [NSMutableArray arrayWithCapacity: max + 1]; - if (max > 0) - { - for (count = 0; count < max; count++) - { - record = [records objectAtIndex: count]; - path = [record objectForKey: @"c_path"]; - sql = [NSMutableString stringWithFormat: @"UPDATE %@" - @" SET c_path = '%@'", - [self tableName], - [path stringByReplacingPrefix: oldPath - withPrefix: newPath]]; - parentPath = [record objectForKey: @"c_parent_path"]; - if ([parentPath isNotNull]) - [sql appendFormat: @", c_parent_path = '%@'", - [parentPath stringByReplacingPrefix: oldPath - withPrefix: newPath]]; - [sql appendFormat: @" WHERE c_path = '%@'", path]; - [queries addObject: sql]; - } - [self performBatchSQLQueries: queries]; - } - - /* change the path in this folder record */ - [super changePathTo: newPath]; -} - -- (void) changePathTo: (NSString *) newPath intoNewContainer: (id) newContainer -{ - [self changePathTo: newPath]; - container = newContainer; - if ([self doesRetainContainer]) - [container retain]; -} - -// - (NSArray *) toOneRelationshipKeysMatchingQualifier: (EOQualifier *) qualifier -// andSortOrderings: (NSArray *) sortOrderings -// { -// NSArray *allKeys; -// NSMutableArray *keys; -// NSUInteger count, max; -// NSString *messageKey; -// SOGoMAPIDBMessage *message; - -// if (sortOrderings) -// [self warnWithFormat: @"sorting is not handled yet"]; - -// allKeys = [self toOneRelationshipKeys]; -// if (qualifier) -// { -// [self logWithFormat: @"%s: getting restricted FAI keys", __PRETTY_FUNCTION__]; -// max = [allKeys count]; -// keys = [NSMutableArray arrayWithCapacity: max]; -// for (count = 0; count < max; count++) -// { -// messageKey = [allKeys objectAtIndex: count]; -// message = [self lookupName: messageKey -// inContext: nil -// acquire: NO]; -// if ([qualifier evaluateMAPIVolatileMessage: message]) -// [keys addObject: messageKey]; -// } -// } -// else -// keys = (NSMutableArray *) allKeys; - -// return keys; -// } - -- (id) lookupName: (NSString *) childName - inContext: (WOContext *) woContext - acquire: (BOOL) acquire -{ - id object; - Class objectClass; - NSString *childPath; - NSDictionary *record; - - childPath = [self pathForChild: childName]; - record = [self lookupRecord: childPath newerThanVersion: -1]; - if (record) - { - if ([[record objectForKey: @"c_type"] intValue] == MAPIFolderCacheObject) - objectClass = isa; - else - objectClass = SOGoCacheGCSObjectK; - - object = [objectClass objectWithName: childName - inContainer: self]; - [object setupFromRecord: record]; - } - else - object = nil; - - return object; -} - -- (id) lookupFolder: (NSString *) folderName - inContext: (WOContext *) woContext -{ - id object; - - object = [SOGoCacheGCSFolder objectWithName: folderName - inContainer: self]; - [object reloadIfNeeded]; - - return object; -} - -// - (id) _fileAttributeForKey: (NSString *) key -// { -// NSDictionary *attributes; - -// attributes = [[NSFileManager defaultManager] -// fileAttributesAtPath: directory -// traverseLink: NO]; - -// return [attributes objectForKey: key]; -// } - -// - (NSCalendarDate *) creationTime -// { -// return [self _fileAttributeForKey: NSFileCreationDate]; -// } - -// - (NSCalendarDate *) lastModificationTime -// { -// return [self _fileAttributeForKey: NSFileModificationDate]; -// } - -/* acl */ -- (NSString *) defaultUserID -{ - return @"default"; -} - -- (NSMutableDictionary *) _aclEntries -{ - NSMutableDictionary *aclEntries; - - [aclMessage reloadIfNeeded]; - aclEntries = [aclMessage properties]; - if (![aclEntries objectForKey: @"users"]) - [aclEntries setObject: [NSMutableArray array] forKey: @"users"]; - if (![aclEntries objectForKey: @"entries"]) - [aclEntries setObject: [NSMutableDictionary dictionary] - forKey: @"entries"]; - - return aclEntries; -} - -- (void) addUserInAcls: (NSString *) user -{ - NSMutableDictionary *acl; - NSMutableArray *users; - - acl = [self _aclEntries]; - users = [acl objectForKey: @"users"]; - [users addObjectUniquely: user]; - [aclMessage save]; -} - -- (void) removeAclsForUsers: (NSArray *) oldUsers -{ - NSDictionary *acl; - NSMutableDictionary *entries; - NSMutableArray *users; - - acl = [self _aclEntries]; - entries = [acl objectForKey: @"entries"]; - [entries removeObjectsForKeys: oldUsers]; - users = [acl objectForKey: @"users"]; - [users removeObjectsInArray: oldUsers]; - [aclMessage save]; -} - -- (NSArray *) aclUsers -{ - return [[self _aclEntries] objectForKey: @"users"]; -} - -- (NSArray *) aclsForUser: (NSString *) uid -{ - NSDictionary *entries; - - entries = [[self _aclEntries] objectForKey: @"entries"]; - - return [entries objectForKey: uid]; -} - -- (void) setRoles: (NSArray *) roles - forUser: (NSString *) uid -{ - NSMutableDictionary *acl; - NSMutableDictionary *entries; - - acl = [self _aclEntries]; - entries = [acl objectForKey: @"entries"]; - [entries setObject: roles forKey: uid]; - [aclMessage save]; -} - -@end diff --git a/SoObjects/SOGo/SQLSource.m.orig b/SoObjects/SOGo/SQLSource.m.orig deleted file mode 100644 index aa881b9d3..000000000 --- a/SoObjects/SOGo/SQLSource.m.orig +++ /dev/null @@ -1,971 +0,0 @@ -/* SQLSource.h - this file is part of SOGo - * - * Copyright (C) 2009-2012 Inverse inc. - * - * Authors: Ludovic Marcotte - * Francis Lachapelle - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#import -#import -#import -#import -#import -#import -#import - -#import -#import - -#import -#import -#import -#import - -#import - -#import "SOGoConstants.h" -#import "NSString+Utilities.h" -#import "NSString+Crypto.h" - -#import "SQLSource.h" - -/** - * The view MUST contain the following columns: - * - * c_uid - will be used for authentication - it's a username or username@domain.tld) - * c_name - which can be identical to c_uid - will be used to uniquely identify entries) - * c_password - password of the user, can be encoded in {scheme}pass format, or when stored without - * scheme it uses the scheme set in userPasswordAlgorithm. - * Possible algorithms are: plain, md5, crypt-md5, sha, ssha (including 256/512 variants), - * cram-md5, smd5, crypt, crypt-md5 - * c_cn - the user's common name - * mail - the user's mail address - * - * Other columns can be defined - see LDAPSource.m for the complete list. - * - * - * A SQL source can be defined like this: - * - * { - * id = zot; - * type = sql; - * viewURL = "mysql://sogo:sogo@127.0.0.1:5432/sogo/sogo_view"; - * canAuthenticate = YES; - * isAddressBook = YES; - * userPasswordAlgorithm = md5; - * prependPasswordScheme = YES; - * } - * - * If prependPasswordScheme is set to YES, the generated passwords will have the format {scheme}password. - * If it is NO (the default), the password will be written to database without encryption scheme. - * - */ - -@implementation SQLSource - -+ (id) sourceFromUDSource: (NSDictionary *) udSource - inDomain: (NSString *) domain -{ - return [[[self alloc] initFromUDSource: udSource - inDomain: domain] autorelease]; -} - -- (id) init -{ - if ((self = [super init])) - { - _sourceID = nil; - _domainField = nil; - _authenticationFilter = nil; - _loginFields = nil; - _mailFields = nil; - _userPasswordAlgorithm = nil; - _viewURL = nil; - _kindField = nil; - _multipleBookingsField = nil; - _imapHostField = nil; - _sieveHostField = nil; - } - - return self; -} - -- (void) dealloc -{ - [_sourceID release]; - [_authenticationFilter release]; - [_loginFields release]; - [_mailFields release]; - [_userPasswordAlgorithm release]; - [_viewURL release]; - [_kindField release]; - [_multipleBookingsField release]; - [_domainField release]; - [_imapHostField release]; - [_sieveHostField release]; - - [super dealloc]; -} - -- (id) initFromUDSource: (NSDictionary *) udSource - inDomain: (NSString *) sourceDomain -{ - self = [self init]; - - ASSIGN(_sourceID, [udSource objectForKey: @"id"]); - ASSIGN(_authenticationFilter, [udSource objectForKey: @"authenticationFilter"]); - ASSIGN(_loginFields, [udSource objectForKey: @"LoginFieldNames"]); - ASSIGN(_mailFields, [udSource objectForKey: @"MailFieldNames"]); - ASSIGN(_userPasswordAlgorithm, [udSource objectForKey: @"userPasswordAlgorithm"]); - ASSIGN(_imapLoginField, [udSource objectForKey: @"IMAPLoginFieldName"]); - ASSIGN(_imapHostField, [udSource objectForKey: @"IMAPHostFieldName"]); - ASSIGN(_sieveHostField, [udSource objectForKey: @"SieveHostFieldName"]); - ASSIGN(_kindField, [udSource objectForKey: @"KindFieldName"]); - ASSIGN(_multipleBookingsField, [udSource objectForKey: @"MultipleBookingsFieldName"]); - ASSIGN(_domainField, [udSource objectForKey: @"DomainFieldName"]); - if ([udSource objectForKey: @"prependPasswordScheme"]) - _prependPasswordScheme = [[udSource objectForKey: @"prependPasswordScheme"] boolValue]; - else - _prependPasswordScheme = NO; - - if (!_userPasswordAlgorithm) - _userPasswordAlgorithm = @"none"; - - if ([udSource objectForKey: @"viewURL"]) - _viewURL = [[NSURL alloc] initWithString: [udSource objectForKey: @"viewURL"]]; - -#warning this domain code has no effect yet - if ([sourceDomain length]) - ASSIGN (_domain, sourceDomain); - - if (!_viewURL) - { - [self autorelease]; - return nil; - } - - return self; -} - -- (NSString *) domain -{ - return _domain; -} - -- (BOOL) _isPassword: (NSString *) plainPassword - equalTo: (NSString *) encryptedPassword -{ - if (!plainPassword || !encryptedPassword) - return NO; - - return [plainPassword isEqualToCrypted: encryptedPassword - withDefaultScheme: _userPasswordAlgorithm]; -} - -/** - * Encrypts a string using this source password algorithm. - * @param plainPassword the unencrypted password. - * @return a new encrypted string. - * @see _isPassword:equalTo: - */ -- (NSString *) _encryptPassword: (NSString *) plainPassword -{ - NSString *pass; - NSString* result; - - pass = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm]; - - if (pass == nil) - { - [self errorWithFormat: @"Unsupported user-password algorithm: %@", _userPasswordAlgorithm]; - return nil; - } - - if (_prependPasswordScheme) - result = [NSString stringWithFormat: @"{%@}%@", _userPasswordAlgorithm, pass]; - else - result = pass; - - return result; -} - -// -// SQL sources don't support right now all the password policy -// stuff supported by OpenLDAP (and others). If we want to support -// this for SQL sources, we'll have to implement the same -// kind of logic in this module. -// -- (BOOL) checkLogin: (NSString *) _login - password: (NSString *) _pwd - perr: (SOGoPasswordPolicyError *) _perr - expire: (int *) _expire - grace: (int *) _grace -{ - EOAdaptorChannel *channel; - EOQualifier *qualifier; - GCSChannelManager *cm; - NSException *ex; - NSMutableString *sql; - BOOL rc; - - rc = NO; - - _login = [_login stringByReplacingString: @"'" withString: @"''"]; - cm = [GCSChannelManager defaultChannelManager]; - channel = [cm acquireOpenChannelForURL: _viewURL]; - if (channel) - { - if (_loginFields) - { - NSMutableArray *qualifiers; - NSString *field; - EOQualifier *loginQualifier; - int i; - - qualifiers = [NSMutableArray arrayWithCapacity: [_loginFields count]]; - for (i = 0; i < [_loginFields count]; i++) - { - field = [_loginFields objectAtIndex: i]; - loginQualifier = [[EOKeyValueQualifier alloc] initWithKey: field - operatorSelector: EOQualifierOperatorEqual - value: _login]; - [loginQualifier autorelease]; - [qualifiers addObject: loginQualifier]; - } - qualifier = [[EOOrQualifier alloc] initWithQualifierArray: qualifiers]; - } - else - { - qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"c_uid" - operatorSelector: EOQualifierOperatorEqual - value: _login]; - } - [qualifier autorelease]; - sql = [NSMutableString stringWithFormat: @"SELECT c_password" - @" FROM %@" - @" WHERE ", - [_viewURL gcsTableName]]; - if (_authenticationFilter) - { - qualifier = [[EOAndQualifier alloc] initWithQualifiers: - qualifier, - [EOQualifier qualifierWithQualifierFormat: _authenticationFilter], - nil]; - [qualifier autorelease]; - } - [qualifier _gcsAppendToString: sql]; - - ex = [channel evaluateExpressionX: sql]; - if (!ex) - { - NSDictionary *row; - NSArray *attrs; - NSString *value; - - attrs = [channel describeResults: NO]; - row = [channel fetchAttributes: attrs withZone: NULL]; - value = [row objectForKey: @"c_password"]; - - rc = [self _isPassword: _pwd equalTo: value]; - [channel cancelFetch]; - } - else - [self errorWithFormat: @"could not run SQL '%@': %@", qualifier, ex]; - - [cm releaseChannel: channel]; - } - else - [self errorWithFormat:@"failed to acquire channel for URL: %@", - [_viewURL absoluteString]]; - - return rc; -} - -/** - * Change a user's password. - * @param login the user's login name. - * @param oldPassword the previous password. - * @param newPassword the new password. - * @param perr is not used. - * @return YES if the password was successfully changed. - */ -- (BOOL) changePasswordForLogin: (NSString *) login - oldPassword: (NSString *) oldPassword - newPassword: (NSString *) newPassword - perr: (SOGoPasswordPolicyError *) perr -{ - EOAdaptorChannel *channel; - GCSChannelManager *cm; - NSException *ex; - NSString *sqlstr; - BOOL didChange; - BOOL isOldPwdOk; - - isOldPwdOk = NO; - didChange = NO; - - // Verify current password - isOldPwdOk = [self checkLogin:login password:oldPassword perr:perr expire:0 grace:0]; - - if (isOldPwdOk) - { - // Encrypt new password - NSString *encryptedPassword = [self _encryptPassword: newPassword]; - if(encryptedPassword == nil) - return NO; - - // Save new password - login = [login stringByReplacingString: @"'" withString: @"''"]; - cm = [GCSChannelManager defaultChannelManager]; - channel = [cm acquireOpenChannelForURL: _viewURL]; - if (channel) - { - sqlstr = [NSString stringWithFormat: (@"UPDATE %@" - @" SET c_password = '%@'" - @" WHERE c_uid = '%@'"), - [_viewURL gcsTableName], encryptedPassword, login]; - - ex = [channel evaluateExpressionX: sqlstr]; - if (!ex) - { - didChange = YES; - } - else - { - [self errorWithFormat: @"could not run SQL '%@': %@", sqlstr, ex]; - } - [cm releaseChannel: channel]; - } - } - - return didChange; -} - -- (NSString *) _whereClauseFromArray: (NSArray *) theArray - value: (NSString *) theValue - exact: (BOOL) theBOOL -{ - NSMutableString *s; - int i; - - s = [NSMutableString string]; - - for (i = 0; i < [theArray count]; i++) - { - if (theBOOL) - [s appendFormat: @" OR LOWER(%@) = '%@'", [theArray objectAtIndex: i], theValue]; - else - [s appendFormat: @" OR LOWER(%@) LIKE '%%%@%%'", [theArray objectAtIndex: i], theValue]; - } - - return s; -} - -- (NSDictionary *) _lookupContactEntry: (NSString *) theID - considerEmail: (BOOL) b - inDomain: (NSString *) domain -{ - NSMutableDictionary *response; - NSMutableArray *qualifiers; - NSArray *fieldNames; - EOAdaptorChannel *channel; - EOQualifier *loginQualifier, *domainQualifier, *qualifier; - GCSChannelManager *cm; - NSMutableString *sql; - NSString *value, *field; - NSException *ex; - int i; - - response = nil; - - theID = [theID stringByReplacingString: @"'" withString: @"''"]; - cm = [GCSChannelManager defaultChannelManager]; - channel = [cm acquireOpenChannelForURL: _viewURL]; - if (channel) - { - qualifiers = [NSMutableArray arrayWithCapacity: [_loginFields count] + 1]; - - // Always compare against the c_uid field - loginQualifier = [[EOKeyValueQualifier alloc] initWithKey: @"c_uid" - operatorSelector: EOQualifierOperatorEqual - value: theID]; - [loginQualifier autorelease]; - [qualifiers addObject: loginQualifier]; - - if (_loginFields) - { - for (i = 0; i < [_loginFields count]; i++) - { - field = [_loginFields objectAtIndex: i]; - if ([field caseInsensitiveCompare: @"c_uid"] != NSOrderedSame) - { - loginQualifier = [[EOKeyValueQualifier alloc] initWithKey: field - operatorSelector: EOQualifierOperatorEqual - value: theID]; - [loginQualifier autorelease]; - [qualifiers addObject: loginQualifier]; - } - } - } - - domainQualifier = nil; - if (_domainField && domain) - { - domainQualifier = [[EOKeyValueQualifier alloc] initWithKey: _domainField - operatorSelector: EOQualifierOperatorEqual - value: domain]; - [domainQualifier autorelease]; - } - - if (b) - { - // Always compare againts the mail field - loginQualifier = [[EOKeyValueQualifier alloc] initWithKey: @"mail" - operatorSelector: EOQualifierOperatorEqual - value: [theID lowercaseString]]; - [loginQualifier autorelease]; - [qualifiers addObject: loginQualifier]; - - if (_mailFields) - { - for (i = 0; i < [_mailFields count]; i++) - { - field = [_mailFields objectAtIndex: i]; - if ([field caseInsensitiveCompare: @"mail"] != NSOrderedSame - && ![_loginFields containsObject: field]) - { - loginQualifier = [[EOKeyValueQualifier alloc] initWithKey: field - operatorSelector: EOQualifierOperatorEqual - value: [theID lowercaseString]]; - [loginQualifier autorelease]; - [qualifiers addObject: loginQualifier]; - } - } - } - } - - sql = [NSMutableString stringWithFormat: @"SELECT *" - @" FROM %@" - @" WHERE ", - [_viewURL gcsTableName]]; - qualifier = [[EOOrQualifier alloc] initWithQualifierArray: qualifiers]; - if (domainQualifier) - qualifier = [[EOAndQualifier alloc] initWithQualifiers: domainQualifier, qualifier, nil]; - [qualifier _gcsAppendToString: sql]; - - ex = [channel evaluateExpressionX: sql]; - if (!ex) - { - NSMutableArray *emails; - - response = [[channel fetchAttributes: [channel describeResults: NO] - withZone: NULL] mutableCopy]; - [response autorelease]; - [channel cancelFetch]; - - /* Convert all c_ fields to obtain their ldif equivalent */ - fieldNames = [response allKeys]; - for (i = 0; i < [fieldNames count]; i++) - { - field = [fieldNames objectAtIndex: i]; - if ([field hasPrefix: @"c_"]) - [response setObject: [response objectForKey: field] - forKey: [field substringFromIndex: 2]]; - } - - // FIXME - // We have to do this here since we do not manage modules - // constraints right now over a SQL backend. - [response setObject: [NSNumber numberWithBool: YES] forKey: @"CalendarAccess"]; - [response setObject: [NSNumber numberWithBool: YES] forKey: @"MailAccess"]; - [response setObject: [NSNumber numberWithBool: YES] forKey: @"ActiveSyncAccess"]; - - // We set the domain, if any - value = nil; - if (_domain) - value = _domain; - else if (_domainField) - value = [response objectForKey: _domainField]; - if (![value isNotNull]) - value = @""; - [response setObject: value forKey: @"c_domain"]; - - // We populate all mail fields - emails = [NSMutableArray array]; - - if ([response objectForKey: @"mail"]) - [emails addObject: [response objectForKey: @"mail"]]; - - if (_mailFields && [_mailFields count] > 0) - { - NSString *s; - int i; - - for (i = 0; i < [_mailFields count]; i++) - if ((s = [response objectForKey: [_mailFields objectAtIndex: i]]) && - [[s stringByTrimmingSpaces] length] > 0) - [emails addObject: s]; - } - - [response setObject: emails forKey: @"c_emails"]; - if (_imapHostField) - { - value = [response objectForKey: _imapHostField]; - if ([value isNotNull]) - [response setObject: value forKey: @"c_imaphostname"]; - } - - if (_sieveHostField) - { - value = [response objectForKey: _sieveHostField]; - if ([value isNotNull]) - [response setObject: value forKey: @"c_sievehostname"]; - } - - // We check if the user can authenticate - if (_authenticationFilter) - { - EOQualifier *q_uid, *q_auth; - - sql = [NSMutableString stringWithFormat: @"SELECT c_uid" - @" FROM %@" - @" WHERE ", - [_viewURL gcsTableName]]; - - q_auth = [EOQualifier qualifierWithQualifierFormat: _authenticationFilter]; - - q_uid = [[EOKeyValueQualifier alloc] initWithKey: @"c_uid" - operatorSelector: EOQualifierOperatorEqual - value: theID]; - [q_uid autorelease]; - - qualifier = [[EOAndQualifier alloc] initWithQualifiers: q_uid, q_auth, nil]; - [qualifier autorelease]; - [qualifier _gcsAppendToString: sql]; - - ex = [channel evaluateExpressionX: sql]; - if (!ex) - { - NSDictionary *authResponse; - - authResponse = [channel fetchAttributes: [channel describeResults: NO] withZone: NULL]; - [response setObject: [NSNumber numberWithBool: [authResponse count] > 0] forKey: @"canAuthenticate"]; - [channel cancelFetch]; - } - else - [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex]; - } - else - [response setObject: [NSNumber numberWithBool: YES] forKey: @"canAuthenticate"]; - - // We check if we should use a different login for IMAP - if (_imapLoginField) - { - if ([[response objectForKey: _imapLoginField] isNotNull]) - [response setObject: [response objectForKey: _imapLoginField] forKey: @"c_imaplogin"]; - } - - // We check if it's a resource of not - if (_kindField) - { - if ((value = [response objectForKey: _kindField]) && [value isNotNull]) - { - if ([value caseInsensitiveCompare: @"location"] == NSOrderedSame || - [value caseInsensitiveCompare: @"thing"] == NSOrderedSame || - [value caseInsensitiveCompare: @"group"] == NSOrderedSame) - { - [response setObject: [NSNumber numberWithInt: 1] - forKey: @"isResource"]; - } - } - } - - if (_multipleBookingsField) - { - if ((value = [response objectForKey: _multipleBookingsField])) - { - [response setObject: [NSNumber numberWithInt: [value intValue]] - forKey: @"numberOfSimultaneousBookings"]; - } - } - - [response setObject: self forKey: @"source"]; - } - else - [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex]; - [cm releaseChannel: channel]; - } - else - [self errorWithFormat:@"failed to acquire channel for URL: %@", - [_viewURL absoluteString]]; - - return response; -} - - -- (NSDictionary *) lookupContactEntry: (NSString *) theID -{ - return [self _lookupContactEntry: theID considerEmail: NO inDomain: nil]; -} - -- (NSDictionary *) lookupContactEntryWithUIDorEmail: (NSString *) entryID - inDomain: (NSString *) domain -{ - return [self _lookupContactEntry: entryID considerEmail: YES inDomain: domain]; -} - -/* Returns an EOQualifier of the following form: - * (_domainField = domain OR _domainField = visibleDomain1 [...]) - * Should only be called on SQL sources using _domainField name. - */ -- (EOQualifier *) _visibleDomainsQualifierFromDomain: (NSString *) domain -{ - int i; - EOQualifier *qualifier, *domainQualifier; - NSArray *visibleDomains; - NSMutableArray *qualifiers; - NSString *currentDomain; - - SOGoSystemDefaults *sd; - - /* Return early if no domain or if being called on a 'static' sql source */ - if (!domain || !_domainField) - return nil; - - sd = [SOGoSystemDefaults sharedSystemDefaults]; - visibleDomains = [sd visibleDomainsForDomain: domain]; - qualifier = nil; - - domainQualifier = - [[EOKeyValueQualifier alloc] initWithKey: _domainField - operatorSelector: EOQualifierOperatorEqual - value: domain]; - [domainQualifier autorelease]; - - if ([visibleDomains count]) - { - qualifiers = [NSMutableArray arrayWithCapacity: [visibleDomains count] + 1]; - [qualifiers addObject: domainQualifier]; - for(i = 0; i < [visibleDomains count]; i++) - { - currentDomain = [visibleDomains objectAtIndex: i]; - qualifier = - [[EOKeyValueQualifier alloc] initWithKey: _domainField - operatorSelector: EOQualifierOperatorEqual - value: currentDomain]; - [qualifier autorelease]; - [qualifiers addObject: qualifier]; - } - qualifier = [[EOOrQualifier alloc] initWithQualifierArray: qualifiers]; - [qualifier autorelease]; - } - - return qualifier ? qualifier : domainQualifier; -} - - -- (NSArray *) allEntryIDsVisibleFromDomain: (NSString *) domain -{ - EOAdaptorChannel *channel; - EOQualifier *domainQualifier; - GCSChannelManager *cm; - NSException *ex; - NSMutableArray *results; - NSMutableString *sql; - - results = [NSMutableArray array]; - - cm = [GCSChannelManager defaultChannelManager]; - channel = [cm acquireOpenChannelForURL: _viewURL]; - if (channel) - { - sql = [NSMutableString stringWithFormat: @"SELECT c_uid FROM %@", - [_viewURL gcsTableName]]; - - if (_domainField) - { - if ([domain length]) - { - domainQualifier = - [self _visibleDomainsQualifierFromDomain: domain]; - if (domainQualifier) - { - [sql appendString: @" WHERE "]; - [domainQualifier _gcsAppendToString: sql]; - } - } - else - { - /* Should not happen but avoid returning the whole table - * if a domain should have been defined */ - [sql appendFormat: @" WHERE %@ is NULL", _domainField]; - } - } - - ex = [channel evaluateExpressionX: sql]; - if (!ex) - { - NSDictionary *row; - NSArray *attrs; - NSString *value; - - attrs = [channel describeResults: NO]; - - while ((row = [channel fetchAttributes: attrs withZone: NULL])) - { - value = [row objectForKey: @"c_uid"]; - if (value) - [results addObject: value]; - } - } - else - [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex]; - [cm releaseChannel: channel]; - } - else - [self errorWithFormat:@"failed to acquire channel for URL: %@", - [_viewURL absoluteString]]; - - - return results; -} - -- (NSArray *) allEntryIDs -{ - return [self allEntryIDsVisibleFromDomain: nil]; -} - -- (NSArray *) fetchContactsMatching: (NSString *) filter - inDomain: (NSString *) domain -{ - EOAdaptorChannel *channel; - NSMutableArray *results; - GCSChannelManager *cm; - NSException *ex; - NSMutableString *sql; - NSString *lowerFilter; - - results = [NSMutableArray array]; - - cm = [GCSChannelManager defaultChannelManager]; - channel = [cm acquireOpenChannelForURL: _viewURL]; - if (channel) - { - lowerFilter = [filter lowercaseString]; - lowerFilter = [lowerFilter stringByReplacingString: @"'" withString: @"''"]; - - sql = [NSMutableString stringWithFormat: (@"SELECT *" - @" FROM %@" - @" WHERE" - @" (LOWER(c_cn) LIKE '%%%@%%'" - @" OR LOWER(mail) LIKE '%%%@%%'"), - [_viewURL gcsTableName], - lowerFilter, lowerFilter]; - - if (_mailFields && [_mailFields count] > 0) - { - [sql appendString: [self _whereClauseFromArray: _mailFields value: lowerFilter exact: NO]]; - } - - [sql appendString: @")"]; - - if (_domainField) - { - if ([domain length]) - { - EOQualifier *domainQualifier; - domainQualifier = - [self _visibleDomainsQualifierFromDomain: domain]; - if (domainQualifier) - { - [sql appendFormat: @" AND ("]; - [domainQualifier _gcsAppendToString: sql]; - [sql appendFormat: @")"]; - } - } - else - [sql appendFormat: @" AND %@ IS NULL", _domainField]; - } - - ex = [channel evaluateExpressionX: sql]; - if (!ex) - { - NSDictionary *row; - NSArray *attrs; - - attrs = [channel describeResults: NO]; - - while ((row = [channel fetchAttributes: attrs withZone: NULL])) - { - row = [row mutableCopy]; - [(NSMutableDictionary *) row setObject: self forKey: @"source"]; - [results addObject: row]; - [row release]; - } - } - else - [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex]; - [cm releaseChannel: channel]; - } - else - [self errorWithFormat:@"failed to acquire channel for URL: %@", - [_viewURL absoluteString]]; - - return results; -} - -- (void) setSourceID: (NSString *) newSourceID -{ -} - -- (NSString *) sourceID -{ - return _sourceID; -} - -- (void) setDisplayName: (NSString *) newDisplayName -{ -} - -- (NSString *) displayName -{ - /* This method is only used when supporting user "source" addressbooks, - which is only supported by the LDAP backend for now. */ - return _sourceID; -} - -- (void) setListRequiresDot: (BOOL) newListRequiresDot -{ -} - -- (BOOL) listRequiresDot -{ - /* This method is not implemented for SQLSource. It must enable a mechanism - where using "." is not required to list the content of addressbooks. */ - return YES; -} - -/* card editing */ -- (void) setModifiers: (NSArray *) newModifiers -{ -} - -- (NSArray *) modifiers -{ - /* This method is only used when supporting card editing, - which is only supported by the LDAP backend for now. */ - return nil; -} - -- (NSException *) addContactEntry: (NSDictionary *) roLdifRecord - withID: (NSString *) aId -{ - NSString *reason; - - reason = [NSString stringWithFormat: @"method '%@' is not available" - @" for class '%@'", NSStringFromSelector (_cmd), - NSStringFromClass (isa)]; - - return [NSException exceptionWithName: @"SQLSourceIOException" - reason: reason - userInfo: nil]; -} - -- (NSException *) updateContactEntry: (NSDictionary *) roLdifRecord -{ - NSString *reason; - - reason = [NSString stringWithFormat: @"method '%@' is not available" - @" for class '%@'", NSStringFromSelector (_cmd), - NSStringFromClass (isa)]; - - return [NSException exceptionWithName: @"SQLSourceIOException" - reason: reason - userInfo: nil]; -} - -- (NSException *) removeContactEntryWithID: (NSString *) aId -{ - NSString *reason; - - reason = [NSString stringWithFormat: @"method '%@' is not available" - @" for class '%@'", NSStringFromSelector (_cmd), - NSStringFromClass (isa)]; - - return [NSException exceptionWithName: @"SQLSourceIOException" - reason: reason - userInfo: nil]; -} - -/* user addressbooks */ -- (BOOL) hasUserAddressBooks -{ - return NO; -} - -- (NSArray *) addressBookSourcesForUser: (NSString *) user -{ - return nil; -} - -- (NSException *) addAddressBookSource: (NSString *) newId - withDisplayName: (NSString *) newDisplayName - forUser: (NSString *) user -{ - NSString *reason; - - reason = [NSString stringWithFormat: @"method '%@' is not available" - @" for class '%@'", NSStringFromSelector (_cmd), - NSStringFromClass (isa)]; - - return [NSException exceptionWithName: @"SQLSourceIOException" - reason: reason - userInfo: nil]; -} - -- (NSException *) renameAddressBookSource: (NSString *) newId - withDisplayName: (NSString *) newDisplayName - forUser: (NSString *) user -{ - NSString *reason; - - reason = [NSString stringWithFormat: @"method '%@' is not available" - @" for class '%@'", NSStringFromSelector (_cmd), - NSStringFromClass (isa)]; - - return [NSException exceptionWithName: @"SQLSourceIOException" - reason: reason - userInfo: nil]; -} - -- (NSException *) removeAddressBookSource: (NSString *) newId - forUser: (NSString *) user -{ - NSString *reason; - - reason = [NSString stringWithFormat: @"method '%@' is not available" - @" for class '%@'", NSStringFromSelector (_cmd), - NSStringFromClass (isa)]; - - return [NSException exceptionWithName: @"SQLSourceIOException" - reason: reason - userInfo: nil]; -} - -@end From 7a5e550510a809e19f951e338dd42bf8e337e861 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 5 Nov 2015 09:36:11 -0500 Subject: [PATCH 28/69] (fix) manually added patch from PR#113 to avoid many "broken" commits --- SoObjects/Appointments/SOGoCalendarComponent.m | 8 ++++++++ SoObjects/SOGo/SOGoParentFolder.m | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/SoObjects/Appointments/SOGoCalendarComponent.m b/SoObjects/Appointments/SOGoCalendarComponent.m index ce600715b..d2df0755f 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.m +++ b/SoObjects/Appointments/SOGoCalendarComponent.m @@ -140,6 +140,14 @@ return aclManager; } +- (NSException *) changeParticipationStatus: (NSString *) newPartStat + withDelegate: (iCalPerson *) delegate + alarm: (iCalAlarm *) alarm +{ + // Required for protocol + return nil; +} + - (id) init { if ((self = [super init])) diff --git a/SoObjects/SOGo/SOGoParentFolder.m b/SoObjects/SOGo/SOGoParentFolder.m index 501f90f9f..eb454dc97 100644 --- a/SoObjects/SOGo/SOGoParentFolder.m +++ b/SoObjects/SOGo/SOGoParentFolder.m @@ -433,7 +433,7 @@ static SoSecurityManager *sm = nil; error = [self appendPersonalSources]; if (!error) if ([self respondsToSelector:@selector(appendCollectedSources)]) - error = [self appendCollectedSources]; + error = [self performSelector:@selector(appendCollectedSources)]; if (!error) error = [self appendSystemSources]; // TODO : Not really a testcase, see function if (error) From 41e80bb1057f19840c10a40ea32e4fe00437797c Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 5 Nov 2015 09:48:17 -0500 Subject: [PATCH 29/69] Updated NEWS file regarding PR#120 --- NEWS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/NEWS b/NEWS index f232dc16b..915438aee 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,15 @@ Bug fixes - inline images sent from SOGo webmail are not displayed in Mozilla Thunderbird (#3271) - prevent postal address showing on single line over EAS (#2614) - display missing events when printing working hours only + - fix corner case making server crash when syncing hard deleted messages when clear offline items was set up (Zentyal) + - avoid infinite Outlook client loops trying to set read flag when it is already set (Zentyal) + - avoid crashing when calendar metadata is missing in the cache (Zentyal) + - fix recurrence pattern event corner case created by Mozilla Thunderbird which made server crash (Zentyal) + - fix corner case that removes attachments on sending messages from Outlook (Zentyal) + - freebusy on web interface works again in multidomain environments (Zentyal) + - fix double creation of folders in Outlook when the folder name starts with a digit (Zentyal) + - avoid crashing Outlook after setting a custom view in a calendar folder (Zentyal) + 2.3.2 (2015-09-16) ------------------ From 9d310237246ba79a316a4cb006368ddd53c20a87 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 5 Nov 2015 11:11:18 -0500 Subject: [PATCH 30/69] (fix) additional fix for #3118 --- ActiveSync/SOGoActiveSyncDispatcher.m | 23 ++++++++++++++++------- SoObjects/SOGo/SOGoCacheGCSObject.h | 1 + 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index daa1999da..24993aae3 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -850,6 +850,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. lookupName: [cKey substringFromIndex: [cKey rangeOfString: @"/"].location+1] inContext: context acquire: NO]; // Remove the folder from device if it doesn't exist, we don't want to sync it, or it doesn't have the proper permissions + // No need to check for personal folders here since they can't be deleted if (!currentFolder || ![currentFolder synchronize] || [sm validatePermission: SoPerm_DeleteObjects @@ -964,20 +965,29 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. personalFolderName = [[[context activeUser] personalCalendarFolderInContext: context] nameInContainer]; folders = [[[[[context activeUser] homeFolderInContext: context] lookupName: @"Calendar" inContext: context acquire: NO] subFolders] mutableCopy]; + [folders autorelease]; + [folders addObjectsFromArray: [[[[context activeUser] homeFolderInContext: context] lookupName: @"Contacts" inContext: context acquire: NO] subFolders]]; // We remove all the folders that aren't GCS-ones, that we don't want to synchronize and - // the ones without write/delete permissions + // the ones without write/delete permissions. count = [folders count]-1; for (; count >= 0; count--) { - if (![[folders objectAtIndex: count] isKindOfClass: [SOGoGCSFolder class]] || - ![[folders objectAtIndex: count] synchronize] || + currentFolder = [folders objectAtIndex: count]; + + // We skip personal GCS folders - we always want to synchronize these + if ([currentFolder isKindOfClass: [SOGoGCSFolder class]] && + [[currentFolder nameInContainer] isEqualToString: @"personal"]) + continue; + + if (![currentFolder isKindOfClass: [SOGoGCSFolder class]] || + ![currentFolder synchronize] || [sm validatePermission: SoPerm_DeleteObjects - onObject: [folders objectAtIndex: count] + onObject: currentFolder inContext: context] || [sm validatePermission: SoPerm_AddDocumentsImagesAndFiles - onObject: [folders objectAtIndex: count] + onObject: currentFolder inContext: context]) { [folders removeObjectAtIndex: count]; @@ -3227,8 +3237,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. return nil; urlString = [[user domainDefaults] folderInfoURL]; - parts = [[urlString componentsSeparatedByString: @"/"] - mutableCopy]; + parts = [[urlString componentsSeparatedByString: @"/"] mutableCopy]; [parts autorelease]; if ([parts count] == 5) { diff --git a/SoObjects/SOGo/SOGoCacheGCSObject.h b/SoObjects/SOGo/SOGoCacheGCSObject.h index 1f6969d16..a2ffe5bf0 100644 --- a/SoObjects/SOGo/SOGoCacheGCSObject.h +++ b/SoObjects/SOGo/SOGoCacheGCSObject.h @@ -55,6 +55,7 @@ typedef enum { - (void) reloadIfNeeded; - (void) save; +- (NSException *) destroy; + (id) objectWithName: (NSString *) key inContainer: (id) theContainer useCache: (BOOL) useCache; From eda84c7b5d6cc6060ae9f3981a9865ae9210493b Mon Sep 17 00:00:00 2001 From: Euan Thoms Date: Fri, 6 Nov 2015 01:29:26 +0800 Subject: [PATCH 31/69] Stage1b (v2 branch) of clang compiler warning patches. --- ActiveSync/iCalAlarm+ActiveSync.m | 2 +- ActiveSync/iCalEvent+ActiveSync.m | 2 +- ActiveSync/iCalToDo+ActiveSync.m | 2 +- Main/SOGo+DAV.m | 2 +- SOPE/GDLContentStore/GCSChannelManager.m | 2 +- SOPE/NGCards/CardElement.m | 2 +- SOPE/NGCards/CardGroup.m | 2 +- SOPE/NGCards/NSCalendarDate+NGCards.m | 10 +++++----- SOPE/NGCards/iCalMonthlyRecurrenceCalculator.m | 4 ++-- SOPE/NGCards/iCalRecurrenceRule.m | 4 ++-- SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.m | 4 ++-- SoObjects/Appointments/MSExchangeFreeBusy.m | 2 +- SoObjects/Appointments/SOGoAppointmentFolder.h | 3 --- SoObjects/Appointments/SOGoAppointmentFolder.m | 3 ++- SoObjects/Appointments/SOGoAppointmentObject.m | 2 +- SoObjects/Appointments/SOGoCalendarComponent.m | 10 +++++++++- SoObjects/Appointments/iCalAlarm+SOGo.m | 4 ++-- SoObjects/Appointments/iCalEvent+SOGo.h | 2 +- SoObjects/Appointments/iCalEvent+SOGo.m | 2 +- SoObjects/Contacts/NGVCard+SOGo.m | 8 ++++---- SoObjects/Contacts/SOGoContactEntryPhoto.m | 2 +- SoObjects/Contacts/SOGoContactFolders.m | 6 +++--- SoObjects/Contacts/SOGoContactGCSEntry.m | 2 +- SoObjects/Contacts/SOGoContactGCSList.m | 2 +- SoObjects/Contacts/SOGoContactSourceFolder.m | 2 +- SoObjects/Mailer/NSString+Mail.m | 2 +- SoObjects/Mailer/SOGoMailBodyPart.m | 2 +- SoObjects/Mailer/SOGoMailFolder.m | 2 +- SoObjects/Mailer/SOGoMailForward.m | 4 ++-- SoObjects/Mailer/SOGoMailLabel.m | 2 +- SoObjects/Mailer/SOGoMailObject.m | 17 ++++++++++++----- SoObjects/SOGo/BSONCodec.m | 4 ++-- SoObjects/SOGo/LDAPSource.m | 2 +- SoObjects/SOGo/NSCalendarDate+SOGo.m | 12 ++++++------ SoObjects/SOGo/NSDictionary+DAV.m | 2 +- SoObjects/SOGo/NSString+Utilities.m | 2 +- SoObjects/SOGo/SOGoCacheGCSObject.m | 16 +++++++--------- SoObjects/SOGo/SOGoContentObject.m | 9 ++++++--- SoObjects/SOGo/SOGoFolder.m | 2 +- SoObjects/SOGo/SOGoGCSFolder.m | 8 ++++---- SoObjects/SOGo/SOGoGroup.m | 4 ++-- SoObjects/SOGo/SOGoObject.m | 6 +++--- Tests/Unit/SOGoTest.m | 4 ++-- Tests/Unit/TestNGMailAddressParser.m | 6 ++---- .../TestNGMimeAddressHeaderFieldGenerator.m | 7 +++---- Tests/Unit/TestNGMimeMessageGenerator.m | 2 +- Tests/Unit/TestVersit.m | 14 ++++++++++++++ Tests/Unit/TestiCalRecurrenceCalculator.m | 6 +++--- Tests/Unit/TestiCalTimeZonePeriod.m | 4 ++-- Tools/SOGoEAlarmsNotifier.m | 4 ++-- Tools/SOGoSockDOperation.m | 2 +- Tools/SOGoToolCreateFolder.m | 2 +- Tools/SOGoToolExpireUserSessions.m | 2 +- Tools/SOGoToolManageEAS.m | 2 +- Tools/SOGoToolRemoveDoubles.m | 2 +- Tools/SOGoToolRenameUser.m | 4 ++-- Tools/SOGoToolUserPreferences.m | 2 +- Tools/sogo-tool.m | 2 +- UI/Common/UIxToolbar.m | 2 +- UI/Contacts/UIxContactActions.m | 2 +- UI/Contacts/UIxContactFolderActions.m | 4 ++-- UI/Contacts/UIxContactFolderProperties.m | 4 +++- UI/Contacts/UIxContactView.m | 4 ++-- UI/MailPartViewers/UIxMailPartHTMLViewer.m | 4 ++-- UI/MailPartViewers/UIxMailPartMixedViewer.m | 2 +- UI/MailPartViewers/UIxMailPartViewer.m | 2 +- UI/MailerUI/UIxMailFolderActions.m | 2 +- UI/MailerUI/UIxMailListActions.h | 1 - UI/MailerUI/UIxMailMainFrame.m | 2 +- UI/MailerUI/UIxMailView.m | 1 + UI/MainUI/SOGoMicrosoftActiveSyncActions.m | 2 +- UI/PreferencesUI/UIxPreferences.m | 6 +++--- UI/SOGoUI/SOGoAptFormatter.m | 10 +++++----- UI/Scheduler/UIxCalDateSelector.m | 4 ++-- UI/Scheduler/UIxCalDayTable.h | 5 +++-- UI/Scheduler/UIxCalDayTable.m | 15 +++++++++------ UI/Scheduler/UIxCalDayView.m | 2 +- UI/Scheduler/UIxCalListingActions.m | 10 +++++----- UI/Scheduler/UIxCalMonthView.m | 2 +- UI/Scheduler/UIxCalViewPrint.m | 3 ++- UI/Scheduler/UIxComponentEditor.m | 2 +- UI/Scheduler/UIxRecurrenceEditor.m | 2 +- 82 files changed, 186 insertions(+), 154 deletions(-) diff --git a/ActiveSync/iCalAlarm+ActiveSync.m b/ActiveSync/iCalAlarm+ActiveSync.m index 33e6dbb73..62a3ad146 100644 --- a/ActiveSync/iCalAlarm+ActiveSync.m +++ b/ActiveSync/iCalAlarm+ActiveSync.m @@ -58,7 +58,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // don't send negative reminder - not supported if (delta > 0) - [s appendFormat: @"%d", delta]; + [s appendFormat: @"%d", (int)delta]; } return s; diff --git a/ActiveSync/iCalEvent+ActiveSync.m b/ActiveSync/iCalEvent+ActiveSync.m index 2156b1eac..d590e11a6 100644 --- a/ActiveSync/iCalEvent+ActiveSync.m +++ b/ActiveSync/iCalEvent+ActiveSync.m @@ -285,7 +285,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. { [s appendString: @""]; [s appendFormat: @"%d", 1]; - [s appendFormat: @"%d", [o length]]; + [s appendFormat: @"%d", (int)[o length]]; [s appendFormat: @"%@", o]; [s appendString: @""]; } diff --git a/ActiveSync/iCalToDo+ActiveSync.m b/ActiveSync/iCalToDo+ActiveSync.m index e9417752c..94e0f9756 100644 --- a/ActiveSync/iCalToDo+ActiveSync.m +++ b/ActiveSync/iCalToDo+ActiveSync.m @@ -140,7 +140,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. { [s appendString: @""]; [s appendFormat: @"%d", 1]; - [s appendFormat: @"%d", [o length]]; + [s appendFormat: @"%d", (int)[o length]]; [s appendFormat: @"%@", o]; [s appendString: @""]; } diff --git a/Main/SOGo+DAV.m b/Main/SOGo+DAV.m index 256abdba9..994e742e6 100644 --- a/Main/SOGo+DAV.m +++ b/Main/SOGo+DAV.m @@ -267,7 +267,7 @@ NSObject *list; NSObject *valueNode; NSArray *elements; - NSString *property, *match; + NSString *property=nil, *match=nil; list = [searchElement getElementsByTagName: @"prop"]; if ([list length]) diff --git a/SOPE/GDLContentStore/GCSChannelManager.m b/SOPE/GDLContentStore/GCSChannelManager.m index d04d70446..360901e97 100644 --- a/SOPE/GDLContentStore/GCSChannelManager.m +++ b/SOPE/GDLContentStore/GCSChannelManager.m @@ -500,7 +500,7 @@ static NSTimeInterval ChannelCollectionTimer = 5 * 60; ms = [NSMutableString stringWithCapacity: 256]; [ms appendFormat: @"<0x%p[%@]: ", self, NSStringFromClass ([self class])]; - [ms appendFormat: @" #adaptors=%d", [urlToAdaptor count]]; + [ms appendFormat: @" #adaptors=%d", (int)[urlToAdaptor count]]; [ms appendString: @">"]; return ms; diff --git a/SOPE/NGCards/CardElement.m b/SOPE/NGCards/CardElement.m index 00d7c7b35..ce410e1bc 100644 --- a/SOPE/NGCards/CardElement.m +++ b/SOPE/NGCards/CardElement.m @@ -414,7 +414,7 @@ if (group) [str appendFormat: @"%@ (group: %@)\n", tag, group]; else - [str appendFormat: @"%@\n", tag, group]; + [str appendFormat: @"%@\n", tag]; [str appendString: [self versitString]]; diff --git a/SOPE/NGCards/CardGroup.m b/SOPE/NGCards/CardGroup.m index b3c21e9d9..301cfb2b8 100644 --- a/SOPE/NGCards/CardGroup.m +++ b/SOPE/NGCards/CardGroup.m @@ -409,7 +409,7 @@ static NGCardsSaxHandler *sax = nil; max = [children count]; if (max > 0) { - [str appendFormat: @"\n %d children: {\n", [children count]]; + [str appendFormat: @"\n %d children: {\n", (int)[children count]]; for (count = 0; count < max; count++) [str appendFormat: @" %@\n", [[children objectAtIndex: count] description]]; diff --git a/SOPE/NGCards/NSCalendarDate+NGCards.m b/SOPE/NGCards/NSCalendarDate+NGCards.m index b34471695..f70a12f42 100644 --- a/SOPE/NGCards/NSCalendarDate+NGCards.m +++ b/SOPE/NGCards/NSCalendarDate+NGCards.m @@ -32,16 +32,16 @@ - (NSString *) iCalFormattedDateTimeString { return [NSString stringWithFormat: @"%.4d%.2d%.2dT%.2d%.2d%.2d", - [self yearOfCommonEra], [self monthOfYear], - [self dayOfMonth], [self hourOfDay], - [self minuteOfHour], [self secondOfMinute]]; + (int)[self yearOfCommonEra], (int)[self monthOfYear], + (int)[self dayOfMonth], (int)[self hourOfDay], + (int)[self minuteOfHour], (int)[self secondOfMinute]]; } - (NSString *) iCalFormattedDateString { return [NSString stringWithFormat: @"%.4d%.2d%.2d", - [self yearOfCommonEra], [self monthOfYear], - [self dayOfMonth]]; + (int)[self yearOfCommonEra], (int)[self monthOfYear], + (int)[self dayOfMonth]]; } @end diff --git a/SOPE/NGCards/iCalMonthlyRecurrenceCalculator.m b/SOPE/NGCards/iCalMonthlyRecurrenceCalculator.m index 3b6577ee8..0694cc6c2 100644 --- a/SOPE/NGCards/iCalMonthlyRecurrenceCalculator.m +++ b/SOPE/NGCards/iCalMonthlyRecurrenceCalculator.m @@ -348,7 +348,7 @@ static inline unsigned iCalDoWForNSDoW (int dow) if ([byDayMask occursOnDay: currentWeekDay]) { if ([bySetPos containsObject: - [NSString stringWithFormat: @"%d", currentPos]]) + [NSString stringWithFormat: @"%d", (int)currentPos]]) monthDays[monthDay+1] = YES; currentPos++; } @@ -362,7 +362,7 @@ static inline unsigned iCalDoWForNSDoW (int dow) if ([byDayMask occursOnDay: currentWeekDay]) { if ([bySetPos containsObject: - [NSString stringWithFormat: @"%d", currentPos]]) + [NSString stringWithFormat: @"%d", (int)currentPos]]) monthDays[monthDay] = YES; currentPos--; } diff --git a/SOPE/NGCards/iCalRecurrenceRule.m b/SOPE/NGCards/iCalRecurrenceRule.m index 5e99429e7..dc9ed28fa 100644 --- a/SOPE/NGCards/iCalRecurrenceRule.m +++ b/SOPE/NGCards/iCalRecurrenceRule.m @@ -309,10 +309,10 @@ NSString *iCalWeekDayString[] = { @"SU", @"MO", @"TU", @"WE", @"TH", @"FR", else if ([frequency isEqualToString:@"SECONDLY"]) freq = iCalRecurrenceFrequenceSecondly; else - freq = NSNotFound; + freq = (iCalRecurrenceFrequency)NSNotFound; } else - freq = NSNotFound; + freq = (iCalRecurrenceFrequency)NSNotFound; return freq; } diff --git a/SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.m b/SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.m index d9b906c8e..0e56a2d51 100644 --- a/SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.m +++ b/SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.m @@ -1045,7 +1045,7 @@ static NSCharacterSet *whitespaceCharSet = nil; if (debugOn) { NSLog(@"%s: trying to decode data (0x%p,len=%d) ...", - __PRETTY_FUNCTION__, _data, [_data length]); + __PRETTY_FUNCTION__, _data, (int)[_data length]); } if ((len = [_data length]) == 0) @@ -1164,7 +1164,7 @@ static NSCharacterSet *whitespaceCharSet = nil; if (debugOn) { NSLog(@"%s: trying to parse string (0x%p,len=%d) ...", - __PRETTY_FUNCTION__, _source, [_source length]); + __PRETTY_FUNCTION__, _source, (int)[_source length]); } if (!_sysId) _sysId = @""; [self _parseString: _source]; diff --git a/SoObjects/Appointments/MSExchangeFreeBusy.m b/SoObjects/Appointments/MSExchangeFreeBusy.m index 57ae29e5d..de55e534f 100644 --- a/SoObjects/Appointments/MSExchangeFreeBusy.m +++ b/SoObjects/Appointments/MSExchangeFreeBusy.m @@ -344,7 +344,7 @@ size_t curl_body_function_freebusy(void *ptr, size_t size, size_t nmemb, void *i NSMutableString *s; s = [NSMutableString stringWithCapacity: 64]; - [s appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])]; + [s appendFormat:@"<0x%08X[%@]:", (unsigned int)self, NSStringFromClass([self class])]; if (freeBusyViewType) [s appendFormat:@" freeBusyViewType='%@'", freeBusyViewType]; if (mergedFreeBusy) diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.h b/SoObjects/Appointments/SOGoAppointmentFolder.h index 7139b53ea..2ec2c6bb2 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.h +++ b/SoObjects/Appointments/SOGoAppointmentFolder.h @@ -157,9 +157,6 @@ typedef enum { - (BOOL) showCalendarTasks; - (void) setShowCalendarTasks: (BOOL) new; -- (NSString *) syncTag; -- (void) setSyncTag: (NSString *) newSyncTag; - - (BOOL) includeInFreeBusy; - (void) setIncludeInFreeBusy: (BOOL) newInclude; diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index cfda94c82..cb553e795 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -84,6 +84,7 @@ #import "SOGoAppointmentFolders.h" #import "SOGoFreeBusyObject.h" #import "SOGoTaskObject.h" +#import "SOGoWebAppointmentFolder.h" #import "SOGoAppointmentFolder.h" @@ -2337,7 +2338,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir request = [context request]; if (!([request isIPhone] || [request isICal4])) { - gdRT = [self groupDavResourceType]; + gdRT = (NSArray *)[self groupDavResourceType]; gdVEventCol = [NSArray arrayWithObjects: [gdRT objectAtIndex: 0], XMLNS_GROUPDAV, nil]; [colType addObject: gdVEventCol]; diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index 6bb1b5ee2..13256f57d 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -1994,7 +1994,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent if ([container resourceNameForEventUID: eventUID]) { return [NSException exceptionWithHTTPStatus: 403 - reason: [NSString stringWithFormat: @"Event UID already in use. (%s)", eventUID]]; + reason: [NSString stringWithFormat: @"Event UID already in use. (%@)", eventUID]]; } // diff --git a/SoObjects/Appointments/SOGoCalendarComponent.m b/SoObjects/Appointments/SOGoCalendarComponent.m index d2df0755f..cfeca6c0c 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.m +++ b/SoObjects/Appointments/SOGoCalendarComponent.m @@ -140,6 +140,14 @@ return aclManager; } +- (NSException *) changeParticipationStatus: (NSString *) newPartStat + withDelegate: (iCalPerson *) delegate + alarm: (iCalAlarm *) alarm +{ + // required for protocol + return nil; +} + - (NSException *) changeParticipationStatus: (NSString *) newPartStat withDelegate: (iCalPerson *) delegate alarm: (iCalAlarm *) alarm @@ -179,7 +187,7 @@ - (Class *) parsingClass { - return [iCalCalendar class]; + return (Class *)[iCalCalendar class]; } - (NSString *) davContentType diff --git a/SoObjects/Appointments/iCalAlarm+SOGo.m b/SoObjects/Appointments/iCalAlarm+SOGo.m index cb1cc57c0..4f98fc5eb 100644 --- a/SoObjects/Appointments/iCalAlarm+SOGo.m +++ b/SoObjects/Appointments/iCalAlarm+SOGo.m @@ -102,9 +102,9 @@ } if ([reminderReference caseInsensitiveCompare: @"BEFORE"] == NSOrderedSame) - aValue = [NSString stringWithString: @"-P"]; + aValue = (NSString *) @"-P"; else - aValue = [NSString stringWithString: @"P"]; + aValue = (NSString *) @"P"; if ([reminderUnit caseInsensitiveCompare: @"MINUTES"] == NSOrderedSame || [reminderUnit caseInsensitiveCompare: @"HOURS"] == NSOrderedSame) diff --git a/SoObjects/Appointments/iCalEvent+SOGo.h b/SoObjects/Appointments/iCalEvent+SOGo.h index d04d89427..fa14ba55c 100644 --- a/SoObjects/Appointments/iCalEvent+SOGo.h +++ b/SoObjects/Appointments/iCalEvent+SOGo.h @@ -28,7 +28,7 @@ @interface iCalEvent (SOGoExtensions) - (BOOL) isStillRelevant; -- (unsigned int) occurenceInterval; +- (NSTimeInterval) occurenceInterval; - (void) updateRecurrenceRulesUntilDate: (NSCalendarDate *) previousEndDate; @end diff --git a/SoObjects/Appointments/iCalEvent+SOGo.m b/SoObjects/Appointments/iCalEvent+SOGo.m index a19dcf3cd..7948f7c23 100644 --- a/SoObjects/Appointments/iCalEvent+SOGo.m +++ b/SoObjects/Appointments/iCalEvent+SOGo.m @@ -259,7 +259,7 @@ - (NSTimeInterval) occurenceInterval { - return [[self endDate] timeIntervalSinceDate: [self startDate]]; + return (NSTimeInterval)[[self endDate] timeIntervalSinceDate: [self startDate]]; } /** diff --git a/SoObjects/Contacts/NGVCard+SOGo.m b/SoObjects/Contacts/NGVCard+SOGo.m index 83e646c06..6a0a6cf5d 100644 --- a/SoObjects/Contacts/NGVCard+SOGo.m +++ b/SoObjects/Contacts/NGVCard+SOGo.m @@ -297,7 +297,7 @@ convention: if (year && month && day) [self setBday: [NSString stringWithFormat: @"%.4d-%.2d-%.2d", - year, month, day]]; + (int)year, (int)month, (int)day]]; else [self setBday: @""]; @@ -614,11 +614,11 @@ convention: birthDay = [[self bday] asCalendarDate]; if (birthDay) { - stringValue = [NSString stringWithFormat: @"%.4d", [birthDay yearOfCommonEra]]; + stringValue = [NSString stringWithFormat: @"%.4d", (int)[birthDay yearOfCommonEra]]; [self _setValue: @"birthyear" to: stringValue inLDIFRecord: ldifRecord]; - stringValue = [NSString stringWithFormat: @"%.2d", [birthDay monthOfYear]]; + stringValue = [NSString stringWithFormat: @"%.2d", (int)[birthDay monthOfYear]]; [self _setValue: @"birthmonth" to: stringValue inLDIFRecord: ldifRecord]; - stringValue = [NSString stringWithFormat: @"%.2d", [birthDay dayOfMonth]]; + stringValue = [NSString stringWithFormat: @"%.2d", (int)[birthDay dayOfMonth]]; [self _setValue: @"birthday" to: stringValue inLDIFRecord: ldifRecord]; } [self _setValue: @"description" to: [self note] inLDIFRecord: ldifRecord]; diff --git a/SoObjects/Contacts/SOGoContactEntryPhoto.m b/SoObjects/Contacts/SOGoContactEntryPhoto.m index 0cbf83cf6..6ae248e08 100644 --- a/SoObjects/Contacts/SOGoContactEntryPhoto.m +++ b/SoObjects/Contacts/SOGoContactEntryPhoto.m @@ -59,7 +59,7 @@ [response setHeader: [self davContentType] forKey: @"content-type"]; [response setHeader: [NSString stringWithFormat:@" %d", - [data length]] + (int)[data length]] forKey: @"content-length"]; [response setContent: data]; } diff --git a/SoObjects/Contacts/SOGoContactFolders.m b/SoObjects/Contacts/SOGoContactFolders.m index b0428f7f4..e7984b172 100644 --- a/SoObjects/Contacts/SOGoContactFolders.m +++ b/SoObjects/Contacts/SOGoContactFolders.m @@ -268,9 +268,9 @@ Class SOGoContactSourceFolderK; SOGoUser *currentUser; id source; - if ([sourceID isEqualToString: @"personal"]) - result = [NSException exceptionWithHTTPStatus: 403 - reason: (@"folder '%@' cannot be deleted", sourceID)]; + if ([sourceID isEqualToString: @"personal"]){ + result = [NSException exceptionWithHTTPStatus: 403 reason: [NSString stringWithFormat: (@"folder '%@' cannot be deleted"), sourceID]]; + } else { result = nil; diff --git a/SoObjects/Contacts/SOGoContactGCSEntry.m b/SoObjects/Contacts/SOGoContactGCSEntry.m index 4b5d5bd21..dfaa66426 100644 --- a/SoObjects/Contacts/SOGoContactGCSEntry.m +++ b/SoObjects/Contacts/SOGoContactGCSEntry.m @@ -50,7 +50,7 @@ - (Class *) parsingClass { - return [NGVCard class]; + return (Class *)[NGVCard class]; } /* content */ diff --git a/SoObjects/Contacts/SOGoContactGCSList.m b/SoObjects/Contacts/SOGoContactGCSList.m index 61e68bf85..ad1b3277b 100644 --- a/SoObjects/Contacts/SOGoContactGCSList.m +++ b/SoObjects/Contacts/SOGoContactGCSList.m @@ -45,7 +45,7 @@ - (Class *) parsingClass { - return [NGVList class]; + return (Class *)[NGVList class]; } diff --git a/SoObjects/Contacts/SOGoContactSourceFolder.m b/SoObjects/Contacts/SOGoContactSourceFolder.m index 9c7eb8ce7..9e9a19f1c 100644 --- a/SoObjects/Contacts/SOGoContactSourceFolder.m +++ b/SoObjects/Contacts/SOGoContactSourceFolder.m @@ -95,7 +95,7 @@ { if (![newDisplayName length]) newDisplayName = newName; - ASSIGN (displayName, newDisplayName); + ASSIGN (displayName, (NSMutableString *)newDisplayName); } return self; diff --git a/SoObjects/Mailer/NSString+Mail.m b/SoObjects/Mailer/NSString+Mail.m index 1efdcb62f..f0e19046a 100644 --- a/SoObjects/Mailer/NSString+Mail.m +++ b/SoObjects/Mailer/NSString+Mail.m @@ -533,7 +533,7 @@ messageID = [NSMutableString string]; [messageID appendFormat: @"<%@", [SOGoObject globallyUniqueObjectId]]; pGUID = [[NSProcessInfo processInfo] globallyUniqueString]; - [messageID appendFormat: @"@%u>", [pGUID hash]]; + [messageID appendFormat: @"@%u>", (unsigned int)[pGUID hash]]; return [messageID lowercaseString]; } diff --git a/SoObjects/Mailer/SOGoMailBodyPart.m b/SoObjects/Mailer/SOGoMailBodyPart.m index 2fb075f4c..71d2dd385 100644 --- a/SoObjects/Mailer/SOGoMailBodyPart.m +++ b/SoObjects/Mailer/SOGoMailBodyPart.m @@ -396,7 +396,7 @@ static BOOL debugOn = NO; mimeType = @"application/octet-stream"; [response setHeader: mimeType forKey: @"content-type"]; - [response setHeader: [NSString stringWithFormat:@"%d", [data length]] + [response setHeader: [NSString stringWithFormat:@"%d", (int)[data length]] forKey: @"content-length"]; if (asAttachment) diff --git a/SoObjects/Mailer/SOGoMailFolder.m b/SoObjects/Mailer/SOGoMailFolder.m index f64e6c411..529a138e7 100644 --- a/SoObjects/Mailer/SOGoMailFolder.m +++ b/SoObjects/Mailer/SOGoMailFolder.m @@ -1645,7 +1645,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) sortOrderings = [NSMutableArray array]; - if ([self _sortElementIsAscending: sortElement]) + if ([self _sortElementIsAscending: (NGDOMNodeWithChildren *)sortElement]) sortOrderingOrder = EOCompareAscending; else sortOrderingOrder = EOCompareDescending; diff --git a/SoObjects/Mailer/SOGoMailForward.m b/SoObjects/Mailer/SOGoMailForward.m index 3450539d5..eab520cb8 100644 --- a/SoObjects/Mailer/SOGoMailForward.m +++ b/SoObjects/Mailer/SOGoMailForward.m @@ -73,10 +73,10 @@ - (NSString *) newLine { - NSString *rc = [NSString stringWithString: @" "]; + NSString *rc = @" "; if (htmlComposition) - rc = [NSString stringWithString: @"
"]; + rc = @"
"; return rc; } diff --git a/SoObjects/Mailer/SOGoMailLabel.m b/SoObjects/Mailer/SOGoMailLabel.m index 1d822e90b..8a88c1bd8 100644 --- a/SoObjects/Mailer/SOGoMailLabel.m +++ b/SoObjects/Mailer/SOGoMailLabel.m @@ -74,7 +74,7 @@ int i; allLabels = [NSMutableArray array]; - allKeys = [[theDefaults allKeys] sortedArrayUsingSelector: @selector (caseInsensitiveCompare:)]; + allKeys = (NSMutableArray *)[[theDefaults allKeys] sortedArrayUsingSelector: @selector (caseInsensitiveCompare:)]; for (i = 0; i < [allKeys count]; i++) { diff --git a/SoObjects/Mailer/SOGoMailObject.m b/SoObjects/Mailer/SOGoMailObject.m index 4fae67630..332c7cc64 100644 --- a/SoObjects/Mailer/SOGoMailObject.m +++ b/SoObjects/Mailer/SOGoMailObject.m @@ -770,6 +770,7 @@ static BOOL debugSoParts = NO; [part objectForKey: @"subtype"]]; if (!filename) + { // We might end up here because of MUA that actually strips the // Content-Disposition (and thus, the filename) when mails containing // attachments have been forwarded. Thunderbird (2.x) does just that @@ -778,9 +779,15 @@ static BOOL debugSoParts = NO; [mimeType hasPrefix: @"audio/"] || [mimeType hasPrefix: @"image/"] || [mimeType hasPrefix: @"video/"]) + { filename = [NSString stringWithFormat: @"unknown_%@", path]; - else if ([mimeType isEqualToString: @"message/rfc822"]) - filename = [NSString stringWithFormat: @"email_%@.eml", path]; + } + else + { + if ([mimeType isEqualToString: @"message/rfc822"]) + filename = [NSString stringWithFormat: @"email_%@.eml", path]; + } + } if (filename) @@ -824,13 +831,13 @@ static BOOL debugSoParts = NO; { currentPart = [subparts objectAtIndex: i-1]; if (path) - newPath = [NSString stringWithFormat: @"%@.%d", path, i]; + newPath = [NSString stringWithFormat: @"%@.%d", path, (int)i]; else - newPath = [NSString stringWithFormat: @"%d", i]; + newPath = [NSString stringWithFormat: @"%d", (int)i]; [self _fetchFileAttachmentKeysInPart: currentPart intoArray: keys withPath: newPath - andPrefix: [NSString stringWithFormat: @"%@/%i", prefix, i]]; + andPrefix: [NSString stringWithFormat: @"%@/%i", prefix, (int)i]]; } } else diff --git a/SoObjects/SOGo/BSONCodec.m b/SoObjects/SOGo/BSONCodec.m index ce4c01e24..0e68f966c 100644 --- a/SoObjects/SOGo/BSONCodec.m +++ b/SoObjects/SOGo/BSONCodec.m @@ -310,7 +310,7 @@ static NSDictionary *BSONTypes() case 'q': return 0x12; default: - [NSException raise: NSInvalidArgumentException format: @"%@::%s - invalid encoding type '%c'", [self class], _cmd, encoding]; + [NSException raise: NSInvalidArgumentException format: @"%@::%@ - invalid encoding type '%c'", [self class], NSStringFromSelector(_cmd), encoding]; } return 0; } @@ -385,7 +385,7 @@ static NSDictionary *BSONTypes() } - [NSException raise: NSInvalidArgumentException format: @"%@::%s - invalid encoding type '%c'", [self class], _cmd, encoding]; + [NSException raise: NSInvalidArgumentException format: @"%@::%@ - invalid encoding type '%c'", [self class], NSStringFromSelector(_cmd), encoding]; return nil; } diff --git a/SoObjects/SOGo/LDAPSource.m b/SoObjects/SOGo/LDAPSource.m index d537c6aec..55115efdd 100644 --- a/SoObjects/SOGo/LDAPSource.m +++ b/SoObjects/SOGo/LDAPSource.m @@ -1703,7 +1703,7 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection, hostname: hostname port: [NSString stringWithFormat: @"%d", port] encryption: encryption - bindAsCurrentUser: NO]; + bindAsCurrentUser: [NSString stringWithFormat: @"%d", NO]]; [ab setBaseDN: [entry dn] IDField: @"cn" CNField: @"displayName" diff --git a/SoObjects/SOGo/NSCalendarDate+SOGo.m b/SoObjects/SOGo/NSCalendarDate+SOGo.m index 4ba4cd4ed..c2bac7996 100644 --- a/SoObjects/SOGo/NSCalendarDate+SOGo.m +++ b/SoObjects/SOGo/NSCalendarDate+SOGo.m @@ -88,9 +88,9 @@ static NSString *rfc822Months[] = {@"", @"Jan", @"Feb", @"Mar", @"Apr", NSString *str; str = [NSString stringWithFormat: @"%.4d%.2d%.2d", - [self yearOfCommonEra], - [self monthOfYear], - [self dayOfMonth]]; + (int)[self yearOfCommonEra], + (int)[self monthOfYear], + (int)[self dayOfMonth]]; return str; } @@ -107,9 +107,9 @@ static NSString *rfc822Months[] = {@"", @"Jan", @"Feb", @"Mar", @"Apr", return [NSString stringWithFormat: @"%@, %.2d %@ %d %.2d:%.2d:%.2d %+.4d", - rfc822Days[[self dayOfWeek]], [self dayOfMonth], - rfc822Months[[self monthOfYear]], [self yearOfCommonEra], - [self hourOfDay], [self minuteOfHour], [self secondOfMinute], + rfc822Days[[self dayOfWeek]], (int)[self dayOfMonth], + rfc822Months[[self monthOfYear]], (int)[self yearOfCommonEra], + (int)[self hourOfDay], (int)[self minuteOfHour], (int)[self secondOfMinute], timeZoneShift]; } diff --git a/SoObjects/SOGo/NSDictionary+DAV.m b/SoObjects/SOGo/NSDictionary+DAV.m index 722e20511..a2d52e71e 100644 --- a/SoObjects/SOGo/NSDictionary+DAV.m +++ b/SoObjects/SOGo/NSDictionary+DAV.m @@ -46,7 +46,7 @@ { NSString *newTag; - newTag = [NSString stringWithFormat: @"n%d", [namespaces count]]; + newTag = [NSString stringWithFormat: @"n%d", (int)[namespaces count]]; [namespaces setObject: newTag forKey: newNS]; return newTag; diff --git a/SoObjects/SOGo/NSString+Utilities.m b/SoObjects/SOGo/NSString+Utilities.m index 3895cd923..6dbcb9689 100644 --- a/SoObjects/SOGo/NSString+Utilities.m +++ b/SoObjects/SOGo/NSString+Utilities.m @@ -309,7 +309,7 @@ static int cssEscapingCount; c == 0xD || (c >= 0x20 && c <= 0xD7FF) || (c >= 0xE000 && c <= 0xFFFD) || - (c >= 0x10000 && c <= 0x10FFFF)) + (c >= (unichar)0x10000 && c <= (unichar)0x10FFFF)) { *(start+j) = c; j++; diff --git a/SoObjects/SOGo/SOGoCacheGCSObject.m b/SoObjects/SOGo/SOGoCacheGCSObject.m index 7956ecefd..ed2d33f52 100644 --- a/SoObjects/SOGo/SOGoCacheGCSObject.m +++ b/SoObjects/SOGo/SOGoCacheGCSObject.m @@ -87,7 +87,7 @@ static EOAttribute *textColumn = nil; { tableUrl = nil; initialized = NO; - objectType = -1; + objectType = (SOGoCacheObjectType) -1; deleted = NO; version = 0; } @@ -394,7 +394,7 @@ static EOAttribute *textColumn = nil; @"SELECT * FROM %@ WHERE c_path = %@", tableName, pathValue]; if (startVersion > -1) - [sql appendFormat: @" AND c_version > %d", startVersion]; + [sql appendFormat: @" AND c_version > %d", (int)startVersion]; /* execution */ records = [self performSQLQuery: sql]; @@ -422,18 +422,16 @@ static EOAttribute *textColumn = nil; tableName = [self tableName]; adaptor = [self tableChannelAdaptor]; - pathValue = [adaptor formatValue: [NSString stringWithFormat: @"/%@", deviceId] - forAttribute: textColumn]; /* query */ sql = [NSMutableString stringWithFormat: @"SELECT * FROM %@ WHERE c_type = %d AND c_deleted <> 1", tableName, objectType]; if (startVersion > -1) - [sql appendFormat: @" AND c_version > %d", startVersion]; + [sql appendFormat: @" AND c_version > %d", (int)startVersion]; if (deviceId) { - pathValue = [adaptor formatValue: [NSString stringWithFormat: @"/%@%", deviceId] + pathValue = [adaptor formatValue: [NSString stringWithFormat: @"/%@", deviceId] forAttribute: textColumn]; [sql appendFormat: @" AND c_path like %@", pathValue]; } @@ -557,7 +555,7 @@ static EOAttribute *textColumn = nil; lastModifiedValue = (NSInteger) [lastModified timeIntervalSince1970]; - if (objectType == -1) + if (objectType == (SOGoCacheObjectType) -1) [NSException raise: @"SOGoCacheIOException" format: @"object type has not been set for object '%@'", self]; @@ -587,7 +585,7 @@ static EOAttribute *textColumn = nil; @")"), tableName, pathValue, parentPathValue, objectType, - creationDateValue, lastModifiedValue, + (int)creationDateValue, (int)lastModifiedValue, propsValue]; isNew = NO; } @@ -601,7 +599,7 @@ static EOAttribute *textColumn = nil; @" c_version = %d, c_content = %@" @" WHERE c_path = %@"), tableName, - lastModifiedValue, deletedValue, version, propsValue, + (int)lastModifiedValue, (int)deletedValue, (int)version, propsValue, pathValue]; } diff --git a/SoObjects/SOGo/SOGoContentObject.m b/SoObjects/SOGo/SOGoContentObject.m index 088fe522e..2092deacc 100644 --- a/SoObjects/SOGo/SOGoContentObject.m +++ b/SoObjects/SOGo/SOGoContentObject.m @@ -94,7 +94,7 @@ { [self subclassResponsibility: _cmd]; - return nil; + return (Class *) nil; } - (void) _setRecord: (NSDictionary *) objectRecord @@ -254,6 +254,9 @@ - (NSException *) moveToFolder: (SOGoGCSFolder *) newFolder { [self subclassResponsibility: _cmd]; + + // TODO: Add exception handling code and return it + return (NSException *) nil; } - (NSException *) delete @@ -345,7 +348,7 @@ /* attempt a save */ - error = [self saveComponent: [[self parsingClass] parseSingleFromSource: [rq contentAsString]] + error = [self saveComponent: [(id)[self parsingClass] parseSingleFromSource: [rq contentAsString]] baseVersion: baseVersion]; if (error) response = (WOResponse *) error; @@ -410,7 +413,7 @@ else length = 0; - return [NSString stringWithFormat: @"%u", length]; + return [NSString stringWithFormat: @"%u", (unsigned int)length]; } - (NSException *) davMoveToTargetObject: (id) _target diff --git a/SoObjects/SOGo/SOGoFolder.m b/SoObjects/SOGo/SOGoFolder.m index b089c258a..ac9d019c8 100644 --- a/SoObjects/SOGo/SOGoFolder.m +++ b/SoObjects/SOGo/SOGoFolder.m @@ -107,7 +107,7 @@ - (void) setDisplayName: (NSString *) newDisplayName { - ASSIGN (displayName, newDisplayName); + ASSIGN (displayName, (NSMutableString *)newDisplayName); } - (NSString *) displayName diff --git a/SoObjects/SOGo/SOGoGCSFolder.m b/SoObjects/SOGo/SOGoGCSFolder.m index 8e245bd9a..adbcb348e 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.m +++ b/SoObjects/SOGo/SOGoGCSFolder.m @@ -399,12 +399,12 @@ static NSArray *childRecordFields = nil; if (!displayName) { if (activeUserIsOwner) - displayName = [self _displayNameFromOwner]; + displayName = (NSMutableString *)[self _displayNameFromOwner]; else { - displayName = [self _displayNameFromSubscriber]; + displayName = (NSMutableString *)[self _displayNameFromSubscriber]; if (!displayName) - displayName = [self _displayNameFromOwner]; + displayName = (NSMutableString *)[self _displayNameFromOwner]; } [displayName retain]; } @@ -852,7 +852,7 @@ static NSArray *childRecordFields = nil; { currentID = [ids objectAtIndex: count]; names = [[currentID componentsSeparatedByString: @"/"] objectEnumerator]; - deleteObject = self; + deleteObject = (SOGoContentObject *)self; while ((currentName = [names nextObject])) { deleteObject = [deleteObject lookupName: currentName diff --git a/SoObjects/SOGo/SOGoGroup.m b/SoObjects/SOGo/SOGoGroup.m index cacdd7770..c72095fb6 100644 --- a/SoObjects/SOGo/SOGoGroup.m +++ b/SoObjects/SOGo/SOGoGroup.m @@ -135,7 +135,7 @@ { NSArray *allSources; NGLdapEntry *entry; - NSObject *source; + NSObject *source; id o; NSEnumerator *gclasses; NSString *gclass; @@ -154,7 +154,7 @@ for (i = 0; i < [allSources count]; i++) { - source = [[SOGoUserManager sharedUserManager] sourceWithID: [allSources objectAtIndex: i]]; + source = (NSObject *) [[SOGoUserManager sharedUserManager] sourceWithID: [allSources objectAtIndex: i]]; // Our different sources might not all implements groups support if ([source respondsToSelector: theSelector]) diff --git a/SoObjects/SOGo/SOGoObject.m b/SoObjects/SOGo/SOGoObject.m index 3f3afcb9b..c54c0b76c 100644 --- a/SoObjects/SOGo/SOGoObject.m +++ b/SoObjects/SOGo/SOGoObject.m @@ -1066,7 +1066,7 @@ [_ms appendFormat:@" name=%@", nameInContainer]; if (container) [_ms appendFormat:@" container=0x%08X/%@", - container, [container valueForKey:@"nameInContainer"]]; + (unsigned int)container, [container valueForKey:@"nameInContainer"]]; } - (NSString *) description @@ -1074,7 +1074,7 @@ NSMutableString *ms; ms = [NSMutableString stringWithCapacity:64]; - [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])]; + [ms appendFormat:@"<0x%08X[%@]:", (unsigned int) self, NSStringFromClass([self class])]; [self appendAttributesToDescription:ms]; [ms appendString:@">"]; @@ -1084,7 +1084,7 @@ - (NSString *) loggingPrefix { return [NSString stringWithFormat:@"<0x%08X[%@]:%@>", - self, NSStringFromClass([self class]), + (unsigned int) self, NSStringFromClass([self class]), [self nameInContainer]]; } diff --git a/Tests/Unit/SOGoTest.m b/Tests/Unit/SOGoTest.m index ddee98f30..48dafb17c 100644 --- a/Tests/Unit/SOGoTest.m +++ b/Tests/Unit/SOGoTest.m @@ -245,10 +245,10 @@ NSString *_stringForCharacterAtIndex(NSUInteger index, NSString *str, NSUInteger sc2 = _stringForCharacterAtIndex(i, str2, length2); if ([sc1 isEqualToString: sc2]) - finalSTR = [finalSTR stringByAppendingFormat: @"%u |%@|\n", i, sc1]; + finalSTR = [finalSTR stringByAppendingFormat: @"%lu |%@|\n", i, sc1]; else { - finalSTR = [finalSTR stringByAppendingFormat: @"%u |%@|%@|<--\n", i, sc1, sc2]; + finalSTR = [finalSTR stringByAppendingFormat: @"%lu |%@|%@|<--\n", i, sc1, sc2]; differencesFound = YES; } } diff --git a/Tests/Unit/TestNGMailAddressParser.m b/Tests/Unit/TestNGMailAddressParser.m index 9801d509f..e5118e464 100644 --- a/Tests/Unit/TestNGMailAddressParser.m +++ b/Tests/Unit/TestNGMailAddressParser.m @@ -37,8 +37,8 @@ @"", // email between brackets @"\"\" ", // doubled // @"\"johndown@inverse.ca\" ", // with and without br. - @"inoblabla ", // accented full name - @"inoblabla Bla Bl ", // accented and multiword + @"=?utf-8?q?=C3=80=C3=B1in=C3=A9oblabla?= ", // accented full name + @"=?utf-8?q?=C3=80=C3=B1in=C3=A9oblabla_Bla_Bl=C3=A9?= ", // accented and multiword @"John Down \"Bla Bla\" ", // partly quoted @"John Down ", // full name + email @"John, Down ", // full name with comma + email @@ -123,8 +123,6 @@ testWithMessage([result isEqualToString: currentExp], error); } - currentRaw++; - currentExp++; } } @end diff --git a/Tests/Unit/TestNGMimeAddressHeaderFieldGenerator.m b/Tests/Unit/TestNGMimeAddressHeaderFieldGenerator.m index 3a5fe5fdf..ef34ae149 100644 --- a/Tests/Unit/TestNGMimeAddressHeaderFieldGenerator.m +++ b/Tests/Unit/TestNGMimeAddressHeaderFieldGenerator.m @@ -39,8 +39,8 @@ @"", // email between brackets @"\"\" ", // doubled @"\"wolfgang@inverse.ca\" ", // with and without br. - @"inoblabla ", // accented full name - @"inoblabla Bla Bl ", // accented and multiword + @"=?utf-8?q?=C3=80=C3=B1in=C3=A9oblabla?= ", // accented full name + @"=?utf-8?q?=C3=80=C3=B1in=C3=A9oblabla_Bla_Bl=C3=A9?= ", // accented and multiword @"Wolfgang Sourdeau \"Bla Bla\" ", // partly quoted @"Wolfgang Sourdeau ", // full name + email nil }; @@ -50,8 +50,7 @@ @"\"\" ", // doubled @"\"wolfgang@inverse.ca\" ", // with and without br. @"=?utf-8?q?=C3=80=C3=B1in=C3=A9oblabla?= ", // accented full name - @"=?utf-8?q?=C3=80=C3=B1in=C3=A9oblabla_Bla_Bl=C3=A9?= ", // accented - // and multiword + @"=?utf-8?q?=C3=80=C3=B1in=C3=A9oblabla_Bla_Bl=C3=A9?= ", // accented and multiword /* NOTE: the following are wrong but tolerated for now */ @"Wolfgang Sourdeau \"Bla Bla\" ", // partly quoted diff --git a/Tests/Unit/TestNGMimeMessageGenerator.m b/Tests/Unit/TestNGMimeMessageGenerator.m index 51b4e4745..f440bda97 100644 --- a/Tests/Unit/TestNGMimeMessageGenerator.m +++ b/Tests/Unit/TestNGMimeMessageGenerator.m @@ -117,7 +117,7 @@ NSString *diff = [self stringFromDiffBetween: [NSString stringWithString: resultString] and: [NSString stringWithString: expected]]; NSString *testErrorMsg = [NSString - stringWithFormat: @">> For %@ header received:\n%@[END]\n>> instead of:\n%@[END]\n>> for:\n%@\n>> diff:\n%@\n>> lengthReceived: %u lengthExpected: %u", + stringWithFormat: @">> For %@ header received:\n%@[END]\n>> instead of:\n%@[END]\n>> for:\n%@\n>> diff:\n%@\n>> lengthReceived: %lu lengthExpected: %lu", header, resultString, expected, diff --git a/Tests/Unit/TestVersit.m b/Tests/Unit/TestVersit.m index 92ddf02bb..e09dce49f 100644 --- a/Tests/Unit/TestVersit.m +++ b/Tests/Unit/TestVersit.m @@ -34,6 +34,10 @@ - (void) test_rendering { + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-string-compare" + CardElement *element; CardVersitRenderer *renderer; NSString *result; @@ -137,10 +141,17 @@ testEquals(result, @"ELEM:NONEMPTY=coucou\r\n"); /** tests about parameters handling could be nice */ + +#pragma clang diagnostic pop + } - (void) test_parsing { + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-string-compare" + CardGroup *group; CardElement *element; NSString *versit; @@ -201,6 +212,9 @@ element = [group firstChildWithTag: @"element"]; testEquals([element flattenedValueAtIndex: 0 forKey: @""], @"value"); testEquals([element value: 0 ofAttribute: @"param1"], @"paramvalue1, with comma"); + +#pragma clang diagnostic pop + } @end diff --git a/Tests/Unit/TestiCalRecurrenceCalculator.m b/Tests/Unit/TestiCalRecurrenceCalculator.m index dcd9529d9..ae1d34f3d 100644 --- a/Tests/Unit/TestiCalRecurrenceCalculator.m +++ b/Tests/Unit/TestiCalRecurrenceCalculator.m @@ -107,7 +107,7 @@ [currentOccurrence descriptionWithCalendarFormat: dateFormat]]; testWithMessage([currentOccurrence isDateOnSameDay: [[occurrences objectAtIndex: j] startDate]], error); } - error = [NSString stringWithFormat: @"Unexpected number of occurrences for recurrence rule %@ (found %i, expected %i)", + error = [NSString stringWithFormat: @"Unexpected number of occurrences for recurrence rule %@ (found %ld, expected %ld)", [currentRule objectAtIndex: 1], [occurrences count], [currentRule count] - 2]; @@ -211,7 +211,7 @@ [currentOccurrence descriptionWithCalendarFormat: dateFormat]]; testWithMessage([currentOccurrence isDateOnSameDay: [[occurrences objectAtIndex: j] startDate]], error); } - error = [NSString stringWithFormat: @"Unexpected number of occurrences for recurrence rule %@ (found %i, expected %i)", + error = [NSString stringWithFormat: @"Unexpected number of occurrences for recurrence rule %@ (found %ld, expected %ld)", [currentRule objectAtIndex: 1], [occurrences count], [currentRule count] - 2]; @@ -376,7 +376,7 @@ [currentOccurrence descriptionWithCalendarFormat: dateFormat]]; testWithMessage([currentOccurrence isDateOnSameDay: [[occurrences objectAtIndex: j] startDate]], error); } - error = [NSString stringWithFormat: @"Unexpected number of occurrences for recurrence rule %@ (found %i, expected %i)", + error = [NSString stringWithFormat: @"Unexpected number of occurrences for recurrence rule %@ (found %ld, expected %ld)", [currentRule objectAtIndex: 1], [occurrences count], [currentRule count] - 2]; diff --git a/Tests/Unit/TestiCalTimeZonePeriod.m b/Tests/Unit/TestiCalTimeZonePeriod.m index 02325e9bc..b6eb1b1b7 100644 --- a/Tests/Unit/TestiCalTimeZonePeriod.m +++ b/Tests/Unit/TestiCalTimeZonePeriod.m @@ -80,8 +80,8 @@ testWithMessage ((NSInteger) [testDate timeIntervalSince1970] == occurrenceSeconds[count], ([NSString stringWithFormat: - @"test %d: seconds do not match:" - @" delta = %d", count, delta])); + @"test %ld: seconds do not match:" + @" delta = %ld", count, delta])); } } diff --git a/Tools/SOGoEAlarmsNotifier.m b/Tools/SOGoEAlarmsNotifier.m index ad8cc8f7a..382e25675 100644 --- a/Tools/SOGoEAlarmsNotifier.m +++ b/Tools/SOGoEAlarmsNotifier.m @@ -81,8 +81,8 @@ timestamp = (int) [[NSDate date] timeIntervalSince1970]; pGUID = [[NSProcessInfo processInfo] globallyUniqueString]; - messageID = [NSString stringWithFormat: @"<%0X-%0X-%0X-%0X@%u>", - pid, timestamp, sequence, random(), [pGUID hash]]; + messageID = [NSString stringWithFormat: @"<%0X-%0X-%0X-%0X@%lu>", + pid, timestamp, sequence, (unsigned int)random(), [pGUID hash]]; return [messageID lowercaseString]; } diff --git a/Tools/SOGoSockDOperation.m b/Tools/SOGoSockDOperation.m index c3604b865..56cae6b35 100644 --- a/Tools/SOGoSockDOperation.m +++ b/Tools/SOGoSockDOperation.m @@ -145,7 +145,7 @@ Class SOGoContactSourceFolderKlass = Nil; [self _appendEntry: [resultEntries objectAtIndex: count] toResult: result]; - [result appendFormat: @"RESULT\ncode: %", resultCode]; + [result appendFormat: @"RESULT\ncode: %d", resultCode]; [responseSocket safeWriteData: [result dataUsingEncoding: NSASCIIStringEncoding]]; } diff --git a/Tools/SOGoToolCreateFolder.m b/Tools/SOGoToolCreateFolder.m index 423ec7266..2eba46e8b 100644 --- a/Tools/SOGoToolCreateFolder.m +++ b/Tools/SOGoToolCreateFolder.m @@ -97,7 +97,7 @@ rc = [self createFolder: folder withFM: fm]; if (!rc) { - NSLog (@"Create directory failed at path %s", folder); + NSLog (@"Create directory failed at path %@", folder); return NO; } diff --git a/Tools/SOGoToolExpireUserSessions.m b/Tools/SOGoToolExpireUserSessions.m index 98d40470e..dbe78d6d9 100644 --- a/Tools/SOGoToolExpireUserSessions.m +++ b/Tools/SOGoToolExpireUserSessions.m @@ -143,7 +143,7 @@ else { if (verbose) - NSLog(@"No session to remove", sessionsToDelete); + NSLog(@"No session to remove"); } [cm releaseChannel: channel]; diff --git a/Tools/SOGoToolManageEAS.m b/Tools/SOGoToolManageEAS.m index 0e01da733..115f49eb6 100644 --- a/Tools/SOGoToolManageEAS.m +++ b/Tools/SOGoToolManageEAS.m @@ -227,7 +227,7 @@ typedef enum NSMutableString *sql; - sql = [NSMutableString stringWithFormat: @"DELETE FROM %@" @" WHERE c_path like '/%@%'", [oc tableName], deviceId]; + sql = [NSMutableString stringWithFormat: @"DELETE FROM %@ WHERE c_path like '/%@'", [oc tableName], deviceId]; [oc performBatchSQLQueries: [NSArray arrayWithObject: sql]]; rc = YES; diff --git a/Tools/SOGoToolRemoveDoubles.m b/Tools/SOGoToolRemoveDoubles.m index c26254a6a..4d6a36115 100644 --- a/Tools/SOGoToolRemoveDoubles.m +++ b/Tools/SOGoToolRemoveDoubles.m @@ -175,7 +175,7 @@ now = [NSCalendarDate date]; delSql = [NSString stringWithFormat: @"UPDATE %@" - @" SET c_deleted = 1, c_lastmodified = %d," + @" SET c_deleted = 1, c_lastmodified = %lu," @" c_content = ''" @" WHERE c_name = '%@'", tableName, diff --git a/Tools/SOGoToolRenameUser.m b/Tools/SOGoToolRenameUser.m index 937424d76..cf260134e 100644 --- a/Tools/SOGoToolRenameUser.m +++ b/Tools/SOGoToolRenameUser.m @@ -148,7 +148,7 @@ if (sqlError) { [ac rollbackTransaction]; - NSLog([sqlError reason]); + NSLog(@"%@", [sqlError reason]); } else rc = [ac commitTransaction]; @@ -201,7 +201,7 @@ if (sqlError) { [ac rollbackTransaction]; - NSLog([sqlError reason]); + NSLog(@"%@", [sqlError reason]); } else rc = [ac commitTransaction]; diff --git a/Tools/SOGoToolUserPreferences.m b/Tools/SOGoToolUserPreferences.m index 4f981cb4f..a51505f53 100644 --- a/Tools/SOGoToolUserPreferences.m +++ b/Tools/SOGoToolUserPreferences.m @@ -115,7 +115,7 @@ typedef enum [theKey caseInsensitiveCompare: @"Vacation"] == NSOrderedSame) { /* credentials file handling */ - NSString *credsFilename, *authname, *authpwd; + NSString *credsFilename, *authname=nil, *authpwd=nil; SOGoCredentialsFile *cf; credsFilename = [[NSUserDefaults standardUserDefaults] stringForKey: @"p"]; diff --git a/Tools/sogo-tool.m b/Tools/sogo-tool.m index bc4755416..85c87d528 100644 --- a/Tools/sogo-tool.m +++ b/Tools/sogo-tool.m @@ -149,7 +149,7 @@ command, [currentTool objectAtIndex: 1]]; } - NSLog (helpString); + NSLog (@"%@", helpString); } - (void) registerTools diff --git a/UI/Common/UIxToolbar.m b/UI/Common/UIxToolbar.m index b4376b311..3deda0ce5 100644 --- a/UI/Common/UIxToolbar.m +++ b/UI/Common/UIxToolbar.m @@ -166,7 +166,7 @@ [self errorWithFormat: @"not toolbar configuration found on SoObject: %@ (%@)", [self clientObject], [[self clientObject] soClass]]; - toolbarConfig = [[NSNull null] retain]; + toolbarConfig = (NSArray *)[[NSNull null] retain]; return nil; } diff --git a/UI/Contacts/UIxContactActions.m b/UI/Contacts/UIxContactActions.m index b1d9e1269..9706e402c 100644 --- a/UI/Contacts/UIxContactActions.m +++ b/UI/Contacts/UIxContactActions.m @@ -141,7 +141,7 @@ content = [NSMutableString string]; response = [context response]; - [content appendFormat: [[self clientObject] contentAsString]]; + [content appendFormat: @"%@", [[self clientObject] contentAsString]]; [response setHeader: @"text/plain; charset=utf-8" forKey: @"content-type"]; [response appendContentString: content]; diff --git a/UI/Contacts/UIxContactFolderActions.m b/UI/Contacts/UIxContactFolderActions.m index 6a9fb29fd..d19d826c8 100644 --- a/UI/Contacts/UIxContactFolderActions.m +++ b/UI/Contacts/UIxContactFolderActions.m @@ -82,9 +82,9 @@ inContext: [self context] acquire: NO]; if ([currentChild respondsToSelector: @selector (vCard)]) - [content appendFormat: [[currentChild ldifRecord] ldifRecordAsString]]; + [content appendFormat: @"%@", [[currentChild ldifRecord] ldifRecordAsString]]; else if ([currentChild respondsToSelector: @selector (vList)]) - [content appendFormat: [[currentChild vList] ldifString]]; + [content appendFormat: @"%@", [[currentChild vList] ldifString]]; [content appendString: @"\n"]; } diff --git a/UI/Contacts/UIxContactFolderProperties.m b/UI/Contacts/UIxContactFolderProperties.m index 1f5a92a01..cf93a83eb 100644 --- a/UI/Contacts/UIxContactFolderProperties.m +++ b/UI/Contacts/UIxContactFolderProperties.m @@ -19,6 +19,8 @@ */ #import +#import +#import #import "UIxContactFolderProperties.h" @@ -48,7 +50,7 @@ return [addressBook displayName]; } -- (NSString *) setAddressBookName: (NSString *) newName +- (void) setAddressBookName: (NSString *) newName { [addressBook renameTo: newName]; } diff --git a/UI/Contacts/UIxContactView.m b/UI/Contacts/UIxContactView.m index 4a4541794..94d475e6f 100644 --- a/UI/Contacts/UIxContactView.m +++ b/UI/Contacts/UIxContactView.m @@ -350,7 +350,7 @@ data = [NSMutableString string]; [data appendString: postalCode]; if ([postalCode length] > 0 && [country length] > 0) - [data appendFormat: @", ", country]; + [data appendFormat: @", "]; [data appendString: country]; return [self _cardStringWithLabel: nil value: data]; @@ -562,7 +562,7 @@ data = [NSMutableString string]; [data appendString: postalCode]; if ([postalCode length] > 0 && [country length] > 0) - [data appendFormat: @" ", country]; + [data appendFormat: @" "]; [data appendString: country]; return [self _cardStringWithLabel: nil value: data]; diff --git a/UI/MailPartViewers/UIxMailPartHTMLViewer.m b/UI/MailPartViewers/UIxMailPartHTMLViewer.m index f1ead5ffd..19035615b 100644 --- a/UI/MailPartViewers/UIxMailPartHTMLViewer.m +++ b/UI/MailPartViewers/UIxMailPartHTMLViewer.m @@ -741,7 +741,7 @@ static NSData* _sanitizeContent(NSData *theData) /* SaxLexicalHandler */ - (void) comment: (unichar *) _chars - length: (NSUInteger) _len + length: (int) _len { showWhoWeAre(); if (inStyle) @@ -807,7 +807,7 @@ static NSData* _sanitizeContent(NSData *theData) } [dump appendFormat: @"--- end ---\n"]; - NSLog(dump); + NSLog(@"%@",dump); [dump release]; } diff --git a/UI/MailPartViewers/UIxMailPartMixedViewer.m b/UI/MailPartViewers/UIxMailPartMixedViewer.m index 39544c0d8..707650c93 100644 --- a/UI/MailPartViewers/UIxMailPartMixedViewer.m +++ b/UI/MailPartViewers/UIxMailPartMixedViewer.m @@ -57,7 +57,7 @@ - (NSString *)childPartName { char buf[8]; - sprintf(buf, "%d", [self childIndex] + 1); + sprintf(buf, "%d", (int)[self childIndex] + 1); return [NSString stringWithCString:buf]; } diff --git a/UI/MailPartViewers/UIxMailPartViewer.m b/UI/MailPartViewers/UIxMailPartViewer.m index c973acbb6..1fc29721d 100644 --- a/UI/MailPartViewers/UIxMailPartViewer.m +++ b/UI/MailPartViewers/UIxMailPartViewer.m @@ -270,7 +270,7 @@ if ([filename length]) // We replace any slash by a dash since Apache won't allow encoded slashes by default. // See http://httpd.apache.org/docs/2.2/mod/core.html#allowencodedslashes - filename = [filename stringByReplacingString: @"/" withString: @"-"]; + filename = (NSMutableString *)[filename stringByReplacingString: @"/" withString: @"-"]; else [filename appendFormat: @"%@-%@", [self labelForKey: @"Untitled"], diff --git a/UI/MailerUI/UIxMailFolderActions.m b/UI/MailerUI/UIxMailFolderActions.m index 80fd4fa20..3c7c1edb4 100644 --- a/UI/MailerUI/UIxMailFolderActions.m +++ b/UI/MailerUI/UIxMailFolderActions.m @@ -383,7 +383,7 @@ - (WOResponse *) moveMessagesAction { SOGoMailFolder *co; - SOGoUserSettings *us; + SOGoUserSettings *us=nil; WOResponse *response; NSArray *uids; NSString *value, *destinationFolder; diff --git a/UI/MailerUI/UIxMailListActions.h b/UI/MailerUI/UIxMailListActions.h index 26a917778..8d0f2dda5 100644 --- a/UI/MailerUI/UIxMailListActions.h +++ b/UI/MailerUI/UIxMailListActions.h @@ -43,7 +43,6 @@ } - (NSString *) defaultSortKey; -- (NSString *) imap4SortKey; - (NSString *) imap4SortOrdering; - (EOQualifier *) searchQualifier; - (NSString *) msgLabels; diff --git a/UI/MailerUI/UIxMailMainFrame.m b/UI/MailerUI/UIxMailMainFrame.m index e6ee428e5..d34e795d6 100644 --- a/UI/MailerUI/UIxMailMainFrame.m +++ b/UI/MailerUI/UIxMailMainFrame.m @@ -627,7 +627,7 @@ - (NSString *) columnsDisplayCount { - return [NSString stringWithFormat: @"%d", [[self columnsDisplayOrder] count]]; + return [NSString stringWithFormat: @"%d", (int)[[self columnsDisplayOrder] count]]; } - (void) setCurrentColumn: (NSDictionary *) newCurrentColumn diff --git a/UI/MailerUI/UIxMailView.m b/UI/MailerUI/UIxMailView.m index 110fac61b..453169327 100644 --- a/UI/MailerUI/UIxMailView.m +++ b/UI/MailerUI/UIxMailView.m @@ -53,6 +53,7 @@ #import #import // cyclic #import +#import #import "WOContext+UIxMailer.h" diff --git a/UI/MainUI/SOGoMicrosoftActiveSyncActions.m b/UI/MainUI/SOGoMicrosoftActiveSyncActions.m index 1cfb8bc2f..ea9566dee 100644 --- a/UI/MainUI/SOGoMicrosoftActiveSyncActions.m +++ b/UI/MainUI/SOGoMicrosoftActiveSyncActions.m @@ -50,7 +50,7 @@ id dispatcher; Class clazz; - request = [context request]; + request = (WORequest *)[context request]; response = [self responseWithStatus: 200]; bundle = [NSBundle bundleForClass: NSClassFromString(@"ActiveSyncProduct")]; diff --git a/UI/PreferencesUI/UIxPreferences.m b/UI/PreferencesUI/UIxPreferences.m index 5558c7d77..0de77d28f 100644 --- a/UI/PreferencesUI/UIxPreferences.m +++ b/UI/PreferencesUI/UIxPreferences.m @@ -1366,15 +1366,15 @@ static NSArray *reminderValues = nil; if ([account updateFilters]) // If Sieve is not enabled, the SOGoSieveManager will immediatly return a positive answer // See [SOGoSieveManager updateFiltersForAccount:withUsername:andPassword:] - results = [self responseWithStatus: 200 + results = (id )[self responseWithStatus: 200 andJSONRepresentation: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:hasChanged], @"hasChanged", nil]]; else - results = [self responseWithStatus: 502 + results = (id )[self responseWithStatus: 502 andJSONRepresentation: [NSDictionary dictionaryWithObjectsAndKeys: @"Connection error", @"textStatus", nil]]; } else - results = [self responseWithStatus: 503 + results = (id )[self responseWithStatus: 503 andJSONRepresentation: [NSDictionary dictionaryWithObjectsAndKeys: @"Service temporarily unavailable", @"textStatus", nil]]; } else diff --git a/UI/SOGoUI/SOGoAptFormatter.m b/UI/SOGoUI/SOGoAptFormatter.m index dbd49401d..163606d42 100644 --- a/UI/SOGoUI/SOGoAptFormatter.m +++ b/UI/SOGoUI/SOGoAptFormatter.m @@ -155,14 +155,14 @@ */ [_buf appendFormat:@"%02i:%02i", - [_date hourOfDay], - [_date minuteOfHour]]; + (int)[_date hourOfDay], + (int)[_date minuteOfHour]]; if (_refDate && ![_date isDateOnSameDay:_refDate]) { [_buf appendFormat:@" (%02i-%02i", - [_date monthOfYear], - [_date dayOfMonth]]; + (int)[_date monthOfYear], + (int)[_date dayOfMonth]]; if ([_date yearOfCommonEra] != [_refDate yearOfCommonEra]) - [_buf appendFormat:@"-%04i", [_date yearOfCommonEra]]; + [_buf appendFormat:@"-%04i", (int)[_date yearOfCommonEra]]; [_buf appendString:@")"]; } } diff --git a/UI/Scheduler/UIxCalDateSelector.m b/UI/Scheduler/UIxCalDateSelector.m index 2b2559fe3..a9bb518f3 100644 --- a/UI/Scheduler/UIxCalDateSelector.m +++ b/UI/Scheduler/UIxCalDateSelector.m @@ -117,7 +117,7 @@ date = [self startDate]; - return [NSString stringWithFormat: @"%.2d", [date monthOfYear]]; + return [NSString stringWithFormat: @"%.2d", (int)[date monthOfYear]]; } - (NSString *) headerMonthString @@ -136,7 +136,7 @@ date = [self startDate]; - return [NSString stringWithFormat: @"%d", [date yearOfCommonEra]]; + return [NSString stringWithFormat: @"%d", (int)[date yearOfCommonEra]]; } - (NSString *) localizedDayOfWeekName diff --git a/UI/Scheduler/UIxCalDayTable.h b/UI/Scheduler/UIxCalDayTable.h index 57d91e846..ceaed3b56 100644 --- a/UI/Scheduler/UIxCalDayTable.h +++ b/UI/Scheduler/UIxCalDayTable.h @@ -39,7 +39,8 @@ NSArray *weekDays; NSString *currentView, *timeFormat, *currentTableHour; NSCalendarDate *startDate, *currentTableDay; - NSMutableArray *daysToDisplay, *calendarsToDisplay, *currentCalendar, *hoursToDisplay; + NSMutableArray *daysToDisplay, *calendarsToDisplay, *hoursToDisplay; + NSMutableDictionary *currentCalendar; unsigned int numberOfDays; } @@ -55,7 +56,7 @@ - (NSArray *) calendarsToDisplay; - (void) setCurrentTableDay: (NSCalendarDate *) aTableDay; - (NSCalendarDate *) currentTableDay; -- (NSMutableArray *) currentCalendar; +- (NSMutableDictionary *) currentCalendar; @end diff --git a/UI/Scheduler/UIxCalDayTable.m b/UI/Scheduler/UIxCalDayTable.m index 7cfde1b1f..bea4ad518 100644 --- a/UI/Scheduler/UIxCalDayTable.m +++ b/UI/Scheduler/UIxCalDayTable.m @@ -37,6 +37,9 @@ #import #import +#import +#import + #import "UIxCalDayTable.h" @class SOGoAppointment; @@ -191,7 +194,7 @@ NSMutableDictionary *calendar; unsigned int count, foldersCount; NSString *folderName, *fDisplayName; - BOOL *isActive; + BOOL isActive; co = [self clientObject]; folders = [co subFolders]; @@ -200,8 +203,8 @@ for (count = 0; count < foldersCount; count++) { folder = [folders objectAtIndex: count]; - isActive = [NSNumber numberWithBool: [folder isActive]]; - if ([isActive intValue] != 0) { + isActive = [folder isActive]; + if (isActive != NO) { calendar = [NSMutableDictionary dictionary]; folderName = [folder nameInContainer]; fDisplayName = [folder displayName]; @@ -214,7 +217,7 @@ [calendar setObject: fDisplayName forKey: @"displayName"]; [calendar setObject: folderName forKey: @"folder"]; [calendar setObject: [folder calendarColor] forKey: @"color"]; - [calendar setObject: isActive forKey: @"active"]; + [calendar setObject: [NSNumber numberWithBool:isActive] forKey: @"active"]; [calendar setObject: [folder ownerInContext: context] forKey: @"owner"]; [calendarsToDisplay addObject: calendar]; @@ -235,12 +238,12 @@ return currentTableDay; } -- (void) setCurrentCalendar: (NSMutableArray *) aCalendar +- (void) setCurrentCalendar: (NSMutableDictionary *) aCalendar { ASSIGN(currentCalendar, aCalendar); } -- (NSMutableArray *) currentCalendar +- (NSMutableDictionary *) currentCalendar { return currentCalendar; } diff --git a/UI/Scheduler/UIxCalDayView.m b/UI/Scheduler/UIxCalDayView.m index 8dca83462..8f2644370 100644 --- a/UI/Scheduler/UIxCalDayView.m +++ b/UI/Scheduler/UIxCalDayView.m @@ -164,7 +164,7 @@ date = [self selectedDate]; hmString = [NSString stringWithFormat:@"%.2d%.2d", - [date hourOfDay], [date minuteOfHour]]; + (int)[date hourOfDay], (int)[date minuteOfHour]]; qp = [[self queryParameters] mutableCopy]; [self setSelectedDateQueryParameter:date inDictionary:qp]; [qp setObject: hmString forKey:@"hm"]; diff --git a/UI/Scheduler/UIxCalListingActions.m b/UI/Scheduler/UIxCalListingActions.m index bb94e7c4f..87f1e6168 100644 --- a/UI/Scheduler/UIxCalListingActions.m +++ b/UI/Scheduler/UIxCalListingActions.m @@ -316,7 +316,7 @@ static NSArray *tasksFields = nil; NSString *owner, *role, *calendarName, *filters, *iCalString; NSRange match; iCalCalendar *calendar; - iCalObject *master; + iCalEntityObject *master; SOGoAppointmentFolder *currentFolder; SOGoAppointmentFolders *clientObject; SOGoUser *ownerUser; @@ -350,7 +350,7 @@ static NSArray *tasksFields = nil; else if ([criteria isEqualToString:@"entireContent"]) { // First search : Through the quick table inside the location, category and title columns - quickInfos = [currentFolder fetchCoreInfosFrom: startDate + quickInfos = (NSMutableArray *)[currentFolder fetchCoreInfosFrom: startDate to: endDate title: value component: component @@ -366,7 +366,7 @@ static NSArray *tasksFields = nil; } // Second research : Every objects except for those already in the quickInfos array - allInfos = [currentFolder fetchCoreInfosFrom: startDate + allInfos = (NSMutableArray *)[currentFolder fetchCoreInfosFrom: startDate to: endDate title: nil component: component]; @@ -383,7 +383,7 @@ static NSArray *tasksFields = nil; { iCalString = [[allInfos objectAtIndex:i] objectForKey:@"c_content"]; calendar = [iCalCalendar parseSingleFromSource: iCalString]; - master = [calendar firstChildWithTag:component]; + master = (iCalEntityObject *)[calendar firstChildWithTag:component]; if (master) { if ([[master comment] length] > 0) { @@ -1089,7 +1089,7 @@ _computeBlocksPosition (NSArray *blocks) if ([currentView isEqualToString: @"multicolumndayview"]) { - calendars = [self _selectedCalendars]; + calendars = (NSMutableArray *)[self _selectedCalendars]; eventsByCalendars = [NSMutableArray arrayWithCapacity:[calendars count]]; for (i = 0; i < [calendars count]; i++) // For each calendar { diff --git a/UI/Scheduler/UIxCalMonthView.m b/UI/Scheduler/UIxCalMonthView.m index c0c51097f..be35a5b75 100644 --- a/UI/Scheduler/UIxCalMonthView.m +++ b/UI/Scheduler/UIxCalMonthView.m @@ -299,7 +299,7 @@ [classes appendFormat: @"day weekOf%d week%dof%d day%d", numberOfWeeks, - [weeksToDisplay indexOfObject: currentWeek], + (int)[weeksToDisplay indexOfObject: currentWeek], numberOfWeeks, dayOfWeek]; if (realDayOfWeek == 0 || realDayOfWeek == 6) [classes appendString: @" weekEndDay"]; diff --git a/UI/Scheduler/UIxCalViewPrint.m b/UI/Scheduler/UIxCalViewPrint.m index 813e2e2ad..c49fd8a18 100644 --- a/UI/Scheduler/UIxCalViewPrint.m +++ b/UI/Scheduler/UIxCalViewPrint.m @@ -49,6 +49,7 @@ static NSArray *layoutItems = nil; - (void) dealloc { [item release]; + [super dealloc]; } - (void) setItem: (NSString *) newItem @@ -68,7 +69,7 @@ static NSArray *layoutItems = nil; - (NSString *) itemPrintLayoutText { - return [self labelForKey: [NSString stringWithFormat: item]]; + return [self labelForKey: [NSString stringWithFormat: @"%@", item]]; } // diff --git a/UI/Scheduler/UIxComponentEditor.m b/UI/Scheduler/UIxComponentEditor.m index a7581e57c..531daa076 100644 --- a/UI/Scheduler/UIxComponentEditor.m +++ b/UI/Scheduler/UIxComponentEditor.m @@ -2472,7 +2472,7 @@ RANGE(2); content = [NSMutableString string]; response = [context response]; - [content appendFormat: [[self clientObject] contentAsString]]; + [content appendFormat: @"%@", [[self clientObject] contentAsString]]; [response setHeader: @"text/plain; charset=utf-8" forKey: @"content-type"]; [response appendContentString: content]; diff --git a/UI/Scheduler/UIxRecurrenceEditor.m b/UI/Scheduler/UIxRecurrenceEditor.m index d4449b40a..45f3d7324 100644 --- a/UI/Scheduler/UIxRecurrenceEditor.m +++ b/UI/Scheduler/UIxRecurrenceEditor.m @@ -148,7 +148,7 @@ [shortWeekDaysList retain]; } - id = [NSString stringWithFormat: @"weekDay%i", [shortWeekDaysList indexOfObject: item]]; + id = [NSString stringWithFormat: @"weekDay%i", (int)[shortWeekDaysList indexOfObject: item]]; return id; } From e4fae417b9a6f93127b99519ac3473ef69b4655d Mon Sep 17 00:00:00 2001 From: Euan Thoms Date: Wed, 4 Nov 2015 06:02:45 +0800 Subject: [PATCH 32/69] Stage 8 of clang compiler warning patches. Update WORequest+SOGo.m --- SoObjects/SOGo/WORequest+SOGo.m | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/SoObjects/SOGo/WORequest+SOGo.m b/SoObjects/SOGo/WORequest+SOGo.m index f528343dd..d7c750b65 100644 --- a/SoObjects/SOGo/WORequest+SOGo.m +++ b/SoObjects/SOGo/WORequest+SOGo.m @@ -180,14 +180,21 @@ cc = [self clientCapabilities]; b = ( - [[cc userAgent] rangeOfString: @"CFNetwork"].location != NSNotFound - && [[cc userAgent] rangeOfString: @"Darwin"].location != NSNotFound - || ( - [[cc userAgent] rangeOfString: @"CFNetwork"].location != NSNotFound - || [[cc userAgent] rangeOfString: @"Mac OS X"].location != NSNotFound - ) - && [[cc userAgent] rangeOfString: @"AddressBook"].location != NSNotFound - ); + ( + [[cc userAgent] rangeOfString: @"CFNetwork"].location != NSNotFound + && [[cc userAgent] rangeOfString: @"Darwin"].location != NSNotFound + ) + || + ( + [[cc userAgent] rangeOfString: @"CFNetwork"].location != NSNotFound + && [[cc userAgent] rangeOfString: @"AddressBook"].location != NSNotFound + ) + || + ( + [[cc userAgent] rangeOfString: @"Mac OS X"].location != NSNotFound + && [[cc userAgent] rangeOfString: @"AddressBook"].location != NSNotFound + ) + ); return b; } From ca274649841493cba9f6a6bc856fc45596413ed8 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 5 Nov 2015 13:43:48 -0500 Subject: [PATCH 33/69] (fix) added type cast --- ActiveSync/iCalRecurrenceRule+ActiveSync.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ActiveSync/iCalRecurrenceRule+ActiveSync.m b/ActiveSync/iCalRecurrenceRule+ActiveSync.m index a9060fe27..225bbd90a 100644 --- a/ActiveSync/iCalRecurrenceRule+ActiveSync.m +++ b/ActiveSync/iCalRecurrenceRule+ActiveSync.m @@ -140,7 +140,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Simple reccurrence rule of type "Monthly" type = 2; [s appendFormat: @"%d", - [[[self parent] startDate] dayOfMonth]]; + (int)[[[self parent] startDate] dayOfMonth]]; } } else if ([self frequency] == iCalRecurrenceFrequenceYearly) From c2e4f7441d9dec4bbbf4825621c9354c123b8250 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 5 Nov 2015 13:57:51 -0500 Subject: [PATCH 34/69] (fix) double declaration of same method --- SoObjects/Appointments/SOGoCalendarComponent.m | 8 -------- 1 file changed, 8 deletions(-) diff --git a/SoObjects/Appointments/SOGoCalendarComponent.m b/SoObjects/Appointments/SOGoCalendarComponent.m index cfeca6c0c..762139774 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.m +++ b/SoObjects/Appointments/SOGoCalendarComponent.m @@ -140,14 +140,6 @@ return aclManager; } -- (NSException *) changeParticipationStatus: (NSString *) newPartStat - withDelegate: (iCalPerson *) delegate - alarm: (iCalAlarm *) alarm -{ - // required for protocol - return nil; -} - - (NSException *) changeParticipationStatus: (NSString *) newPartStat withDelegate: (iCalPerson *) delegate alarm: (iCalAlarm *) alarm From f919df050a4e566ef09e564d69bdc67f2f4d00ec Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 5 Nov 2015 14:58:58 -0500 Subject: [PATCH 35/69] (fix) one more fix for #3118 --- ActiveSync/SOGoActiveSyncDispatcher.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 24993aae3..013a4a226 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -849,8 +849,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. currentFolder = [[[[context activeUser] homeFolderInContext: context] lookupName: folderType inContext: context acquire: NO] lookupName: [cKey substringFromIndex: [cKey rangeOfString: @"/"].location+1] inContext: context acquire: NO]; + // We skip personal GCS folders - we always want to synchronize these + if ([currentFolder isKindOfClass: [SOGoGCSFolder class]] && + [[currentFolder nameInContainer] isEqualToString: @"personal"]) + continue; + // Remove the folder from device if it doesn't exist, we don't want to sync it, or it doesn't have the proper permissions - // No need to check for personal folders here since they can't be deleted if (!currentFolder || ![currentFolder synchronize] || [sm validatePermission: SoPerm_DeleteObjects From b55ec52bfe1ba32bc43bb7bc124dbc84d7f79e11 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 5 Nov 2015 15:21:13 -0500 Subject: [PATCH 36/69] (fix) handle emails having an attachment as their content --- NEWS | 2 +- SoObjects/Mailer/SOGoMailObject.m | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 915438aee..14f96b5f1 100644 --- a/NEWS +++ b/NEWS @@ -27,7 +27,7 @@ Bug fixes - freebusy on web interface works again in multidomain environments (Zentyal) - fix double creation of folders in Outlook when the folder name starts with a digit (Zentyal) - avoid crashing Outlook after setting a custom view in a calendar folder (Zentyal) - + - handle emails having an attachment as their content 2.3.2 (2015-09-16) ------------------ diff --git a/SoObjects/Mailer/SOGoMailObject.m b/SoObjects/Mailer/SOGoMailObject.m index 332c7cc64..dd4ee0358 100644 --- a/SoObjects/Mailer/SOGoMailObject.m +++ b/SoObjects/Mailer/SOGoMailObject.m @@ -389,8 +389,11 @@ static BOOL debugSoParts = NO; [[[info valueForKey: @"subtype"] lowercaseString] isEqualToString: @"calendar"]) return info; - if ([[[info valueForKey: @"type"] lowercaseString] isEqualToString: @"application"] && - [[[info valueForKey: @"subtype"] lowercaseString] isEqualToString: @"pkcs7-mime"]) + // deal with mails that contain only an attachment, for example: + // application/pkcs7-mime + // application/pdf + // etc. + if ([[[info valueForKey: @"type"] lowercaseString] isEqualToString: @"application"]) return info; /* @@ -1045,6 +1048,14 @@ static BOOL debugSoParts = NO; return obj; } } + // Handles cases where the email is itself an attachment, so its Content-Type + // is application/*, image/* etc. + else if ([_key isEqualToString: @"asAttachment"] && + (obj = [self lookupImap4BodyPartKey: @"0" inContext:_ctx]) != nil) + { + [obj setAsAttachment]; + return obj; + } /* return 404 to stop acquisition */ return [NSException exceptionWithHTTPStatus:404 /* Not Found */ From 7f26d47cd110a3a6b2502a811cf220e194d033f7 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 5 Nov 2015 15:39:08 -0500 Subject: [PATCH 37/69] We drop Lucid. --- Documentation/SOGoInstallationGuide.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/SOGoInstallationGuide.asciidoc b/Documentation/SOGoInstallationGuide.asciidoc index de828a6d7..6851dad81 100644 --- a/Documentation/SOGoInstallationGuide.asciidoc +++ b/Documentation/SOGoInstallationGuide.asciidoc @@ -170,7 +170,7 @@ supported by SOGo: * Red Hat Enterprise Linux (RHEL) Server 5, 6 and 7 * Community ENTerprise Operating System (CentOS) 5, 6 and 7 * Debian GNU/Linux 6.0 (Squeeze) to 8.0 (Jessie) -* Ubuntu 10.04 (Lucid) to 14.04 (Trusty) +* Ubuntu 12.04 (Precise) to 14.04 (Trusty) Make sure the required components are started automatically at boot time and that they are running before proceeding with the SOGo configuration. From 7c69e37449b72bbf55f3c5d1017c75e36dfc590e Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Fri, 6 Nov 2015 08:45:42 -0500 Subject: [PATCH 38/69] (fix) JavaScript syntax error in attendees editor --- NEWS | 1 + UI/WebServerResources/UIxAttendeesEditor.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 14f96b5f1..c29382b78 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,7 @@ Bug fixes - fix double creation of folders in Outlook when the folder name starts with a digit (Zentyal) - avoid crashing Outlook after setting a custom view in a calendar folder (Zentyal) - handle emails having an attachment as their content + - fixed JavaScript syntax error in attendees editor 2.3.2 (2015-09-16) ------------------ diff --git a/UI/WebServerResources/UIxAttendeesEditor.js b/UI/WebServerResources/UIxAttendeesEditor.js index c33f45451..8bb7669c8 100644 --- a/UI/WebServerResources/UIxAttendeesEditor.js +++ b/UI/WebServerResources/UIxAttendeesEditor.js @@ -225,7 +225,8 @@ function performSearchCallback(http) { if (contact["c_domain"]) login += "@" + contact["c_domain"]; node.uid = (contact["isMSExchange"]? UserLogin + ":" : "") + login; - } else + } + else { node.uid = null; node.appendChild(new Element('div').addClassName('colorBox').addClassName('noFreeBusy')); } From b170ae866f72cbb6be64a3fd0cf10518194d5c62 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Fri, 6 Nov 2015 08:56:23 -0500 Subject: [PATCH 39/69] (fix) small fixes --- ActiveSync/SOGoActiveSyncDispatcher.m | 2 +- SoObjects/SOGo/SOGoCacheGCSObject.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 013a4a226..e9300b3ce 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -2590,7 +2590,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. email = [identity objectForKey: @"email"]; if ([fullName length]) - new_from_header = [[NSString stringWithFormat: @"From: %@ <%@>\r\n", fullName, email] dataUsingEncoding:NSUTF8StringEncoding]; + new_from_header = [[NSString stringWithFormat: @"From: %@ <%@>\r\n", [fullName asQPSubjectString: @"utf-8"], email] dataUsingEncoding:NSUTF8StringEncoding]; else new_from_header = [[NSString stringWithFormat: @"From: %@\r\n", email] dataUsingEncoding:NSUTF8StringEncoding]; diff --git a/SoObjects/SOGo/SOGoCacheGCSObject.m b/SoObjects/SOGo/SOGoCacheGCSObject.m index ed2d33f52..a6a5c52a3 100644 --- a/SoObjects/SOGo/SOGoCacheGCSObject.m +++ b/SoObjects/SOGo/SOGoCacheGCSObject.m @@ -431,7 +431,7 @@ static EOAttribute *textColumn = nil; [sql appendFormat: @" AND c_version > %d", (int)startVersion]; if (deviceId) { - pathValue = [adaptor formatValue: [NSString stringWithFormat: @"/%@", deviceId] + pathValue = [adaptor formatValue: [NSString stringWithFormat: @"/%@%", deviceId] forAttribute: textColumn]; [sql appendFormat: @" AND c_path like %@", pathValue]; } From 1915748e33ab3f652cb34d07c989cde079cb0d13 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Fri, 6 Nov 2015 09:03:20 -0500 Subject: [PATCH 40/69] Bump version to 2.3.3 --- Version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Version b/Version index 2ffd63e92..c3e8773ab 100644 --- a/Version +++ b/Version @@ -4,4 +4,4 @@ MAJOR_VERSION=2 MINOR_VERSION=3 -SUBMINOR_VERSION=2 +SUBMINOR_VERSION=3 From ba37353e42163a1411f7b32e74d571d8ad4a5135 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Fri, 6 Nov 2015 09:14:09 -0500 Subject: [PATCH 41/69] (fix) Position of popup menu Fixes #3381 --- UI/WebServerResources/UIxAppointmentEditor.css | 2 +- UI/WebServerResources/generic.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/UI/WebServerResources/UIxAppointmentEditor.css b/UI/WebServerResources/UIxAppointmentEditor.css index da00a3857..b07d6a394 100644 --- a/UI/WebServerResources/UIxAppointmentEditor.css +++ b/UI/WebServerResources/UIxAppointmentEditor.css @@ -10,7 +10,7 @@ DIV#eventView position: absolute; top: 0; bottom: 0; left: 0; right: 0; } -DIV#eventView > DIV +DIV#eventView > DIV[id$=Div] { padding: 1em; } DIV.appointmentRightLabel diff --git a/UI/WebServerResources/generic.js b/UI/WebServerResources/generic.js index 385c5c835..6eb5270a1 100644 --- a/UI/WebServerResources/generic.js +++ b/UI/WebServerResources/generic.js @@ -764,7 +764,7 @@ function popupMenu(event, menuId, target) { var leftDiff = ((window.width() + deltaX) - (menuLeft + popup.offsetWidth)); if (leftDiff < 0) - menuLeft -= (popup.offsetWidth + 1); + menuLeft += leftDiff - 1; var isVisible = true; if (popup.prepareVisibility) { From 863f182e844fcb1f3a8f7d5a07a2dffee9a39d17 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Fri, 6 Nov 2015 09:25:49 -0500 Subject: [PATCH 42/69] (fix) fixed wrong comparison of meta vs. META tag in HTML mails --- NEWS | 1 + UI/MailPartViewers/UIxMailPartHTMLViewer.m | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index c29382b78..01021452b 100644 --- a/NEWS +++ b/NEWS @@ -29,6 +29,7 @@ Bug fixes - avoid crashing Outlook after setting a custom view in a calendar folder (Zentyal) - handle emails having an attachment as their content - fixed JavaScript syntax error in attendees editor + - fixed wrong comparison of meta vs. META tag in HTML mails 2.3.2 (2015-09-16) ------------------ diff --git a/UI/MailPartViewers/UIxMailPartHTMLViewer.m b/UI/MailPartViewers/UIxMailPartHTMLViewer.m index 19035615b..d464fbfc2 100644 --- a/UI/MailPartViewers/UIxMailPartHTMLViewer.m +++ b/UI/MailPartViewers/UIxMailPartHTMLViewer.m @@ -161,10 +161,10 @@ static NSData* _sanitizeContent(NSData *theData) if (i < len-5) { if ((*bytes == '<') && - (*(bytes+1) == 'm' || *(bytes+2) == 'M') && - (*(bytes+2) == 'e' || *(bytes+3) == 'E') && - (*(bytes+3) == 't' || *(bytes+4) == 'T') && - (*(bytes+4) == 'a' || *(bytes+5) == 'A') && + (*(bytes+1) == 'm' || *(bytes+1) == 'M') && + (*(bytes+2) == 'e' || *(bytes+2) == 'E') && + (*(bytes+3) == 't' || *(bytes+3) == 'T') && + (*(bytes+4) == 'a' || *(bytes+4) == 'A') && (*(bytes+5) == ' ')) in_meta = YES; } From 106c6a24ae1a2254a8a983de829a89e1c0313bae Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Fri, 6 Nov 2015 09:40:36 -0500 Subject: [PATCH 43/69] (js) Fix dialogs when partially visible Fixes #2646, #3378 --- NEWS | 2 ++ UI/WebServerResources/SchedulerUI.js | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/NEWS b/NEWS index 01021452b..2e6c82aa1 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,8 @@ Bug fixes - handle emails having an attachment as their content - fixed JavaScript syntax error in attendees editor - fixed wrong comparison of meta vs. META tag in HTML mails + - fixed popup menu position when moved to the left (#3381) + - fixed dialog position when at the bottom of the window (#2646, #3378) 2.3.2 (2015-09-16) ------------------ diff --git a/UI/WebServerResources/SchedulerUI.js b/UI/WebServerResources/SchedulerUI.js index 2151ce95e..50ff9763c 100644 --- a/UI/WebServerResources/SchedulerUI.js +++ b/UI/WebServerResources/SchedulerUI.js @@ -856,6 +856,11 @@ function onViewEventCallback(http) { } else para.hide(); + var windowHeight = window.height(); + var bottomOffset = windowHeight - div.getDimensions().height - top; + if (bottomOffset < 0) + top += bottomOffset; + div.setStyle({ left: left + "px", top: top + "px" }); div.show(); configureLinks(div); @@ -3493,6 +3498,12 @@ function onCalendarImport(event) { var div = $("uploadDialog"); var res = $("uploadResults"); + + var windowHeight = window.height(); + var bottomOffset = windowHeight - div.getDimensions().height - top; + if (bottomOffset < 0) + top += bottomOffset; + res.setStyle({ top: top + "px", left: left + "px" }); div.setStyle({ top: top + "px", left: left + "px" }); div.show(); From ef7de7c10d6d0b722850347e88fb3d620588a66b Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Fri, 6 Nov 2015 13:33:36 -0500 Subject: [PATCH 44/69] (fix) fixed addressbrook-only source entires having a c_uid set --- NEWS | 1 + SoObjects/Contacts/SOGoContactSourceFolder.m | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 2e6c82aa1..5cda7f31d 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,7 @@ Bug fixes - fixed wrong comparison of meta vs. META tag in HTML mails - fixed popup menu position when moved to the left (#3381) - fixed dialog position when at the bottom of the window (#2646, #3378) + - fixed addressbrook-only source entires having a c_uid set 2.3.2 (2015-09-16) ------------------ diff --git a/SoObjects/Contacts/SOGoContactSourceFolder.m b/SoObjects/Contacts/SOGoContactSourceFolder.m index 9e9a19f1c..b92b953a1 100644 --- a/SoObjects/Contacts/SOGoContactSourceFolder.m +++ b/SoObjects/Contacts/SOGoContactSourceFolder.m @@ -47,6 +47,7 @@ #import #import #import +#import #import #import #import @@ -225,8 +226,16 @@ NSObject *recordSource; newRecord = [NSMutableDictionary dictionaryWithCapacity: 8]; - [newRecord setObject: [oldRecord objectForKey: @"c_uid"] - forKey: @"c_uid"]; + + // We set the c_uid only for authentication sources. SOGoUserSources set + // with canAuthenticate = NO and isAddressBook = YES have absolutely *NO REASON* + // to have entries with a c_uid. These can collide with real uids. + if ([[[[SOGoUserManager sharedUserManager] metadataForSourceID: [source sourceID]] objectForKey: @"canAuthenticate"] boolValue]) + { + [newRecord setObject: [oldRecord objectForKey: @"c_uid"] + forKey: @"c_uid"]; + } + [newRecord setObject: [oldRecord objectForKey: @"c_name"] forKey: @"c_name"]; From 64456c10e5539550b49b699babef6422e19df5df Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Mon, 9 Nov 2015 14:30:58 -0500 Subject: [PATCH 45/69] (fix) additional fix for #3356 --- ActiveSync/SOGoActiveSyncDispatcher.m | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index e9300b3ce..7f2f11657 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -3149,7 +3149,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #else value = [[NSDate date] descriptionWithCalendarFormat: @"%a, %d %b %Y %H:%M:%S %z" timeZone: [NSTimeZone timeZoneWithName: @"GMT"] - locale: [[[NSLocale alloc] initWithLocaleIdentifier: @"en_US"] autorelease]]; + locale: [NSDictionary dictionaryWithObjectsAndKeys: + [NSArray arrayWithObjects: @"Jan", @"Feb", @"Mar", @"Apr", + @"May", @"Jun", @"Jul", @"Aug", + @"Sep", @"Oct", @"Nov", @"Dec", nil], + @"NSShortMonthNameArray", + [NSArray arrayWithObjects: @"Sun", @"Mon", @"Tue", @"Wed", @"Thu", + @"Fri", @"Sat", nil], + @"NSShortWeekDayNameArray", + nil]]; + #endif s = [NSString stringWithFormat: @"Date: %@\n%@", value, [theRequest contentAsString]]; } From 76b8de180514bdd1a6b53bde37bd464848bce0a3 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Tue, 10 Nov 2015 16:33:11 -0500 Subject: [PATCH 46/69] Add Portuguese translation --- .tx/config | 11 + SoObjects/Appointments/GNUmakefile | 2 +- .../Portuguese.lproj/Localizable.strings | 67 +++ SoObjects/Contacts/GNUmakefile | 2 +- .../Portuguese.lproj/Localizable.strings | 2 + SoObjects/Mailer/GNUmakefile | 4 +- .../Portuguese.lproj/Localizable.strings | 2 + SoObjects/Mailer/SOGoMailForward.h | 3 + SoObjects/Mailer/SOGoMailForward.m | 3 + .../SOGoMailPortugueseForward.html | 16 + .../SOGoMailPortugueseForward.wod | 88 +++ .../SOGoMailPortugueseReply.html | 17 + .../SOGoMailPortugueseReply.wod | 106 ++++ SoObjects/Mailer/SOGoMailReply.h | 3 + SoObjects/Mailer/SOGoMailReply.m | 3 + SoObjects/SOGo/SOGoDefaults.plist | 2 +- Tests/Integration/preferences.py | 2 +- UI/AdministrationUI/GNUmakefile | 2 +- .../Portuguese.lproj/Localizable.strings | 15 + UI/Common/GNUmakefile | 2 +- .../Portuguese.lproj/Localizable.strings | 118 ++++ UI/Contacts/GNUmakefile | 2 +- .../Portuguese.lproj/Localizable.strings | 215 +++++++ UI/MailPartViewers/GNUmakefile | 2 +- .../Portuguese.lproj/Localizable.strings | 48 ++ UI/MailerUI/GNUmakefile | 2 +- .../Portuguese.lproj/Localizable.strings | 308 ++++++++++ UI/MainUI/Arabic.lproj/Localizable.strings | 1 + UI/MainUI/Basque.lproj/Localizable.strings | 1 + .../Localizable.strings | 1 + UI/MainUI/Catalan.lproj/Localizable.strings | 1 + .../ChineseTaiwan.lproj/Localizable.strings | 1 + UI/MainUI/Czech.lproj/Localizable.strings | 1 + UI/MainUI/Danish.lproj/Localizable.strings | 1 + UI/MainUI/Dutch.lproj/Localizable.strings | 1 + UI/MainUI/English.lproj/Localizable.strings | 1 + UI/MainUI/Finnish.lproj/Localizable.strings | 1 + UI/MainUI/French.lproj/Localizable.strings | 1 + UI/MainUI/GNUmakefile | 2 +- UI/MainUI/German.lproj/Localizable.strings | 1 + UI/MainUI/Hungarian.lproj/Localizable.strings | 1 + UI/MainUI/Icelandic.lproj/Localizable.strings | 1 + UI/MainUI/Italian.lproj/Localizable.strings | 1 + .../NorwegianBokmal.lproj/Localizable.strings | 1 + .../Localizable.strings | 1 + UI/MainUI/Polish.lproj/Localizable.strings | 1 + UI/MainUI/Portuguese.lproj/Locale | 35 ++ .../Portuguese.lproj/Localizable.strings | 78 +++ UI/MainUI/Russian.lproj/Localizable.strings | 1 + UI/MainUI/Slovenian.lproj/Localizable.strings | 1 + .../Localizable.strings | 1 + .../SpanishSpain.lproj/Localizable.strings | 1 + UI/MainUI/Swedish.lproj/Localizable.strings | 1 + UI/MainUI/Ukrainian.lproj/Localizable.strings | 1 + UI/MainUI/Welsh.lproj/Localizable.strings | 1 + .../Arabic.lproj/Localizable.strings | 1 + .../Basque.lproj/Localizable.strings | 1 + .../Localizable.strings | 1 + .../Catalan.lproj/Localizable.strings | 1 + .../ChineseTaiwan.lproj/Localizable.strings | 1 + .../Czech.lproj/Localizable.strings | 1 + .../Dutch.lproj/Localizable.strings | 1 + .../English.lproj/Localizable.strings | 1 + .../Finnish.lproj/Localizable.strings | 1 + .../French.lproj/Localizable.strings | 1 + UI/PreferencesUI/GNUmakefile | 2 +- .../German.lproj/Localizable.strings | 1 + .../Hungarian.lproj/Localizable.strings | 1 + .../Icelandic.lproj/Localizable.strings | 1 + .../Italian.lproj/Localizable.strings | 1 + .../Localizable.strings | 1 + .../Polish.lproj/Localizable.strings | 1 + .../Portuguese.lproj/Localizable.strings | 325 ++++++++++ .../Russian.lproj/Localizable.strings | 1 + .../Slovak.lproj/Localizable.strings | 1 + .../Slovenian.lproj/Localizable.strings | 1 + .../Localizable.strings | 1 + .../SpanishSpain.lproj/Localizable.strings | 1 + .../Swedish.lproj/Localizable.strings | 1 + .../Ukrainian.lproj/Localizable.strings | 1 + .../Welsh.lproj/Localizable.strings | 1 + UI/Scheduler/GNUmakefile | 2 +- .../Portuguese.lproj/Localizable.strings | 564 ++++++++++++++++++ .../SOGoACLPortugueseAdditionAdvisory.wox | 28 + .../SOGoACLPortugueseModificationAdvisory.wox | 28 + .../SOGoACLPortugueseRemovalAdvisory.wox | 28 + .../SOGoFolderPortugueseAdditionAdvisory.wox | 23 + .../SOGoFolderPortugueseRemovalAdvisory.wox | 23 + 88 files changed, 2221 insertions(+), 13 deletions(-) create mode 100644 SoObjects/Appointments/Portuguese.lproj/Localizable.strings create mode 100644 SoObjects/Contacts/Portuguese.lproj/Localizable.strings create mode 100644 SoObjects/Mailer/Portuguese.lproj/Localizable.strings create mode 100644 SoObjects/Mailer/SOGoMailPortugueseForward.wo/SOGoMailPortugueseForward.html create mode 100644 SoObjects/Mailer/SOGoMailPortugueseForward.wo/SOGoMailPortugueseForward.wod create mode 100644 SoObjects/Mailer/SOGoMailPortugueseReply.wo/SOGoMailPortugueseReply.html create mode 100644 SoObjects/Mailer/SOGoMailPortugueseReply.wo/SOGoMailPortugueseReply.wod create mode 100644 UI/AdministrationUI/Portuguese.lproj/Localizable.strings create mode 100644 UI/Common/Portuguese.lproj/Localizable.strings create mode 100644 UI/Contacts/Portuguese.lproj/Localizable.strings create mode 100644 UI/MailPartViewers/Portuguese.lproj/Localizable.strings create mode 100644 UI/MailerUI/Portuguese.lproj/Localizable.strings create mode 100644 UI/MainUI/Portuguese.lproj/Locale create mode 100644 UI/MainUI/Portuguese.lproj/Localizable.strings create mode 100644 UI/PreferencesUI/Portuguese.lproj/Localizable.strings create mode 100644 UI/Scheduler/Portuguese.lproj/Localizable.strings create mode 100644 UI/Templates/SOGoACLPortugueseAdditionAdvisory.wox create mode 100644 UI/Templates/SOGoACLPortugueseModificationAdvisory.wox create mode 100644 UI/Templates/SOGoACLPortugueseRemovalAdvisory.wox create mode 100644 UI/Templates/SOGoFolderPortugueseAdditionAdvisory.wox create mode 100644 UI/Templates/SOGoFolderPortugueseRemovalAdvisory.wox diff --git a/.tx/config b/.tx/config index 9a5f5d566..631311f83 100644 --- a/.tx/config +++ b/.tx/config @@ -22,6 +22,7 @@ trans.nb_NO = UI/MailerUI/NorwegianBokmal.lproj/Localizable.strings trans.nl = UI/MailerUI/Dutch.lproj/Localizable.strings trans.nn_NO = UI/MailerUI/NorwegianNynorsk.lproj/Localizable.strings trans.pl = UI/MailerUI/Polish.lproj/Localizable.strings +trans.pt = UI/MailerUI/Portuguese.lproj/Localizable.strings trans.pt_BR = UI/MailerUI/BrazilianPortuguese.lproj/Localizable.strings trans.ru = UI/MailerUI/Russian.lproj/Localizable.strings trans.sk = UI/MailerUI/Slovak.lproj/Localizable.strings @@ -51,6 +52,7 @@ trans.nb_NO = UI/PreferencesUI/NorwegianBokmal.lproj/Localizable.strings trans.nl = UI/PreferencesUI/Dutch.lproj/Localizable.strings trans.nn_NO = UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings trans.pl = UI/PreferencesUI/Polish.lproj/Localizable.strings +trans.pt = UI/PreferencesUI/Portuguese.lproj/Localizable.strings trans.pt_BR = UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings trans.ru = UI/PreferencesUI/Russian.lproj/Localizable.strings trans.sk = UI/PreferencesUI/Slovak.lproj/Localizable.strings @@ -80,6 +82,7 @@ trans.nb_NO = UI/Scheduler/NorwegianBokmal.lproj/Localizable.strings trans.nl = UI/Scheduler/Dutch.lproj/Localizable.strings trans.nn_NO = UI/Scheduler/NorwegianNynorsk.lproj/Localizable.strings trans.pl = UI/Scheduler/Polish.lproj/Localizable.strings +trans.pt = UI/Scheduler/Portuguese.lproj/Localizable.strings trans.pt_BR = UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings trans.ru = UI/Scheduler/Russian.lproj/Localizable.strings trans.sk = UI/Scheduler/Slovak.lproj/Localizable.strings @@ -109,6 +112,7 @@ trans.nb_NO = UI/Contacts/NorwegianBokmal.lproj/Localizable.strings trans.nl = UI/Contacts/Dutch.lproj/Localizable.strings trans.nn_NO = UI/Contacts/NorwegianNynorsk.lproj/Localizable.strings trans.pl = UI/Contacts/Polish.lproj/Localizable.strings +trans.pt = UI/Contacts/Portuguese.lproj/Localizable.strings trans.pt_BR = UI/Contacts/BrazilianPortuguese.lproj/Localizable.strings trans.ru = UI/Contacts/Russian.lproj/Localizable.strings trans.sk = UI/Contacts/Slovak.lproj/Localizable.strings @@ -138,6 +142,7 @@ trans.nb_NO = UI/MainUI/NorwegianBokmal.lproj/Localizable.strings trans.nl = UI/MainUI/Dutch.lproj/Localizable.strings trans.nn_NO = UI/MainUI/NorwegianNynorsk.lproj/Localizable.strings trans.pl = UI/MainUI/Polish.lproj/Localizable.strings +trans.pt = UI/MainUI/Portuguese.lproj/Localizable.strings trans.pt_BR = UI/MainUI/BrazilianPortuguese.lproj/Localizable.strings trans.ru = UI/MainUI/Russian.lproj/Localizable.strings trans.sk = UI/MainUI/Slovak.lproj/Localizable.strings @@ -167,6 +172,7 @@ trans.nb_NO = UI/Common/NorwegianBokmal.lproj/Localizable.strings trans.nl = UI/Common/Dutch.lproj/Localizable.strings trans.nn_NO = UI/Common/NorwegianNynorsk.lproj/Localizable.strings trans.pl = UI/Common/Polish.lproj/Localizable.strings +trans.pt = UI/Common/Portuguese.lproj/Localizable.strings trans.pt_BR = UI/Common/BrazilianPortuguese.lproj/Localizable.strings trans.ru = UI/Common/Russian.lproj/Localizable.strings trans.sk = UI/Common/Slovak.lproj/Localizable.strings @@ -196,6 +202,7 @@ trans.nb_NO = UI/AdministrationUI/NorwegianBokmal.lproj/Localizable.strings trans.nl = UI/AdministrationUI/Dutch.lproj/Localizable.strings trans.nn_NO = UI/AdministrationUI/NorwegianNynorsk.lproj/Localizable.strings trans.pl = UI/AdministrationUI/Polish.lproj/Localizable.strings +trans.pt = UI/AdministrationUI/Portuguese.lproj/Localizable.strings trans.pt_BR = UI/AdministrationUI/BrazilianPortuguese.lproj/Localizable.strings trans.ru = UI/AdministrationUI/Russian.lproj/Localizable.strings trans.sk = UI/AdministrationUI/Slovak.lproj/Localizable.strings @@ -225,6 +232,7 @@ trans.nb_NO = SoObjects/Appointments/NorwegianBokmal.lproj/Localizable.strings trans.nl = SoObjects/Appointments/Dutch.lproj/Localizable.strings trans.nn_NO = SoObjects/Appointments/NorwegianNynorsk.lproj/Localizable.strings trans.pl = SoObjects/Appointments/Polish.lproj/Localizable.strings +trans.pt = SoObjects/Appointments/Portuguese.lproj/Localizable.strings trans.pt_BR = SoObjects/Appointments/BrazilianPortuguese.lproj/Localizable.strings trans.ru = SoObjects/Appointments/Russian.lproj/Localizable.strings trans.sk = SoObjects/Appointments/Slovak.lproj/Localizable.strings @@ -254,6 +262,7 @@ trans.nb_NO = SoObjects/Contacts/NorwegianBokmal.lproj/Localizable.strings trans.nl = SoObjects/Contacts/Dutch.lproj/Localizable.strings trans.nn_NO = SoObjects/Contacts/NorwegianNynorsk.lproj/Localizable.strings trans.pl = SoObjects/Contacts/Polish.lproj/Localizable.strings +trans.pt = SoObjects/Contacts/Portuguese.lproj/Localizable.strings trans.pt_BR = SoObjects/Contacts/BrazilianPortuguese.lproj/Localizable.strings trans.ru = SoObjects/Contacts/Russian.lproj/Localizable.strings trans.sk = SoObjects/Contacts/Slovak.lproj/Localizable.strings @@ -283,6 +292,7 @@ trans.nb_NO = SoObjects/Mailer/NorwegianBokmal.lproj/Localizable.strings trans.nl = SoObjects/Mailer/Dutch.lproj/Localizable.strings trans.nn_NO = SoObjects/Mailer/NorwegianNynorsk.lproj/Localizable.strings trans.pl = SoObjects/Mailer/Polish.lproj/Localizable.strings +trans.pt = SoObjects/Mailer/Portuguese.lproj/Localizable.strings trans.pt_BR = SoObjects/Mailer/BrazilianPortuguese.lproj/Localizable.strings trans.ru = SoObjects/Mailer/Russian.lproj/Localizable.strings trans.sk = SoObjects/Mailer/Slovak.lproj/Localizable.strings @@ -312,6 +322,7 @@ trans.nb_NO = UI/MailPartViewers/NorwegianBokmal.lproj/Localizable.strings trans.nl = UI/MailPartViewers/Dutch.lproj/Localizable.strings trans.nn_NO = UI/MailPartViewers/NorwegianNynorsk.lproj/Localizable.strings trans.pl = UI/MailPartViewers/Polish.lproj/Localizable.strings +trans.pt = UI/MailPartViewers/Portuguese.lproj/Localizable.strings trans.pt_BR = UI/MailPartViewers/BrazilianPortuguese.lproj/Localizable.strings trans.ru = UI/MailPartViewers/Russian.lproj/Localizable.strings trans.sk = UI/MailPartViewers/Slovak.lproj/Localizable.strings diff --git a/SoObjects/Appointments/GNUmakefile b/SoObjects/Appointments/GNUmakefile index 257104e14..5a37d3e6b 100644 --- a/SoObjects/Appointments/GNUmakefile +++ b/SoObjects/Appointments/GNUmakefile @@ -54,7 +54,7 @@ Appointments_RESOURCE_FILES += \ MSExchangeFreeBusySOAPResponseMap.plist \ MSExchangeFreeBusySOAPRequest.wo -Appointments_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +Appointments_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh Appointments_LOCALIZED_RESOURCE_FILES = Localizable.strings diff --git a/SoObjects/Appointments/Portuguese.lproj/Localizable.strings b/SoObjects/Appointments/Portuguese.lproj/Localizable.strings new file mode 100644 index 000000000..b96a84cab --- /dev/null +++ b/SoObjects/Appointments/Portuguese.lproj/Localizable.strings @@ -0,0 +1,67 @@ +"Inviting the following persons is prohibited:" = "Convidando as seguintes pessoas é proibido:"; +"Personal Calendar" = "Calendário Pessoal"; +vevent_class0 = "(Evento Público)"; +vevent_class1 = "(Evento Privado)"; +vevent_class2 = "(Evento Confidencial)"; + +vtodo_class0 = "(Tarefa Pública)"; +vtodo_class1 = "(Tarefa Privada)"; +vtodo_class2 = "(Tarefa Confidencial)"; + +/* Receipts */ +"The event \"%{Summary}\" was created" = "O evento \"%{Summary}\" foi criado"; +"The event \"%{Summary}\" was deleted" = "O evento \"%{Summary}\" foi removido"; +"The event \"%{Summary}\" was updated" = "O evento \"%{Summary}\" foi atualizado"; +"The following attendees(s) were notified:" = "Os seguintes participantes foram notificados:"; +"The following attendees(s) were added:" = "Os seguintes participantes foram adicionados:"; +"The following attendees(s) were removed:" = "Os seguintes participantes foram removidos:"; + +/* IMIP messages */ +"calendar_label" = "Calendário:"; +"startDate_label" = "Início:"; +"endDate_label" = "Fim:"; +"due_label" = "Data de Vencimento:"; +"location_label" = "Local:"; +"summary_label" = "Resumo:"; +"comment_label" = "Comentário:"; + +/* Invitation */ +"Event Invitation: \"%{Summary}\"" = "Convite do Evento: \"%{Summary}\""; +"(sent by %{SentBy}) " = "(enviado por %{SentBy}) "; +"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}convidou-o para %{Summary}.\n\nInicio: %{StartDate}\nFim: %{EndDate}\nDescrição: %{Description}"; +"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} convidou-o para %{Summary}.\n\nInício: %{StartDate} as %{StartTime}\nFim: %{EndDate} as %{EndTime}\nDescrição: %{Description}"; + +/* Deletion */ +"Event Cancelled: \"%{Summary}\"" = "Evento Cancelado: \"%{Summary}\""; +"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" += "%{Organizer} %{SentByText}cancelou este evento: %{Summary}.\n\nInicio: %{StartDate}\nFim: %{EndDate}\nDescrição: %{Description}"; +"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" += "%{Organizer} %{SentByText} cancelou este evento: %{Summary}.\n\nInício: %{StartDate} às %{StartTime}\nFim: %{EndDate} as %{EndTime}\nDescrição: %{Description}"; + +/* Update */ +"The appointment \"%{Summary}\" for the %{OldStartDate} has changed" += "O compromisso \"%{Summary}\" de %{OldStartDate} mudou"; +"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed" += "O Compromisso \"%{Summary}\" de %{OldStartDate} as %{OldStartTime} mudou"; +"The following parameters have changed in the \"%{Summary}\" meeting:" += "Os seguintes parâmetros mudaram na reunião \"%{Summary}\" :\n\n"; +"Please accept or decline those changes." += "Por favor, aceitar ou recusar as alterações."; + +/* Reply */ +"Accepted invitation: \"%{Summary}\"" = "Convite aceite: \"%{Summary}\""; +"Declined invitation: \"%{Summary}\"" = "Convite recusado: \"%{Summary}\""; +"Delegated invitation: \"%{Summary}\"" = "Convite delegado: \"%{Summary}\""; +"Not yet decided on invitation: \"%{Summary}\"" = "Convite ainda não decidido: \"%{Summary}\""; +"%{Attendee} %{SentByText}has accepted your event invitation." += "%{Attendee} %{SentByText} aceitou seu convite ao evento."; +"%{Attendee} %{SentByText}has declined your event invitation." += "%{Attendee} %{SentByText} rejeitou seu convite ao evento."; +"%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}." += "%{Attendee} %{SentByText} delegou o convite para %{Delegate}."; +"%{Attendee} %{SentByText}has not yet decided upon your event invitation." += "%{Attendee} %{SentByText} ainda não decidiu seu convite ao evento."; + +/* Resources */ +"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Não foi possível aceder ao recurso: \"%{Cn} %{SystemEmail}\""; +"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "O número máximo de reservas simultâneas (%{NumberOfSimultaneousBookings}) acabou para o recurso \"%{Cn} %{SystemEmail}\". O evento em conflito é \"%{EventTitle}\", e inicia em %{StartDate}."; diff --git a/SoObjects/Contacts/GNUmakefile b/SoObjects/Contacts/GNUmakefile index 510c38c1f..ec9a353e1 100644 --- a/SoObjects/Contacts/GNUmakefile +++ b/SoObjects/Contacts/GNUmakefile @@ -27,7 +27,7 @@ Contacts_OBJC_FILES = \ Contacts_RESOURCE_FILES += \ product.plist \ -Contacts_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +Contacts_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh Contacts_LOCALIZED_RESOURCE_FILES = Localizable.strings diff --git a/SoObjects/Contacts/Portuguese.lproj/Localizable.strings b/SoObjects/Contacts/Portuguese.lproj/Localizable.strings new file mode 100644 index 000000000..9f9c1e535 --- /dev/null +++ b/SoObjects/Contacts/Portuguese.lproj/Localizable.strings @@ -0,0 +1,2 @@ +"Personal Address Book" = "Livro de Endereços Pessoais"; +"Collected Address Book" = "Catálogos Coleccionados"; diff --git a/SoObjects/Mailer/GNUmakefile b/SoObjects/Mailer/GNUmakefile index da5092ea6..0ba367343 100644 --- a/SoObjects/Mailer/GNUmakefile +++ b/SoObjects/Mailer/GNUmakefile @@ -78,6 +78,8 @@ Mailer_RESOURCE_FILES += \ SOGoMailNorwegianNynorskReply.wo \ SOGoMailPolishForward.wo \ SOGoMailPolishReply.wo \ + SOGoMailPortugueseForward.wo \ + SOGoMailPortugueseReply.wo \ SOGoMailRussianForward.wo \ SOGoMailRussianReply.wo \ SOGoMailSlovakForward.wo \ @@ -95,7 +97,7 @@ Mailer_RESOURCE_FILES += \ SOGoMailWelshForward.wo \ SOGoMailWelshReply.wo -Mailer_LANGUAGES = Arabic Basque BrazilianPortuguese ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovenian Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +Mailer_LANGUAGES = Arabic Basque BrazilianPortuguese ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovenian Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh Mailer_LOCALIZED_RESOURCE_FILES = Localizable.strings diff --git a/SoObjects/Mailer/Portuguese.lproj/Localizable.strings b/SoObjects/Mailer/Portuguese.lproj/Localizable.strings new file mode 100644 index 000000000..a1593eb84 --- /dev/null +++ b/SoObjects/Mailer/Portuguese.lproj/Localizable.strings @@ -0,0 +1,2 @@ +"OtherUsersFolderName" = "Outros Utilizadores"; +"SharedFoldersName" = "Pastas Partilhadas"; diff --git a/SoObjects/Mailer/SOGoMailForward.h b/SoObjects/Mailer/SOGoMailForward.h index d4c516959..cd5bc20a9 100644 --- a/SoObjects/Mailer/SOGoMailForward.h +++ b/SoObjects/Mailer/SOGoMailForward.h @@ -100,6 +100,9 @@ @interface SOGoMailPolishForward : SOGoMailForward @end +@interface SOGoMailPortugueseForward : SOGoMailForward +@end + @interface SOGoMailRussianForward : SOGoMailForward @end diff --git a/SoObjects/Mailer/SOGoMailForward.m b/SoObjects/Mailer/SOGoMailForward.m index eab520cb8..5a2c9b1fd 100644 --- a/SoObjects/Mailer/SOGoMailForward.m +++ b/SoObjects/Mailer/SOGoMailForward.m @@ -311,6 +311,9 @@ @implementation SOGoMailPolishForward @end +@implementation SOGoMailPortugueseForward +@end + @implementation SOGoMailRussianForward @end diff --git a/SoObjects/Mailer/SOGoMailPortugueseForward.wo/SOGoMailPortugueseForward.html b/SoObjects/Mailer/SOGoMailPortugueseForward.wo/SOGoMailPortugueseForward.html new file mode 100644 index 000000000..2ff0e65b2 --- /dev/null +++ b/SoObjects/Mailer/SOGoMailPortugueseForward.wo/SOGoMailPortugueseForward.html @@ -0,0 +1,16 @@ +<#newLine/> +<#newLine/> +<#signaturePlacementOnTop><#newLine/> +<#signature/><#newLine/> +-------- Mensagem Original --------<#newLine/> +Assunto: <#subject/><#newLine/> +Data: <#date/><#newLine/> +De: <#from/><#newLine/> +<#hasReplyTo>Responder-Para: <#replyTo/><#hasOrganization>Organização: <#organization/>Para: <#to/><#newLine/> +<#hasCc>CC: <#cc/><#hasNewsGroups>Newsgroups: <#newsgroups/><#hasReferences>Referências: <#references/><#newLine/> +<#newLine/> +<#messageBody/><#newLine/> +<#signaturePlacementOnBottom><#newLine/> +<#newLine/> +<#signature/> +<#newLine/> diff --git a/SoObjects/Mailer/SOGoMailPortugueseForward.wo/SOGoMailPortugueseForward.wod b/SoObjects/Mailer/SOGoMailPortugueseForward.wo/SOGoMailPortugueseForward.wod new file mode 100644 index 000000000..f2436acc9 --- /dev/null +++ b/SoObjects/Mailer/SOGoMailPortugueseForward.wo/SOGoMailPortugueseForward.wod @@ -0,0 +1,88 @@ +subject: WOString { + value = subject; + escapeHTML = NO; +} + +date: WOString { + value = date; + escapeHTML = NO; +} + +from: WOString { + value = from; + escapeHTML = NO; +} + +newLine: WOString { + value = newLine; + escapeHTML = NO; +} + +hasReplyTo: WOConditional { + condition = hasReplyTo; +} + +replyTo: WOString { + value = replyTo; + escapeHTML = NO; +} + +hasOrganization: WOConditional { + condition = hasOrganization; +} + +organization: WOString { + value = organization; + escapeHTML = NO; +} + +to: WOString { + value = to; + escapeHTML = NO; +} + +hasCc: WOConditional { + condition = hasCc; +} + +cc: WOString { + value = cc; + escapeHTML = NO; +} + +hasNewsGroups: WOConditional { + condition = hasNewsGroups; +} + +newsgroups: WOString { + value = newsgroups; + escapeHTML = NO; +} + +hasReferences: WOConditional { + condition = hasReferences; +} + +references: WOString { + value = references; + escapeHTML = NO; +} + +messageBody: WOString { + value = messageBody; + escapeHTML = NO; +} + +signature: WOString { + value = signature; + escapeHTML = NO; +} + +signaturePlacementOnTop: WOConditional { + condition = signaturePlacementOnTop; +} + +signaturePlacementOnBottom: WOConditional { + condition = signaturePlacementOnTop; + negate = YES; +} diff --git a/SoObjects/Mailer/SOGoMailPortugueseReply.wo/SOGoMailPortugueseReply.html b/SoObjects/Mailer/SOGoMailPortugueseReply.wo/SOGoMailPortugueseReply.html new file mode 100644 index 000000000..bc032f3f7 --- /dev/null +++ b/SoObjects/Mailer/SOGoMailPortugueseReply.wo/SOGoMailPortugueseReply.html @@ -0,0 +1,17 @@ +<#signaturePlacementOnTop> + + +<#signature/> + +<#outlookMode>-------- Mensagem Original -------- +Assunto: <#subject/> +Data: <#date/> +De: <#from/> +<#hasReplyTo>Responder-Para: <#replyTo/><#hasOrganization>Organização: <#organization/>Para: <#to/> +<#hasCc>CC: <#cc/><#hasNewsGroups>Newsgroups: <#newsgroups/><#hasReferences>Referências: <#references/> +<#standardMode>Em <#date/>, <#from/> escreveu: + +<#messageBody/> + + +<#signaturePlacementOnBottom><#signature/> diff --git a/SoObjects/Mailer/SOGoMailPortugueseReply.wo/SOGoMailPortugueseReply.wod b/SoObjects/Mailer/SOGoMailPortugueseReply.wo/SOGoMailPortugueseReply.wod new file mode 100644 index 000000000..3fbed6d61 --- /dev/null +++ b/SoObjects/Mailer/SOGoMailPortugueseReply.wo/SOGoMailPortugueseReply.wod @@ -0,0 +1,106 @@ +outlookMode: WOConditional { + condition = outlookMode; +} + +standardMode: WOConditional { + condition = outlookMode; + negate = YES; +} + +subject: WOString { + value = subject; + escapeHTML = NO; +} + +date: WOString { + value = date; + escapeHTML = NO; +} + +from: WOString { + value = from; + escapeHTML = NO; +} + +newLine: WOString { + value = newLine; + escapeHTML = NO; +} + +hasReplyTo: WOConditional { + condition = hasReplyTo; +} + +replyTo: WOString { + value = replyTo; + escapeHTML = NO; +} + +hasOrganization: WOConditional { + condition = hasOrganization; +} + +organization: WOString { + value = organization; + escapeHTML = NO; +} + +to: WOString { + value = to; + escapeHTML = NO; +} + +hasCc: WOConditional { + condition = hasCc; +} + +cc: WOString { + value = cc; + escapeHTML = NO; +} + +hasNewsGroups: WOConditional { + condition = hasNewsGroups; +} + +newsgroups: WOString { + value = newsgroups; + escapeHTML = NO; +} + +hasReferences: WOConditional { + condition = hasReferences; +} + +references: WOString { + value = references; + escapeHTML = NO; +} + +messageBody: WOString { + value = messageBody; + escapeHTML = NO; +} + +signature: WOString { + value = signature; + escapeHTML = NO; +} + +replyPlacementOnTop: WOConditional { + condition = replyPlacementOnTop; +} + +replyPlacementOnBottom: WOConditional { + condition = replyPlacementOnTop; + negate = YES; +} + +signaturePlacementOnTop: WOConditional { + condition = signaturePlacementOnTop; +} + +signaturePlacementOnBottom: WOConditional { + condition = signaturePlacementOnTop; + negate = YES; +} diff --git a/SoObjects/Mailer/SOGoMailReply.h b/SoObjects/Mailer/SOGoMailReply.h index e2af0fcaf..0cbaace16 100644 --- a/SoObjects/Mailer/SOGoMailReply.h +++ b/SoObjects/Mailer/SOGoMailReply.h @@ -99,6 +99,9 @@ @interface SOGoMailPolishReply : SOGoMailReply @end +@interface SOGoMailPortugueseReply : SOGoMailReply +@end + @interface SOGoMailRussianReply : SOGoMailReply @end diff --git a/SoObjects/Mailer/SOGoMailReply.m b/SoObjects/Mailer/SOGoMailReply.m index d0bd38801..ce2ec7496 100644 --- a/SoObjects/Mailer/SOGoMailReply.m +++ b/SoObjects/Mailer/SOGoMailReply.m @@ -149,6 +149,9 @@ @implementation SOGoMailPolishReply @end +@implementation SOGoMailPortugueseReply +@end + @implementation SOGoMailRussianReply @end diff --git a/SoObjects/SOGo/SOGoDefaults.plist b/SoObjects/SOGo/SOGoDefaults.plist index 572f90e73..e15e3bd3a 100644 --- a/SoObjects/SOGo/SOGoDefaults.plist +++ b/SoObjects/SOGo/SOGoDefaults.plist @@ -41,7 +41,7 @@ SOGoLanguage = "English"; SOGoSupportedLanguages = ( "Arabic", "Basque", "Catalan", "ChineseTaiwan", "Czech", "Dutch", "Danish", "Welsh", "English", "SpanishSpain", "SpanishArgentina", "Finnish", "French", "German", - "Icelandic", "Italian", "Hungarian", "BrazilianPortuguese", + "Icelandic", "Italian", "Hungarian", "Portuguese", "BrazilianPortuguese", "NorwegianBokmal", "NorwegianNynorsk", "Polish", "Russian", "Slovak", "Slovenian", "Ukrainian", "Swedish" ); diff --git a/Tests/Integration/preferences.py b/Tests/Integration/preferences.py index e8d715a9a..8f8a5c6ac 100644 --- a/Tests/Integration/preferences.py +++ b/Tests/Integration/preferences.py @@ -12,7 +12,7 @@ import sogoLogin # this should probably be fetched magically... SOGoSupportedLanguages = [ "Arabic", "Basque", "Catalan", "ChineseTaiwan", "Czech", "Dutch", "Danish", "Welsh", "English", "Finnish", "SpanishSpain", "SpanishArgentina", "French", "German", - "Icelandic", "Italian", "Hungarian", "BrazilianPortuguese", + "Icelandic", "Italian", "Hungarian", "Portuguese", "BrazilianPortuguese", "NorwegianBokmal", "NorwegianNynorsk", "Polish", "Russian", "Slovak", "Slovenian", "Ukrainian", "Swedish" ]; daysBetweenResponseList=[1,2,3,5,7,14,21,30] diff --git a/UI/AdministrationUI/GNUmakefile b/UI/AdministrationUI/GNUmakefile index 450973525..049d73802 100644 --- a/UI/AdministrationUI/GNUmakefile +++ b/UI/AdministrationUI/GNUmakefile @@ -6,7 +6,7 @@ BUNDLE_NAME = AdministrationUI AdministrationUI_PRINCIPAL_CLASS = AdministrationUIProduct -AdministrationUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +AdministrationUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh AdministrationUI_OBJC_FILES = \ AdministrationUIProduct.m \ diff --git a/UI/AdministrationUI/Portuguese.lproj/Localizable.strings b/UI/AdministrationUI/Portuguese.lproj/Localizable.strings new file mode 100644 index 000000000..77b5d071e --- /dev/null +++ b/UI/AdministrationUI/Portuguese.lproj/Localizable.strings @@ -0,0 +1,15 @@ +/* this file is in UTF-8 format! */ + +"Help" = "Ajuda"; +"Close" = "Fechar"; + +"Modules" = "Módulos"; + +/* Modules short names */ +"ACLs" = "ACLs"; + +/* Modules titles */ +"ACLs_title" = "Gerenciamento de ACLs para Utilizadores"; + +/* Modules descriptions */ +"ACLs_description" = "

O módulo administrativo das Listas de Controlo de Acessos permitem alterar os ACLs de Calendário e Contactos de cada utilizador.

Para modificar as ACLs do utilizador, digite o nome no campo de pesquisa, no topo da janela e dê um duplo-click na opção desejada.

"; diff --git a/UI/Common/GNUmakefile b/UI/Common/GNUmakefile index 4f03c40fd..cd3d77bb4 100644 --- a/UI/Common/GNUmakefile +++ b/UI/Common/GNUmakefile @@ -6,7 +6,7 @@ BUNDLE_NAME = CommonUI CommonUI_PRINCIPAL_CLASS = CommonUIProduct -CommonUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +CommonUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh CommonUI_OBJC_FILES += \ CommonUIProduct.m \ diff --git a/UI/Common/Portuguese.lproj/Localizable.strings b/UI/Common/Portuguese.lproj/Localizable.strings new file mode 100644 index 000000000..1a78a4e6a --- /dev/null +++ b/UI/Common/Portuguese.lproj/Localizable.strings @@ -0,0 +1,118 @@ +/* this file is in UTF-8 format! */ + +/* toolbars */ +"Save" = "Gravar"; +"Close" = "Fechar"; +"Edit User Rights" = "Editar direitos do utilizador"; + +"Home" = "Início"; +"Calendar" = "Calendário"; +"Address Book" = "Contactos"; +"Mail" = "Correio"; +"Preferences" = "Preferências"; +"Administration" = "Administração"; +"Disconnect" = "Sair"; +"Right Administration" = "Administração de permissões"; +"Log Console (dev.)" = "Log Console (dev.)"; + +"User" = "Utilizador"; +"Vacation message is enabled" = "Mensagem de ausência está ativa"; + +"Help" = "Ajuda"; + +"noJavascriptError" = "SOGo requer Javascript para correr. Por favor, certifique-se que a opção está disponível e habilitada nas preferências de seu navegador."; +"noJavascriptRetry" = "Repetir"; + +"Owner:" = "Proprietário:"; +"Publish the Free/Busy information" = "Divulgar a informação Livre/Ocupado"; + +"Add..." = "Adicionar..."; +"Remove" = "Remover"; + +"Subscribe User" = "Utilizador Inscrito"; + +"Any Authenticated User" = "Qualquer Utilizador Autenticado"; +"Public Access" = "Acesso Público"; +"Any user not listed above" = "Qualquer utilizador não listado abaixo"; +"Anybody accessing this resource from the public area" = "Ninguém acedendo a este recurso de uma área pública"; + +"Sorry, the user rights can not be configured for that object." = "Desculpe, os accessos do utilizador não podem ser modificados para este objeto."; + +"Any user with an account on this system will be able to access your mailbox \"%{0}\". Are you certain you trust them all?" + = "Qualquer utilizador com uma conta neste sistema será capaz de aceder à sua caixa postal \"% {0}\". Tem a certeza que confia em todos?"; +"Any user with an account on this system will be able to access your calendar \"%{0}\". Are you certain you trust them all?" + = "Qualquer utilizador com uma conta neste sistema poderá aceder seu calendário \"%{0}\". Você tem certeza que confia em todos?"; +"Potentially anyone on the Internet will be able to access your calendar \"%{0}\", even if they do not have an account on this system. Is this information suitable for the public Internet?" + = "Qualquer um na Internet poderá aceder ao seu calendário \"%{0}\". Estas informações podem ser publicadas na Internet?"; +"Any user with an account on this system will be able to access your address book \"%{0}\". Are you certain you trust them all?" + = "Qualquer utilizador com uma conta neste sistema será capaz de aceder seu catálogo de endereços \"% {0}\". Tem a certeza que confia em todos?"; +"Potentially anyone on the Internet will be able to access your address book \"%{0}\", even if they do not have an account on this system. Is this information suitable for the public Internet?" + = "Qualquer pessoa na Internet será capaz de aceder ao seu catálogo de endereços \"% {0}\", mesmo se não tiver uma conta no sistema. Esta informação pode ser tornar pública na Internet?"; +"Give Access" = "Conceder Acesso"; +"Keep Private" = "Manter Privado"; + +/* generic.js */ +"Unable to subscribe to that folder!" + = "Não foi possível inscrever-se nesta pasta!"; +"You cannot subscribe to a folder that you own!" + = "Você não se pode inscrever numa pasta que é proprietário!"; +"Unable to unsubscribe from that folder!" + = "Não foi possível anular a subscrição desta pasta!"; +"You cannot unsubscribe from a folder that you own!" + = "Você não pode anular subscrição de uma pasta que você é proprietário!"; +"Unable to rename that folder!" = "Não foi possível renomear esta pasta!"; +"You have already subscribed to that folder!" + = "Você já se inscreveu nesta pasta!"; +"The user rights cannot be edited for this object!" + = "Os direitos do utilizador não podem ser editados para este objeto!"; +"A folder by that name already exists." = "Este nome já existe."; +"You cannot create a list in a shared address book." + = "Você não pode criar uma lista num catálogo de endereços público"; +"Warning" = "Aviso"; +"Can't contact server" = "Um erro ocorreu na ligação ao servidor. Por favor, tente mais tarde."; + +"You are not allowed to access this module or this system. Please contact your system administrator." += "Você não está autrizado para aceder a este módulo ou este sistema. Por favor, contate seu administrador de sistemas."; +"You don't have the required privileges to perform the operation." += "Você não tem os privilégios necessários para realizar esta operação."; + +"noEmailForDelegation" = "Você deve informar o endereço ao qual deseja delegar seu convite."; +"delegate is organizer" = "O delegado é o organizador. Por favor, especifique um delegado diferente."; +"delegate is a participant" = "O delegado já é um participante."; +"delegate is a group" = "O endereço especificado corresponde a um grupo. Você só pode delegar a uma pessoa única."; + +"Snooze for " = "Uma pausa de"; +"5 minutes" = "5 minutos"; +"10 minutes" = "10 minutos"; +"15 minutes" = "15 minutos"; +"30 minutes" = "30 minutos"; +"45 minutes" = "45 minutos"; +"1 hour" = "1 hora"; +"1 day" = "1 dia"; + +/* common buttons */ +"OK" = "OK"; +"Cancel" = "Cancelar"; +"Yes" = "Sim"; +"No" = "No"; + +/* alarms */ +"Reminder:" = "Lembrete:"; +"Start:" = "Inicio:"; +"Due Date:" = "Data de vencimento:"; +"Location:" = "Localização:"; + +/* mail labels */ +"Important" = "Importante"; +"Work" = "Trabalho"; +"Personal" = "Pessoal"; +"To Do" = "A fazer"; +"Later" = "Adiar"; + +"a2_Sunday" = "Do"; +"a2_Monday" = "Se"; +"a2_Tuesday" = "Te"; +"a2_Wednesday" = "Qu"; +"a2_Thursday" = "Qu"; +"a2_Friday" = "Se"; +"a2_Saturday" = "Sa"; diff --git a/UI/Contacts/GNUmakefile b/UI/Contacts/GNUmakefile index 01521a8bc..27c90806e 100644 --- a/UI/Contacts/GNUmakefile +++ b/UI/Contacts/GNUmakefile @@ -6,7 +6,7 @@ BUNDLE_NAME = ContactsUI ContactsUI_PRINCIPAL_CLASS = ContactsUIProduct -ContactsUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +ContactsUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh ContactsUI_OBJC_FILES = \ UIxContactsUserFolders.m \ diff --git a/UI/Contacts/Portuguese.lproj/Localizable.strings b/UI/Contacts/Portuguese.lproj/Localizable.strings new file mode 100644 index 000000000..83196548f --- /dev/null +++ b/UI/Contacts/Portuguese.lproj/Localizable.strings @@ -0,0 +1,215 @@ +/* this file is in UTF-8 format! */ + +"Contact" = "Contacto"; +"Address" = "Endereço"; +"Photos" = "Fotos"; +"Other" = "Outros"; + +"Address Books" = "Catálogo de endereços"; +"Addressbook" = "Catálogo de endereço"; +"Addresses" = "Contacto"; +"Update" = "Atualizar"; +"Cancel" = "Cancelar"; +"Common" = "Comum"; +"Contact editor" = "Editor de Contatos"; +"Contact viewer" = "Visualizador de Contatos"; +"Email" = "Email"; +"Screen Name" = "Nome de Apresentação"; +"Extended" = "Extendido"; +"Fax" = "Fax"; +"Firstname" = "Primeiro Nome"; +"Home" = "Residencia"; +"HomePhone" = "Telefone Residencia"; +"Lastname" = "Último Nome"; +"Location" = "Localização"; +"MobilePhone" = "Telefone móvel"; +"Name" = "Nome"; +"OfficePhone" = "Telefone de escritório"; +"Organization" = "Organização"; +"Work Phone" = "Telefone de trabalho"; +"Phone" = "Telefone"; +"Phones" = "Telefones"; +"Postal" = "Código postal"; +"Save" = "Gravar"; +"Internet" = "Internet"; +"Unit" = "Sector"; +"delete" = "apagar"; +"edit" = "editar"; +"invalidemailwarn" = "O email é inválido"; +"new" = "novo"; +"Preferred Phone" = "Telefone Preferencial"; + +"Move To" = "Mover para"; +"Copy To" = "Copiar para"; +"Add to:" = "Adicionar a:"; + +/* Tooltips */ + +"Create a new address book card" = "Cria um novo contato"; +"Create a new list" = "Cria uma nova lista"; +"Edit the selected card" = "Edita o contacto selecionado"; +"Send a mail message" = "Envia uma mensagem de email"; +"Delete selected card or address book" = "Apaga o contacto ou catálogo selecionado"; +"Reload all contacts" = "Actualizar todos os contactos"; + +"htmlMailFormat_UNKNOWN" = "Desconhecido"; +"htmlMailFormat_FALSE" = "Apenas texto"; +"htmlMailFormat_TRUE" = "HTML"; + +"Name or Email" = "Nome ou Email"; +"Category" = "Categoria"; +"Personal Addressbook" = "Catálogo Pessoal"; +"Search in Addressbook" = "Localizar no Catálogo"; + +"New Card" = "Novo Contato"; +"New List" = "Nova Lista"; +"Edit" = "Editar"; +"Properties" = "Propriedades"; +"Sharing..." = "Partilhando..."; +"Write" = "Escrever"; +"Delete" = "Apagar"; +"Instant Message" = "Mensagem Instantânea"; +"Add..." = "Adicionar..."; +"Remove" = "Remover"; + +"Please wait..." = "Por favor, aguarde..."; +"No possible subscription" = "Sem possibilidades de inscrição"; + +"Preferred" = "Preferido"; +"Display:" = "Exibir:"; +"Display Name:" = "Exibir Nome:"; +"Email:" = "Endereço de Email:"; +"Additional Email:" = "Email adicional:"; + +"Phone Number:" = "Numero de telegone:"; +"Prefers to receive messages formatted as:" = "Preferências na recepção de mensagens no formato:"; +"Screen Name:" = "Nome de apresentado:"; +"Categories:" = "Categorias:"; + +"First:" = "Primeiro Nome:"; +"Last:" = "Último Nome:"; +"Nickname:" = "Apelido:"; + +"Telephone" = "Telefone"; +"Work:" = "Trabalho:"; +"Home:" = "Residencia:"; +"Fax:" = "Fax:"; +"Mobile:" = "Móvel:"; +"Pager:" = "Pager:"; + +/* categories */ +"contacts_category_labels" = "Colega, Concorrência, Cliente, Amigo, Familia, Parceiro económico, Fornecedor, Impressa, VIP"; +"Categories" = "Categorias"; +"New category" = "New categoria"; + +/* adresses */ +"Title:" = "Título:"; +"Service:" = "Serviço:"; +"Company:" = "Empresa:"; +"Department:" = "Departmento:"; +"Organization:" = "Organização:"; +"Address:" = "Endereço:"; +"City:" = "Cidade:"; +"State_Province:" = "Região:"; +"ZIP_Postal Code:" = "Código postal:"; +"Country:" = "País:"; +"Web Page:" = "Página web:"; + +"Work" = "Trabalho"; +"Other Infos" = "Outras Informações"; + +"Note:" = "Notas:"; +"Timezone:" = "Fuso Horário:"; +"Birthday:" = "Aniversário:"; +"Birthday (yyyy-mm-dd):" = "Aniversário (yyyy-mm-dd):"; +"Freebusy URL:" = "URL Livre/Ocupado:"; + +"Add as..." = "Adicionar como..."; +"Recipient" = "Beneficiário"; +"Carbon Copy" = "Cópia em bloco"; +"Blind Carbon Copy" = "Cópia em bloco Oculta"; + +"New Addressbook..." = "Novo Catálogo..."; +"Subscribe to an Addressbook..." = "Inscrever-se num Catálogo..."; +"Remove the selected Addressbook" = "Remover o Catálogo selecionado"; + +"Name of the Address Book" = "Nome do Catálogo"; +"Are you sure you want to delete the selected address book?" += "Você tem certeza que quer apagar o catálogo selecionado?"; +"You cannot remove nor unsubscribe from a public addressbook." += "Você não pode apagar nem retirar-se de uma catálogo público."; +"You cannot remove nor unsubscribe from your personal addressbook." += "Você não pode apagar nem retirar-se de uma catálogo pessoal."; + +"Are you sure you want to delete the selected contacts?" += "Você tem certeza que quer apagar os contatos selecionados?"; + +"You cannot delete the card of \"%{0}\"." += "Você não pode apagar o contato de \"%{0}\"."; + + + +"You cannot subscribe to a folder that you own!" += "Você não pode inscrever-se numa pasta que você é dono."; +"Unable to subscribe to that folder!" += "Não foi possível inscrever-se nesta pasta."; + +/* acls */ +"Access rights to" = "Direitos de acesso para"; +"For user" = "Para usuário"; + +"Any Authenticated User" = "Qualquer utilizador autenticado"; +"Public Access" = "Acesso Publico"; + +"This person can add cards to this addressbook." += "Essa pessoa pode adicionar contatos ao meu catálogo."; +"This person can edit the cards of this addressbook." += "Essa pessoa pode editar contatos deste catálogo."; +"This person can list the content of this addressbook." += "Essa pessoa pode listar o conteudo deste catálogo."; +"This person can read the cards of this addressbook." += "Essa pessoa pode ler os contatos deste catálogo."; +"This person can erase cards from this addressbook." += "Essa pessoa pode apagar contatos deste catálogo."; + +"The selected contact has no email address." += "O contato selecionado não tem endereço de email."; + +"Please select a contact." = "Por favor, selecione um contato."; + +/* Error messages for move and copy */ + +"SoAccessDeniedException" = "Você não pode gravar neste catálogo."; +"Forbidden" = "Você não pode gravar neste catálogo."; +"Invalid Contact" = "O contato selecionado não existe."; +"Unknown Destination Folder" = "O catálogo de destino selecionado não existe."; + +/* Lists */ +"List details" = "List details"; +"List name:" = "List name:"; +"List nickname:" = "List nickname:"; +"List description:" = "List description:"; +"Members" = "Members"; +"Contacts" = "Contacts"; +"Add" = "Add"; +"Lists can't be moved or copied." = "Lists can't be moved or copied."; +"Export" = "Export"; +"Export Address Book..." = "Export Address Book..."; +"View Raw Source" = "Visualizar Fonte"; +"Import Cards" = "Import Cards"; +"Select a vCard or LDIF file." = "Select a vCard or LDIF file."; +"Upload" = "Upload"; +"Uploading" = "Carregando"; +"Done" = "Done"; +"An error occured while importing contacts." = "An error occured while importing contacts."; +"No card was imported." = "No card was imported."; +"A total of %{0} cards were imported in the addressbook." = "A total of %{0} cards were imported in the addressbook."; + +"Reload" = "Atualizar"; + +/* Properties window */ +"Address Book Name:" = "Nome do Catálogo:"; +"Links to this Address Book" = "Link para este Catálogo"; +"Authenticated User Access" = "Acesso de Usuário Autenticado"; +"CardDAV URL: " = "CardDAV URL:"; + diff --git a/UI/MailPartViewers/GNUmakefile b/UI/MailPartViewers/GNUmakefile index bbaa636e6..43761686c 100644 --- a/UI/MailPartViewers/GNUmakefile +++ b/UI/MailPartViewers/GNUmakefile @@ -6,7 +6,7 @@ BUNDLE_NAME = MailPartViewers MailPartViewers_PRINCIPAL_CLASS = MailPartViewersProduct -MailPartViewers_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +MailPartViewers_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh MailPartViewers_OBJC_FILES += \ MailPartViewersProduct.m \ diff --git a/UI/MailPartViewers/Portuguese.lproj/Localizable.strings b/UI/MailPartViewers/Portuguese.lproj/Localizable.strings new file mode 100644 index 000000000..e03c76a5c --- /dev/null +++ b/UI/MailPartViewers/Portuguese.lproj/Localizable.strings @@ -0,0 +1,48 @@ +ACCEPTED = "aceite"; +COMPLETED = "finalizado"; +DECLINED = "rejeitado"; +DELEGATED = "apagado"; +"IN-PROCESS" = "em processamento"; +"NEEDS-ACTION" = "acções necessárias"; +TENTATIVE = "tentativa"; +organized_by_you = "organizado por si"; +you_are_an_attendee = "você é um participante"; +add_info_text = "As solicitações iMIP 'ADD' ainda não são suportadas pelo SOGo."; +publish_info_text = "O solicitante informa-o sobre um evento anexo."; +cancel_info_text = "O seu convite ou evento foi cancelado."; +request_info_no_attendee = "está propondo uma reunião aos participantes. Está recebendo este email como uma notificação, não está agendado como um particiopante."; +Appointment = "Compromisso"; +"Status Update" = "Estado da Atualização"; +was = "foi"; + +Organizer = "Organizador"; +Time = "Hora"; +Attendees = "Participantes"; +request_info = "convidou-o para participar numa reunião."; +"Add to calendar" = "Adicionar ao calendário"; +"Delete from calendar" = "Apagar do calendário"; +"Update status" = "Atualizar estado"; +Accept = "Aceitar"; +Decline = "Rejeitar"; +Tentative = "Tentativa"; +"Delegate ..." = "Delegado ..."; +"Delegated to" = "Delegado para"; +"Update status in calendar" = "Atualizar estado no calendário"; +"delegated from" = "delegado de"; + +reply_info_no_attendee = "Recebeu uma resposta de um evento agendado, mas o remetente da resposta não é um participante."; +reply_info = "Esta é uma resposta de um convite feito por si."; + +"to" = "para"; + +"Untitled" = "Sem título"; + +"Size" = "Tamanho"; + +"Digital signature is not valid" = "Assinatura digital inválida"; +"Message is signed" = "A Mensagem está assinada"; +"Subject" = "Assunto"; +"From" = "De"; +"Date" = "Data"; +"To" = "Para"; +"Issuer" = "Emissor"; diff --git a/UI/MailerUI/GNUmakefile b/UI/MailerUI/GNUmakefile index 2e8892603..a8db21420 100644 --- a/UI/MailerUI/GNUmakefile +++ b/UI/MailerUI/GNUmakefile @@ -6,7 +6,7 @@ BUNDLE_NAME = MailerUI MailerUI_PRINCIPAL_CLASS = MailerUIProduct -MailerUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +MailerUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh MailerUI_OBJC_FILES += \ MailerUIProduct.m \ diff --git a/UI/MailerUI/Portuguese.lproj/Localizable.strings b/UI/MailerUI/Portuguese.lproj/Localizable.strings new file mode 100644 index 000000000..e9393fc9c --- /dev/null +++ b/UI/MailerUI/Portuguese.lproj/Localizable.strings @@ -0,0 +1,308 @@ +/* this file is in UTF-8 format! */ + +/* Icon's label */ +"Create" = "Criar"; +"Empty Trash" = "Esvaziar Lixo"; +"Delete" = "Apagar"; +"Expunge" = "Expurgar"; +"Forward" = "Reencaminhar"; +"Get Mail" = "Receber"; +"Junk" = "Lixo Eletrônico"; +"Reply" = "Responder"; +"Reply All" = "Responder a Todos"; +"Print" = "Imprimir"; +"Stop" = "Parar"; +"Write" = "Escrever"; +"Search" = "Pesquisar"; + +"Send" = "Enviar"; +"Contacts" = "Contatos"; +"Attach" = "Anexo"; +"Save" = "Gravar"; +"Options" = "Opções"; +"Close" = "Fechar"; +"Size" = "Tamanho"; + +/* Tooltips */ + +"Send this message now" = "Enviar esta mensagem agora"; +"Select a recipient from an Address Book" = "Seleciona um destinatário a partir de um Catálogo de Endereços"; +"Include an attachment" = "Incluir um anexo"; +"Save this message" = "Gravar esta mensagem"; +"Get new messages" = "Receber novas mensagens"; +"Create a new message" = "Cria uma nova mensagem"; +"Go to address book" = "Ir para Catálogo de Endereços"; +"Reply to the message" = "Responder a esta mensagem"; +"Reply to sender and all recipients" = "Responder ao remetente e a todos os destinatários"; +"Forward selected message" = "Reencaminhar a mensagem selecionada"; +"Delete selected message or folder" = "Apagar a mensagem ou pasta seleccionada"; +"Mark the selected messages as junk" = "Marca a mensagem selecionada como Lixo Eletrônico"; +"Print this message" = "Imprimir esta mensagem"; +"Stop the current transfer" = "Interromper a transferência"; +"Attachment" = "Anexos"; +"Unread" = "Não Lido"; +"Flagged" = "Sinalizado"; +"Search multiple mailboxes" = "Pesquisar múltiplas caixas de correio"; + +/* Main Frame */ + +"Home" = "Início"; +"Calendar" = "Calendário"; +"Addressbook" = "Contactos"; +"Mail" = "Correio"; +"Right Administration" = "Administração de permissões"; + +"Help" = "Ajuda"; + +/* Mail account main windows */ + +"Welcome to the SOGo Mailer. Use the folder tree on the left to browse your mail accounts!" = "Bem-Vindo ao SOGo WebMail. Use as pastas à esquerda para exibir suas contas de email!"; + +"Read messages" = "Ler mensagens"; +"Write a new message" = "Escrever uma nova mensagem"; + +"Share: " = "Partilha: "; +"Account: " = "Conta: "; +"Shared Account: " = "Conta partilhada: "; + +/* acls */ +"Access rights to" = "Permissões de acesso para"; +"For user" = "Para utilizador"; + +"Any Authenticated User" = "Qualquer Utilizador Autenticado"; + +"List and see this folder" = "Listar e ver esta pasta"; +"Read mails from this folder" = "Ler emails desta pasta"; +"Mark mails read and unread" = "Marcar emails como lido e não lido"; +"Modify the flags of the mails in this folder" = "Modificar a sinalização dos emails nesta pasta"; +"Insert, copy and move mails into this folder" = "Inserir, copiar e mover emails nesta pasta"; +"Post mails" = "Remetar emails"; +"Add subfolders to this folder" = "Adicionar subpastas a esta pasta"; +"Remove this folder" = "Apagar esta pasta"; +"Erase mails from this folder" = "Apagar emails desta pasta"; +"Expunge this folder" = "Expurgar esta pasta"; +"Export This Folder" = "Exportar esta pasta"; +"Modify the acl of this folder" = "Modificar as permissões desta pasta"; + +"Saved Messages.zip" = "Mensagens Gravadas.zip"; + +"Update" = "Actualizar"; +"Cancel" = "Cancelar"; + +/* Mail edition */ + +"From" = "De"; +"Subject" = "Assunto"; +"To" = "Para"; +"Cc" = "Cc"; +"Bcc" = "Bcc"; +"Reply-To" = "Responder-Para"; +"Add address" = "Adicionar endereço"; +"Body" = "Corpo"; + +"Open" = "Abrir"; +"Select All" = "Seleccionar Tudo"; +"Attach Web Page..." = "Anexar Página Web..."; +"file" = "arquivo"; +"files" = "arquivos"; +"Save all" = "Gravar tudo"; + +"to" = "Para"; +"cc" = "Cc"; +"bcc" = "Bcc"; + +"Edit Draft..." = "Editar Rascunho..."; +"Load Images" = "Carregar Imagens"; + +"Return Receipt" = "Endereço de Resposta"; +"The sender of this message has asked to be notified when you read this message. Do you with to notify the sender?" = "O remetente desta mensagem pediu para ser notificado quando ler esta mensagem. Deseja notificar o remetente?"; +"Return Receipt (displayed) - %@"= "Endereço de Resposta - %@"; +"This is a Return Receipt for the mail that you sent to %@.\n\nNote: This Return Receipt only acknowledges that the message was displayed on the recipient's computer. There is no guarantee that the recipient has read or understood the message contents." = "Este é o Endereço de Resposta do e-mail que enviou para %@.\n\nNota: Este Endereço de Resposta permite saber que a mensagem foi visualizada pelo destinatário. Não há garantia de que o destinatário tenha lido ou entendido o conteúdo da mensagem."; + +"Priority" = "Prioridade"; +"highest" = "Muito Alta"; +"high" = "Alta"; +"normal" = "Normal"; +"low" = "Baixa"; +"lowest" = "Muito Baixa"; + +"This mail is being sent from an unsecure network!" = "Este email está sendo enviado por uma rede não segura!"; + +"Address Book:" = "Contactos:"; +"Search For:" = "Pesquisar Por:"; + +/* Popup "show" */ + +"all" = "todos"; +"read" = "lido"; +"unread" = "não lido"; +"deleted" = "apagados"; +"flagged" = "sinalizados"; + +/* MailListView */ + +"Sender" = "Remetente"; +"Subject or Sender" = "Assunto ou Remetente"; +"To or Cc" = "Para ou Cc"; +"Entire Message" = "Mensagem Inteira"; + +"Date" = "Data"; +"View" = "Vista"; +"All" = "Tudo"; +"No message" = "Sem mensagem"; +"messages" = "mensagens"; + +"first" = "Primeiro"; +"previous" = "Anterior"; +"next" = "Próximo"; +"last" = "Último"; + +"msgnumber_to" = "para"; +"msgnumber_of" = "de"; + +"Mark Unread" = "Marcar como Não Lido"; +"Mark Read" = "Marcar como Lido"; + +"Untitled" = "Sem título"; + +/* Tree */ + +"SentFolderName" = "Enviados"; +"TrashFolderName" = "Lixo"; +"InboxFolderName" = "Entrada"; +"DraftsFolderName" = "Rascunhos"; +"SieveFolderName" = "Filtros"; +"Folders" = "Pastas"; /* title line */ + +/* MailMoveToPopUp */ + +"MoveTo" = "Mover …"; + +/* Address Popup menu */ +"Add to Address Book..." = "Adicionar a Catálogo..."; +"Compose Mail To" = "Escrever Mensagem Para"; +"Create Filter From Message..." = "Criar Filtro Da Mensagem..."; + +/* Image Popup menu */ +"Save Image" = "Gravar Imagem"; +"Save Attachment" = "Gravar Anexo."; + +/* Mailbox popup menus */ +"Open in New Mail Window" = "Abrir numa Nova Janela"; +"Copy Folder Location" = "Copiar o Local da Pasta"; +"Subscribe..." = "Subscrever..."; +"Mark Folder Read" = "Marcar Pasta como Lido..."; +"New Folder..." = "Nova Pasta..."; +"Compact This Folder" = "Compactar Esta Pasta"; +"Search Messages..." = "Procurar Mensagens..."; +"Sharing..." = "Partilhando..."; +"New Subfolder..." = "Nova Subpasta..."; +"Rename Folder..." = "Renomear Pasta..."; +"Delete Folder" = "Apagar Pasta"; +"Use This Folder For" = "Usar Esta Pasta Para"; +"Get Messages for Account" = "Receber Mensagens por Conta"; +"Properties..." = "Propriedades..."; +"Delegation..." = "Delegação..."; + +/* Use This Folder menu */ +"Sent Messages" = "Enviar Mensagens"; +"Drafts" = "Rascunhos"; +"Deleted Messages" = "Mensagens Apagadas"; + +/* Message list popup menu */ +"Open Message In New Window" = "Abrir Mensagens numa nova Nova Janela"; +"Reply to Sender Only" = "Responder somente para o Remetente"; +"Reply to All" = "Responder a Todos"; +"Edit As New..." = "Editar Como Novo..."; +"Move To" = "Mover Para"; +"Copy To" = "Copiar Para"; +"Label" = "Etiqueta"; +"Mark" = "Marcar"; +"Save As..." = "Gravar Como..."; +"Print Preview" = "Pré-visualizar Impressão"; +"View Message Source" = "Ver Código-Fonte da Mensagem"; +"Print..." = "Imprimir..."; +"Delete Message" = "Apagar Mensagem"; +"Delete Selected Messages" = "Apagar Mensagens Selecionadas"; + +"This Folder" = "Esta Pasta"; + +/* Label popup menu */ +"None" = "Nenhum"; + +/* Mark popup menu */ +"As Read" = "Como Lido"; +"Thread As Read" = "Tarefa Como Lida"; +"As Read By Date..." = "Como Lido Por Data..."; +"All Read" = "Tudo Lido"; +"Flag" = "Sinalizado"; +"As Junk" = "Como Lixo Eletrônico"; +"As Not Junk" = "Como Não é Lixo Eletrônico"; +"Run Junk Mail Controls" = "Executar Controlo de Lixo Eletrônico"; + +"Search messages in:" = "Pesquisar mensagens em:"; +"Search" = "Pesquisar"; +"Search subfolders" = "Pesquisar sub-pastas"; +"Match any of the following" = "Corresponder qualquer uma das seguintes"; +"Match all of the following" = "Corresponder todas as seguintes"; +"contains" = "contêm"; +"does not contain" = "não contêm"; +"No matches found" = "Nenhuma correspondencia encontrada"; +"results found" = "Resultados encontrados"; +"result found" = "Resultado encontrado"; +"Please specify at least one filter" = "Por favor, especifique pelo menos um filtro"; + +/* Folder operations */ +"Name :" = "Nome :"; +"Enter the new name of your folder :" + = "Introduza o novo nome de sua pasta :"; +"Do you really want to move this folder into the trash ?" + = "Você realmente quer mover esta pasta para o Lixo ?"; +"Operation failed" = "Falha na Operação"; + +"Quota" = "Quota:"; +"quotasFormat" = "%{0}% utilizado de %{1} MB"; + +"Please select a message." = "Por favor, selecione uma mensagem."; +"Please select a message to print." = "Por favor, selecione a mensagem para imprimir."; +"Please select only one message to print." = "Por favor, selecione apenas uma mensagem para imprimir."; +"The message you have selected doesn't exist anymore." = "A mensagem que você seleccionou não existe mais."; + +"The folder with name \"%{0}\" could not be created." += "A pasta com o nome \"%{0}\" não pode ser criada."; +"This folder could not be renamed to \"%{0}\"." += "Esta pasta não pode ser renomeada para \"%{0}\"."; +"The folder could not be deleted." += "A pasta não pode ser apagada."; +"The trash could not be emptied." += "O Lixo não pode ser esvaziado."; +"The folder functionality could not be changed." += "A funcionalidade da pasta não pode ser alterada"; + +"You need to choose a non-virtual folder!" = "Você precisa escolher uma pasta não-virtual!"; + +"Moving a message into its own folder is impossible!" += "Mover a mensagem na própria pasta é impossível!"; +"Copying a message into its own folder is impossible!" += "Copiar a mensagem na própria pasta é impossível!"; + +/* Message operations */ +"The messages could not be moved to the trash folder. Would you like to delete them immediately?" += "As mensagens não podem ser movidas para a pasta do lixo. Gostaria de eliminar imediatamente?"; + +/* Message editing */ +"error_missingsubject" = "Falta o Assunto"; +"error_missingrecipients" = "Sem destinatários seleccionados"; +"Send Anyway" = "Enviar na mesma"; +"Error while saving the draft:" = "Erro ao gravar o rascunho:"; +"Error while uploading the file \"%{0}\":" = "Erro ao carregar o arquivo \"%{0}\":"; +"There is an active file upload. Closing the window will interrupt it." = "Este arquivo está a ser carregado. Se fechar a janela irá interromper o processo."; + +/* Message sending */ +"cannot send message: (smtp) all recipients discarded" = "Não é possível enviar a mensagem: todos os destinatários são inválidos."; +"cannot send message (smtp) - recipients discarded:" = "Não é possível enviar a mensagem. Os seguintes endereços estão inválidos:"; +"cannot send message: (smtp) error when connecting" = "Não é possível enviar a mensagem: erro ao conectar ao servidor SMTP."; + +/* Contacts list in mail editor */ +"Email" = "Email"; +"Name" = "Nome"; diff --git a/UI/MainUI/Arabic.lproj/Localizable.strings b/UI/MainUI/Arabic.lproj/Localizable.strings index 9501bd65e..c4f4e1784 100644 --- a/UI/MainUI/Arabic.lproj/Localizable.strings +++ b/UI/MainUI/Arabic.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/Basque.lproj/Localizable.strings b/UI/MainUI/Basque.lproj/Localizable.strings index 1c8cc1327..cb6f1e307 100644 --- a/UI/MainUI/Basque.lproj/Localizable.strings +++ b/UI/MainUI/Basque.lproj/Localizable.strings @@ -35,6 +35,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/BrazilianPortuguese.lproj/Localizable.strings b/UI/MainUI/BrazilianPortuguese.lproj/Localizable.strings index b9d376927..7026d44e0 100644 --- a/UI/MainUI/BrazilianPortuguese.lproj/Localizable.strings +++ b/UI/MainUI/BrazilianPortuguese.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Noruega (Norsk bokmål)"; "NorwegianNynorsk" = "Noruega (Norsk nynorsk)"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/Catalan.lproj/Localizable.strings b/UI/MainUI/Catalan.lproj/Localizable.strings index 83d0ed6a1..bc7bac2ab 100644 --- a/UI/MainUI/Catalan.lproj/Localizable.strings +++ b/UI/MainUI/Catalan.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/ChineseTaiwan.lproj/Localizable.strings b/UI/MainUI/ChineseTaiwan.lproj/Localizable.strings index 36eb103ed..1ea4ec935 100644 --- a/UI/MainUI/ChineseTaiwan.lproj/Localizable.strings +++ b/UI/MainUI/ChineseTaiwan.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/Czech.lproj/Localizable.strings b/UI/MainUI/Czech.lproj/Localizable.strings index 17e168ac0..1e4076833 100644 --- a/UI/MainUI/Czech.lproj/Localizable.strings +++ b/UI/MainUI/Czech.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/Danish.lproj/Localizable.strings b/UI/MainUI/Danish.lproj/Localizable.strings index 6c40e18cc..28f2bfe07 100644 --- a/UI/MainUI/Danish.lproj/Localizable.strings +++ b/UI/MainUI/Danish.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk"; "NorwegianNynorsk" = "Nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/Dutch.lproj/Localizable.strings b/UI/MainUI/Dutch.lproj/Localizable.strings index b84ff789f..f075241f1 100644 --- a/UI/MainUI/Dutch.lproj/Localizable.strings +++ b/UI/MainUI/Dutch.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk Bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/English.lproj/Localizable.strings b/UI/MainUI/English.lproj/Localizable.strings index 36eb103ed..1ea4ec935 100644 --- a/UI/MainUI/English.lproj/Localizable.strings +++ b/UI/MainUI/English.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/Finnish.lproj/Localizable.strings b/UI/MainUI/Finnish.lproj/Localizable.strings index 21458c5c2..953f8b9d4 100644 --- a/UI/MainUI/Finnish.lproj/Localizable.strings +++ b/UI/MainUI/Finnish.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/French.lproj/Localizable.strings b/UI/MainUI/French.lproj/Localizable.strings index 3131bf5df..ade0650aa 100644 --- a/UI/MainUI/French.lproj/Localizable.strings +++ b/UI/MainUI/French.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/GNUmakefile b/UI/MainUI/GNUmakefile index f25dc04ef..08b72bebd 100644 --- a/UI/MainUI/GNUmakefile +++ b/UI/MainUI/GNUmakefile @@ -6,7 +6,7 @@ BUNDLE_NAME = MainUI MainUI_PRINCIPAL_CLASS = MainUIProduct -MainUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +MainUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh MainUI_OBJC_FILES += \ MainUIProduct.m \ diff --git a/UI/MainUI/German.lproj/Localizable.strings b/UI/MainUI/German.lproj/Localizable.strings index fa7d2ce07..cdd05889f 100644 --- a/UI/MainUI/German.lproj/Localizable.strings +++ b/UI/MainUI/German.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk Bokmål"; "NorwegianNynorsk" = "Norsk Nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/Hungarian.lproj/Localizable.strings b/UI/MainUI/Hungarian.lproj/Localizable.strings index 85acf770b..a873dd360 100644 --- a/UI/MainUI/Hungarian.lproj/Localizable.strings +++ b/UI/MainUI/Hungarian.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/Icelandic.lproj/Localizable.strings b/UI/MainUI/Icelandic.lproj/Localizable.strings index 778dc07e9..68633bfdc 100644 --- a/UI/MainUI/Icelandic.lproj/Localizable.strings +++ b/UI/MainUI/Icelandic.lproj/Localizable.strings @@ -35,6 +35,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/Italian.lproj/Localizable.strings b/UI/MainUI/Italian.lproj/Localizable.strings index 469c6468a..bd70ffd0c 100644 --- a/UI/MainUI/Italian.lproj/Localizable.strings +++ b/UI/MainUI/Italian.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/NorwegianBokmal.lproj/Localizable.strings b/UI/MainUI/NorwegianBokmal.lproj/Localizable.strings index 2c70931c3..9d31d19d4 100644 --- a/UI/MainUI/NorwegianBokmal.lproj/Localizable.strings +++ b/UI/MainUI/NorwegianBokmal.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/NorwegianNynorsk.lproj/Localizable.strings b/UI/MainUI/NorwegianNynorsk.lproj/Localizable.strings index 3481093e3..cc886dc21 100644 --- a/UI/MainUI/NorwegianNynorsk.lproj/Localizable.strings +++ b/UI/MainUI/NorwegianNynorsk.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/Polish.lproj/Localizable.strings b/UI/MainUI/Polish.lproj/Localizable.strings index 62105b655..5d6e922ab 100644 --- a/UI/MainUI/Polish.lproj/Localizable.strings +++ b/UI/MainUI/Polish.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/Portuguese.lproj/Locale b/UI/MainUI/Portuguese.lproj/Locale new file mode 100644 index 000000000..eab1d0149 --- /dev/null +++ b/UI/MainUI/Portuguese.lproj/Locale @@ -0,0 +1,35 @@ +/* Portuguese */ +{ + NSLanguageName = "Portuguese"; + NSFormalName = "Portuguese"; + NSLocaleCode = "pt_PT"; /* ISO-639-1 */ + NSLanguageCode = "por"; /* ISO 639-2 */ + NSParentContext = ""; + + NSAMPMDesignation = (AM, PM); + NSCurrencySymbol = " €"; + NSDateFormatString = "%A, %B de %e de %Y"; + NSDateTimeOrdering = DMYH; + NSDecimalDigits = ("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"); + NSDecimalSeparator = ","; + NSEarlierTimeDesignations = (anterior, "último", passado, "atrás"); + NSHourNameDesignations = ((0, meia-noite), (10, "manhã"), (12, meio-dia, "almoço"), (14, tarde), (19, noite)); + NSInternationalCurrencyString = EUR; /* ISO 4217 */ + NSLaterTimeDesignations = ("próxima"); + NSMonthNameArray = (Janeiro, Fevereiro, "Março", Abril, Maio, Junho, Julho, Agosto, Setembro, Outubro, Novembro, Dezembro); + NSNextDayDesignations = ("amanhã"); + NSNextNextDayDesignations = ("dia seguinte"); + NSPriorDayDesignations = (ontem); + NSShortDateFormatString = "%e/%m/%y"; + NSShortMonthNameArray = (Jan, Fev, Mar, Abr, Mai, Jun, Jul, Ago, Set, Out, Nov, Dez); + NSShortTimeDateFormatString = "%e/%m/%y %H%M"; + NSShortWeekDayNameArray = (Dom, Seg, Ter, Qua, Qui, Sex, Sab); + NSThisDayDesignations = (hoje, agora); + NSThousandsSeparator = "."; + NSTimeDateFormatString = "%A, %B de %e de %Y %I:%M:%S %p %Z"; + NSTimeFormatString = "%H:%M:%S"; + NSWeekDayNameArray = (Domingo, Segunda, "Terça", Quarta, Quinta, Sexta, "Sábado"); + NSYearMonthWeekDesignations = (ano, "mês", semana); + NSPositiveCurrencyFormatString = "€ 9.999,00"; + NSNegativeCurrencyFormatString = "(€ -9.999,00)"; +} diff --git a/UI/MainUI/Portuguese.lproj/Localizable.strings b/UI/MainUI/Portuguese.lproj/Localizable.strings new file mode 100644 index 000000000..299e54bfb --- /dev/null +++ b/UI/MainUI/Portuguese.lproj/Localizable.strings @@ -0,0 +1,78 @@ +/* this file is in UTF-8 format! */ + +"title" = "SOGo"; + +"Username:" = "Utilizador:"; +"Password:" = "Senha:"; +"Domain:" = "Domínio:"; +"Remember username" = "Memorizar login"; + +"Connect" = "Conectar"; + +"Wrong username or password." = "Utilizador ou Senha Inválida."; +"cookiesNotEnabled" = "Você não pode logar por a opção cookies está desabilitada. Por favor, habilite os cookies nas configurações de seu navegador e tente novamente."; + +"browserNotCompatible" = "Foi detectado que a atual versão de seu navegador não é suportado neste site. Recomentamos que use o Firefox. Clique no link abaixo para baixar a versão atual deste navegador."; +"alternativeBrowsers" = "Alternativamente, você pode usar os seguinte navegadores compatíveis"; +"alternativeBrowserSafari" = "Alternativamente, você pode usar o Safari."; +"Download" = "Download"; + +"Language:" = "Idioma:"; +"choose" = "Escolha ..."; +"Arabic" = "العربية"; +"Catalan" = "Català"; +"Czech" = "Česky"; +"Danish" = "Dansk (Danmark)"; +"Dutch" = "Nederlands"; +"English" = "English"; +"Finnish" = "Suomi"; +"French" = "Français"; +"German" = "Deutsch"; +"Hungarian" = "Magyar"; +"Icelandic" = "Íslenska"; +"Italian" = "Italiano"; +"NorwegianBokmal" = "Noruega (Norsk bokmål)"; +"NorwegianNynorsk" = "Noruega (Norsk nynorsk)"; +"Polish" = "Polski"; +"BrazilianPortuguese" = "Português brasileiro"; +"Russian" = "Русский"; +"Slovak" = "Slovensky"; +"SpanishSpain" = "Espanhol (Espanha)"; +"SpanishArgentina" = "Espanhol (Argentina)"; +"Swedish" = "Svenska"; +"Ukrainian" = "Українська"; +"Welsh" = "Cymraeg"; + +"About" = "Sobre"; +"AboutBox" = "Developed by Inverse, SOGo is a fully-featured groupware server with a focus on scalability and simplicity.

⏎ \nSOGo provides a rich AJAX-based Web interface and supports multiple native clients through the use of standard protocols such as CalDAV and CardDAV.

⏎ \nSOGo is distributed under the GNU GPL version 2 or later and parts are distributed under the GNU LGPL version 2. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.

⏎ \nSee this page for various support options."; + +"Your account was locked due to too many failed attempts." = "A sua conta foi bloqueada devido a muitas tentativas falhadas."; +"Your account was locked due to an expired password." = "A sua conta foi bloqueada devido a uma senha expirada."; +"Login failed due to unhandled error case: " = "O Login falhou pelo seguinte erro:"; +"Change your Password" = "Altere sua Senha"; +"The password was changed successfully." = "Senha alterada com sucesso."; +"Your password has expired, please enter a new one below:" = "A sua senha expirou, por favor, insira uma nova abaixo:"; +"Password must not be empty." = "A Senha não pode estar vazia."; +"The passwords do not match. Please try again." = "As senhas não coincidem. Por favor, tente novamente."; +"Password Grace Period" = "Periodo de carência da Senha"; +"You have %{0} logins remaining before your account is locked. Please change your password in the preference dialog." = "Você tem %{0} logins restantes antes de bloquear a sua conta. Por favor, altere sua senha no menu preferências."; +"Password about to expire" = "Senha prestes a expirar"; +"Your password is going to expire in %{0} %{1}." = "A sua senha vai expirar em %{0} %{1}."; +"days" = "dias"; +"hours" = "horas"; +"minutes" = "minutos"; +"seconds" = "segundos"; +"Password change failed" = "Alteração da senha falhou"; +"Password change failed - Permission denied" = "Alteração da senha falhou - Permissão negada"; +"Password change failed - Insufficient password quality" = "Alteração da senha falhou - Senha muito fraca"; +"Password change failed - Password is too short" = "Alteração da senha falhou - Senha muito curta"; +"Password change failed - Password is too young" = "Alteração da senha falhou - Senha usada recentemente"; +"Password change failed - Password is in history" = "Password is too young - Senha está no histórico"; +"Unhandled policy error: %{0}" = "Política de erro não tratada: %{0}"; +"Unhandled error response" = "Erro de resposta não tratado"; +"Password change is not supported." = "Alteração da senha não suportada."; +"Unhandled HTTP error code: %{0}" = "Erro HTTP não tratado: %{0}"; +"New password:" = "Nova senha:"; +"Confirmation:" = "Confirmação:"; +"Cancel" = "Cancelar"; +"Please wait..." = "Por favor, aguarde..."; diff --git a/UI/MainUI/Russian.lproj/Localizable.strings b/UI/MainUI/Russian.lproj/Localizable.strings index fde95a6ae..b79d80d1f 100644 --- a/UI/MainUI/Russian.lproj/Localizable.strings +++ b/UI/MainUI/Russian.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/Slovenian.lproj/Localizable.strings b/UI/MainUI/Slovenian.lproj/Localizable.strings index 9d7f33379..eca1e952a 100644 --- a/UI/MainUI/Slovenian.lproj/Localizable.strings +++ b/UI/MainUI/Slovenian.lproj/Localizable.strings @@ -35,6 +35,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/SpanishArgentina.lproj/Localizable.strings b/UI/MainUI/SpanishArgentina.lproj/Localizable.strings index 804006ff9..6da931eea 100644 --- a/UI/MainUI/SpanishArgentina.lproj/Localizable.strings +++ b/UI/MainUI/SpanishArgentina.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/SpanishSpain.lproj/Localizable.strings b/UI/MainUI/SpanishSpain.lproj/Localizable.strings index 479321ffb..23a93fc0e 100644 --- a/UI/MainUI/SpanishSpain.lproj/Localizable.strings +++ b/UI/MainUI/SpanishSpain.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/Swedish.lproj/Localizable.strings b/UI/MainUI/Swedish.lproj/Localizable.strings index e8c165a34..252895026 100644 --- a/UI/MainUI/Swedish.lproj/Localizable.strings +++ b/UI/MainUI/Swedish.lproj/Localizable.strings @@ -35,6 +35,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/Ukrainian.lproj/Localizable.strings b/UI/MainUI/Ukrainian.lproj/Localizable.strings index f18b55f22..9fa81afa3 100644 --- a/UI/MainUI/Ukrainian.lproj/Localizable.strings +++ b/UI/MainUI/Ukrainian.lproj/Localizable.strings @@ -36,6 +36,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk bokmål"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/MainUI/Welsh.lproj/Localizable.strings b/UI/MainUI/Welsh.lproj/Localizable.strings index d9e58ef44..2c0a70851 100644 --- a/UI/MainUI/Welsh.lproj/Localizable.strings +++ b/UI/MainUI/Welsh.lproj/Localizable.strings @@ -35,6 +35,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/PreferencesUI/Arabic.lproj/Localizable.strings b/UI/PreferencesUI/Arabic.lproj/Localizable.strings index b3ed4f16e..5515a9f7f 100644 --- a/UI/PreferencesUI/Arabic.lproj/Localizable.strings +++ b/UI/PreferencesUI/Arabic.lproj/Localizable.strings @@ -220,6 +220,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/PreferencesUI/Basque.lproj/Localizable.strings b/UI/PreferencesUI/Basque.lproj/Localizable.strings index bfdd4d1cf..950a4cc72 100644 --- a/UI/PreferencesUI/Basque.lproj/Localizable.strings +++ b/UI/PreferencesUI/Basque.lproj/Localizable.strings @@ -233,6 +233,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings b/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings index 6e39affc0..9520a0b44 100644 --- a/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings +++ b/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings @@ -233,6 +233,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Eslovaco"; "SpanishSpain" = "Español (España)"; diff --git a/UI/PreferencesUI/Catalan.lproj/Localizable.strings b/UI/PreferencesUI/Catalan.lproj/Localizable.strings index 94265f14b..c45e0a20a 100644 --- a/UI/PreferencesUI/Catalan.lproj/Localizable.strings +++ b/UI/PreferencesUI/Catalan.lproj/Localizable.strings @@ -234,6 +234,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings b/UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings index 547d283c2..189df1f06 100644 --- a/UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings +++ b/UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings @@ -231,6 +231,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "SpanishSpain" = "Español (España)"; diff --git a/UI/PreferencesUI/Czech.lproj/Localizable.strings b/UI/PreferencesUI/Czech.lproj/Localizable.strings index 964738409..1c8a25114 100644 --- a/UI/PreferencesUI/Czech.lproj/Localizable.strings +++ b/UI/PreferencesUI/Czech.lproj/Localizable.strings @@ -233,6 +233,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Portugues brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/Dutch.lproj/Localizable.strings b/UI/PreferencesUI/Dutch.lproj/Localizable.strings index 0500dd1d9..ad1a04099 100644 --- a/UI/PreferencesUI/Dutch.lproj/Localizable.strings +++ b/UI/PreferencesUI/Dutch.lproj/Localizable.strings @@ -233,6 +233,7 @@ "NorwegianNynorsk" = "Norsk Nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/English.lproj/Localizable.strings b/UI/PreferencesUI/English.lproj/Localizable.strings index 8036e6166..941f6a58d 100644 --- a/UI/PreferencesUI/English.lproj/Localizable.strings +++ b/UI/PreferencesUI/English.lproj/Localizable.strings @@ -233,6 +233,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/Finnish.lproj/Localizable.strings b/UI/PreferencesUI/Finnish.lproj/Localizable.strings index 780d98acf..164e14aa1 100644 --- a/UI/PreferencesUI/Finnish.lproj/Localizable.strings +++ b/UI/PreferencesUI/Finnish.lproj/Localizable.strings @@ -233,6 +233,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/French.lproj/Localizable.strings b/UI/PreferencesUI/French.lproj/Localizable.strings index 2be47f50c..aefb884da 100644 --- a/UI/PreferencesUI/French.lproj/Localizable.strings +++ b/UI/PreferencesUI/French.lproj/Localizable.strings @@ -233,6 +233,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/GNUmakefile b/UI/PreferencesUI/GNUmakefile index 450ede6ec..d771e28cb 100644 --- a/UI/PreferencesUI/GNUmakefile +++ b/UI/PreferencesUI/GNUmakefile @@ -6,7 +6,7 @@ BUNDLE_NAME = PreferencesUI PreferencesUI_PRINCIPAL_CLASS = PreferencesUIProduct -PreferencesUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +PreferencesUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh PreferencesUI_OBJC_FILES = \ PreferencesUIProduct.m \ diff --git a/UI/PreferencesUI/German.lproj/Localizable.strings b/UI/PreferencesUI/German.lproj/Localizable.strings index 48bd15efc..0a63608f3 100644 --- a/UI/PreferencesUI/German.lproj/Localizable.strings +++ b/UI/PreferencesUI/German.lproj/Localizable.strings @@ -233,6 +233,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/Hungarian.lproj/Localizable.strings b/UI/PreferencesUI/Hungarian.lproj/Localizable.strings index ce7ae70e6..0790e0920 100644 --- a/UI/PreferencesUI/Hungarian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Hungarian.lproj/Localizable.strings @@ -233,6 +233,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Szlovén"; "SpanishSpain" = "Español (España)"; diff --git a/UI/PreferencesUI/Icelandic.lproj/Localizable.strings b/UI/PreferencesUI/Icelandic.lproj/Localizable.strings index 05088a434..3f3e1e1a6 100644 --- a/UI/PreferencesUI/Icelandic.lproj/Localizable.strings +++ b/UI/PreferencesUI/Icelandic.lproj/Localizable.strings @@ -191,6 +191,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/Italian.lproj/Localizable.strings b/UI/PreferencesUI/Italian.lproj/Localizable.strings index 0df2c568a..001436e19 100644 --- a/UI/PreferencesUI/Italian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Italian.lproj/Localizable.strings @@ -212,6 +212,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings b/UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings index e1d9bae0e..1e9d27842 100644 --- a/UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings +++ b/UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings @@ -198,6 +198,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/Polish.lproj/Localizable.strings b/UI/PreferencesUI/Polish.lproj/Localizable.strings index db83009b2..4e1853eba 100644 --- a/UI/PreferencesUI/Polish.lproj/Localizable.strings +++ b/UI/PreferencesUI/Polish.lproj/Localizable.strings @@ -233,6 +233,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovenská"; "SpanishSpain" = "Español (España)"; diff --git a/UI/PreferencesUI/Portuguese.lproj/Localizable.strings b/UI/PreferencesUI/Portuguese.lproj/Localizable.strings new file mode 100644 index 000000000..78147ba4a --- /dev/null +++ b/UI/PreferencesUI/Portuguese.lproj/Localizable.strings @@ -0,0 +1,325 @@ +/* toolbar */ +"Save and Close" = "Gravar e Fechar"; +"Close" = "Fechar"; + +/* tabs */ +"General" = "Geral"; +"Calendar Options" = "Calendário"; +"Contacts Options" = "Opções de Contatos"; +"Mail Options" = "Correio"; +"IMAP Accounts" = "Contas IMAP"; +"Vacation" = "Férias"; +"Forward" = "Reencaminhar"; +"Password" = "Senha"; +"Categories" = "Categorias"; +"Appointments invitations" = "Convites de Compromissos"; +"Name" = "Nome"; +"Color" = "Cor"; +"Add" = "Adicionar"; +"Delete" = "Excluir"; + +/* contacts categories */ +"contacts_category_labels" = "Colega, Concorrente, Cliente, Amigo, Família, Parceiro de Negócios, Provedor, Imprensa, VIP"; + +/* vacation (auto-reply) */ +"Enable vacation auto reply" = "Habilitar auto resposta de férias"; +"Auto reply message :" = "AutoResponder somente uma vez a cada remetente com o seguinte texto :"; +"Email addresses (separated by commas) :" = "Endereço de e-mail (separado por vírgulas):"; +"Add default email addresses" = "Adicionar endereço de e-mail padrão"; +"Days between responses :" = "Dias entre respostas:"; +"Do not send responses to mailing lists" = "Não envie respostas para lista de e-mails"; +"Disable auto reply on" = "Desativar resposta automática em"; +"Please specify your message and your email addresses for which you want to enable auto reply." += "Por favor especifique a sua mensagem e os seus endereços de e-mail para o qual você deseja ativar resposta automática."; +"Your vacation message must not end with a single dot on a line." = "A sua mensagem de férias não deve terminar com um ponto final na linha."; +"End date of your auto reply must be in the future." += "A data final da resposta automática deve estar no futuro."; + +/* forward messages */ +"Forward incoming messages" = "Reencaminhar mensagens recebidas"; +"Keep a copy" = "Manter uma cópia"; +"Please specify an address to which you want to forward your messages." += "Por favor especificar um endereço para o qual você deseja encaminhar suas mensagens."; + +/* d & t */ +"Current Time Zone :" = "Fuso Horário :"; +"Short Date Format :" = "Formato da Data (Curto) :"; +"Long Date Format :" = "Formato da Data (Longo) :"; +"Time Format :" = "Formato da Hora :"; + +"default" = "Padrão"; + +"shortDateFmt_0" = "%d-%b-%y"; + +"shortDateFmt_1" = "%d-%m-%y"; +"shortDateFmt_2" = "%d/%m/%y"; +"shortDateFmt_3" = "%e/%m/%y"; + +"shortDateFmt_4" = "%d-%m-%Y"; +"shortDateFmt_5" = "%d/%m/%Y"; + +"shortDateFmt_6" = "%m-%d-%y"; +"shortDateFmt_7" = "%m/%d/%y"; +"shortDateFmt_8" = "%m/%e/%y"; + +"shortDateFmt_9" = "%y-%m-%d"; +"shortDateFmt_10" = "%y/%m/%d"; +"shortDateFmt_11" = "%y.%m.%d"; + +"shortDateFmt_12" = "%Y-%m-%d"; +"shortDateFmt_13" = "%Y/%m/%d"; +"shortDateFmt_14" = "%Y.%m.%d"; + +"shortDateFmt_15" = ""; + +"longDateFmt_0" = "%A, %B %d, %Y"; +"longDateFmt_1" = "%B %d, %Y"; +"longDateFmt_2" = "%A, %d %B, %Y"; +"longDateFmt_3" = "%d %B, %Y"; +"longDateFmt_4" = ""; +"longDateFmt_5" = ""; +"longDateFmt_6" = ""; +"longDateFmt_7" = ""; +"longDateFmt_8" = ""; +"longDateFmt_9" = ""; +"longDateFmt_10" = ""; + +"timeFmt_0" = "%I:%M %p"; +"timeFmt_1" = "%H:%M"; +"timeFmt_2" = ""; +"timeFmt_3" = ""; +"timeFmt_4" = ""; + +/* calendar */ +"Week begins on :" = "A Semana começa em :"; +"Day start time :" = "O Dia começa às :"; +"Day end time :" = "O Dia termina às :"; +"Day start time must be prior to day end time." = "Dia de inicio deve ser anterior ao dia de fim."; +"Show time as busy outside working hours" = "Exibir horas como ocupadas quando fora do horário de serviço"; +"First week of year :" = "Primeira semana do ano :"; +"Enable reminders for Calendar items" = "Habilitar lembretes para os itens do Calendário"; +"Play a sound when a reminder comes due" = "Executar um som quando existir um lembrete"; +"Default reminder :" = "Lembrete padrão :"; + +"firstWeekOfYear_January1" = "Inicia em 01 de janeiro"; +"firstWeekOfYear_First4DayWeek" = "Primeira semana com 4 dias"; +"firstWeekOfYear_FirstFullWeek" = "Primeira semana com 5 dias"; + +"Prevent from being invited to appointments" = "Impedir de ser convidado para um compromisso"; +"White list for appointment invitations:" = "Lista branca para convites de compromissos:"; +"Contacts Names" = "Nomes de Contatos"; + +/* Default Calendar */ +"Default calendar :" = "Calendário Padrão"; +"selectedCalendar" = "Calendário selecionado"; +"personalCalendar" = "Calendário pessoal"; +"firstCalendar" = "Calendário habilizado pela primeira vez"; + +"reminder_NONE" = "Não lembrar"; +"reminder_5_MINUTES_BEFORE" = "5 minutos"; +"reminder_10_MINUTES_BEFORE" = "10 minutos"; +"reminder_15_MINUTES_BEFORE" = "15 minutos"; +"reminder_30_MINUTES_BEFORE" = "30 minutos"; +"reminder_45_MINUTES_BEFORE" = "45 minutos antes"; +"reminder_1_HOUR_BEFORE" = "1 hora"; +"reminder_2_HOURS_BEFORE" = "2 horas"; +"reminder_5_HOURS_BEFORE" = "5 horas"; +"reminder_15_HOURS_BEFORE" = "15 horas"; +"reminder_1_DAY_BEFORE" = "1 dia"; +"reminder_2_DAYS_BEFORE" = "2 dias"; +"reminder_1_WEEK_BEFORE" = "1 semana antes"; + +/* Mailer */ +"Labels" = "Etiquetas"; +"Label" = "Etiqueta"; +"Show subscribed mailboxes only" = "Exibir somente caixas de correio inscritas"; +"Sort messages by threads" = "Ordenar mensagens por tópicos"; +"When sending mail, add unknown recipients to my" = "Ao enviar e-mail, adicionar destinatários desconhecidos ao meu"; + +"Forward messages:" = "Encaminhar mensagens:"; +"messageforward_inline" = "No corpo da mensagem"; +"messageforward_attached" = "Como anexo"; + +"When replying to a message:" = "Ao responder a uma mensagem:"; +"replyplacement_above" = "Começar minha resposta acima das citações"; +"replyplacement_below" = "Começar minha resposta abaixo das citações"; +"And place my signature" = "E colocar minha assinatura"; +"signatureplacement_above" = "abaixo da minha resposta"; +"signatureplacement_below" = "abaixo da citação"; +"Compose messages in" = "Escrever mensagens em"; +"composemessagestype_html" = "HTML"; +"composemessagestype_text" = "Texto puro"; +"Display remote inline images" = "Exibir imagens remotas"; +"displayremoteinlineimages_never" = "Nunca"; +"displayremoteinlineimages_always" = "Sempre"; + +"Auto save every" = "Gravar automatáticamente cada"; +"minutes" = "minutos"; + +/* Contact */ +"Personal Address Book" = "Contactos Pessoais"; +"Collected Address Book" = "Contactos Coleccionados"; + +/* IMAP Accounts */ +"New Mail Account" = "Nova conta de e-mail"; + +"Server Name:" = "Nome do Servidor:"; +"Port:" = "Porta:"; +"Encryption:" = "Encriptação:"; +"None" = "Nenhum"; +"User Name:" = "Nome do Utilizador:"; +"Password:" = "Senha:"; + +"Full Name:" = "Nome Completo:"; +"Email:" = "E-mail:"; +"Reply To Email:" = "Responder para o Email:"; +"Signature:" = "Assinatura:"; +"(Click to create)" = "(Click para criar)"; + +"Signature" = "Assinatura"; +"Please enter your signature below:" = "Por favor, digite sua assinatura abaixo:"; + +"Please specify a valid sender address." = "Por favor, especifique um endereço de email válido."; +"Please specify a valid reply-to address." = "Por favor,especifique um endereço de resposta válido."; + +/* Additional Parameters */ +"Additional Parameters" = "Parâmetros Adicionais"; + +/* password */ +"New password:" = "Nova senha:"; +"Confirmation:" = "Confirmação:"; +"Change" = "Alterar"; + +/* Event+task classifications */ +"Default events classification :" = "Classificação padrão do compromisso:"; +"Default tasks classification :" = "Classificação padrão da tarefa:"; +"PUBLIC_item" = "Público"; +"CONFIDENTIAL_item" = "Confidencial"; +"PRIVATE_item" = "Particular"; + +/* Event+task categories */ +"category_none" = "Nenhum"; +"calendar_category_labels" = "Aniversário,Negócios,Ligações,Concorrência,Cliente,Favoritos,Acompanhamento,Presentes,Feriados,Idéias,Meeting,Problemas,Miscelânea,Pessoal,Projetos,Feriado público,Posição,Fornecedores,Viagem,Férias"; + +/* Default module */ +"Calendar" = "Calendário"; +"Contacts" = "Contactos"; +"Mail" = "Correio"; +"Last" = "Último usado"; +"Default Module :" = "Módulo Padrão:"; +"SOGo Version :" = "Versão SOGo:"; + +"Language :" = "Idioma :"; +"choose" = "Escolha ..."; +"Arabic" = "العربية"; +"Catalan" = "Català"; +"Czech" = "Česky"; +"Danish" = "Dansk (Danmark)"; +"Dutch" = "Nederlands"; +"English" = "English"; +"Finnish" = "Suomi"; +"French" = "Français"; +"German" = "Deutsch"; +"Hungarian" = "Magyar"; +"Icelandic" = "Íslenska"; +"Italian" = "Italiano"; +"NorwegianBokmal" = "Norsk bokmål"; +"NorwegianNynorsk" = "Norsk nynorsk"; +"BrazilianPortuguese" = "Português brasileiro"; +"Polish" = "Polski"; +"Russian" = "Русский"; +"Slovak" = "Eslovaco"; +"SpanishSpain" = "Español (España)"; +"SpanishArgentina" = "Español (Argentina)"; +"Swedish" = "Svenska"; +"Ukrainian" = "Українська"; +"Welsh" = "Cymraeg"; + +"Refresh View :" = "Actualizar a Visualização:"; +"refreshview_manually" = "Manualmente"; +"refreshview_every_minute" = "A cada minuto"; +"refreshview_every_2_minutes" = "A cada 2 minutos"; +"refreshview_every_5_minutes" = "A cada 5 minutos"; +"refreshview_every_10_minutes" = "A cada 10 minutos"; +"refreshview_every_20_minutes" = "A cada 20 minutos"; +"refreshview_every_30_minutes" = "A cada 30 minutos"; +"refreshview_once_per_hour" = "Uma vez por hora"; + +/* Return receipts */ +"When I receive a request for a return receipt:" = "Quando eu receber uma confirmação de leitura:"; +"Never send a return receipt" = "Nunca enviar confirmação"; +"Allow return receipts for some messages" = "Permitir confirmação para algumas mensagens"; +"If I'm not in the To or Cc of the message:" = "Se eu não estiver no Para ou Cc da mensagem:"; +"If the sender is outside my domain:" = "Se o remetente está fora do meu domínio:"; +"In all other cases:" = "Em todos os outros casos:"; + +"Never send" = "Nunca enviar"; +"Always send" = "Sempre enviar"; +"Ask me" = "Pergunte-me"; + +/* Filters - UIxPreferences */ +"Filters" = "Filtros"; +"Active" = "Ativo"; +"Move Up" = "Mover para cima"; +"Move Down" = "Move para baixo"; +"Connection error" = "Erro de conexão"; +"Service temporarily unavailable" = "Serviço temporariamente indisponível"; + +/* Filters - UIxFilterEditor */ +"Filter name:" = "Nome do filtro:"; +"For incoming messages that" = "Para mensagens recebidas que"; +"match all of the following rules:" = "correspondem a todas as seguintes regras:"; +"match any of the following rules:" = "corresponde a nenhuma das seguintes regras:"; +"match all messages" = "corresponder a todas as mensagens"; +"Perform these actions:" = "Realizar essas ações:"; +"Untitled Filter" = "Filtro sem título"; + +"Subject" = "Assunto"; +"From" = "De"; +"To" = "Para"; +"Cc" = "Cc"; +"To or Cc" = "Para ou Cc"; +"Size (Kb)" = "Tamanho (Kb)"; +"Header" = "Cabeçalho"; +"Body" = "Corpo"; +"Flag the message with:" = "Marcar a mensagem com:"; +"Discard the message" = "Descartar a mensagem"; +"File the message in:" = "Arquivo da mensagem em:"; +"Keep the message" = "Manter a mensagem"; +"Forward the message to:" = "Rencaminhar a mensagem para:"; +"Send a reject message:" = "Enviar uma mensagem de rejeição:"; +"Send a vacation message" = "Enviar uma mensagem de ausência"; +"Stop processing filter rules" = "Parar o processamento dos filtros"; + +"is under" = "abaixo"; +"is over" = "acima"; +"is" = "é"; +"is not" = "não é"; +"contains" = "contêm"; +"does not contain" = "não contêm"; +"matches" = "corresponde"; +"does not match" = "não corresponde"; +"matches regex" = "coincide com a expressão"; +"does not match regex" = "não coincide com a expressão"; + +"Seen" = "Visto"; +"Deleted" = "Removido"; +"Answered" = "Respondido"; +"Flagged" = "Marcado"; +"Junk" = "Lixo"; +"Not Junk" = "Não é Lixo"; + +/* Password policy */ +"The password was changed successfully." = "Senha alterada com sucesso."; +"Password must not be empty." = "A senha não pode ser vazia."; +"The passwords do not match. Please try again." = "A senha não coincide. Por favor tente novamente."; +"Password change failed" = "Alteração de senha falhada"; +"Password change failed - Permission denied" = "Alteração de senha falhada - não tem permissões"; +"Password change failed - Insufficient password quality" = "Alteraçao de senha falhada - Qualidade da senha insuficiente"; +"Password change failed - Password is too short" = "Alteração de senha falhada - Senha é demasiado curta"; +"Password change failed - Password is too young" = "Alteração de senha falhada - Senha é demasiado comprida"; +"Password change failed - Password is in history" = "Alteração de senha falhada - Senha já foi utilizada no passado"; +"Unhandled policy error: %{0}" = "Erro de politica não controlada: %{0}"; +"Unhandled error response" = "Erro de resposta não controlada"; +"Password change is not supported." = "A alteração de senha não é suportada."; +"Unhandled HTTP error code: %{0}" = "Erro de HTTP não controlado, código: %{0}"; diff --git a/UI/PreferencesUI/Russian.lproj/Localizable.strings b/UI/PreferencesUI/Russian.lproj/Localizable.strings index 73eecd64e..2b7a18699 100644 --- a/UI/PreferencesUI/Russian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Russian.lproj/Localizable.strings @@ -233,6 +233,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/Slovak.lproj/Localizable.strings b/UI/PreferencesUI/Slovak.lproj/Localizable.strings index d3c4a31eb..a29d3a019 100644 --- a/UI/PreferencesUI/Slovak.lproj/Localizable.strings +++ b/UI/PreferencesUI/Slovak.lproj/Localizable.strings @@ -230,6 +230,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/Slovenian.lproj/Localizable.strings b/UI/PreferencesUI/Slovenian.lproj/Localizable.strings index 3e7d70165..49168c92f 100644 --- a/UI/PreferencesUI/Slovenian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Slovenian.lproj/Localizable.strings @@ -232,6 +232,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings b/UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings index a325aa53c..cea60d883 100644 --- a/UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings +++ b/UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings @@ -226,6 +226,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings b/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings index 357701090..c77db22c5 100644 --- a/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings +++ b/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings @@ -233,6 +233,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Eslovaquia"; "SpanishSpain" = "Español (España)"; diff --git a/UI/PreferencesUI/Swedish.lproj/Localizable.strings b/UI/PreferencesUI/Swedish.lproj/Localizable.strings index 5b654e532..7e01cb6f9 100644 --- a/UI/PreferencesUI/Swedish.lproj/Localizable.strings +++ b/UI/PreferencesUI/Swedish.lproj/Localizable.strings @@ -200,6 +200,7 @@ Servernamn:"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings b/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings index fb68f09c1..18bfe70d7 100644 --- a/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings @@ -210,6 +210,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/Welsh.lproj/Localizable.strings b/UI/PreferencesUI/Welsh.lproj/Localizable.strings index 3c289b703..3d4460490 100644 --- a/UI/PreferencesUI/Welsh.lproj/Localizable.strings +++ b/UI/PreferencesUI/Welsh.lproj/Localizable.strings @@ -198,6 +198,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; +"Portuguese" = "Portuguese"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/Scheduler/GNUmakefile b/UI/Scheduler/GNUmakefile index e376c5a9a..f1f824d17 100644 --- a/UI/Scheduler/GNUmakefile +++ b/UI/Scheduler/GNUmakefile @@ -6,7 +6,7 @@ BUNDLE_NAME = SchedulerUI SchedulerUI_PRINCIPAL_CLASS = SchedulerUIProduct -SchedulerUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +SchedulerUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh SchedulerUI_OBJC_FILES = \ SchedulerUIProduct.m \ diff --git a/UI/Scheduler/Portuguese.lproj/Localizable.strings b/UI/Scheduler/Portuguese.lproj/Localizable.strings new file mode 100644 index 000000000..48f7c0939 --- /dev/null +++ b/UI/Scheduler/Portuguese.lproj/Localizable.strings @@ -0,0 +1,564 @@ +/* this file is in UTF-8 format! */ + +/* Tooltips */ + +"Create a new event" = "Criar um novo evento"; +"Create a new task" = "Criar uma nova tarefa"; +"Edit this event or task" = "Editar este evento ou tarefa"; +"Print the current calendar view" = "Imprimir a visualização do calendário atual"; +"Delete this event or task" = "Apagar este evento ou tarefa"; +"Go to today" = "Ir para hoje"; +"Switch to day view" = "Visualizar Dia"; +"Switch to week view" = "Visualizar Semana"; +"Switch to month view" = "Visualizar Mês"; +"Reload all calendars" = "Recarregar todos os calendários"; + +/* Tabs */ +"Date" = "Data"; +"Calendars" = "Calendários"; + +/* Day */ + +"DayOfTheMonth" = "Dia do mês"; +"dayLabelFormat" = "%m/%d/%Y"; +"today" = "Hoje"; + +"Previous Day" = "Dia Anterior"; +"Next Day" = "Próximo Dia"; + +/* Week */ + +"Week" = "Semana"; +"this week" = "esta semana"; + +"Week %d" = "Semana %d"; + +"Previous Week" = "Semana Anterior"; +"Next Week" = "Próxima Semana"; + +/* Month */ + +"this month" = "este mês"; + +"Previous Month" = "Mês Anterior"; +"Next Month" = "Próximo Mês"; + +/* Year */ + +"this year" = "este ano"; + +/* Menu */ + +"Calendar" = "Calendário"; +"Contacts" = "Contatos"; + +"New Calendar..." = "Novo Calendário..."; +"Delete Calendar" = "Apagar Calendário"; +"Unsubscribe Calendar" = "Cancelar Calendário"; +"Sharing..." = "Partilhando..."; +"Export Calendar..." = "Exportar Calendário..."; +"Import Events..." = "Importar Eventos..."; +"Import Events" = "Importar Eventos"; +"Select an iCalendar file (.ics)." = "Selecione um arquivo iCalendar (.ics)."; +"Upload" = "Carregar"; +"Uploading" = "Carregando"; +"Publish Calendar..." = "Publicar Calendário..."; +"Reload Remote Calendars" = "Recarregar Calendários Remotos"; +"Properties" = "Propriedades"; +"Done" = "Efectuado"; +"An error occurred while importing calendar." = "Um erro ocorreu na importação do calendário."; +"No event was imported." = "Nenhum evento importado."; +"A total of %{0} events were imported in the calendar." = "Um total de %{0} eventos foram importados no calendário."; + +"Compose E-Mail to All Attendees" = "Compor E-Mail para Todos os Participantes"; +"Compose E-Mail to Undecided Attendees" = "Compor E-Mail para os Participantes não confirmados"; + +/* Folders */ +"Personal calendar" = "Calendário Pessoal"; + +/* Misc */ + +"OpenGroupware.org" = "OpenGroupware.org"; +"Forbidden" = "Proibido"; + +/* acls */ + +"Access rights to" = "Permissões de acesso para"; +"For user" = "Para utilizador"; + +"Any Authenticated User" = "Qualquer utilizador autenticado"; +"Public Access" = "Acesso Público"; + +"label_Public" = "Público"; +"label_Private" = "Privado"; +"label_Confidential" = "Confidencial"; + +"label_Viewer" = "Ver Tudo"; +"label_DAndTViewer" = "Ver Data e Hora"; +"label_Modifier" = "Modificar"; +"label_Responder" = "Responder Para"; +"label_None" = "Nenhum"; + +"View All" = "Ver Tudo"; +"View the Date & Time" = "Ver Data e Hora"; +"Modify" = "Modificar"; +"Respond To" = "Responder Para"; +"None" = "Nenhum"; + +"This person can create objects in my calendar." += "Esta pessoa pode criar objetos no meu calendário."; +"This person can erase objects from my calendar." += "Esta pessoa pode apagar objetos no meu calendário."; + +/* Button Titles */ + +"Subscribe to a Calendar..." = "Inscrever-se num Calendário..."; +"Remove the selected Calendar" = "Remover o Calendário seleccionado"; + +"Name of the Calendar" = "Nome deste Calendário"; + +"new" = "Novo"; +"Print view" = "Visualização de Impressão"; +"edit" = "Editar"; +"delete" = "Apagar"; +"proposal" = "Proposta"; +"Save and Close" = "Gravar e Fechar"; +"Close" = "Fechar"; +"Invite Attendees" = "Convidar Participantes"; +"Attach" = "Adicionar atalho"; +"Update" = "Atualizar"; +"Cancel" = "Cancelar"; +"show_rejected_apts" = "Exibir compromissos rejeitados"; +"hide_rejected_apts" = "Ocultar compromissos rejeitados"; + + +/* Schedule */ + +"Schedule" = "Agenda"; +"No appointments found" = "Compromissos não encontrados"; +"Meetings proposed by you" = "Reuniões propostas por si"; +"Meetings proposed to you" = "Reuniões propostas para si"; +"sched_startDateFormat" = "%d/%m %H:%M"; +"action" = "Acção"; +"accept" = "Aceitar"; +"decline" = "Rejeitar"; +"more attendees" = "Mais Participantes"; +"Hide already accepted and rejected appointments" = "Ocultar compromissos já aceites e rejeitados"; +"Show already accepted and rejected appointments" = "Exibir compromissos já aceites e rejeitados"; + +/* Print view */ + +"LIST" = "Lista"; +"Print Settings" = "Configurações de Impressão"; +"Title:" = "Título:"; +"Layout:" = "Disposição:"; +"What to Print" = "O que imprimir"; +"Options" = "Opções"; +"Tasks with no due date" = "Tarefas sem data de vencimento"; +"Display working hours only" = "Exibir somente o horário de trabalho"; +"Completed tasks" = "Tarefas efectuadas"; +"Display events and tasks colors" = "Exibir eventos e tarefas com cores"; +"Borders" = "Margens"; +"Backgrounds" = "Plano de fundo"; + +/* Appointments */ + +"Appointment viewer" = "Visualizador de Compromissos"; +"Appointment editor" = "Editor de Compromissos"; +"Appointment proposal" = "Compromisso Proposto"; +"Appointment on" = "Compromisso a"; +"Start:" = "Inicio:"; +"End:" = "Fim:"; +"Due Date:" = "Data:"; +"Title:" = "Título:"; +"Calendar:" = "Calendário:"; +"Name" = "Nome"; +"Email" = "Correio"; +"Status:" = "Estado:"; +"% complete" = "% efectuado"; +"Location:" = "Localização:"; +"Priority:" = "Prioridade:"; +"Privacy" = "Privacidade"; +"Cycle" = "Ciclo"; +"Cycle End" = "Ciclo Final"; +"Categories" = "Categorias"; +"Classification" = "Classificação"; +"Duration" = "Duração"; +"Attendees:" = "Participantes:"; +"Resources" = "Recursos"; +"Organizer:" = "Organizador:"; +"Description:" = "Descrição:"; +"Document:" = "Documento:"; +"Category:" = "Categoria:"; +"Repeat:" = "Repetir:"; +"Reminder:" = "Lembrete:"; +"General:" = "Geral:"; +"Reply:" = "Responder:"; +"Created by:" = "Criado por:"; + + +"Target:" = "Destino:"; + +"attributes" = "atributos"; +"attendees" = "participantes"; +"delegated from" = "delegado por"; + +/* checkbox title */ +"is private" = "é privado"; +/* classification */ +"Public" = "Público"; +"Private" = "Privado"; +/* text used in overviews and tooltips */ +"empty title" = "Título Vazio"; +"private appointment" = "Compromisso privado"; + +"Change..." = "Alterar..."; + +/* Appointments (participation state) */ + +"partStat_NEEDS-ACTION" = "Ações necessárias"; +"partStat_ACCEPTED" = "Vou participar"; +"partStat_DECLINED" = "Não vou participar"; +"partStat_TENTATIVE" = "Confirmarei depois"; +"partStat_DELEGATED" = "Delegado"; +"partStat_OTHER" = "Outro"; + +/* Appointments (error messages) */ + +"Conflicts found!" = "Conflitos encontrados!"; +"Invalid iCal data!" = "Dados iCal inválidos!"; +"Could not create iCal data!" = "Não foi possível criar dados iCal!"; + +/* Searching */ + +"view_all" = "Tudo"; +"view_today" = "Hoje"; +"view_next7" = "Próximos 7 dias"; +"view_next14" = "Próximos 14 dias"; +"view_next31" = "Próximos 31 dias"; +"view_thismonth" = "Este Mês"; +"view_future" = "Todos os Eventos Futuros"; +"view_selectedday" = "Dia Selecionado"; + +"view_not_started" = "Tarefas não iniciadas"; +"view_overdue" = "Tarefas em atraso"; +"view_incomplete" = "Tarefas incompletas"; + +"View:" = "Vista:"; +"Title, category or location" = "Título, categoria ou localização"; +"Entire content" = "Todo o conteúdo"; + +"Search" = "Pesquisar"; +"Search attendees" = "Pesquisar participantes"; +"Search resources" = "Pesquisar recursos"; +"Search appointments" = "Pesquisar compromissos"; + +"All day Event" = "Evento diário"; +"check for conflicts" = "Verificar conflitos"; + +"Browse URL" = "Abrir URL"; + +"newAttendee" = "Adicionar participante"; + +/* calendar modes */ + +"Overview" = "Visão Geral"; +"Chart" = "Gráfico"; +"List" = "Lista"; +"Columns" = "Colunas"; + +/* Priorities */ + +"prio_0" = "Não especificado"; +"prio_1" = "Alta 3"; +"prio_2" = "Alta 2"; +"prio_3" = "Alta 1"; +"prio_4" = "Alta"; +"prio_5" = "Normal"; +"prio_6" = "Baixa"; +"prio_7" = "Baixa 1"; +"prio_8" = "Baixa 2"; +"prio_9" = "Baixa 3"; + +/* access classes (privacy) */ +"PUBLIC_vevent" = "Evento Público"; +"CONFIDENTIAL_vevent" = "Evento Confidencial"; +"PRIVATE_vevent" = "Evento Privado"; +"PUBLIC_vtodo" = "Tarefa Pública"; +"CONFIDENTIAL_vtodo" = "Tarefa Confidencial"; +"PRIVATE_vtodo" = "Tarefa Privada"; + +/* status type */ +"status_" = "Não especificado"; +"status_NOT-SPECIFIED" = "Não especificado"; +"status_TENTATIVE" = "Tentativa"; +"status_CONFIRMED" = "Confirmado"; +"status_CANCELLED" = "Cancelado"; +"status_NEEDS-ACTION" = "Ações Necessárias"; +"status_IN-PROCESS" = "Em Processamento"; +"status_COMPLETED" = "Completado"; + +/* Cycles */ + +"cycle_once" = "Uma Vez"; +"cycle_daily" = "Diariamente"; +"cycle_weekly" = "Semanalmente"; +"cycle_2weeks" = "2 semanas"; +"cycle_4weeks" = "4 semanas"; +"cycle_monthly" = "Mensalmente"; +"cycle_weekday" = "Dia da Semana"; +"cycle_yearly" = "Anualmente"; + +"cycle_end_never" = "Sem fim"; +"cycle_end_until" = "Finalizar até"; + +"Recurrence pattern" = "Padrão de Repetição"; +"Range of recurrence" = "Intervalo de Repetição"; + +"Repeat" = "Repetir"; +"Daily" = "Diariamente"; +"Weekly" = "Semanalmente"; +"Monthly" = "Mensalmente"; +"Yearly" = "Anualmente"; +"Every" = "A cada"; +"Days" = "Dias"; +"Week(s)" = "Semana(s)"; +"On" = "Em"; +"Month(s)" = "Mês(es)"; +"The" = "O/A"; +"Recur on day(s)" = "Retorne em dia(s)"; +"Year(s)" = "Ano(s)"; +"cycle_of" = "de"; +"No end date" = "Sem data final"; +"Create" = "Criar"; +"appointment(s)" = "compromissos(s)"; +"Repeat until" = "Repetir até"; + +"First" = "Primeiro"; +"Second" = "Segundo"; +"Third" = "Terceiro"; +"Fourth" = "Quarto"; +"Fift" = "Quinto"; +"Last" = "Último"; + +/* Appointment categories */ + +"category_none" = "Nenhum"; +"category_labels" = "Aniversário,Negócios,Ligações,Concorrência,Cliente,Favoritos,Acompanhamento,Presentes,Feriados,Idéias,Problemas,Miscelânea,Meeting,Pessoal,Projetos,Feriado público,Posição,Fornecedores,Viagem,Férias"; + +"repeat_NEVER" = "Sem repetição"; +"repeat_DAILY" = "Diariamente"; +"repeat_WEEKLY" = "Semanalmente"; +"repeat_BI-WEEKLY" = "Bi-semanal"; +"repeat_EVERY WEEKDAY" = "Cada dia útil"; +"repeat_MONTHLY" = "Mensalmente"; +"repeat_YEARLY" = "Anualmente"; +"repeat_CUSTOM" = "Personalizar..."; + +"reminder_NONE" = "Não lembrar"; +"reminder_5_MINUTES_BEFORE" = "5 minutos antes"; +"reminder_10_MINUTES_BEFORE" = "10 minutos antes"; +"reminder_15_MINUTES_BEFORE" = "15 minutos antes"; +"reminder_30_MINUTES_BEFORE" = "30 minutos antes"; +"reminder_45_MINUTES_BEFORE" = "45 minutos antes"; +"reminder_1_HOUR_BEFORE" = "1 hora antes"; +"reminder_2_HOURS_BEFORE" = "2 horas antes"; +"reminder_5_HOURS_BEFORE" = "5 horas antes"; +"reminder_15_HOURS_BEFORE" = "15 horas antes"; +"reminder_1_DAY_BEFORE" = "1 dia antes"; +"reminder_2_DAYS_BEFORE" = "2 dias antes"; +"reminder_1_WEEK_BEFORE" = "1 semana antes"; +"reminder_CUSTOM" = "Personalizar..."; + +"reminder_MINUTES" = "minutos"; +"reminder_HOURS" = "horas"; +"reminder_DAYS" = "dias"; +"reminder_BEFORE" = "antes"; +"reminder_AFTER" = "depois"; +"reminder_START" = "inicio do evento"; +"reminder_END" = "fim do evento"; +"Reminder Details" = "Detalhes do Lembrete"; + +"Choose a Reminder Action" = "Escolha uma ação"; +"Show an Alert" = "Exibir um Alerta"; +"Send an E-mail" = "Enviar um E-mail"; +"Email Organizer" = "Organizador de Email"; +"Email Attendees" = "Email Participantes"; + +"zoom_400" = "400%"; +"zoom_200" = "200%"; +"zoom_100" = "100%"; +"zoom_50" = "50%"; +"zoom_25" = "25%"; + +/* transparency */ + +"Show Time as Free" = "Exibir Hora como Livre"; + +/* email notifications */ +"Send Appointment Notifications" = "Enviar Notificações de Apontamento"; + +/* validation errors */ + +validate_notitle = "Nenhum título informado, continue?"; +validate_invalid_startdate = "Campo Data Inicial incorreto!"; +validate_invalid_enddate = "Campo Data Final incorreto!"; +validate_endbeforestart = "A data que informou ocorre antes da data inicial."; + +"Events" = "Eventos"; +"Tasks" = "Tarefas"; +"Show completed tasks" = "Exibir tarefas efectuadas"; + +/* tabs */ +"Task" = "Tarefa"; +"Event" = "Evento"; +"Recurrence" = "Recorrencia"; + +/* toolbar */ +"New Event" = "Novo Evento"; +"New Task" = "Nova Tarefa"; +"Edit" = "Editar"; +"Delete" = "Apagar"; +"Go to Today" = "Ir para Hoje"; +"Day View" = "Visualizar Dia"; +"Week View" = "Visualizar Semana"; +"Month View" = "Visualizar Mês"; +"Reload" = "Recarregar"; + +"eventPartStatModificationError" = "O seu estado de participação não pode ser modificado."; + +/* menu */ +"New Event..." = "Novo Evento..."; +"New Task..." = "Nova Tarefa..."; +"Edit Selected Event..." = "Editar o Evento Selecionado..."; +"Delete Selected Event" = "Apagar o Evento Selecionado"; +"Select All" = "Selecionar Tudo"; +"Workweek days only" = "Apenas semanas úteis"; +"Tasks in View" = "Tarefas na vista"; + +"eventDeleteConfirmation" = "O(s) seguinte(s) evento(s) será(ão) apagado(s):"; +"taskDeleteConfirmation" = "Apagar permanentemente esta tarefa."; +"Would you like to continue?" = "Pretende continuar?"; + +"You cannot remove nor unsubscribe from your personal calendar." += "Você não pode remover nem retirar-se do seu calendário pessoal."; +"Are you sure you want to delete the calendar \"%{0}\"?" += "Você tem certeza que quer apagar o calendário \"%{0}\"?"; + +/* Legend */ +"Participant" = "Participante"; +"Optional Participant" = "Participante Opcional"; +"Non Participant" = "Não Participante"; +"Chair" = "Cadeira"; + +"Needs action" = "Ações necessárias"; +"Accepted" = "Aceite"; +"Declined" = "Rejeitado"; +"Tentative" = "Tentativa"; + +"Free" = "Livre"; +"Busy" = "Ocupado"; +"Maybe busy" = "Talvez ocupado"; +"No free-busy information" = "Sem informação Livre/Ocupado"; + +/* FreeBusy panel buttons and labels */ +"Suggest time slot:" = "Sugerir espaço de tempo:"; +"Zoom:" = "Zoom:"; +"Previous slot" = "Espaço anterior"; +"Next slot" = "Próximo espaço"; +"Previous hour" = "Hora anterior"; +"Next hour" = "Próxima hora"; +"Work days only" = "Somente dias de trabalho"; +"The whole day" = "O dia inteiro"; +"Between" = "Entre"; +"and" = "e"; + +"A time conflict exists with one or more attendees.\nWould you like to keep the current settings anyway?" += "Existe um conflito de tempo com um ou mais participantes.\nGostaria de manter as configurações atuais?"; + +/* apt list */ +"Title" = "Título"; +"Start" = "Início"; +"End" = "Fim"; +"Due Date" = "Data de Vencimento"; +"Location" = "Localização"; + +"(Private Event)" = "(Evento Privado)"; + +vevent_class0 = "(Evento Público)"; +vevent_class1 = "(Evento Privado)"; +vevent_class2 = "(Evento Confidencial)"; + +"Priority" = "Prioridade"; +"Category" = "Categoria"; + +vtodo_class0 = "(Tarefa Pública)"; +vtodo_class1 = "(Tarefa Privada)"; +vtodo_class2 = "(Tarefa Confidencial)"; + +"closeThisWindowMessage" = "Obrigado! Agora já pode fechar esta janela ou visualização "; +"Multicolumn Day View" = "Visão Diária Multicolunas"; + +"Please select an event or a task." = "Por favor, selecione um evento ou tarefa."; + +"editRepeatingItem" = "O item que está editando é um item repetitivo. Você quer editar todas as ocorrências deste ou somente este?"; +"button_thisOccurrenceOnly" = "Somente esta ocorrência"; +"button_allOccurrences" = "Todas as ocorrências"; + +/* Properties dialog */ +"Name:" = "Nome:"; +"Color:" = "Cor:"; + +"Include in free-busy" = "Incluir na disponibilidade"; + +"Synchronization" = "Sincronização"; +"Synchronize" = "Sincronizar"; +"Tag:" = "Marca:"; + +"Display" = "Exibir"; +"Show alarms" = "Exibir alarmes"; +"Show tasks" = "Exibir tarefas"; + +"Notifications" = "Notificações"; +"Receive a mail when I modify my calendar" = "Receber um email quando eu modificar meu calendário"; +"Receive a mail when someone else modifies my calendar" = "Receber um email quando alguem modificar meu calendário"; +"When I modify my calendar, send a mail to:" = "Quando eu modificar meu calendário, enviar um email para:"; + +"Links to this Calendar" = "Links para este Calendário"; +"Authenticated User Access" = "Acesso a Utilizador Autenticado"; +"CalDAV URL" = "CalDAV URL:"; +"WebDAV ICS URL" = "WebDAV ICS URL"; +"WebDAV XML URL" = "WebDAV XML URL"; + +/* Error messages */ +"dayFieldInvalid" = "Por favor, especifique um valor numérico no campo Dias, maior ou igual a 1."; +"weekFieldInvalid" = "Por favor, especifique um valor numérico no campo Semana(s), maior ou igual a 1."; +"monthFieldInvalid" = "Por favor, especifique um valor numérico no campo Mes(es), maior ou igual a 1."; +"monthDayFieldInvalid" = "Por favor, especifique um valor numéricio no campo Dia do Mes, maior ou igual a 1."; +"yearFieldInvalid" = "Por favor, especifique um valor numéricio no campo Ano(s), maior ou igual a 1."; +"appointmentFieldInvalid" = "Por favor, especifique um valor numéricio no campo Apontamento(s) maior ou igual a 1."; +"recurrenceUnsupported" = "Este tipo de recorrência não é suportado."; +"Please specify a calendar name." = "Por favor, especifique um nome de calendário."; +"tagNotDefined" = "Você deve especificar um dispositivo se deseja sincronizar este calendário."; +"tagAlreadyExists" = "Este dispositivo especificado já está associado a outro calendário."; +"tagHasChanged" = "Se você trocar o dispositivo deste calendário, será necessário sincronizar novamente seus dados no dispositivo móvel.\nContinuar?"; +"tagWasAdded" = "Se você quisar sincronizar este calendário, será necessário sincronizar novamente seus dados no dispositivo móvel.\nContinuar?"; +"tagWasRemoved" = "Se você remover este calendário da sincronização, será necessário sincronizar novamente seus dados no dispositivo móvel.\nContinuar?"; +"DestinationCalendarError" = "Os calendários de origem e destino são os mesmos. Por favor, tente copiar para outro calendário diferente."; +"EventCopyError" = "A cópia falhou. Por favor, tente copiar para um calendário diferente."; +"Please select at least one calendar" = "Por favor, selecione pelo menos um calendário"; + + +"Open Task..." = "Abrir Tarefa..."; +"Mark Completed" = "Marcar como Concluída"; +"Delete Task" = "Remover Tarefa"; +"Delete Event" = "Remover Evento"; +"Copy event to my calendar" = "Copiar evento para o meu calendário"; +"View Raw Source" = "Visualizar Fonte"; + +"Subscribe to a web calendar..." = "Inscrever-se num calendário web..."; +"URL of the Calendar" = "URL do Calendário"; +"Web Calendar" = "Calendário Web"; +"Reload on login" = "Recarregar no login"; +"Invalid number." = "Número inválido."; +"Please identify yourself to %{0}" = "Por favor, identifique-se para %{0}"; diff --git a/UI/Templates/SOGoACLPortugueseAdditionAdvisory.wox b/UI/Templates/SOGoACLPortugueseAdditionAdvisory.wox new file mode 100644 index 000000000..0da292c67 --- /dev/null +++ b/UI/Templates/SOGoACLPortugueseAdditionAdvisory.wox @@ -0,0 +1,28 @@ + + + + + + adicionou a si + + + + adicionou a si na lista de acesso da pasta + + + + diff --git a/UI/Templates/SOGoACLPortugueseModificationAdvisory.wox b/UI/Templates/SOGoACLPortugueseModificationAdvisory.wox new file mode 100644 index 000000000..bf5f82d77 --- /dev/null +++ b/UI/Templates/SOGoACLPortugueseModificationAdvisory.wox @@ -0,0 +1,28 @@ + + + + + + modificou os seus direitos de acesso + + + + modificou o seu direito de acesso para a pasta + + + + diff --git a/UI/Templates/SOGoACLPortugueseRemovalAdvisory.wox b/UI/Templates/SOGoACLPortugueseRemovalAdvisory.wox new file mode 100644 index 000000000..c0803627b --- /dev/null +++ b/UI/Templates/SOGoACLPortugueseRemovalAdvisory.wox @@ -0,0 +1,28 @@ + + + + + + removeu a si + + + + removeu a si da lista de acesso da pasta + + + + diff --git a/UI/Templates/SOGoFolderPortugueseAdditionAdvisory.wox b/UI/Templates/SOGoFolderPortugueseAdditionAdvisory.wox new file mode 100644 index 000000000..568227057 --- /dev/null +++ b/UI/Templates/SOGoFolderPortugueseAdditionAdvisory.wox @@ -0,0 +1,23 @@ + + + + + + criada + + + +A pasta foi criada. + + + + diff --git a/UI/Templates/SOGoFolderPortugueseRemovalAdvisory.wox b/UI/Templates/SOGoFolderPortugueseRemovalAdvisory.wox new file mode 100644 index 000000000..5f172e7b9 --- /dev/null +++ b/UI/Templates/SOGoFolderPortugueseRemovalAdvisory.wox @@ -0,0 +1,23 @@ + + + + + + apagada + + + +A pasta foi apagada. + + + + From f58554a70051b647a8fd33db01c5175dad0be34c Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Wed, 11 Nov 2015 09:31:54 -0500 Subject: [PATCH 47/69] Add Macedonian translation --- .tx/config | 11 + SoObjects/Appointments/GNUmakefile | 2 +- .../Macedonian.lproj/Localizable.strings | 67 +++ SoObjects/Contacts/GNUmakefile | 2 +- .../Macedonian.lproj/Localizable.strings | 2 + SoObjects/Mailer/GNUmakefile | 4 +- .../Macedonian.lproj/Localizable.strings | 2 + SoObjects/Mailer/SOGoMailForward.h | 3 + SoObjects/Mailer/SOGoMailForward.m | 3 + .../SOGoMailMacedonianForward.html | 16 + .../SOGoMailMacedonianForward.wod | 88 +++ .../SOGoMailMacedonianReply.html | 16 + .../SOGoMailMacedonianReply.wod | 106 ++++ SoObjects/Mailer/SOGoMailReply.h | 3 + SoObjects/Mailer/SOGoMailReply.m | 3 + SoObjects/SOGo/SOGoDefaults.plist | 2 +- Tests/Integration/preferences.py | 2 +- UI/AdministrationUI/GNUmakefile | 2 +- .../Macedonian.lproj/Localizable.strings | 15 + UI/Common/GNUmakefile | 2 +- .../Macedonian.lproj/Localizable.strings | 118 ++++ UI/Contacts/GNUmakefile | 2 +- .../Macedonian.lproj/Localizable.strings | 215 +++++++ UI/MailPartViewers/GNUmakefile | 2 +- .../Macedonian.lproj/Localizable.strings | 48 ++ UI/MailerUI/GNUmakefile | 2 +- .../Macedonian.lproj/Localizable.strings | 308 ++++++++++ UI/MainUI/Arabic.lproj/Localizable.strings | 1 + UI/MainUI/Basque.lproj/Localizable.strings | 1 + .../Localizable.strings | 1 + UI/MainUI/Catalan.lproj/Localizable.strings | 1 + .../ChineseTaiwan.lproj/Localizable.strings | 1 + UI/MainUI/Czech.lproj/Localizable.strings | 1 + UI/MainUI/Danish.lproj/Localizable.strings | 1 + UI/MainUI/Dutch.lproj/Localizable.strings | 1 + UI/MainUI/English.lproj/Localizable.strings | 1 + UI/MainUI/Finnish.lproj/Localizable.strings | 1 + UI/MainUI/French.lproj/Localizable.strings | 1 + UI/MainUI/GNUmakefile | 2 +- UI/MainUI/German.lproj/Localizable.strings | 1 + UI/MainUI/Hungarian.lproj/Localizable.strings | 1 + UI/MainUI/Icelandic.lproj/Localizable.strings | 1 + UI/MainUI/Italian.lproj/Localizable.strings | 1 + UI/MainUI/Macedonian.lproj/Locale | 35 ++ .../Macedonian.lproj/Localizable.strings | 78 +++ .../NorwegianBokmal.lproj/Localizable.strings | 1 + .../Localizable.strings | 1 + UI/MainUI/Polish.lproj/Localizable.strings | 1 + .../Portuguese.lproj/Localizable.strings | 1 + UI/MainUI/Russian.lproj/Localizable.strings | 1 + UI/MainUI/Slovenian.lproj/Localizable.strings | 1 + .../Localizable.strings | 1 + .../SpanishSpain.lproj/Localizable.strings | 1 + UI/MainUI/Swedish.lproj/Localizable.strings | 1 + UI/MainUI/Ukrainian.lproj/Localizable.strings | 1 + UI/MainUI/Welsh.lproj/Localizable.strings | 1 + .../Arabic.lproj/Localizable.strings | 1 + .../Basque.lproj/Localizable.strings | 1 + .../Localizable.strings | 1 + .../Catalan.lproj/Localizable.strings | 1 + .../ChineseTaiwan.lproj/Localizable.strings | 1 + .../Czech.lproj/Localizable.strings | 1 + .../Danish.lproj/Localizable.strings | 1 + .../Dutch.lproj/Localizable.strings | 1 + .../English.lproj/Localizable.strings | 1 + .../Finnish.lproj/Localizable.strings | 1 + .../French.lproj/Localizable.strings | 1 + UI/PreferencesUI/GNUmakefile | 2 +- .../German.lproj/Localizable.strings | 1 + .../Hungarian.lproj/Localizable.strings | 1 + .../Icelandic.lproj/Localizable.strings | 1 + .../Italian.lproj/Localizable.strings | 1 + .../Macedonian.lproj/Localizable.strings | 334 +++++++++++ .../NorwegianBokmal.lproj/Localizable.strings | 1 + .../Localizable.strings | 1 + .../Polish.lproj/Localizable.strings | 1 + .../Portuguese.lproj/Localizable.strings | 1 + .../Russian.lproj/Localizable.strings | 1 + .../Slovak.lproj/Localizable.strings | 1 + .../Slovenian.lproj/Localizable.strings | 1 + .../Localizable.strings | 1 + .../SpanishSpain.lproj/Localizable.strings | 1 + .../Swedish.lproj/Localizable.strings | 1 + .../Ukrainian.lproj/Localizable.strings | 1 + .../Welsh.lproj/Localizable.strings | 1 + UI/Scheduler/GNUmakefile | 2 +- .../Macedonian.lproj/Localizable.strings | 565 ++++++++++++++++++ .../SOGoACLMacedonianAdditionAdvisory.wox | 28 + .../SOGoACLMacedonianModificationAdvisory.wox | 28 + .../SOGoACLMacedonianRemovalAdvisory.wox | 28 + .../SOGoFolderMacedonianAdditionAdvisory.wox | 23 + .../SOGoFolderMacedonianRemovalAdvisory.wox | 23 + 92 files changed, 2234 insertions(+), 13 deletions(-) create mode 100644 SoObjects/Appointments/Macedonian.lproj/Localizable.strings create mode 100644 SoObjects/Contacts/Macedonian.lproj/Localizable.strings create mode 100644 SoObjects/Mailer/Macedonian.lproj/Localizable.strings create mode 100644 SoObjects/Mailer/SOGoMailMacedonianForward.wo/SOGoMailMacedonianForward.html create mode 100644 SoObjects/Mailer/SOGoMailMacedonianForward.wo/SOGoMailMacedonianForward.wod create mode 100644 SoObjects/Mailer/SOGoMailMacedonianReply.wo/SOGoMailMacedonianReply.html create mode 100644 SoObjects/Mailer/SOGoMailMacedonianReply.wo/SOGoMailMacedonianReply.wod create mode 100644 UI/AdministrationUI/Macedonian.lproj/Localizable.strings create mode 100644 UI/Common/Macedonian.lproj/Localizable.strings create mode 100644 UI/Contacts/Macedonian.lproj/Localizable.strings create mode 100644 UI/MailPartViewers/Macedonian.lproj/Localizable.strings create mode 100644 UI/MailerUI/Macedonian.lproj/Localizable.strings create mode 100644 UI/MainUI/Macedonian.lproj/Locale create mode 100644 UI/MainUI/Macedonian.lproj/Localizable.strings create mode 100644 UI/PreferencesUI/Macedonian.lproj/Localizable.strings create mode 100644 UI/Scheduler/Macedonian.lproj/Localizable.strings create mode 100644 UI/Templates/SOGoACLMacedonianAdditionAdvisory.wox create mode 100644 UI/Templates/SOGoACLMacedonianModificationAdvisory.wox create mode 100644 UI/Templates/SOGoACLMacedonianRemovalAdvisory.wox create mode 100644 UI/Templates/SOGoFolderMacedonianAdditionAdvisory.wox create mode 100644 UI/Templates/SOGoFolderMacedonianRemovalAdvisory.wox diff --git a/.tx/config b/.tx/config index 631311f83..aaff32706 100644 --- a/.tx/config +++ b/.tx/config @@ -18,6 +18,7 @@ trans.fr = UI/MailerUI/French.lproj/Localizable.strings trans.hu = UI/MailerUI/Hungarian.lproj/Localizable.strings trans.is = UI/MailerUI/Icelandic.lproj/Localizable.strings trans.it = UI/MailerUI/Italian.lproj/Localizable.strings +trans.mk_MK = UI/MailerUI/Macedonian.lproj/Localizable.strings trans.nb_NO = UI/MailerUI/NorwegianBokmal.lproj/Localizable.strings trans.nl = UI/MailerUI/Dutch.lproj/Localizable.strings trans.nn_NO = UI/MailerUI/NorwegianNynorsk.lproj/Localizable.strings @@ -48,6 +49,7 @@ trans.fr = UI/PreferencesUI/French.lproj/Localizable.strings trans.hu = UI/PreferencesUI/Hungarian.lproj/Localizable.strings trans.is = UI/PreferencesUI/Icelandic.lproj/Localizable.strings trans.it = UI/PreferencesUI/Italian.lproj/Localizable.strings +trans.mk_MK = UI/PreferencesUI/Macedonian.lproj/Localizable.strings trans.nb_NO = UI/PreferencesUI/NorwegianBokmal.lproj/Localizable.strings trans.nl = UI/PreferencesUI/Dutch.lproj/Localizable.strings trans.nn_NO = UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings @@ -78,6 +80,7 @@ trans.fr = UI/Scheduler/French.lproj/Localizable.strings trans.hu = UI/Scheduler/Hungarian.lproj/Localizable.strings trans.is = UI/Scheduler/Icelandic.lproj/Localizable.strings trans.it = UI/Scheduler/Italian.lproj/Localizable.strings +trans.mk_MK = UI/Scheduler/Macedonian.lproj/Localizable.strings trans.nb_NO = UI/Scheduler/NorwegianBokmal.lproj/Localizable.strings trans.nl = UI/Scheduler/Dutch.lproj/Localizable.strings trans.nn_NO = UI/Scheduler/NorwegianNynorsk.lproj/Localizable.strings @@ -108,6 +111,7 @@ trans.fr = UI/Contacts/French.lproj/Localizable.strings trans.hu = UI/Contacts/Hungarian.lproj/Localizable.strings trans.is = UI/Contacts/Icelandic.lproj/Localizable.strings trans.it = UI/Contacts/Italian.lproj/Localizable.strings +trans.mk_MK = UI/Contacts/Macedonian.lproj/Localizable.strings trans.nb_NO = UI/Contacts/NorwegianBokmal.lproj/Localizable.strings trans.nl = UI/Contacts/Dutch.lproj/Localizable.strings trans.nn_NO = UI/Contacts/NorwegianNynorsk.lproj/Localizable.strings @@ -138,6 +142,7 @@ trans.fr = UI/MainUI/French.lproj/Localizable.strings trans.hu = UI/MainUI/Hungarian.lproj/Localizable.strings trans.is = UI/MainUI/Icelandic.lproj/Localizable.strings trans.it = UI/MainUI/Italian.lproj/Localizable.strings +trans.mk_MK = UI/MainUI/Macedonian.lproj/Localizable.strings trans.nb_NO = UI/MainUI/NorwegianBokmal.lproj/Localizable.strings trans.nl = UI/MainUI/Dutch.lproj/Localizable.strings trans.nn_NO = UI/MainUI/NorwegianNynorsk.lproj/Localizable.strings @@ -168,6 +173,7 @@ trans.fr = UI/Common/French.lproj/Localizable.strings trans.hu = UI/Common/Hungarian.lproj/Localizable.strings trans.is = UI/Common/Icelandic.lproj/Localizable.strings trans.it = UI/Common/Italian.lproj/Localizable.strings +trans.mk_MK = UI/Common/Macedonian.lproj/Localizable.strings trans.nb_NO = UI/Common/NorwegianBokmal.lproj/Localizable.strings trans.nl = UI/Common/Dutch.lproj/Localizable.strings trans.nn_NO = UI/Common/NorwegianNynorsk.lproj/Localizable.strings @@ -198,6 +204,7 @@ trans.fr = UI/AdministrationUI/French.lproj/Localizable.strings trans.hu = UI/AdministrationUI/Hungarian.lproj/Localizable.strings trans.is = UI/AdministrationUI/Icelandic.lproj/Localizable.strings trans.it = UI/AdministrationUI/Italian.lproj/Localizable.strings +trans.mk_MK = UI/AdministrationUI/Macedonian.lproj/Localizable.strings trans.nb_NO = UI/AdministrationUI/NorwegianBokmal.lproj/Localizable.strings trans.nl = UI/AdministrationUI/Dutch.lproj/Localizable.strings trans.nn_NO = UI/AdministrationUI/NorwegianNynorsk.lproj/Localizable.strings @@ -228,6 +235,7 @@ trans.fr = SoObjects/Appointments/French.lproj/Localizable.strings trans.hu = SoObjects/Appointments/Hungarian.lproj/Localizable.strings trans.is = SoObjects/Appointments/Icelandic.lproj/Localizable.strings trans.it = SoObjects/Appointments/Italian.lproj/Localizable.strings +trans.mk_MK = SoObjects/Appointments/Macedonian.lproj/Localizable.strings trans.nb_NO = SoObjects/Appointments/NorwegianBokmal.lproj/Localizable.strings trans.nl = SoObjects/Appointments/Dutch.lproj/Localizable.strings trans.nn_NO = SoObjects/Appointments/NorwegianNynorsk.lproj/Localizable.strings @@ -258,6 +266,7 @@ trans.fr = SoObjects/Contacts/French.lproj/Localizable.strings trans.hu = SoObjects/Contacts/Hungarian.lproj/Localizable.strings trans.is = SoObjects/Contacts/Icelandic.lproj/Localizable.strings trans.it = SoObjects/Contacts/Italian.lproj/Localizable.strings +trans.mk_MK = SoObjects/Contacts/Macedonian.lproj/Localizable.strings trans.nb_NO = SoObjects/Contacts/NorwegianBokmal.lproj/Localizable.strings trans.nl = SoObjects/Contacts/Dutch.lproj/Localizable.strings trans.nn_NO = SoObjects/Contacts/NorwegianNynorsk.lproj/Localizable.strings @@ -288,6 +297,7 @@ trans.fr = SoObjects/Mailer/French.lproj/Localizable.strings trans.hu = SoObjects/Mailer/Hungarian.lproj/Localizable.strings trans.is = SoObjects/Mailer/Icelandic.lproj/Localizable.strings trans.it = SoObjects/Mailer/Italian.lproj/Localizable.strings +trans.mk_MK = SoObjects/Mailer/Macedonian.lproj/Localizable.strings trans.nb_NO = SoObjects/Mailer/NorwegianBokmal.lproj/Localizable.strings trans.nl = SoObjects/Mailer/Dutch.lproj/Localizable.strings trans.nn_NO = SoObjects/Mailer/NorwegianNynorsk.lproj/Localizable.strings @@ -318,6 +328,7 @@ trans.fr = UI/MailPartViewers/French.lproj/Localizable.strings trans.hu = UI/MailPartViewers/Hungarian.lproj/Localizable.strings trans.is = UI/MailPartViewers/Icelandic.lproj/Localizable.strings trans.it = UI/MailPartViewers/Italian.lproj/Localizable.strings +trans.mk_MK = UI/MailPartViewers/Macedonian.lproj/Localizable.strings trans.nb_NO = UI/MailPartViewers/NorwegianBokmal.lproj/Localizable.strings trans.nl = UI/MailPartViewers/Dutch.lproj/Localizable.strings trans.nn_NO = UI/MailPartViewers/NorwegianNynorsk.lproj/Localizable.strings diff --git a/SoObjects/Appointments/GNUmakefile b/SoObjects/Appointments/GNUmakefile index 5a37d3e6b..78e6b7889 100644 --- a/SoObjects/Appointments/GNUmakefile +++ b/SoObjects/Appointments/GNUmakefile @@ -54,7 +54,7 @@ Appointments_RESOURCE_FILES += \ MSExchangeFreeBusySOAPResponseMap.plist \ MSExchangeFreeBusySOAPRequest.wo -Appointments_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +Appointments_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian Macedonian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh Appointments_LOCALIZED_RESOURCE_FILES = Localizable.strings diff --git a/SoObjects/Appointments/Macedonian.lproj/Localizable.strings b/SoObjects/Appointments/Macedonian.lproj/Localizable.strings new file mode 100644 index 000000000..9ecb8973f --- /dev/null +++ b/SoObjects/Appointments/Macedonian.lproj/Localizable.strings @@ -0,0 +1,67 @@ +"Inviting the following persons is prohibited:" = "Канењето на следните лица е забрането:"; +"Personal Calendar" = "Личен календар"; +vevent_class0 = "(Јавен настан)"; +vevent_class1 = "(Приватен настан)"; +vevent_class2 = "(Доверлив настан)"; + +vtodo_class0 = "(Јавна задача)"; +vtodo_class1 = "(Приватна задача)"; +vtodo_class2 = "(Доверлива задача)"; + +/* Receipts */ +"The event \"%{Summary}\" was created" = "Настанот \"%{Summary}\" беше креиран"; +"The event \"%{Summary}\" was deleted" = "Настанот \"%{Summary}\" беше избришан"; +"The event \"%{Summary}\" was updated" = "Настанот \"%{Summary}\" беше освежен"; +"The following attendees(s) were notified:" = "Следниот учесник(ци) се известени:"; +"The following attendees(s) were added:" = "Следните учесници беа додадени:"; +"The following attendees(s) were removed:" = "Следните учесници беа отстранети:"; + +/* IMIP messages */ +"calendar_label" = "Календар:"; +"startDate_label" = "Почеток:"; +"endDate_label" = "Крај:"; +"due_label" = "Краен датум:"; +"location_label" = "Локација:"; +"summary_label" = "Резиме:"; +"comment_label" = "Коментар:"; + +/* Invitation */ +"Event Invitation: \"%{Summary}\"" = "Покана за настанот: \"%{Summary}\""; +"(sent by %{SentBy}) " = "(испратено од %{SentBy}) "; +"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}ве покани на %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"; +"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}ве покани на %{Summary}.\n\nПочеток: %{StartDate} во %{StartTime}\nКрај: %{EndDate} во %{EndTime}\nОпис: %{Description}"; + +/* Deletion */ +"Event Cancelled: \"%{Summary}\"" = "Настанот е откажан: \"%{Summary}\""; +"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" += "%{Organizer} %{SentByText}ја откажа поканата: %{Summary}.\n\nПочеток: %{StartDate}\nКрај: %{EndDate}\nОпис: %{Description}"; +"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" += "%{Organizer} %{SentByText}го откажа настанот: %{Summary}.\n\nПочеток: %{StartDate} во %{StartTime}\nКрај: %{EndDate} во %{EndTime}\nОпис: %{Description}"; + +/* Update */ +"The appointment \"%{Summary}\" for the %{OldStartDate} has changed" += "Состанокот \"%{Summary}\" за %{OldStartDate} е променет"; +"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed" += "Состанокот \"%{Summary}\" за %{OldStartDate} на %{OldStartTime} се промени"; +"The following parameters have changed in the \"%{Summary}\" meeting:" += "Следните параметри се променија во \"%{Summary}\" состанок:"; +"Please accept or decline those changes." += "Прифатете ги ли отфрлете ги измените."; + +/* Reply */ +"Accepted invitation: \"%{Summary}\"" = "Прифатена покана: \"%{Summary}\""; +"Declined invitation: \"%{Summary}\"" = "Одбиена покана: \"%{Summary}\""; +"Delegated invitation: \"%{Summary}\"" = "Делегирана покана: \"%{Summary}\""; +"Not yet decided on invitation: \"%{Summary}\"" = "Уште не одлучиле по поканата: \"%{Summary}\""; +"%{Attendee} %{SentByText}has accepted your event invitation." += "%{Attendee} %{SentByText}ја прифати вашата покана."; +"%{Attendee} %{SentByText}has declined your event invitation." += "%{Attendee} %{SentByText}ја одби вашаа покана."; +"%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}." += "%{Attendee} %{SentByText}ја делегираше поканата на %{Delegate}."; +"%{Attendee} %{SentByText}has not yet decided upon your event invitation." += "%{Attendee} %{SentByText}уште нема одлучено по вашата покана."; + +/* Resources */ +"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Не можам да ги пристапам ресурсите: \"%{Cn} %{SystemEmail}\""; +"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Максималниот број на едновремени закажувања (%{NumberOfSimultaneousBookings}) е постигнат за ресурсот \"%{Cn} %{SystemEmail}\". Конфликтниот настан е \"%{EventTitle}\", и почнува на %{StartDate}."; diff --git a/SoObjects/Contacts/GNUmakefile b/SoObjects/Contacts/GNUmakefile index ec9a353e1..f888d3451 100644 --- a/SoObjects/Contacts/GNUmakefile +++ b/SoObjects/Contacts/GNUmakefile @@ -27,7 +27,7 @@ Contacts_OBJC_FILES = \ Contacts_RESOURCE_FILES += \ product.plist \ -Contacts_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +Contacts_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian Macedonian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh Contacts_LOCALIZED_RESOURCE_FILES = Localizable.strings diff --git a/SoObjects/Contacts/Macedonian.lproj/Localizable.strings b/SoObjects/Contacts/Macedonian.lproj/Localizable.strings new file mode 100644 index 000000000..843e12432 --- /dev/null +++ b/SoObjects/Contacts/Macedonian.lproj/Localizable.strings @@ -0,0 +1,2 @@ +"Personal Address Book" = "Лична адресна книга"; +"Collected Address Book" = "Собрана адресна книга"; diff --git a/SoObjects/Mailer/GNUmakefile b/SoObjects/Mailer/GNUmakefile index 0ba367343..f25e20edd 100644 --- a/SoObjects/Mailer/GNUmakefile +++ b/SoObjects/Mailer/GNUmakefile @@ -72,6 +72,8 @@ Mailer_RESOURCE_FILES += \ SOGoMailIcelandicReply.wo \ SOGoMailItalianForward.wo \ SOGoMailItalianReply.wo \ + SOGoMailMacedonianForward.wo \ + SOGoMailMacedonianReply.wo \ SOGoMailNorwegianBokmalForward.wo \ SOGoMailNorwegianBokmalReply.wo \ SOGoMailNorwegianNynorskForward.wo \ @@ -97,7 +99,7 @@ Mailer_RESOURCE_FILES += \ SOGoMailWelshForward.wo \ SOGoMailWelshReply.wo -Mailer_LANGUAGES = Arabic Basque BrazilianPortuguese ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovenian Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +Mailer_LANGUAGES = Arabic Basque BrazilianPortuguese ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian Macedonian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovenian Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh Mailer_LOCALIZED_RESOURCE_FILES = Localizable.strings diff --git a/SoObjects/Mailer/Macedonian.lproj/Localizable.strings b/SoObjects/Mailer/Macedonian.lproj/Localizable.strings new file mode 100644 index 000000000..3b4067e1e --- /dev/null +++ b/SoObjects/Mailer/Macedonian.lproj/Localizable.strings @@ -0,0 +1,2 @@ +"OtherUsersFolderName" = "Други корисници"; +"SharedFoldersName" = "Делени папки"; diff --git a/SoObjects/Mailer/SOGoMailForward.h b/SoObjects/Mailer/SOGoMailForward.h index cd5bc20a9..f31e8cb05 100644 --- a/SoObjects/Mailer/SOGoMailForward.h +++ b/SoObjects/Mailer/SOGoMailForward.h @@ -82,6 +82,9 @@ @interface SOGoMailItalianForward : SOGoMailForward @end +@interface SOGoMailMacedonianForward : SOGoMailForward +@end + @interface SOGoMailNorwegianBokmalForward : SOGoMailForward @end diff --git a/SoObjects/Mailer/SOGoMailForward.m b/SoObjects/Mailer/SOGoMailForward.m index 5a2c9b1fd..d27d1b9a5 100644 --- a/SoObjects/Mailer/SOGoMailForward.m +++ b/SoObjects/Mailer/SOGoMailForward.m @@ -293,6 +293,9 @@ @implementation SOGoMailItalianForward @end +@implementation SOGoMailMacedonianForward +@end + @implementation SOGoMailNorwegianBokmalForward @end diff --git a/SoObjects/Mailer/SOGoMailMacedonianForward.wo/SOGoMailMacedonianForward.html b/SoObjects/Mailer/SOGoMailMacedonianForward.wo/SOGoMailMacedonianForward.html new file mode 100644 index 000000000..ef3f4389b --- /dev/null +++ b/SoObjects/Mailer/SOGoMailMacedonianForward.wo/SOGoMailMacedonianForward.html @@ -0,0 +1,16 @@ +<#newLine/> +<#newLine/> +<#signaturePlacementOnTop><#newLine/> +<#signature/><#newLine/> +-------- Оригинална порака --------<#newLine/> +Тема: <#subject/><#newLine/> +Датум: <#date/><#newLine/> +Од: <#from/><#newLine/> +<#hasReplyTo>Reply-To: <#replyTo/><#hasOrganization>Organization: <#organization/>To: <#to/><#newLine/> +<#hasCc>CC: <#cc/><#hasNewsGroups>Newsgroups: <#newsgroups/><#hasReferences>References: <#references/><#newLine/> +<#newLine/> +<#messageBody/><#newLine/> +<#signaturePlacementOnBottom><#newLine/> +<#newLine/> +<#signature/> +<#newLine/> diff --git a/SoObjects/Mailer/SOGoMailMacedonianForward.wo/SOGoMailMacedonianForward.wod b/SoObjects/Mailer/SOGoMailMacedonianForward.wo/SOGoMailMacedonianForward.wod new file mode 100644 index 000000000..f2436acc9 --- /dev/null +++ b/SoObjects/Mailer/SOGoMailMacedonianForward.wo/SOGoMailMacedonianForward.wod @@ -0,0 +1,88 @@ +subject: WOString { + value = subject; + escapeHTML = NO; +} + +date: WOString { + value = date; + escapeHTML = NO; +} + +from: WOString { + value = from; + escapeHTML = NO; +} + +newLine: WOString { + value = newLine; + escapeHTML = NO; +} + +hasReplyTo: WOConditional { + condition = hasReplyTo; +} + +replyTo: WOString { + value = replyTo; + escapeHTML = NO; +} + +hasOrganization: WOConditional { + condition = hasOrganization; +} + +organization: WOString { + value = organization; + escapeHTML = NO; +} + +to: WOString { + value = to; + escapeHTML = NO; +} + +hasCc: WOConditional { + condition = hasCc; +} + +cc: WOString { + value = cc; + escapeHTML = NO; +} + +hasNewsGroups: WOConditional { + condition = hasNewsGroups; +} + +newsgroups: WOString { + value = newsgroups; + escapeHTML = NO; +} + +hasReferences: WOConditional { + condition = hasReferences; +} + +references: WOString { + value = references; + escapeHTML = NO; +} + +messageBody: WOString { + value = messageBody; + escapeHTML = NO; +} + +signature: WOString { + value = signature; + escapeHTML = NO; +} + +signaturePlacementOnTop: WOConditional { + condition = signaturePlacementOnTop; +} + +signaturePlacementOnBottom: WOConditional { + condition = signaturePlacementOnTop; + negate = YES; +} diff --git a/SoObjects/Mailer/SOGoMailMacedonianReply.wo/SOGoMailMacedonianReply.html b/SoObjects/Mailer/SOGoMailMacedonianReply.wo/SOGoMailMacedonianReply.html new file mode 100644 index 000000000..9b8e20c4e --- /dev/null +++ b/SoObjects/Mailer/SOGoMailMacedonianReply.wo/SOGoMailMacedonianReply.html @@ -0,0 +1,16 @@ +<#replyPlacementOnTop><#newLine/> +<#newLine/> +<#signaturePlacementOnTop><#newLine/> +<#signature/><#newLine/> +<#outlookMode>-------- Оригинална порака --------<#newLine/> +Тема: <#subject/><#newLine/> +Датум: <#date/><#newLine/> +Од: <#from/><#newLine/> +<#hasReplyTo>Reply-To: <#replyTo/><#hasOrganization>Organization: <#organization/>To: <#to/><#newLine/> +<#hasCc>CC: <#cc/><#hasNewsGroups>Newsgroups: <#newsgroups/><#hasReferences>References: <#references/><#newLine/> +<#standardMode>On <#date/>, <#from/> wrote:<#newLine/> +<#newLine/> +<#messageBody/><#newLine/> +<#replyPlacementOnBottom><#newLine/> +<#newLine/> +<#signaturePlacementOnBottom><#signature/><#newLine/> diff --git a/SoObjects/Mailer/SOGoMailMacedonianReply.wo/SOGoMailMacedonianReply.wod b/SoObjects/Mailer/SOGoMailMacedonianReply.wo/SOGoMailMacedonianReply.wod new file mode 100644 index 000000000..3fbed6d61 --- /dev/null +++ b/SoObjects/Mailer/SOGoMailMacedonianReply.wo/SOGoMailMacedonianReply.wod @@ -0,0 +1,106 @@ +outlookMode: WOConditional { + condition = outlookMode; +} + +standardMode: WOConditional { + condition = outlookMode; + negate = YES; +} + +subject: WOString { + value = subject; + escapeHTML = NO; +} + +date: WOString { + value = date; + escapeHTML = NO; +} + +from: WOString { + value = from; + escapeHTML = NO; +} + +newLine: WOString { + value = newLine; + escapeHTML = NO; +} + +hasReplyTo: WOConditional { + condition = hasReplyTo; +} + +replyTo: WOString { + value = replyTo; + escapeHTML = NO; +} + +hasOrganization: WOConditional { + condition = hasOrganization; +} + +organization: WOString { + value = organization; + escapeHTML = NO; +} + +to: WOString { + value = to; + escapeHTML = NO; +} + +hasCc: WOConditional { + condition = hasCc; +} + +cc: WOString { + value = cc; + escapeHTML = NO; +} + +hasNewsGroups: WOConditional { + condition = hasNewsGroups; +} + +newsgroups: WOString { + value = newsgroups; + escapeHTML = NO; +} + +hasReferences: WOConditional { + condition = hasReferences; +} + +references: WOString { + value = references; + escapeHTML = NO; +} + +messageBody: WOString { + value = messageBody; + escapeHTML = NO; +} + +signature: WOString { + value = signature; + escapeHTML = NO; +} + +replyPlacementOnTop: WOConditional { + condition = replyPlacementOnTop; +} + +replyPlacementOnBottom: WOConditional { + condition = replyPlacementOnTop; + negate = YES; +} + +signaturePlacementOnTop: WOConditional { + condition = signaturePlacementOnTop; +} + +signaturePlacementOnBottom: WOConditional { + condition = signaturePlacementOnTop; + negate = YES; +} diff --git a/SoObjects/Mailer/SOGoMailReply.h b/SoObjects/Mailer/SOGoMailReply.h index 0cbaace16..abf75e095 100644 --- a/SoObjects/Mailer/SOGoMailReply.h +++ b/SoObjects/Mailer/SOGoMailReply.h @@ -81,6 +81,9 @@ @interface SOGoMailItalianReply : SOGoMailReply @end +@interface SOGoMailMacedonianReply : SOGoMailReply +@end + @interface SOGoMailNorwegianBokmalReply : SOGoMailReply @end diff --git a/SoObjects/Mailer/SOGoMailReply.m b/SoObjects/Mailer/SOGoMailReply.m index ce2ec7496..3d19b3405 100644 --- a/SoObjects/Mailer/SOGoMailReply.m +++ b/SoObjects/Mailer/SOGoMailReply.m @@ -131,6 +131,9 @@ @implementation SOGoMailItalianReply @end +@implementation SOGoMailMacedonianReply +@end + @implementation SOGoMailNorwegianBokmalReply @end diff --git a/SoObjects/SOGo/SOGoDefaults.plist b/SoObjects/SOGo/SOGoDefaults.plist index e15e3bd3a..614959f75 100644 --- a/SoObjects/SOGo/SOGoDefaults.plist +++ b/SoObjects/SOGo/SOGoDefaults.plist @@ -41,7 +41,7 @@ SOGoLanguage = "English"; SOGoSupportedLanguages = ( "Arabic", "Basque", "Catalan", "ChineseTaiwan", "Czech", "Dutch", "Danish", "Welsh", "English", "SpanishSpain", "SpanishArgentina", "Finnish", "French", "German", - "Icelandic", "Italian", "Hungarian", "Portuguese", "BrazilianPortuguese", + "Icelandic", "Italian", "Macedonian", "Hungarian", "Portuguese", "BrazilianPortuguese", "NorwegianBokmal", "NorwegianNynorsk", "Polish", "Russian", "Slovak", "Slovenian", "Ukrainian", "Swedish" ); diff --git a/Tests/Integration/preferences.py b/Tests/Integration/preferences.py index 8f8a5c6ac..3bab30cb4 100644 --- a/Tests/Integration/preferences.py +++ b/Tests/Integration/preferences.py @@ -12,7 +12,7 @@ import sogoLogin # this should probably be fetched magically... SOGoSupportedLanguages = [ "Arabic", "Basque", "Catalan", "ChineseTaiwan", "Czech", "Dutch", "Danish", "Welsh", "English", "Finnish", "SpanishSpain", "SpanishArgentina", "French", "German", - "Icelandic", "Italian", "Hungarian", "Portuguese", "BrazilianPortuguese", + "Icelandic", "Italian", "Macedonian", "Hungarian", "Portuguese", "BrazilianPortuguese", "NorwegianBokmal", "NorwegianNynorsk", "Polish", "Russian", "Slovak", "Slovenian", "Ukrainian", "Swedish" ]; daysBetweenResponseList=[1,2,3,5,7,14,21,30] diff --git a/UI/AdministrationUI/GNUmakefile b/UI/AdministrationUI/GNUmakefile index 049d73802..0bb4829d4 100644 --- a/UI/AdministrationUI/GNUmakefile +++ b/UI/AdministrationUI/GNUmakefile @@ -6,7 +6,7 @@ BUNDLE_NAME = AdministrationUI AdministrationUI_PRINCIPAL_CLASS = AdministrationUIProduct -AdministrationUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +AdministrationUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Italian Macedonian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh AdministrationUI_OBJC_FILES = \ AdministrationUIProduct.m \ diff --git a/UI/AdministrationUI/Macedonian.lproj/Localizable.strings b/UI/AdministrationUI/Macedonian.lproj/Localizable.strings new file mode 100644 index 000000000..56e617650 --- /dev/null +++ b/UI/AdministrationUI/Macedonian.lproj/Localizable.strings @@ -0,0 +1,15 @@ +/* this file is in UTF-8 format! */ + +"Help" = "Помош"; +"Close" = "Затвори"; + +"Modules" = "Модули"; + +/* Modules short names */ +"ACLs" = "КПЛа"; + +/* Modules titles */ +"ACLs_title" = "Управување со КПЛа за кориснички папки"; + +/* Modules descriptions */ +"ACLs_description" = "

Модулот за администрацијата на контролните пристапни листи овозможува да се променат КПЛ на секој кориснички календар и адресна книга.

Да се промени КПЛа на корисничка папка, откуцај го името на корисникот во полето за пребарување на врвот од прозорецот и двојно кликни на посакуваната папка.

"; diff --git a/UI/Common/GNUmakefile b/UI/Common/GNUmakefile index cd3d77bb4..fda8cba2a 100644 --- a/UI/Common/GNUmakefile +++ b/UI/Common/GNUmakefile @@ -6,7 +6,7 @@ BUNDLE_NAME = CommonUI CommonUI_PRINCIPAL_CLASS = CommonUIProduct -CommonUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +CommonUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian Macedonian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh CommonUI_OBJC_FILES += \ CommonUIProduct.m \ diff --git a/UI/Common/Macedonian.lproj/Localizable.strings b/UI/Common/Macedonian.lproj/Localizable.strings new file mode 100644 index 000000000..43110896f --- /dev/null +++ b/UI/Common/Macedonian.lproj/Localizable.strings @@ -0,0 +1,118 @@ +/* this file is in UTF-8 format! */ + +/* toolbars */ +"Save" = "Сними"; +"Close" = "Затвори"; +"Edit User Rights" = "Уреди ги корисничките права"; + +"Home" = "Дома"; +"Calendar" = "Календар"; +"Address Book" = "Адресна книга"; +"Mail" = "Електронска пошта"; +"Preferences" = "Подесувања"; +"Administration" = "Администрација"; +"Disconnect" = "Откачи"; +"Right Administration" = "Администрација на права"; +"Log Console (dev.)" = "Конзола на логови (развој)"; + +"User" = "Корисник"; +"Vacation message is enabled" = "Пораката за отсатност е активна"; + +"Help" = "Помош"; + +"noJavascriptError" = "SOGo бара Javascript за да работи. Бидете сигурни дека оваа поција е овозможена и активирана во вашиот прелистувач."; +"noJavascriptRetry" = "Обиди се повторно"; + +"Owner:" = "Сопственик:"; +"Publish the Free/Busy information" = "Публикувај ја информацијата за слободното/зафатено време"; + +"Add..." = "Додади..."; +"Remove" = "Отстрани"; + +"Subscribe User" = "Запиши го корисникот"; + +"Any Authenticated User" = "Било кој автентициран корисник"; +"Public Access" = "јавен достап"; +"Any user not listed above" = "Било кој корисник кој не долу излистан"; +"Anybody accessing this resource from the public area" = "Било кој може да го пристапи ресурсот од јавната мрежа"; + +"Sorry, the user rights can not be configured for that object." = "Жалам, корисничките привилегии не можат да се конфигурираат за овој објект."; + +"Any user with an account on this system will be able to access your mailbox \"%{0}\". Are you certain you trust them all?" + = "Секој корисник со сметка на овој систем ќе биде во можност да пристапува на вашето сандаче за пошта \"%{0}\". Дали сте сигурни дека можете да им верувате на сите?"; +"Any user with an account on this system will be able to access your calendar \"%{0}\". Are you certain you trust them all?" + = "Било кој корисник со сметка на овој систем че биде во можност да го пристапи вашиот календар \"%{0}\". дали сте сигурни дека можете да им верувате на сите?"; +"Potentially anyone on the Internet will be able to access your calendar \"%{0}\", even if they do not have an account on this system. Is this information suitable for the public Internet?" + = "Потенцијално секој на интернет ќе може да го пристапи вашиот календар \"%{0}\", иако немаат сметка на овој систем. Дали оваа информација е погодна за јавен интернет?"; +"Any user with an account on this system will be able to access your address book \"%{0}\". Are you certain you trust them all?" + = "Секој корисник со сметка на овој систем ќе биде во можност да пристапува на вашиот сдресар \"%{0}\". дали сте сигурни дека можете да им верувате на сите?"; +"Potentially anyone on the Internet will be able to access your address book \"%{0}\", even if they do not have an account on this system. Is this information suitable for the public Internet?" + = "Потенцијално било кој на интеернет ќе може да го достапи адресарот \"%{0}\", дури и ако нема сметка на системот. Дали оваа информација е погодна за јавен интернет?"; +"Give Access" = "Дади пристап"; +"Keep Private" = "Сочувај го приватно"; + +/* generic.js */ +"Unable to subscribe to that folder!" + = "Не е можно да се претплатите на оваа папка!"; +"You cannot subscribe to a folder that you own!" + = "Не можете да се претплатите на папката која е ваша!"; +"Unable to unsubscribe from that folder!" + = "Не е можно да се отпишете од папката!"; +"You cannot unsubscribe from a folder that you own!" + = "Не е можно да се отпишете од папката која е ваша!"; +"Unable to rename that folder!" = "Не е можно да ја преименувате папката!"; +"You have already subscribed to that folder!" + = "Веќе сте претплатени на папката!"; +"The user rights cannot be edited for this object!" + = "Корисничките права не можат да се уредат на овој објект!"; +"A folder by that name already exists." = "Папка со вакво име веќе постои."; +"You cannot create a list in a shared address book." + = "Не можете да креирате листа во споделен адресар."; +"Warning" = "Предупредување"; +"Can't contact server" = "Настана грешка при контактирање на серверот. Подоцна обидете се повторно."; + +"You are not allowed to access this module or this system. Please contact your system administrator." += "Вам не вие дозволено да го пристапите овој модул или овој систем. Контактирајте го вашиот систем администратор."; +"You don't have the required privileges to perform the operation." += "Ги немате потребните привилегии за да ја извршите операцијата."; + +"noEmailForDelegation" = "Морате да ја специфицирате адресата на која сакате да ги делегирате вашите покани."; +"delegate is organizer" = "Делегираниот е и организатор. Ве молиме одберете друг делегат."; +"delegate is a participant" = "Овој делегат е веќе учесник."; +"delegate is a group" = "Адресата не коренспондира со групата. Можете да делегирате на единствена личност."; + +"Snooze for " = "Паузирај го за"; +"5 minutes" = "5 минути"; +"10 minutes" = "10 минути"; +"15 minutes" = "15 минути"; +"30 minutes" = "30 минути"; +"45 minutes" = "45 минути"; +"1 hour" = "1 час"; +"1 day" = "1 ден"; + +/* common buttons */ +"OK" = "Во ред"; +"Cancel" = "Откажи"; +"Yes" = "Да"; +"No" = "Не"; + +/* alarms */ +"Reminder:" = "Потсетник:"; +"Start:" = "Почеток:"; +"Due Date:" = "Краен датум:"; +"Location:" = "Локација:"; + +/* mail labels */ +"Important" = "Важно"; +"Work" = "Работа"; +"Personal" = "Лично"; +"To Do" = "Да се направи"; +"Later" = "Подоцна"; + +"a2_Sunday" = "Нед"; +"a2_Monday" = "Пон"; +"a2_Tuesday" = "Вто"; +"a2_Wednesday" = "Сре"; +"a2_Thursday" = "Чет"; +"a2_Friday" = "Пет"; +"a2_Saturday" = "Саб"; diff --git a/UI/Contacts/GNUmakefile b/UI/Contacts/GNUmakefile index 27c90806e..e386089d6 100644 --- a/UI/Contacts/GNUmakefile +++ b/UI/Contacts/GNUmakefile @@ -6,7 +6,7 @@ BUNDLE_NAME = ContactsUI ContactsUI_PRINCIPAL_CLASS = ContactsUIProduct -ContactsUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +ContactsUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian Macedonian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh ContactsUI_OBJC_FILES = \ UIxContactsUserFolders.m \ diff --git a/UI/Contacts/Macedonian.lproj/Localizable.strings b/UI/Contacts/Macedonian.lproj/Localizable.strings new file mode 100644 index 000000000..465f70a08 --- /dev/null +++ b/UI/Contacts/Macedonian.lproj/Localizable.strings @@ -0,0 +1,215 @@ +/* this file is in UTF-8 format! */ + +"Contact" = "Контакт"; +"Address" = "Адреса"; +"Photos" = "Фотографија"; +"Other" = "Останато"; + +"Address Books" = "Адресари"; +"Addressbook" = "Адресар"; +"Addresses" = "Адреси"; +"Update" = "Освежи"; +"Cancel" = "Откажи"; +"Common" = "Заеднички"; +"Contact editor" = "Уредувач на контакти"; +"Contact viewer" = "Прегледувач на контакти"; +"Email" = "Електронска пошта"; +"Screen Name" = "Прекар"; +"Extended" = "Проширен"; +"Fax" = "Факс"; +"Firstname" = "Име"; +"Home" = "Дома"; +"HomePhone" = "Домашен телефон"; +"Lastname" = "Презиме"; +"Location" = "Локација"; +"MobilePhone" = "Мобилен телефон"; +"Name" = "Име"; +"OfficePhone" = "Службен телефон"; +"Organization" = "Организација"; +"Work Phone" = "Телефон на работа"; +"Phone" = "Телефон"; +"Phones" = "Телефони"; +"Postal" = "Поштенски број"; +"Save" = "Сними"; +"Internet" = "Интернет"; +"Unit" = "Одделение"; +"delete" = "избриши"; +"edit" = "уреди"; +"invalidemailwarn" = "Дотичната порака е невалидна"; +"new" = "нов"; +"Preferred Phone" = "Префериран телефонски број"; + +"Move To" = "Префрли во "; +"Copy To" = "Копирај во"; +"Add to:" = "Додади во:"; + +/* Tooltips */ + +"Create a new address book card" = "Креирај нова адесна картичка"; +"Create a new list" = "Креирај нова листа"; +"Edit the selected card" = "Уреди ја одбраната картичка"; +"Send a mail message" = "Испрати електронска порака"; +"Delete selected card or address book" = "Избриши ја одбраната картичка или адресна книга"; +"Reload all contacts" = "Повторно вчитај ги сите контакти"; + +"htmlMailFormat_UNKNOWN" = "Непознат"; +"htmlMailFormat_FALSE" = "Обичен текст"; +"htmlMailFormat_TRUE" = "HTML"; + +"Name or Email" = "Име или електронска адреса"; +"Category" = "Категорија"; +"Personal Addressbook" = "Личен адресар"; +"Search in Addressbook" = "Пребарај во адресарот"; + +"New Card" = "Нова картичка"; +"New List" = "Нова листа"; +"Edit" = "Уреди"; +"Properties" = "Особини..."; +"Sharing..." = "Делење..."; +"Write" = "Запиши"; +"Delete" = "Избриши"; +"Instant Message" = "Инстантна порака"; +"Add..." = "Додади..."; +"Remove" = "Избриши"; + +"Please wait..." = "Ве молам почекајте..."; +"No possible subscription" = "Претплатата не е можна"; + +"Preferred" = "Преферирана"; +"Display:" = "Прикажи:"; +"Display Name:" = "Прикажи име:"; +"Email:" = "Електронска адреса:"; +"Additional Email:" = "Дополнителна електронска адреса:"; + +"Phone Number:" = "Телефонски број:"; +"Prefers to receive messages formatted as:" = "Претпочита да прима пораки форматирани како:"; +"Screen Name:" = "Прекар"; +"Categories:" = "Категории:"; + +"First:" = "Прв:"; +"Last:" = "Последен:"; +"Nickname:" = "Прекар:"; + +"Telephone" = "Телефон:"; +"Work:" = "Работа:"; +"Home:" = "Дома:"; +"Fax:" = "Факс:"; +"Mobile:" = "Мобилен:"; +"Pager:" = "Пејџер:"; + +/* categories */ +"contacts_category_labels" = "Колега, конкурент, клиент, пријател, фамилија, деловен партнер, провајдер, новинар, ВИП"; +"Categories" = "Категории"; +"New category" = "Нова категорија"; + +/* adresses */ +"Title:" = "Наслов:"; +"Service:" = "Сервис:"; +"Company:" = "Компанија:"; +"Department:" = "Сектор:"; +"Organization:" = "Организација:"; +"Address:" = "Адреса:"; +"City:" = "Град:"; +"State_Province:" = "Држава/провинција:"; +"ZIP_Postal Code:" = "Поштенски број:"; +"Country:" = "Земја:"; +"Web Page:" = "Веб страница:"; + +"Work" = "Работа"; +"Other Infos" = "Други информации"; + +"Note:" = "Забелешка:"; +"Timezone:" = "Временска зона:"; +"Birthday:" = "Роденден:"; +"Birthday (yyyy-mm-dd):" = "Роденден (гггг-мм-дд):"; +"Freebusy URL:" = "Слободно-зафатено URL:"; + +"Add as..." = "Додади како:"; +"Recipient" = "Примач"; +"Carbon Copy" = "Копија"; +"Blind Carbon Copy" = "Скриена копија"; + +"New Addressbook..." = "Нов адресар"; +"Subscribe to an Addressbook..." = "Прептлати се на адресарот..."; +"Remove the selected Addressbook" = "Избриши го избраниот адресар"; + +"Name of the Address Book" = "Име на адресарот"; +"Are you sure you want to delete the selected address book?" += "Дали сте сигурни дека сакате да го избришете адресарот?"; +"You cannot remove nor unsubscribe from a public addressbook." += "Неможете да го отстраните или отпишете од јавниот адресар."; +"You cannot remove nor unsubscribe from your personal addressbook." += "Не можете да се изземете или отпишете од вашиот личен адресар."; + +"Are you sure you want to delete the selected contacts?" += "Дали сте сигурни дека сакате да ги избришете одбраните контакти?"; + +"You cannot delete the card of \"%{0}\"." += "Не можете да ја избришете картичката на \"%{0}\"."; + + + +"You cannot subscribe to a folder that you own!" += "Не можете да се претплатите на папката која е ваша."; +"Unable to subscribe to that folder!" += "Не е можно да се претплатите на оваа папка."; + +/* acls */ +"Access rights to" = "Пристапни права за"; +"For user" = "За корисникот"; + +"Any Authenticated User" = "Било кој автентициран корисник"; +"Public Access" = "Јавен пристап"; + +"This person can add cards to this addressbook." += "Корисникот може да додава картички во овој адресар."; +"This person can edit the cards of this addressbook." += "Корисникот може да ги уредува картичките во овој адресар."; +"This person can list the content of this addressbook." += "Корисникот може да ја листа содржината на адресарот."; +"This person can read the cards of this addressbook." += "Корисникот може да ги чита картичките во овој адресар."; +"This person can erase cards from this addressbook." += "Корисникот може да брише картички во овој адресар."; + +"The selected contact has no email address." += "Одбраниот контакт нема електронска адреса."; + +"Please select a contact." = "Одберете контакт."; + +/* Error messages for move and copy */ + +"SoAccessDeniedException" = "Не можете да запишувате во овој адресар."; +"Forbidden" = "Не можете да запишувате во овој адресар."; +"Invalid Contact" = "Одбраниот контакт повеќе не постои."; +"Unknown Destination Folder" = "Одбраниот адресар повеќе не постои како одредница."; + +/* Lists */ +"List details" = "Детали за листата"; +"List name:" = "Име на листата:"; +"List nickname:" = "Листа на прекари:"; +"List description:" = "Листа на описи:"; +"Members" = "Членови"; +"Contacts" = "Контакти"; +"Add" = "Додади"; +"Lists can't be moved or copied." = "Листите не можат да бидат префрлени или копирани."; +"Export" = "Извези"; +"Export Address Book..." = "Извеси го адресарот..."; +"View Raw Source" = "Види го сировиот извор"; +"Import Cards" = "Увези ја картичката"; +"Select a vCard or LDIF file." = "Одбери vCard или LDIF датотека."; +"Upload" = "Префрли"; +"Uploading" = "Префрлам"; +"Done" = "Завршено"; +"An error occured while importing contacts." = "Настана грешка при увезувањето на контактите."; +"No card was imported." = "Ниту една картичка не е увезена."; +"A total of %{0} cards were imported in the addressbook." = "Во адресарот се увезени вкупно %{0} картички."; + +"Reload" = "Обнови"; + +/* Properties window */ +"Address Book Name:" = "Име на адресарот"; +"Links to this Address Book" = "Линк кон оваа адресна книга"; +"Authenticated User Access" = "Авторизиран кориснички пристап"; +"CardDAV URL: " = "CalDAV URL:"; + diff --git a/UI/MailPartViewers/GNUmakefile b/UI/MailPartViewers/GNUmakefile index 43761686c..5b1f13a1b 100644 --- a/UI/MailPartViewers/GNUmakefile +++ b/UI/MailPartViewers/GNUmakefile @@ -6,7 +6,7 @@ BUNDLE_NAME = MailPartViewers MailPartViewers_PRINCIPAL_CLASS = MailPartViewersProduct -MailPartViewers_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +MailPartViewers_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian Macedonian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh MailPartViewers_OBJC_FILES += \ MailPartViewersProduct.m \ diff --git a/UI/MailPartViewers/Macedonian.lproj/Localizable.strings b/UI/MailPartViewers/Macedonian.lproj/Localizable.strings new file mode 100644 index 000000000..13643908d --- /dev/null +++ b/UI/MailPartViewers/Macedonian.lproj/Localizable.strings @@ -0,0 +1,48 @@ +ACCEPTED = "прифатено"; +COMPLETED = "комплетирано"; +DECLINED = "одбиено"; +DELEGATED = "делегирано"; +"IN-PROCESS" = "во тек"; +"NEEDS-ACTION" = "потребна е активност"; +TENTATIVE = "не е сигурно"; +organized_by_you = "организирано од вас"; +you_are_an_attendee = "вие сте учесник"; +add_info_text = "iMIP 'ADD' барања сеуште не се подржани од SOGo."; +publish_info_text = "Испраќачот ве информира за прикачениот настан."; +cancel_info_text = "Вашата покана или целиот настан е откажан."; +request_info_no_attendee = "предлага состанок на учесниците. Вие ја примивте оваа порака како известување, не сте предвидени како учесник."; +Appointment = "Закажување"; +"Status Update" = "Освежи го статусот"; +was = "беше"; + +Organizer = "Организатор"; +Time = "Време"; +Attendees = "Учесници"; +request_info = "ве поканува да учествувате на состанокот."; +"Add to calendar" = "Додади во календарот"; +"Delete from calendar" = "Избриши од календарот"; +"Update status" = "Освежи го статусот"; +Accept = "Прифати"; +Decline = "Одбиј"; +Tentative = "Условно"; +"Delegate ..." = "Делегирај ..."; +"Delegated to" = "Делегирано на"; +"Update status in calendar" = "Освежи го статусот во календарот"; +"delegated from" = "делегирано од"; + +reply_info_no_attendee = "Вие добивте одговор на закажан настан но испраќачот на одговорот не е учесник."; +reply_info = "Ова е одговор на покана за настан креиран од вас."; + +"to" = "до"; + +"Untitled" = "Без наслов"; + +"Size" = "Големина"; + +"Digital signature is not valid" = "Електронскиот потпис е невалиден"; +"Message is signed" = "Пораката е потпишана"; +"Subject" = "Тема"; +"From" = "Од"; +"Date" = "Датум"; +"To" = "До"; +"Issuer" = "Издавач"; diff --git a/UI/MailerUI/GNUmakefile b/UI/MailerUI/GNUmakefile index a8db21420..2af49a213 100644 --- a/UI/MailerUI/GNUmakefile +++ b/UI/MailerUI/GNUmakefile @@ -6,7 +6,7 @@ BUNDLE_NAME = MailerUI MailerUI_PRINCIPAL_CLASS = MailerUIProduct -MailerUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +MailerUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian Macedonian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh MailerUI_OBJC_FILES += \ MailerUIProduct.m \ diff --git a/UI/MailerUI/Macedonian.lproj/Localizable.strings b/UI/MailerUI/Macedonian.lproj/Localizable.strings new file mode 100644 index 000000000..23f5c6c36 --- /dev/null +++ b/UI/MailerUI/Macedonian.lproj/Localizable.strings @@ -0,0 +1,308 @@ +/* this file is in UTF-8 format! */ + +/* Icon's label */ +"Create" = "Креирај"; +"Empty Trash" = "Испразни го ѓубрето"; +"Delete" = "Избриши"; +"Expunge" = "Уништи"; +"Forward" = "Препрати"; +"Get Mail" = "Зами пошта"; +"Junk" = "Ѓубре"; +"Reply" = "Одговори"; +"Reply All" = "Одговори на сите"; +"Print" = "Отпечати"; +"Stop" = "Стоп"; +"Write" = "Запиши"; +"Search" = "Барај"; + +"Send" = "Испрати"; +"Contacts" = "Контакти"; +"Attach" = "Прикачи"; +"Save" = "Зачувај"; +"Options" = "Опции"; +"Close" = "Затвори"; +"Size" = "Големина"; + +/* Tooltips */ + +"Send this message now" = "Испрати ја пораката сега"; +"Select a recipient from an Address Book" = "Одбери примач од контактите"; +"Include an attachment" = "Прикачи прилог"; +"Save this message" = "Зачувај ја пораката"; +"Get new messages" = "Земи ги новите пораки"; +"Create a new message" = "Креирај нова порака"; +"Go to address book" = "Оди кон адресарот"; +"Reply to the message" = "Одговори на пораката"; +"Reply to sender and all recipients" = "Одговори на испраќачот и сите приматели"; +"Forward selected message" = "Препратија одбраната порака"; +"Delete selected message or folder" = "Избриши ја селектрираната порака или папка"; +"Mark the selected messages as junk" = "Означи ги пораките како Ѓубре"; +"Print this message" = "Отпечати ја пораката"; +"Stop the current transfer" = "Запри го тековниот трансфер"; +"Attachment" = "Прилог"; +"Unread" = "Непрочитано"; +"Flagged" = "Означена"; +"Search multiple mailboxes" = "Пребарај повеќе поштенски сандачиња"; + +/* Main Frame */ + +"Home" = "дома"; +"Calendar" = "Календар"; +"Addressbook" = "Адресар"; +"Mail" = "Пошта"; +"Right Administration" = "Администрација на права"; + +"Help" = "Помош"; + +/* Mail account main windows */ + +"Welcome to the SOGo Mailer. Use the folder tree on the left to browse your mail accounts!" = "Добредојдовте на СОГо мејлерот. Користете го дрвото со папки на левата страна за да се движите низ вашите сметки за пошта!"; + +"Read messages" = "Читај пораки"; +"Write a new message" = "Напиши нова порака"; + +"Share: " = "Дели:"; +"Account: " = "Сметка:"; +"Shared Account: " = "Делени сметки:"; + +/* acls */ +"Access rights to" = "Пристапни права за"; +"For user" = "За корисник"; + +"Any Authenticated User" = "Било кој автентициран корисник"; + +"List and see this folder" = "Излистај и погледни ја оваа папка"; +"Read mails from this folder" = "Читај ги пораките од оваа папка"; +"Mark mails read and unread" = "Означи ги пораките како прочитани и непрочитани"; +"Modify the flags of the mails in this folder" = "Промени ги знаменцата на пораките во оваа папка"; +"Insert, copy and move mails into this folder" = "Внеси, копирај и префрли пораки во оваа папка"; +"Post mails" = "Испрати пораки"; +"Add subfolders to this folder" = "Додади под папка во оваа папка"; +"Remove this folder" = "Избриши ја оваа папка"; +"Erase mails from this folder" = "Избриши ги пораките од оваа папка"; +"Expunge this folder" = "Уништи ја оваа папка"; +"Export This Folder" = "Извези ја оваа папка"; +"Modify the acl of this folder" = "Измени ги пристапните права на оваа папка"; + +"Saved Messages.zip" = "Снимени Messages.zip"; + +"Update" = "Освежи"; +"Cancel" = "Откажи"; + +/* Mail edition */ + +"From" = "Од"; +"Subject" = "Тема"; +"To" = "До"; +"Cc" = "Цц"; +"Bcc" = "Бцц"; +"Reply-To" = "Одговори на"; +"Add address" = "Додади адреса"; +"Body" = "Тело"; + +"Open" = "Отвори"; +"Select All" = "Одбери се"; +"Attach Web Page..." = "Прикачи веб страна..."; +"file" = "датотека"; +"files" = "датотеки"; +"Save all" = "сними се"; + +"to" = "До"; +"cc" = "Цц"; +"bcc" = "Бцц"; + +"Edit Draft..." = "Уреди го драфтот"; +"Load Images" = "Вчитај слики"; + +"Return Receipt" = "Препорачано"; +"The sender of this message has asked to be notified when you read this message. Do you with to notify the sender?" = "Испраќачот на пораката сака да биде известен кога ќе ја прочитате пораката. Дали сакате да го известам испраќачот?"; +"Return Receipt (displayed) - %@"= "Повратен примач (прикажан) - %@"; +"This is a Return Receipt for the mail that you sent to %@.\n\nNote: This Return Receipt only acknowledges that the message was displayed on the recipient's computer. There is no guarantee that the recipient has read or understood the message contents." = "Ова е повратница за пораката која ја испративте на %@.\n\nзабелешка: Оваа повратница само укажува дека пораката била прикажана на екранот на примателот. Нема никаква гаранција дека примачот ја прочитал пораката или пак разбрал содржината на истата."; + +"Priority" = "Приоритет"; +"highest" = "Највисок"; +"high" = "Висок"; +"normal" = "Нормален"; +"low" = "Низок"; +"lowest" = "Најнизок"; + +"This mail is being sent from an unsecure network!" = "Оваа порака е испратена од небезбедна мрежа!"; + +"Address Book:" = "Адресар:"; +"Search For:" = "Барај го:"; + +/* Popup "show" */ + +"all" = "сите"; +"read" = "прочитани"; +"unread" = "непрочитани"; +"deleted" = "избришани"; +"flagged" = "означени"; + +/* MailListView */ + +"Sender" = "Испраќач"; +"Subject or Sender" = "Тема или испраќач"; +"To or Cc" = "До или Цц"; +"Entire Message" = "Целата порака"; + +"Date" = "Датум"; +"View" = "Поглед"; +"All" = "Сите"; +"No message" = "Нема порака"; +"messages" = "пораки"; + +"first" = "Прва"; +"previous" = "Претходна"; +"next" = "Следна"; +"last" = "Последна"; + +"msgnumber_to" = "до"; +"msgnumber_of" = "од"; + +"Mark Unread" = "Означи како непрочитана"; +"Mark Read" = "Означи прочитана"; + +"Untitled" = "Без наслов"; + +/* Tree */ + +"SentFolderName" = "Испратени"; +"TrashFolderName" = "Ѓубре"; +"InboxFolderName" = "Примени"; +"DraftsFolderName" = "Драфтови"; +"SieveFolderName" = "Филтри"; +"Folders" = "Папки"; /* title line */ + +/* MailMoveToPopUp */ + +"MoveTo" = "Префрли …"; + +/* Address Popup menu */ +"Add to Address Book..." = "Додади во адресарот..."; +"Compose Mail To" = "Компонирај порака до"; +"Create Filter From Message..." = "Креирај филтер од пораката..."; + +/* Image Popup menu */ +"Save Image" = "Сними слика"; +"Save Attachment" = "Сними прилог"; + +/* Mailbox popup menus */ +"Open in New Mail Window" = "Отвори во нов прозорец за пораки"; +"Copy Folder Location" = "Копирај ја локацијата на папката"; +"Subscribe..." = "Прептлати се..."; +"Mark Folder Read" = "Означи ја папката како прочитана"; +"New Folder..." = "Нова папка..."; +"Compact This Folder" = "Компактирај ја оваа папка"; +"Search Messages..." = "Пребарај ги пораките..."; +"Sharing..." = "Споделување..."; +"New Subfolder..." = "Нова под папка..."; +"Rename Folder..." = "Преименувај ја папката..."; +"Delete Folder" = "Избриши ја папката"; +"Use This Folder For" = "Користи ја папката за"; +"Get Messages for Account" = "Земи ги пораките за сметката"; +"Properties..." = "Особини..."; +"Delegation..." = "Делегација..."; + +/* Use This Folder menu */ +"Sent Messages" = "Испрати пораки"; +"Drafts" = "Драфтови"; +"Deleted Messages" = "Избришани пораки"; + +/* Message list popup menu */ +"Open Message In New Window" = "Отвори ја пораката во нов прозор"; +"Reply to Sender Only" = "Одговори му само на испраќачот"; +"Reply to All" = "Одговори на сите"; +"Edit As New..." = "Уреди како нова..."; +"Move To" = "Префрли во"; +"Copy To" = "Ископирај во"; +"Label" = "Лабела"; +"Mark" = "Означи"; +"Save As..." = "Сними како..."; +"Print Preview" = "Преглед пред печатење"; +"View Message Source" = "Види ја изворно пораката"; +"Print..." = "Отпечати..."; +"Delete Message" = "Избриши порака"; +"Delete Selected Messages" = "Избриши ги означените пораки"; + +"This Folder" = "Оваа папка"; + +/* Label popup menu */ +"None" = "Ниту една"; + +/* Mark popup menu */ +"As Read" = "Како прочитана"; +"Thread As Read" = "Преписката како прочитана"; +"As Read By Date..." = "Прочитано по датум..."; +"All Read" = "Сето прочитано"; +"Flag" = "Знаменце"; +"As Junk" = "Како ѓубре"; +"As Not Junk" = "Не е ѓубре"; +"Run Junk Mail Controls" = "Изврши ги контролите за ѓубре"; + +"Search messages in:" = "Пребарај ги пораките во:"; +"Search" = "Барај"; +"Search subfolders" = "Пребарај под папки"; +"Match any of the following" = "Одговара на било што од следното"; +"Match all of the following" = "Одговара на се од следното"; +"contains" = "содржи"; +"does not contain" = "не содржи"; +"No matches found" = "Не се пронајдени совпаѓања"; +"results found" = "пронајдени резултати"; +"result found" = "пронајден резултат"; +"Please specify at least one filter" = "Одберете барем еден филтер"; + +/* Folder operations */ +"Name :" = "Име :"; +"Enter the new name of your folder :" + = "Внеси ново име за вашата папка :"; +"Do you really want to move this folder into the trash ?" + = "Навистина сакате папката да ја фрлите во кантата за ѓубре ?"; +"Operation failed" = "Неуспешна операција"; + +"Quota" = "Квота:"; +"quotasFormat" = "%{0}% used on %{1} MB"; + +"Please select a message." = "Одберете порака."; +"Please select a message to print." = "Одберете ја пораката што ќе се печати."; +"Please select only one message to print." = "Одберете само една порака да ја печатите."; +"The message you have selected doesn't exist anymore." = "Пораката која ја обележивте повеќе не постои."; + +"The folder with name \"%{0}\" could not be created." += "Папката со име \"%{0}\" не може да биде креирана."; +"This folder could not be renamed to \"%{0}\"." += "Оваа папка не може да биде преименувана во \"%{0}\"."; +"The folder could not be deleted." += "Оваа папка не може да се избрише."; +"The trash could not be emptied." += "Кантата за ѓубре не може да се испразни."; +"The folder functionality could not be changed." += "Улогата на папката не може да биде променета."; + +"You need to choose a non-virtual folder!" = "Треба да изберете папка која не е виртуелна!"; + +"Moving a message into its own folder is impossible!" += "Префлањето на пораката во сопствената папка не е можно!"; +"Copying a message into its own folder is impossible!" += "Копирањето на пораката во сопствената папка не е можно!"; + +/* Message operations */ +"The messages could not be moved to the trash folder. Would you like to delete them immediately?" += "Пораките не можат да се префрлат во папката за ѓубре. Дали саката веднаш да ги избришам?"; + +/* Message editing */ +"error_missingsubject" = "Пораката нема тема. Дали сте сигурни дека сакате да ја испратите?"; +"error_missingrecipients" = "Одберете барем еден примател."; +"Send Anyway" = "Испрати во секој случај"; +"Error while saving the draft:" = "Грешка при снимањето на привремениот документ:"; +"Error while uploading the file \"%{0}\":" = "Грешка при прикажување на фајл \"%{0}\":"; +"There is an active file upload. Closing the window will interrupt it." = "Во тек е активно прикачување на фајл. Затварањето на прозорецот ќе го прекине прикачувањето."; + +/* Message sending */ +"cannot send message: (smtp) all recipients discarded" = "Не моѓам да ја пратам пораката: сите приматели се невалидни."; +"cannot send message (smtp) - recipients discarded:" = "Не можам да ја испратам пораката. Следните адреси се невалидни:"; +"cannot send message: (smtp) error when connecting" = "Не можам да ја испртам пораката: грешка при поврзувањето со SMPT серверот."; + +/* Contacts list in mail editor */ +"Email" = "Електронска порака"; +"Name" = "Име"; diff --git a/UI/MainUI/Arabic.lproj/Localizable.strings b/UI/MainUI/Arabic.lproj/Localizable.strings index c4f4e1784..7a9189774 100644 --- a/UI/MainUI/Arabic.lproj/Localizable.strings +++ b/UI/MainUI/Arabic.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/Basque.lproj/Localizable.strings b/UI/MainUI/Basque.lproj/Localizable.strings index cb6f1e307..3a1f52b17 100644 --- a/UI/MainUI/Basque.lproj/Localizable.strings +++ b/UI/MainUI/Basque.lproj/Localizable.strings @@ -32,6 +32,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/BrazilianPortuguese.lproj/Localizable.strings b/UI/MainUI/BrazilianPortuguese.lproj/Localizable.strings index 7026d44e0..3ad7c5859 100644 --- a/UI/MainUI/BrazilianPortuguese.lproj/Localizable.strings +++ b/UI/MainUI/BrazilianPortuguese.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Noruega (Norsk bokmål)"; "NorwegianNynorsk" = "Noruega (Norsk nynorsk)"; "Polish" = "Polski"; diff --git a/UI/MainUI/Catalan.lproj/Localizable.strings b/UI/MainUI/Catalan.lproj/Localizable.strings index bc7bac2ab..dd75eed64 100644 --- a/UI/MainUI/Catalan.lproj/Localizable.strings +++ b/UI/MainUI/Catalan.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/ChineseTaiwan.lproj/Localizable.strings b/UI/MainUI/ChineseTaiwan.lproj/Localizable.strings index 1ea4ec935..3d1eaa48a 100644 --- a/UI/MainUI/ChineseTaiwan.lproj/Localizable.strings +++ b/UI/MainUI/ChineseTaiwan.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/Czech.lproj/Localizable.strings b/UI/MainUI/Czech.lproj/Localizable.strings index 1e4076833..e6f98755f 100644 --- a/UI/MainUI/Czech.lproj/Localizable.strings +++ b/UI/MainUI/Czech.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/Danish.lproj/Localizable.strings b/UI/MainUI/Danish.lproj/Localizable.strings index 28f2bfe07..5e3ad8eb4 100644 --- a/UI/MainUI/Danish.lproj/Localizable.strings +++ b/UI/MainUI/Danish.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk"; "NorwegianNynorsk" = "Nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/Dutch.lproj/Localizable.strings b/UI/MainUI/Dutch.lproj/Localizable.strings index f075241f1..1727b505c 100644 --- a/UI/MainUI/Dutch.lproj/Localizable.strings +++ b/UI/MainUI/Dutch.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk Bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/English.lproj/Localizable.strings b/UI/MainUI/English.lproj/Localizable.strings index 1ea4ec935..3d1eaa48a 100644 --- a/UI/MainUI/English.lproj/Localizable.strings +++ b/UI/MainUI/English.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/Finnish.lproj/Localizable.strings b/UI/MainUI/Finnish.lproj/Localizable.strings index 953f8b9d4..20c192f54 100644 --- a/UI/MainUI/Finnish.lproj/Localizable.strings +++ b/UI/MainUI/Finnish.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/French.lproj/Localizable.strings b/UI/MainUI/French.lproj/Localizable.strings index ade0650aa..ad72929e0 100644 --- a/UI/MainUI/French.lproj/Localizable.strings +++ b/UI/MainUI/French.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/GNUmakefile b/UI/MainUI/GNUmakefile index 08b72bebd..998961abd 100644 --- a/UI/MainUI/GNUmakefile +++ b/UI/MainUI/GNUmakefile @@ -6,7 +6,7 @@ BUNDLE_NAME = MainUI MainUI_PRINCIPAL_CLASS = MainUIProduct -MainUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +MainUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian Macedonian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh MainUI_OBJC_FILES += \ MainUIProduct.m \ diff --git a/UI/MainUI/German.lproj/Localizable.strings b/UI/MainUI/German.lproj/Localizable.strings index cdd05889f..7259b45f0 100644 --- a/UI/MainUI/German.lproj/Localizable.strings +++ b/UI/MainUI/German.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk Bokmål"; "NorwegianNynorsk" = "Norsk Nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/Hungarian.lproj/Localizable.strings b/UI/MainUI/Hungarian.lproj/Localizable.strings index a873dd360..6d6d55e8c 100644 --- a/UI/MainUI/Hungarian.lproj/Localizable.strings +++ b/UI/MainUI/Hungarian.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/Icelandic.lproj/Localizable.strings b/UI/MainUI/Icelandic.lproj/Localizable.strings index 68633bfdc..b1f53ab48 100644 --- a/UI/MainUI/Icelandic.lproj/Localizable.strings +++ b/UI/MainUI/Icelandic.lproj/Localizable.strings @@ -32,6 +32,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/Italian.lproj/Localizable.strings b/UI/MainUI/Italian.lproj/Localizable.strings index bd70ffd0c..f3710fb5d 100644 --- a/UI/MainUI/Italian.lproj/Localizable.strings +++ b/UI/MainUI/Italian.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/Macedonian.lproj/Locale b/UI/MainUI/Macedonian.lproj/Locale new file mode 100644 index 000000000..9e7aaded3 --- /dev/null +++ b/UI/MainUI/Macedonian.lproj/Locale @@ -0,0 +1,35 @@ +/* Macedonian */ +{ + NSLanguageName = "Macedonian"; + NSFormalName = "Македонски"; + NSLocaleCode = "mk"; /* ISO 639-1 */ + NSLanguageCode = "mkd"; /* ISO 639-2 */ + NSParentContext = ""; + + NSAMPMDesignation = (AM, PM); + NSCurrencySymbol = "MKD"; + NSDateFormatString = "%A, %B %e, %Y"; + NSDateTimeOrdering = DMYH; + NSDecimalDigits = ("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"); + NSDecimalSeparator = ","; + NSEarlierTimeDesignations = ("претходно", "последен", "минато", "пред"); + NSHourNameDesignations = ((0, "полноќ"), (10, "утро"), (12, "пладне", "ручек"), (14, "попладне"), (19, "вечера")); + NSInternationalCurrencyString = "MKD"; /* ISO 4217 */ + NSLaterTimeDesignations = ("следен"); + NSMonthNameArray = ("Јануари", "Февруари", "Март", "Април", "Мај", "Јуни", "Јули", "Август", "Септември", "Октомври", "Ноември", "Декември"); + NSNextDayDesignations = ("утре"); + NSNextNextDayDesignations = ("утре"); + NSPriorDayDesignations = ("вчера"); + NSShortDateFormatString = "%m/%e/%y"; + NSShortMonthNameArray = ("Јан", "Фев", "Мар", "Апр", "Мај", "Јун", "Јул", "Авг", "Сеп", "Окт", "Ное", "Дек"); + NSShortTimeDateFormatString = "%m/%e/%y %I:%M %p"; + NSShortWeekDayNameArray = ("Нед", "Пон", "Вто", "Сре", "Чет", "Пет", "Саб"); + NSThisDayDesignations = ("денес", "сега"); + NSThousandsSeparator = "."; + NSTimeDateFormatString = "%A, %B %e, %Y %I:%M:%S %p %Z"; + NSTimeFormatString = "%I:%M:%S %p"; + NSWeekDayNameArray = ("Недела", "Понеделник", "Вторник", "Среда", "Четврток", "Петок", "Сабота"); + NSYearMonthWeekDesignations = ("година", "месец", "недела"); + NSPositiveCurrencyFormatString = "9.999,00 МКД"; + NSNegativeCurrencyFormatString = "-9.999,00 МКД"; +} diff --git a/UI/MainUI/Macedonian.lproj/Localizable.strings b/UI/MainUI/Macedonian.lproj/Localizable.strings new file mode 100644 index 000000000..aa535980b --- /dev/null +++ b/UI/MainUI/Macedonian.lproj/Localizable.strings @@ -0,0 +1,78 @@ +/* this file is in UTF-8 format! */ + +"title" = "SOGo"; + +"Username:" = "Корисничко име:"; +"Password:" = "Лозинка:"; +"Domain:" = "Домејн:"; +"Remember username" = "Запомни го корисничкото име"; + +"Connect" = "Поврзи се"; + +"Wrong username or password." = "Погрешно корисничко име или лозинка."; +"cookiesNotEnabled" = "Не можете да се најавите поради тоа што колачињата на вашиот прелистувач не се активирани. Ве молиме да ги овозможите колачињата во опциите на вашиот прелистувач и да се обидете повторно."; + +"browserNotCompatible" = "Забележивме дека вашиот прелистувач во моментов не е поддржан од овој сајт. Наша препорака е да користите Firefox. Клинете врз овој линк да ја преземете последната верзија на овој прелистувач."; +"alternativeBrowsers" = "Како алтернатива исто така можете да користите еден оф компатибилните прелистувачи"; +"alternativeBrowserSafari" = "Како крајна можност, можете исто така да го користите Safari."; +"Download" = "Преземи"; + +"Language:" = "Јазик:"; +"choose" = "Одбери ..."; +"Arabic" = "Арапски"; +"Catalan" = "Каталуња"; +"Czech" = "Чешки"; +"Danish" = "Дански (Данска)"; +"Dutch" = "Холандски"; +"English" = "Англиски"; +"Finnish" = "Фински"; +"French" = "Француски"; +"German" = "Германски"; +"Hungarian" = "Унгарски"; +"Icelandic" = "Исландски"; +"Italian" = "Италијански"; +"NorwegianBokmal" = "Норвешки"; +"NorwegianNynorsk" = "Норвешки"; +"Polish" = "Полски"; +"BrazilianPortuguese" = "Португалски Бразилски"; +"Russian" = "Руски"; +"Slovak" = "Словачки"; +"SpanishSpain" = "Шпански (Шпанија)"; +"SpanishArgentina" = "Шпански (Аргентина)"; +"Swedish" = "Шведски"; +"Ukrainian" = "Украински"; +"Welsh" = "Велшки - Келтски"; + +"About" = "За"; +"AboutBox" = "Развиено од Inverse, SOGo е комплетен групвер сервер со фокус на скалабилност и едноставност.

\nSOGo овозможува богат AJAX-базиран Web интерфејс и поддржува повеќе нативни клиенти користејќи стандардни протоколи како на пример CalDAV и CardDAV.

\nSOGo е дистрибуиран под GNU GPL верзија 2 или подоцнежна и делови се дистрибуиирани под GNU LGPL version 2. Ова е бесплатен софтвер: вие сте слободни да го менувате и редистрибуирате. Нема гаранција, до ниво дозволено со закон.

\nПогледни ја страницава за разни можности на поддршка."; + +"Your account was locked due to too many failed attempts." = "Вашиот налог е блокиран поради премногу погрешни обиди."; +"Your account was locked due to an expired password." = "Вашата сметка е блокирана поради истечена лозинка."; +"Login failed due to unhandled error case: " = "Најавувањето е неуспешно поради непозната грешка:"; +"Change your Password" = "Сменете ја вашата лозинка"; +"The password was changed successfully." = "Лозинката е успешно сменета."; +"Your password has expired, please enter a new one below:" = "Вашата лозинка е истечена. Ве молам да внесете нова:"; +"Password must not be empty." = "Лозинката не може да биде празна."; +"The passwords do not match. Please try again." = "Лозинките не се исти. Обидете се повторно."; +"Password Grace Period" = "Грејс период на лозинката"; +"You have %{0} logins remaining before your account is locked. Please change your password in the preference dialog." = "Ви преостанаа %{0} обиди за најавување пред да се блокира сметката. Ве молиме сменете ја лозинката во дадениот дијалог."; +"Password about to expire" = "Лозинката сао што не истекла"; +"Your password is going to expire in %{0} %{1}." = "Вашата лозинка ќе истеча за %{0} %{1}."; +"days" = "денови"; +"hours" = "часови"; +"minutes" = "минути"; +"seconds" = "секунди"; +"Password change failed" = "Промената на лозинката е неуспешна"; +"Password change failed - Permission denied" = "Промената на лозинката е неуспешна - недозволен пристап"; +"Password change failed - Insufficient password quality" = "Промената на лозинката е неуспешна - недоволен квалитет на лозинката"; +"Password change failed - Password is too short" = "Промената на лозинката е неуспешна - Лозинката е премногу кратка"; +"Password change failed - Password is too young" = "Промената на лозинката е неуспешна - Лозинката е премногу млада"; +"Password change failed - Password is in history" = "Промената на лозинката е неуспешна - Лозинката веќе сте ја користеле"; +"Unhandled policy error: %{0}" = "Непозната грешка на полиса: %{0}"; +"Unhandled error response" = "Непозната грешка"; +"Password change is not supported." = "Промената на лозинката не е подржана."; +"Unhandled HTTP error code: %{0}" = "Непозната HTTP грешка: %{0}"; +"New password:" = "Нова лозинка:"; +"Confirmation:" = "Потврда:"; +"Cancel" = "Откажи"; +"Please wait..." = "Ве молиме почекајте..."; diff --git a/UI/MainUI/NorwegianBokmal.lproj/Localizable.strings b/UI/MainUI/NorwegianBokmal.lproj/Localizable.strings index 9d31d19d4..3e0e00260 100644 --- a/UI/MainUI/NorwegianBokmal.lproj/Localizable.strings +++ b/UI/MainUI/NorwegianBokmal.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/NorwegianNynorsk.lproj/Localizable.strings b/UI/MainUI/NorwegianNynorsk.lproj/Localizable.strings index cc886dc21..37273be38 100644 --- a/UI/MainUI/NorwegianNynorsk.lproj/Localizable.strings +++ b/UI/MainUI/NorwegianNynorsk.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/Polish.lproj/Localizable.strings b/UI/MainUI/Polish.lproj/Localizable.strings index 5d6e922ab..bc647569c 100644 --- a/UI/MainUI/Polish.lproj/Localizable.strings +++ b/UI/MainUI/Polish.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/Portuguese.lproj/Localizable.strings b/UI/MainUI/Portuguese.lproj/Localizable.strings index 299e54bfb..ce0dd02dd 100644 --- a/UI/MainUI/Portuguese.lproj/Localizable.strings +++ b/UI/MainUI/Portuguese.lproj/Localizable.strings @@ -31,6 +31,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Noruega (Norsk bokmål)"; "NorwegianNynorsk" = "Noruega (Norsk nynorsk)"; "Polish" = "Polski"; diff --git a/UI/MainUI/Russian.lproj/Localizable.strings b/UI/MainUI/Russian.lproj/Localizable.strings index b79d80d1f..b19c9ff24 100644 --- a/UI/MainUI/Russian.lproj/Localizable.strings +++ b/UI/MainUI/Russian.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/Slovenian.lproj/Localizable.strings b/UI/MainUI/Slovenian.lproj/Localizable.strings index eca1e952a..fcc3cca00 100644 --- a/UI/MainUI/Slovenian.lproj/Localizable.strings +++ b/UI/MainUI/Slovenian.lproj/Localizable.strings @@ -32,6 +32,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/SpanishArgentina.lproj/Localizable.strings b/UI/MainUI/SpanishArgentina.lproj/Localizable.strings index 6da931eea..55f1889b7 100644 --- a/UI/MainUI/SpanishArgentina.lproj/Localizable.strings +++ b/UI/MainUI/SpanishArgentina.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/SpanishSpain.lproj/Localizable.strings b/UI/MainUI/SpanishSpain.lproj/Localizable.strings index 23a93fc0e..0d8e57ea6 100644 --- a/UI/MainUI/SpanishSpain.lproj/Localizable.strings +++ b/UI/MainUI/SpanishSpain.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/Swedish.lproj/Localizable.strings b/UI/MainUI/Swedish.lproj/Localizable.strings index 252895026..7afd36c37 100644 --- a/UI/MainUI/Swedish.lproj/Localizable.strings +++ b/UI/MainUI/Swedish.lproj/Localizable.strings @@ -32,6 +32,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/MainUI/Ukrainian.lproj/Localizable.strings b/UI/MainUI/Ukrainian.lproj/Localizable.strings index 9fa81afa3..46fe49e76 100644 --- a/UI/MainUI/Ukrainian.lproj/Localizable.strings +++ b/UI/MainUI/Ukrainian.lproj/Localizable.strings @@ -33,6 +33,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk bokmål"; "Polish" = "Polski"; diff --git a/UI/MainUI/Welsh.lproj/Localizable.strings b/UI/MainUI/Welsh.lproj/Localizable.strings index 2c0a70851..74b56a106 100644 --- a/UI/MainUI/Welsh.lproj/Localizable.strings +++ b/UI/MainUI/Welsh.lproj/Localizable.strings @@ -32,6 +32,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/PreferencesUI/Arabic.lproj/Localizable.strings b/UI/PreferencesUI/Arabic.lproj/Localizable.strings index 5515a9f7f..096d7709b 100644 --- a/UI/PreferencesUI/Arabic.lproj/Localizable.strings +++ b/UI/PreferencesUI/Arabic.lproj/Localizable.strings @@ -217,6 +217,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; diff --git a/UI/PreferencesUI/Basque.lproj/Localizable.strings b/UI/PreferencesUI/Basque.lproj/Localizable.strings index 950a4cc72..e56953ca1 100644 --- a/UI/PreferencesUI/Basque.lproj/Localizable.strings +++ b/UI/PreferencesUI/Basque.lproj/Localizable.strings @@ -229,6 +229,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings b/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings index 9520a0b44..252b79d0a 100644 --- a/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings +++ b/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings @@ -229,6 +229,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/Catalan.lproj/Localizable.strings b/UI/PreferencesUI/Catalan.lproj/Localizable.strings index c45e0a20a..5391d4daf 100644 --- a/UI/PreferencesUI/Catalan.lproj/Localizable.strings +++ b/UI/PreferencesUI/Catalan.lproj/Localizable.strings @@ -230,6 +230,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings b/UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings index 189df1f06..acba1bd7d 100644 --- a/UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings +++ b/UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings @@ -227,6 +227,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/Czech.lproj/Localizable.strings b/UI/PreferencesUI/Czech.lproj/Localizable.strings index 1c8a25114..437687cdc 100644 --- a/UI/PreferencesUI/Czech.lproj/Localizable.strings +++ b/UI/PreferencesUI/Czech.lproj/Localizable.strings @@ -229,6 +229,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Portugues brasileiro"; diff --git a/UI/PreferencesUI/Danish.lproj/Localizable.strings b/UI/PreferencesUI/Danish.lproj/Localizable.strings index f30f3066f..3ecb7a20c 100644 --- a/UI/PreferencesUI/Danish.lproj/Localizable.strings +++ b/UI/PreferencesUI/Danish.lproj/Localizable.strings @@ -217,6 +217,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk"; "NorwegianNynorsk" = "Nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/Dutch.lproj/Localizable.strings b/UI/PreferencesUI/Dutch.lproj/Localizable.strings index ad1a04099..048b8d2d2 100644 --- a/UI/PreferencesUI/Dutch.lproj/Localizable.strings +++ b/UI/PreferencesUI/Dutch.lproj/Localizable.strings @@ -229,6 +229,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk Bokmål"; "NorwegianNynorsk" = "Norsk Nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/English.lproj/Localizable.strings b/UI/PreferencesUI/English.lproj/Localizable.strings index 941f6a58d..83c192e60 100644 --- a/UI/PreferencesUI/English.lproj/Localizable.strings +++ b/UI/PreferencesUI/English.lproj/Localizable.strings @@ -229,6 +229,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/Finnish.lproj/Localizable.strings b/UI/PreferencesUI/Finnish.lproj/Localizable.strings index 164e14aa1..84f40b310 100644 --- a/UI/PreferencesUI/Finnish.lproj/Localizable.strings +++ b/UI/PreferencesUI/Finnish.lproj/Localizable.strings @@ -229,6 +229,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/French.lproj/Localizable.strings b/UI/PreferencesUI/French.lproj/Localizable.strings index aefb884da..9096b24d6 100644 --- a/UI/PreferencesUI/French.lproj/Localizable.strings +++ b/UI/PreferencesUI/French.lproj/Localizable.strings @@ -229,6 +229,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/GNUmakefile b/UI/PreferencesUI/GNUmakefile index d771e28cb..8f02f34b4 100644 --- a/UI/PreferencesUI/GNUmakefile +++ b/UI/PreferencesUI/GNUmakefile @@ -6,7 +6,7 @@ BUNDLE_NAME = PreferencesUI PreferencesUI_PRINCIPAL_CLASS = PreferencesUIProduct -PreferencesUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +PreferencesUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian Macedonian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh PreferencesUI_OBJC_FILES = \ PreferencesUIProduct.m \ diff --git a/UI/PreferencesUI/German.lproj/Localizable.strings b/UI/PreferencesUI/German.lproj/Localizable.strings index 0a63608f3..8e16c847f 100644 --- a/UI/PreferencesUI/German.lproj/Localizable.strings +++ b/UI/PreferencesUI/German.lproj/Localizable.strings @@ -229,6 +229,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/Hungarian.lproj/Localizable.strings b/UI/PreferencesUI/Hungarian.lproj/Localizable.strings index 0790e0920..97543698e 100644 --- a/UI/PreferencesUI/Hungarian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Hungarian.lproj/Localizable.strings @@ -229,6 +229,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/Icelandic.lproj/Localizable.strings b/UI/PreferencesUI/Icelandic.lproj/Localizable.strings index 3f3e1e1a6..7fb3f0707 100644 --- a/UI/PreferencesUI/Icelandic.lproj/Localizable.strings +++ b/UI/PreferencesUI/Icelandic.lproj/Localizable.strings @@ -187,6 +187,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/Italian.lproj/Localizable.strings b/UI/PreferencesUI/Italian.lproj/Localizable.strings index 001436e19..bdb669850 100644 --- a/UI/PreferencesUI/Italian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Italian.lproj/Localizable.strings @@ -208,6 +208,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/Macedonian.lproj/Localizable.strings b/UI/PreferencesUI/Macedonian.lproj/Localizable.strings new file mode 100644 index 000000000..30da5173c --- /dev/null +++ b/UI/PreferencesUI/Macedonian.lproj/Localizable.strings @@ -0,0 +1,334 @@ +/* toolbar */ +"Save and Close" = "Сними и затвори"; +"Close" = "Затвори"; + +/* tabs */ +"General" = "Општо"; +"Calendar Options" = "Опции за календарот"; +"Contacts Options" = "Опции за адресната книга"; +"Mail Options" = "Опции за електронската пошта"; +"IMAP Accounts" = "IMAP сметки"; +"Vacation" = "Одмор"; +"Forward" = "Напред"; +"Password" = "Лозинка"; +"Categories" = "Категории"; +"Appointments invitations" = "Покани за состаноци"; +"Name" = "Име"; +"Color" = "Боја"; +"Add" = "Додади"; +"Delete" = "Избриши"; + +/* contacts categories */ +"contacts_category_labels" = "Колега, конкурент, клиент, пријател, фамилија, деловен партнер, провајдер, новинар, ВИП"; + +/* vacation (auto-reply) */ +"Enable vacation auto reply" = "Овозможи автоматски одговор поради одмор"; +"Auto reply message :" = "Порака за автоматски одговор:"; +"Email addresses (separated by commas) :" = "Електронски адреси (раздвоени со запирки):"; +"Add default email addresses" = "Додади ја основната адреса за електронска пошта"; +"Days between responses :" = "Денови помеѓу одговорите:"; +"Do not send responses to mailing lists" = "Не испраќај одговори кон мејлинг листи"; +"Disable auto reply on" = "Исклучи го автоматскиот одговор "; +"Always send vacation message response" = "Секогаш испрати порака кога си на одмор"; +"Please specify your message and your email addresses for which you want to enable auto reply." += "Креирајте ја вашата порака и електронска адреса за која сакате да го овозможите автоматското одговарање."; +"Your vacation message must not end with a single dot on a line." = "Вашата порака кога сте отсатен не смее да заврши со една точка во линијата."; +"End date of your auto reply must be in the future." += "Крајниот датум на вашиот автоматски одговор мора да биде во иднина."; + +/* forward messages */ +"Forward incoming messages" = "Препрати ги пораките кои доаѓаат"; +"Keep a copy" = "Задржи копија"; +"Please specify an address to which you want to forward your messages." += "Определете ја електронската адреса на која сакате да ги препраќате пораките."; +"You are not allowed to forward your messages to an external email address." = "Не ви е дозволено да ја проследите пораката кон надворешна електронска адреса."; +"You are not allowed to forward your messages to an internal email address." = "Не ви е дозволено да ја проследите пораката кон интерна електронска адреса."; + + +/* d & t */ +"Current Time Zone :" = "Тековна временска зона:"; +"Short Date Format :" = "Краток формат за датум:"; +"Long Date Format :" = "Долг формат за даум:"; +"Time Format :" = "Формат за време:"; + +"default" = "Стандардно"; + +"shortDateFmt_0" = "%d-%b-%y"; + +"shortDateFmt_1" = "%d-%b-%y"; +"shortDateFmt_2" = "%d/%m/%y"; +"shortDateFmt_3" = "%e/%m/%y"; + +"shortDateFmt_4" = "%d-%m-%Y"; +"shortDateFmt_5" = "%d/%m/%Y"; + +"shortDateFmt_6" = "%m-%d-%y"; +"shortDateFmt_7" = "%m/%d/%y"; +"shortDateFmt_8" = "%m/%e/%y"; + +"shortDateFmt_9" = "%y-%m-%d"; +"shortDateFmt_10" = "%y/%m/%d"; +"shortDateFmt_11" = "%y.%m.%d"; + +"shortDateFmt_12" = "%Y-%m-%d"; +"shortDateFmt_13" = "%Y/%m/%d"; +"shortDateFmt_14" = "%Y.%m.%d"; + +"shortDateFmt_15" = ""; + +"longDateFmt_0" = "%A, %B %d, %Y"; +"longDateFmt_1" = "%B %d, %Y"; +"longDateFmt_2" = "%A, %d %B, %Y"; +"longDateFmt_3" = "%d %B, %Y"; +"longDateFmt_4" = ""; +"longDateFmt_5" = ""; +"longDateFmt_6" = ""; +"longDateFmt_7" = ""; +"longDateFmt_8" = ""; +"longDateFmt_9" = ""; +"longDateFmt_10" = ""; + +"timeFmt_0" = "%I:%M %p"; +"timeFmt_1" = "%H:%M"; +"timeFmt_2" = ""; +"timeFmt_3" = ""; +"timeFmt_4" = ""; + +/* calendar */ +"Week begins on :" = "Неделата запонува на:"; +"Day start time :" = "Почетен датум:"; +"Day end time :" = "Краен датум:"; +"Day start time must be prior to day end time." = "Почетокот мора да биде пред завршетокот (денови)."; +"Show time as busy outside working hours" = "Прикажи го надвор од работното време како зафатено"; +"First week of year :" = "Прва недела од годината:"; +"Enable reminders for Calendar items" = "Овозможи ги потсетниците за календарот"; +"Play a sound when a reminder comes due" = "Звучно потсети кога ќе дојде потсетникот"; +"Default reminder :" = "Стандарден потсетник:"; + +"firstWeekOfYear_January1" = "Започнува на 1ви Јануари"; +"firstWeekOfYear_First4DayWeek" = "Првата 4-дневна недела"; +"firstWeekOfYear_FirstFullWeek" = "Првата цела недела"; + +"Prevent from being invited to appointments" = "Избегни да бидеш повикан на состаноци"; +"White list for appointment invitations:" = "Бела листа на покани за состанок:"; +"Contacts Names" = "Имиња на контактите"; + +/* Default Calendar */ +"Default calendar :" = "Стандарден календар:"; +"selectedCalendar" = "Одбери го календарот"; +"personalCalendar" = "Личен календар"; +"firstCalendar" = "Првиот активен календар"; + +"reminder_NONE" = "Без потсетник"; +"reminder_5_MINUTES_BEFORE" = "5 минути пред"; +"reminder_10_MINUTES_BEFORE" = "10 минути пред"; +"reminder_15_MINUTES_BEFORE" = "15 минути пред"; +"reminder_30_MINUTES_BEFORE" = "30 минути пред"; +"reminder_45_MINUTES_BEFORE" = "45 минути пред"; +"reminder_1_HOUR_BEFORE" = "1 час пред"; +"reminder_2_HOURS_BEFORE" = "2 часа пред"; +"reminder_5_HOURS_BEFORE" = "5 часа пред"; +"reminder_15_HOURS_BEFORE" = "15 часа пред"; +"reminder_1_DAY_BEFORE" = "1 ден пред"; +"reminder_2_DAYS_BEFORE" = "2 дена пред"; +"reminder_1_WEEK_BEFORE" = "1 недела пред"; + +/* Mailer */ +"Labels" = "Лабели"; +"Label" = "Лабела"; +"Show subscribed mailboxes only" = "Прикажи ги само претплатените поштенски сандачиња"; +"Sort messages by threads" = "Сортирај ги пораките според конверзацијата"; +"When sending mail, add unknown recipients to my" = "Кога испраќаш порака, додади ги непознатите приматели во мојата"; + +"Forward messages:" = "Препрати ги пораките:"; +"messageforward_inline" = "Во текстот"; +"messageforward_attached" = "Како прилог"; + +"When replying to a message:" = "Кога одговарам на порака:"; +"replyplacement_above" = "Започни гоодговорот над цитатот"; +"replyplacement_below" = "Започни го одговорот под цитатот"; +"And place my signature" = "И стави го мојот потпис"; +"signatureplacement_above" = "под мојот одговор"; +"signatureplacement_below" = "под цитатот"; +"Compose messages in" = "Уреди ја пораката во"; +"composemessagestype_html" = "HTML"; +"composemessagestype_text" = "обичен текст"; +"Display remote inline images" = "Прикажи ги фотографиите кои треба да се преземат"; +"displayremoteinlineimages_never" = "Никогаш"; +"displayremoteinlineimages_always" = "Секогаш"; + +"Auto save every" = "Автоматски сними секој(и)"; +"minutes" = "минути"; + +/* Contact */ +"Personal Address Book" = "Лична адресна книга"; +"Collected Address Book" = "Собрана адресна книга"; + +/* IMAP Accounts */ +"New Mail Account" = "Нова сметка за електронска пошта"; + +"Server Name:" = "Име на серверот:"; +"Port:" = "Порт:"; +"Encryption:" = "Шифрирање:"; +"None" = "Ниедна"; +"User Name:" = "Корисничко име:"; +"Password:" = "Лозинка:"; + +"Full Name:" = "Целосно име:"; +"Email:" = "Електронска пошта:"; +"Reply To Email:" = "Одговори на поракта:"; +"Signature:" = "Потпис:"; +"(Click to create)" = "(Кликни да се креира)"; + +"Signature" = "Потпис"; +"Please enter your signature below:" = "Внесете го долу вашиот потпис:"; + +"Please specify a valid sender address." = "Обезбедете валидна адреса на испраќачот."; +"Please specify a valid reply-to address." = "Обезбедете валидна електронска адреса за “одговори на“."; + +/* Additional Parameters */ +"Additional Parameters" = "Дополнителни параметри"; + +/* password */ +"New password:" = "Нова лозинка:"; +"Confirmation:" = "Потврда:"; +"Change" = "Промена"; + +/* Event+task classifications */ +"Default events classification :" = "Стандардна класификација на настани:"; +"Default tasks classification :" = "Стандардна класификација на задачи:"; +"PUBLIC_item" = "Јавно"; +"CONFIDENTIAL_item" = "Доверливо"; +"PRIVATE_item" = "Приватно"; + +/* Event+task categories */ +"category_none" = "Ниту еден"; +"calendar_category_labels" = "Годишница,Роденден,Деловно,Повици,Конкуренција,Корисник,Фаворити,Да се следи,Подарок,Празници,Идеи,Состаноци,Проблеми,Разно,Лични,Проекти,Јавни празници,Статус,Добавувачи,Патување,Одмор"; + +/* Default module */ +"Calendar" = "Календар"; +"Contacts" = "Адресна книга"; +"Mail" = "Електронска пошта"; +"Last" = "Последно користено"; +"Default Module :" = "Стандарден модул:"; +"SOGo Version :" = "Верзија на SOGo:"; + +"Language :" = "Јазик:"; +"choose" = "Одбери ..."; +"Arabic" = "العربية"; +"Basque" = "Euskara"; +"Catalan" = "Català"; +"ChineseTaiwan" = "Chinese (Taiwan)"; +"Czech" = "Česky"; +"Danish" = "Dansk (Danmark)"; +"Dutch" = "Nederlands"; +"English" = "English"; +"Finnish" = "Suomi"; +"French" = "Français"; +"German" = "Deutsch"; +"Hungarian" = "Magyar"; +"Icelandic" = "Íslenska"; +"Italian" = "Italiano"; +"Macedonian" = "Македонски"; +"NorwegianBokmal" = "Norsk bokmål"; +"NorwegianNynorsk" = "Norsk nynorsk"; +"BrazilianPortuguese" = "Português brasileiro"; +"Polish" = "Polski"; +"Portuguese" = "Português"; +"Russian" = "Русский"; +"Slovak" = "Slovensky"; +"Slovenian" = "Slovenščina"; +"SpanishSpain" = "Español (España)"; +"SpanishArgentina" = "Español (Argentina)"; +"Swedish" = "Svenska"; +"Ukrainian" = "Українська"; +"Welsh" = "Cymraeg"; + +"Refresh View :" = "Освежи го погледот:"; +"refreshview_manually" = "Рачно"; +"refreshview_every_minute" = "Секоја минута"; +"refreshview_every_2_minutes" = "Секои 2 минути"; +"refreshview_every_5_minutes" = "Секои 5 минути"; +"refreshview_every_10_minutes" = "Секои 10 минути"; +"refreshview_every_20_minutes" = "Секои 20 минути"; +"refreshview_every_30_minutes" = "Секои 30 минути"; +"refreshview_once_per_hour" = "На секој час"; + +/* Return receipts */ +"When I receive a request for a return receipt:" = "Кога ќе добијам потврда за прием:"; +"Never send a return receipt" = "Никогаш не испраќај потврда за прием"; +"Allow return receipts for some messages" = "Дозволи потврда за прием за некои пораки"; +"If I'm not in the To or Cc of the message:" = "Ако не сум во До или Копија на пораката:"; +"If the sender is outside my domain:" = "Ако испраќачот е надвор од мојот домејн:"; +"In all other cases:" = "Во сите други случаи:"; + +"Never send" = "Никогаш не испраќај"; +"Always send" = "Секогаш испрати"; +"Ask me" = "Прашај ме"; + +/* Filters - UIxPreferences */ +"Filters" = "Филтри"; +"Active" = "Активен"; +"Move Up" = "Качи го горе"; +"Move Down" = "Сини го долу"; +"Connection error" = "Грешка при поврзување"; +"Service temporarily unavailable" = "Услугата е привремено недостапна"; + +/* Filters - UIxFilterEditor */ +"Filter name:" = "Име на филтерот:"; +"For incoming messages that" = "За пораките кои пристигаат кои"; +"match all of the following rules:" = "одговараа на сите следни правила:"; +"match any of the following rules:" = "одговараат на некои од следните правила:"; +"match all messages" = "одговараат сите пораки"; +"Perform these actions:" = "Изврши ги следните активности:"; +"Untitled Filter" = "Неименуван филтер"; + +"Subject" = "Тема"; +"From" = "Од"; +"To" = "До"; +"Cc" = "Копија"; +"To or Cc" = "До или копија"; +"Size (Kb)" = "Големина (Kb)"; +"Header" = "Заглавје"; +"Body" = "Тело"; +"Flag the message with:" = "Означи ја пораката со:"; +"Discard the message" = "Отфрли ја поракта"; +"File the message in:" = "Смести ја пораката во:"; +"Keep the message" = "Зачувај ја пораката"; +"Forward the message to:" = "Препратија пораката до:"; +"Send a reject message:" = "Испрати порака за отфрлање:"; +"Send a vacation message" = "Испрати автоматска порака кога си на одмор"; +"Stop processing filter rules" = "Запри го процесирањето на правилата за филтрирање"; + +"is under" = "е под"; +"is over" = "е над"; +"is" = "е"; +"is not" = "не е"; +"contains" = "содржи"; +"does not contain" = "не содржи"; +"matches" = "се совпаѓа"; +"does not match" = "не се совпаѓа"; +"matches regex" = "Се совпаѓа со регуларни изрази"; +"does not match regex" = "не се совпаѓа со регуларните изрази"; + +"Seen" = "Видена"; +"Deleted" = "Избришана"; +"Answered" = "Одговорена"; +"Flagged" = "Означена"; +"Junk" = "Ѓубре"; +"Not Junk" = "Не е ѓубре"; + +/* Password policy */ +"The password was changed successfully." = "Лозинката е успешно сменета."; +"Password must not be empty." = "Лозинката не може да е празна."; +"The passwords do not match. Please try again." = "Лозинките не се совпаѓаат. Обидете се повторно."; +"Password change failed" = "Промената на лозинката не е успешна"; +"Password change failed - Permission denied" = "Промената на лозинката не е успешна - недозволен пристап"; +"Password change failed - Insufficient password quality" = "Промената на лозинката е неуспешна - недоволен квалитет на лозинката"; +"Password change failed - Password is too short" = "Промената на лозинката е неуспешна - Лозинката е премногу кратка"; +"Password change failed - Password is too young" = "Промената на лозинката е неуспешна - Лозинката е премногу млада"; +"Password change failed - Password is in history" = "Промената на лозинката е неуспешна - Лозинката веќе сте ја користеле"; +"Unhandled policy error: %{0}" = "Непозната грешка на полиса: %{0}"; +"Unhandled error response" = "Непозната грешка"; +"Password change is not supported." = "Промената на лозинката не е подржана."; +"Unhandled HTTP error code: %{0}" = "Непозната HTTP грешка: %{0}"; diff --git a/UI/PreferencesUI/NorwegianBokmal.lproj/Localizable.strings b/UI/PreferencesUI/NorwegianBokmal.lproj/Localizable.strings index 6cec64783..5ab088b28 100644 --- a/UI/PreferencesUI/NorwegianBokmal.lproj/Localizable.strings +++ b/UI/PreferencesUI/NorwegianBokmal.lproj/Localizable.strings @@ -229,6 +229,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings b/UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings index 1e9d27842..4ae8ba8b6 100644 --- a/UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings +++ b/UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings @@ -194,6 +194,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/Polish.lproj/Localizable.strings b/UI/PreferencesUI/Polish.lproj/Localizable.strings index 4e1853eba..0399bdaae 100644 --- a/UI/PreferencesUI/Polish.lproj/Localizable.strings +++ b/UI/PreferencesUI/Polish.lproj/Localizable.strings @@ -229,6 +229,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/Portuguese.lproj/Localizable.strings b/UI/PreferencesUI/Portuguese.lproj/Localizable.strings index 78147ba4a..29c7d774a 100644 --- a/UI/PreferencesUI/Portuguese.lproj/Localizable.strings +++ b/UI/PreferencesUI/Portuguese.lproj/Localizable.strings @@ -223,6 +223,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/Russian.lproj/Localizable.strings b/UI/PreferencesUI/Russian.lproj/Localizable.strings index 2b7a18699..d9234f38d 100644 --- a/UI/PreferencesUI/Russian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Russian.lproj/Localizable.strings @@ -229,6 +229,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/Slovak.lproj/Localizable.strings b/UI/PreferencesUI/Slovak.lproj/Localizable.strings index a29d3a019..68c9d23a9 100644 --- a/UI/PreferencesUI/Slovak.lproj/Localizable.strings +++ b/UI/PreferencesUI/Slovak.lproj/Localizable.strings @@ -226,6 +226,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/Slovenian.lproj/Localizable.strings b/UI/PreferencesUI/Slovenian.lproj/Localizable.strings index 49168c92f..2eab9cb17 100644 --- a/UI/PreferencesUI/Slovenian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Slovenian.lproj/Localizable.strings @@ -228,6 +228,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings b/UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings index cea60d883..346329d3b 100644 --- a/UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings +++ b/UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings @@ -222,6 +222,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings b/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings index c77db22c5..6710cdb4f 100644 --- a/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings +++ b/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings @@ -229,6 +229,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/Swedish.lproj/Localizable.strings b/UI/PreferencesUI/Swedish.lproj/Localizable.strings index 7e01cb6f9..675e30f48 100644 --- a/UI/PreferencesUI/Swedish.lproj/Localizable.strings +++ b/UI/PreferencesUI/Swedish.lproj/Localizable.strings @@ -196,6 +196,7 @@ Servernamn:"; "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings b/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings index 18bfe70d7..80682c6fb 100644 --- a/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings @@ -206,6 +206,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/PreferencesUI/Welsh.lproj/Localizable.strings b/UI/PreferencesUI/Welsh.lproj/Localizable.strings index 3d4460490..525c1c80b 100644 --- a/UI/PreferencesUI/Welsh.lproj/Localizable.strings +++ b/UI/PreferencesUI/Welsh.lproj/Localizable.strings @@ -194,6 +194,7 @@ "Hungarian" = "Magyar"; "Icelandic" = "Íslenska"; "Italian" = "Italiano"; +"Macedonian" = "Македонски"; "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; diff --git a/UI/Scheduler/GNUmakefile b/UI/Scheduler/GNUmakefile index f1f824d17..28f77bf68 100644 --- a/UI/Scheduler/GNUmakefile +++ b/UI/Scheduler/GNUmakefile @@ -6,7 +6,7 @@ BUNDLE_NAME = SchedulerUI SchedulerUI_PRINCIPAL_CLASS = SchedulerUIProduct -SchedulerUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh +SchedulerUI_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian Macedonian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh SchedulerUI_OBJC_FILES = \ SchedulerUIProduct.m \ diff --git a/UI/Scheduler/Macedonian.lproj/Localizable.strings b/UI/Scheduler/Macedonian.lproj/Localizable.strings new file mode 100644 index 000000000..7fd72c6c1 --- /dev/null +++ b/UI/Scheduler/Macedonian.lproj/Localizable.strings @@ -0,0 +1,565 @@ +/* this file is in UTF-8 format! */ + +/* Tooltips */ + +"Create a new event" = "Креирај нов настан"; +"Create a new task" = "Креирај нова задача"; +"Edit this event or task" = "Уреди го овој настан или задача"; +"Print the current calendar view" = "Печати го тековниот поглед на календарот"; +"Delete this event or task" = "Избриши го овој настан или задача"; +"Go to today" = "Оди на денес"; +"Switch to day view" = "Префрли на дневен преглед"; +"Switch to week view" = "Префрли на неделен преглед"; +"Switch to month view" = "Префрли на месечен преглед"; +"Reload all calendars" = "Повторно вчитај ги сите календари"; + +/* Tabs */ +"Date" = "Датум"; +"Calendars" = "Календари"; + +/* Day */ + +"DayOfTheMonth" = "Ден од месецот"; +"dayLabelFormat" = "%m/%d/%Y"; +"today" = "Денес"; + +"Previous Day" = "Претходниот ден"; +"Next Day" = "Следниот ден"; + +/* Week */ + +"Week" = "Недела"; +"this week" = "оваа недела"; + +"Week %d" = "Недела %d"; + +"Previous Week" = "Претходната недела"; +"Next Week" = "Следната недела"; + +/* Month */ + +"this month" = "овој месец"; + +"Previous Month" = "Претходниот месец"; +"Next Month" = "Следниот месец"; + +/* Year */ + +"this year" = "оваа година"; + +/* Menu */ + +"Calendar" = "Календар"; +"Contacts" = "Контакти"; + +"New Calendar..." = "Нов календар..."; +"Delete Calendar" = "Избриши календар..."; +"Unsubscribe Calendar" = "Отпиши се од Календарот"; +"Sharing..." = "Споделување..."; +"Export Calendar..." = "Извези го календарот..."; +"Import Events..." = "Увези ги настаните..."; +"Import Events" = "Увези ги настаните"; +"Select an iCalendar file (.ics)." = "Одбери iCalendar датотека (.ics)."; +"Upload" = "Преземи"; +"Uploading" = "Префрлам"; +"Publish Calendar..." = "Објави го календарот..."; +"Reload Remote Calendars" = "Освежи го далечинските календари"; +"Properties" = "Особини..."; +"Done" = "Завршено"; +"An error occurred while importing calendar." = "Настана грешка при увозот на календарот."; +"No event was imported." = "Нитуе еден настан не е увезен."; +"A total of %{0} events were imported in the calendar." = "Вкупно %{0} од настаните се увезени во календарот."; + +"Compose E-Mail to All Attendees" = "Креирај порака до сите учесници"; +"Compose E-Mail to Undecided Attendees" = "Креирај порака за сите неизјаснети учесници"; + +/* Folders */ +"Personal calendar" = "Личен календар"; + +/* Misc */ + +"OpenGroupware.org" = "OpenGroupware.org"; +"Forbidden" = "Забрането"; + +/* acls */ + +"Access rights to" = "Пристапни права за"; +"For user" = "За корисник"; + +"Any Authenticated User" = "Било кој автентициран корисник"; +"Public Access" = "Јавен пристап"; + +"label_Public" = "Јавно"; +"label_Private" = "Приватно"; +"label_Confidential" = "Доверливо"; + +"label_Viewer" = "Види ги сите"; +"label_DAndTViewer" = "Види го датумот & времето"; +"label_Modifier" = "Измени"; +"label_Responder" = "Одговори на"; +"label_None" = "Ниту едно"; + +"View All" = "Види ги сите"; +"View the Date & Time" = "Види го датумот & времето"; +"Modify" = "Измени"; +"Respond To" = "Одговори на"; +"None" = "Ниту еден"; + +"This person can create objects in my calendar." += "Овој корисник може да креира објекти во мојот календар."; +"This person can erase objects from my calendar." += "Овој корисник може да брише објекти во мојот календар."; + +/* Button Titles */ + +"Subscribe to a Calendar..." = "Претплати се на календарот..."; +"Remove the selected Calendar" = "Отстрани го одбраниот календар"; + +"Name of the Calendar" = "Име на календарот"; + +"new" = "Нов"; +"Print view" = "Преглед пред печатење"; +"edit" = "Уреди"; +"delete" = "Избриши"; +"proposal" = "Предлог"; +"Save and Close" = "Сними и затвори"; +"Close" = "Затвори"; +"Invite Attendees" = "Покани учесници"; +"Attach" = "Приложи"; +"Update" = "Освежи"; +"Cancel" = "Откажи"; +"show_rejected_apts" = "Прикажи ги одбиените состаноци"; +"hide_rejected_apts" = "Сокриј ги одбиените состаноци"; + + +/* Schedule */ + +"Schedule" = "Закажи"; +"No appointments found" = "Не се пронајдени состаноци"; +"Meetings proposed by you" = "Средбата е предложена од вас"; +"Meetings proposed to you" = "Средбата ви е предложена"; +"sched_startDateFormat" = "%d/%m %H:%M"; +"action" = "Акција"; +"accept" = "Прифати"; +"decline" = "Одбиј"; +"more attendees" = "Повеќе учесници"; +"Hide already accepted and rejected appointments" = "Сокриј ги веќе прифатените и одбиените состаноци"; +"Show already accepted and rejected appointments" = "Прикажи ги веќе прифатените и одбиени состаноци"; + +/* Print view */ + +"LIST" = "Листа"; +"Print Settings" = "Подесување на печатењето"; +"Title:" = "Наслов:"; +"Layout:" = "Изглед:"; +"What to Print" = "Што да печатам"; +"Options" = "Опции"; +"Tasks with no due date" = "Задача без краен рок"; +"Display working hours only" = "Прикажи го само работното време"; +"Completed tasks" = "Завршени задачи"; +"Display events and tasks colors" = "Прикажи ги настаните и боите на задачите"; +"Borders" = "Ивици"; +"Backgrounds" = "Позадини"; + +/* Appointments */ + +"Appointment viewer" = "Преглед на состаноци"; +"Appointment editor" = "Уредувач на состаноци"; +"Appointment proposal" = "Предлог состанок"; +"Appointment on" = "Состанок на"; +"Start:" = "Почеток:"; +"End:" = "Крај:"; +"Due Date:" = "До датум:"; +"Title:" = "Наслов:"; +"Calendar:" = "Календар:"; +"Name" = "Име"; +"Email" = "Електронска адреса"; +"Status:" = "Статус:"; +"% complete" = "% complete"; +"Location:" = "Локација:"; +"Priority:" = "Приоритет:"; +"Privacy" = "Приватност"; +"Cycle" = "Циклус"; +"Cycle End" = "Циклусот завршува"; +"Categories" = "Категории"; +"Classification" = "Класификација"; +"Duration" = "Траење"; +"Attendees:" = "Учесници:"; +"Resources" = "Ресурси"; +"Organizer:" = "Организатор:"; +"Description:" = "Опис:"; +"Document:" = "Документ:"; +"Category:" = "Категорија:"; +"Repeat:" = "Повторување:"; +"Reminder:" = "Потсетник:"; +"General:" = "Општо:"; +"Reply:" = "Одговори:"; +"Created by:" = "Креирано од:"; + + +"Target:" = "Цел:"; + +"attributes" = "атрибути"; +"attendees" = "учесници"; +"delegated from" = "делегирано од"; + +/* checkbox title */ +"is private" = "е приватно"; +/* classification */ +"Public" = "Јавно"; +"Private" = "Приватно"; +/* text used in overviews and tooltips */ +"empty title" = "Празен наслов"; +"private appointment" = "Приватен состанок"; + +"Change..." = "Измени..."; + +/* Appointments (participation state) */ + +"partStat_NEEDS-ACTION" = "Ќе потврдам подоцна"; +"partStat_ACCEPTED" = "Ќе присуствувам"; +"partStat_DECLINED" = "Нема да присуствувам"; +"partStat_TENTATIVE" = "Можеби ќе присуствувам"; +"partStat_DELEGATED" = "Ќе делегирам"; +"partStat_OTHER" = "Останато"; + +/* Appointments (error messages) */ + +"Conflicts found!" = "Пронајдени се конфликти!"; +"Invalid iCal data!" = "Невалидни iCal податоци!"; +"Could not create iCal data!" = "Не можам да креирам iCal податоци!"; + +/* Searching */ + +"view_all" = "Сите"; +"view_today" = "Денес"; +"view_next7" = "Следните 7 дена"; +"view_next14" = "Следните 14 дена"; +"view_next31" = "Следните 31 дена"; +"view_thismonth" = "Овој месец"; +"view_future" = "Сите идни настани"; +"view_selectedday" = "Одбраниот ден"; + +"view_not_started" = "Незапочнати задачи"; +"view_overdue" = "Пречекорени задачи"; +"view_incomplete" = "Некомплетирани задачи"; + +"View:" = "Поглед:"; +"Title, category or location" = "Наслов, категорија или локација"; +"Entire content" = "Целата содржина"; + +"Search" = "Барај"; +"Search attendees" = "Барај присутни"; +"Search resources" = "Барак ресурси"; +"Search appointments" = "Барај состаноци"; + +"All day Event" = "Целодневен настан"; +"check for conflicts" = "Провери дали има конфликти"; + +"Browse URL" = "Види го URL"; + +"newAttendee" = "Додади учесник"; + +/* calendar modes */ + +"Overview" = "Преглед"; +"Chart" = "Графикон"; +"List" = "Листа"; +"Columns" = "Колони"; + +/* Priorities */ + +"prio_0" = "Не е специфицирано"; +"prio_1" = "Високо"; +"prio_2" = "Високо"; +"prio_3" = "Високо"; +"prio_4" = "Високо"; +"prio_5" = "Нормално"; +"prio_6" = "Ниско"; +"prio_7" = "Ниско"; +"prio_8" = "Ниско"; +"prio_9" = "Ниско"; + +/* access classes (privacy) */ +"PUBLIC_vevent" = "Јавен настан"; +"CONFIDENTIAL_vevent" = "Доверлив настан"; +"PRIVATE_vevent" = "Привате настан"; +"PUBLIC_vtodo" = "Јавна задача"; +"CONFIDENTIAL_vtodo" = "Доверлива задача"; +"PRIVATE_vtodo" = "Приватна задача"; + +/* status type */ +"status_" = "Не е специфицирано"; +"status_NOT-SPECIFIED" = "Не е специфицирано"; +"status_TENTATIVE" = "Можеби"; +"status_CONFIRMED" = "Потврдено"; +"status_CANCELLED" = "Откажано"; +"status_NEEDS-ACTION" = "Треба активност"; +"status_IN-PROCESS" = "Во тек"; +"status_COMPLETED" = "Завршено на "; + +/* Cycles */ + +"cycle_once" = "cycle_once"; +"cycle_daily" = "cycle_daily"; +"cycle_weekly" = "cycle_weekly"; +"cycle_2weeks" = "cycle_2weeks"; +"cycle_4weeks" = "cycle_4weeks"; +"cycle_monthly" = "cycle_monthly"; +"cycle_weekday" = "cycle_weekday"; +"cycle_yearly" = "cycle_yearly"; + +"cycle_end_never" = "cycle_end_never"; +"cycle_end_until" = "cycle_end_until"; + +"Recurrence pattern" = "Шема која се повторува"; +"Range of recurrence" = "Опсег на повторување"; + +"Repeat" = "Повтори"; +"Daily" = "Дневно"; +"Weekly" = "Неделно"; +"Monthly" = "Месечно"; +"Yearly" = "Годишно"; +"Every" = "Секоја"; +"Days" = "Денови"; +"Week(s)" = "Недели"; +"On" = "На"; +"Month(s)" = "Месеци"; +/* [Event recurrence editor] Ex: _The_ first Sunday */ +"The" = "The"; +"Recur on day(s)" = "Се повторува на ден(ови)"; +"Year(s)" = "Година(ни)"; +/* [Event recurrence editor] Ex: Every first Sunday _of_ April */ +"cycle_of" = "од"; +"No end date" = "Нема краен датум"; +"Create" = "Креирај"; +"appointment(s)" = "закажување(а)"; +"Repeat until" = "Повтори до"; + +"First" = "Прв"; +"Second" = "Втор"; +"Third" = "Трет"; +"Fourth" = "Четврт"; +"Fift" = "Пет"; +"Last" = "Последен"; + +/* Appointment categories */ + +"category_none" = "Ниту еден"; +"category_labels" = "Годишница,Роденден,Деловно,Повици,Конкуренција,Корисник,Фаворити,Да се следи,Подарок,Празници,Идеи,Состаноци,Проблеми,Разно,Лични,Проекти,Јавни празници,Статус,Добавувачи,Патување,Одмор"; + +"repeat_NEVER" = "Не повторувај"; +"repeat_DAILY" = "Дневно"; +"repeat_WEEKLY" = "Неделно"; +"repeat_BI-WEEKLY" = "Дво-неделно"; +"repeat_EVERY WEEKDAY" = "Секоја сабота и недела"; +"repeat_MONTHLY" = "Месечни"; +"repeat_YEARLY" = "Годишно"; +"repeat_CUSTOM" = "Специфично..."; + +"reminder_NONE" = "Без потсетник"; +"reminder_5_MINUTES_BEFORE" = "5 минути пред"; +"reminder_10_MINUTES_BEFORE" = "10 минути пред"; +"reminder_15_MINUTES_BEFORE" = "15 минути пред"; +"reminder_30_MINUTES_BEFORE" = "30 минути пред"; +"reminder_45_MINUTES_BEFORE" = "45 минути пред"; +"reminder_1_HOUR_BEFORE" = "1 час пред"; +"reminder_2_HOURS_BEFORE" = "2 часа пред"; +"reminder_5_HOURS_BEFORE" = "5 часа пред"; +"reminder_15_HOURS_BEFORE" = "15 часа пред"; +"reminder_1_DAY_BEFORE" = "1 ден пред"; +"reminder_2_DAYS_BEFORE" = "2 дена пред"; +"reminder_1_WEEK_BEFORE" = "1 недела пред"; +"reminder_CUSTOM" = "Специфично..."; + +"reminder_MINUTES" = "минути"; +"reminder_HOURS" = "часови"; +"reminder_DAYS" = "денови"; +"reminder_BEFORE" = "пред"; +"reminder_AFTER" = "по"; +"reminder_START" = "настанот понува"; +"reminder_END" = "настанот завршува"; +"Reminder Details" = "Детали за потсетникот"; + +"Choose a Reminder Action" = "Одбери активност на потсетување"; +"Show an Alert" = "Прикажи аларм"; +"Send an E-mail" = "Испрати електронска порака"; +"Email Organizer" = "Организатор на пораки"; +"Email Attendees" = "Учесници во пораката"; + +"zoom_400" = "400%"; +"zoom_200" = "200%"; +"zoom_100" = "100%"; +"zoom_50" = "50%"; +"zoom_25" = "25%"; + +/* transparency */ + +"Show Time as Free" = "Прикажи го времето како слободно"; + +/* email notifications */ +"Send Appointment Notifications" = "Испрати известување за состанок"; + +/* validation errors */ + +validate_notitle = "Нема наслов, да продолжам?"; +validate_invalid_startdate = "Некоректно поле за почетен датум!"; +validate_invalid_enddate = "Некоректно поле за краен датум!"; +validate_endbeforestart = "Внесениот краен датум е пред почетниот датум."; + +"Events" = "Настани"; +"Tasks" = "Задачи"; +"Show completed tasks" = "Прикажи ги завршените задачи"; + +/* tabs */ +"Task" = "Задачи"; +"Event" = "Настани"; +"Recurrence" = "Повторувања"; + +/* toolbar */ +"New Event" = "Нов настан"; +"New Task" = "Нова задача"; +"Edit" = "Уреди"; +"Delete" = "Избриши"; +"Go to Today" = "Оди на денес"; +"Day View" = "Дневен поглед"; +"Week View" = "Неделен поглед"; +"Month View" = "Месечен поглед"; +"Reload" = "Обнови"; + +"eventPartStatModificationError" = "Вашиот статус за учество не може да се измени."; + +/* menu */ +"New Event..." = "Нов настан..."; +"New Task..." = "Нова задача..."; +"Edit Selected Event..." = "Уреди го одбраниот настан..."; +"Delete Selected Event" = "Избриши го означениот настан"; +"Select All" = "Одбери се"; +"Workweek days only" = "Само работни денови"; +"Tasks in View" = "Поглед на задачи"; + +"eventDeleteConfirmation" = "Следниот настан(и) ќе биде избришан:"; +"taskDeleteConfirmation" = "Следниот настан(и) ќе биде избришан:"; +"Would you like to continue?" = "Да продолжам?"; + +"You cannot remove nor unsubscribe from your personal calendar." += "Не можете да се изземете или отпишете од вашиот личен календар."; +"Are you sure you want to delete the calendar \"%{0}\"?" += "Дали сте сигурни дека сакате да го избришете \"%{0}\" календар?"; + +/* Legend */ +"Participant" = "Учесници"; +"Optional Participant" = "Незадолжителни учесници"; +"Non Participant" = "Не е учесник"; +"Chair" = "Претседавач"; + +"Needs action" = "Треба активност"; +"Accepted" = "Прифатено"; +"Declined" = "Одбиено"; +"Tentative" = "Неодредено"; + +"Free" = "Слободно"; +"Busy" = "Зафатено"; +"Maybe busy" = "Можеби зафатено"; +"No free-busy information" = "Нема информација за слободно-зафатено време"; + +/* FreeBusy panel buttons and labels */ +"Suggest time slot:" = "Сугерирај временски слот:"; +"Zoom:" = "Зум:"; +"Previous slot" = "Претходниот слот"; +"Next slot" = "Следниот слот"; +"Previous hour" = "Претходниот час"; +"Next hour" = "Следниот час"; +"Work days only" = "Само работни денови"; +"The whole day" = "Целиот ден"; +"Between" = "Помеѓу"; +"and" = "и"; + +"A time conflict exists with one or more attendees.\nWould you like to keep the current settings anyway?" += "Постои конфликт со временските слотови со еден или повеќе учесници.\nДали сакате и покрај тоа да ги сочувате тековните поставки?"; + +/* apt list */ +"Title" = "Наслов"; +"Start" = "Почеток"; +"End" = "Крај"; +"Due Date" = "Краен датум"; +"Location" = "Локација"; + +"(Private Event)" = "(Приватен настан)"; + +vevent_class0 = "(Јавен настан)"; +vevent_class1 = "(Приватен настан)"; +vevent_class2 = "(Доверлив настан)"; + +"Priority" = "Приоритет"; +"Category" = "Категорија"; + +vtodo_class0 = "(Јавна задача)"; +vtodo_class1 = "(Приватна задача)"; +vtodo_class2 = "(Доверлива задача)"; + +"closeThisWindowMessage" = "Благодарам! Сега можете да го затворите прозорецот или да го погледате вашиот"; +"Multicolumn Day View" = "Дневен поглед во повеќе колони"; + +"Please select an event or a task." = "Одберете настан или задача."; + +"editRepeatingItem" = "Предметот кој го уредувате е повторлив настан. дали сакате да ги уредите сите појавувања или оваа една единствена инстанца?"; +"button_thisOccurrenceOnly" = "Само ова појавување"; +"button_allOccurrences" = "Сите појавувања"; + +/* Properties dialog */ +"Name:" = "Име:"; +"Color:" = "Боја:"; + +"Include in free-busy" = "Вклучи во слободно-зафатено"; + +"Synchronization" = "Синхронизација"; +"Synchronize" = "Синхронизирај"; +"Tag:" = "Таг:"; + +"Display" = "Приказ"; +"Show alarms" = "Прикажи ги алармите"; +"Show tasks" = "Прикажи ги задачите"; + +"Notifications" = "Известувања"; +"Receive a mail when I modify my calendar" = "Прими порака кога јас го модификувам мојот календар"; +"Receive a mail when someone else modifies my calendar" = "Прими порака кога некој друг ќе го мидификува мојот календар"; +"When I modify my calendar, send a mail to:" = "Кога ќе го модификувам мојот календар, испрати порака на:"; + +"Links to this Calendar" = "Линкво кон овој календар"; +"Authenticated User Access" = "Авторизиран кориснички пристап"; +"CalDAV URL" = "CalDAV URL:"; +"WebDAV ICS URL" = "WebDAV ICS URL"; +"WebDAV XML URL" = "WebDAV XML URL"; + +/* Error messages */ +"dayFieldInvalid" = "Внесете нумеричка вредност во полето за денови која е поголема или еднаква на 1."; +"weekFieldInvalid" = "Внесете нумеричка вредност во полето за недели која е поголема или еднаква на 1."; +"monthFieldInvalid" = "Внесете нумеричка вредност во полето за месеци која е поголема или еднаква на 1."; +"monthDayFieldInvalid" = "Обезбеди нумеричка вредност во месец ден поле кое е поголемо или еднакво на 1."; +"yearFieldInvalid" = "Обезбедете конкретна нумеричка вредност во полето на години или поголем од 1."; +"appointmentFieldInvalid" = "Внесете нумеричка вредност во полето за закажувања која е поголема или еднаква на 1."; +"recurrenceUnsupported" = "Овој тип на повторувања моментално не е поддржана."; +"Please specify a calendar name." = "Определете име на календарот."; +"tagNotDefined" = "Морате да специфицирате таг ако сакате да го синхранизира овој календар."; +"tagAlreadyExists" = "Тагот кој го зинесовте е веќе асоциран со друг календар."; +"tagHasChanged" = "Ако го проените тагот на вашиот календар, ќе треба да ги превчитате податоците на вашиот мобилен уред.\nДа продолжам?"; +"tagWasAdded" = "Ако сакате да г синхронизирате овој календар, ќе треба да ги превчитате податоците на вашиот мобилен уред.\nДа продолжам?"; +"tagWasRemoved" = "Ако го отстраните овој календар од синхронизација ќе треба да ги превчитате податоците на вашиот мобилен уред.\nДа продолжам?"; +"DestinationCalendarError" = "Изворниот и целниот календар се исти. Обидете се да копирате во друг календар."; +"EventCopyError" = "Кориањето е неуспешно. Обидете се да копирате во друг календар."; +"Please select at least one calendar" = "Одберете барем еден календар."; + +"Open Task..." = "Отворена задача..."; +"Mark Completed" = "Означи завршени"; +"Delete Task" = "Избриши задача"; +"Delete Event" = "Избриши настан"; +"Copy event to my calendar" = "Копирај го настанот во мојот календар"; +"View Raw Source" = "Види го сировиот извор"; + +"Subscribe to a web calendar..." = "Претплатете се на веб календар..."; +"URL of the Calendar" = "URL на календарот"; +"Web Calendar" = "Веб календар"; +"Reload on login" = "Наново вчитај при најавување"; +"Invalid number." = "Погрешен број."; +"Please identify yourself to %{0}" = "Ве молам идентификувајте се до %{0}"; diff --git a/UI/Templates/SOGoACLMacedonianAdditionAdvisory.wox b/UI/Templates/SOGoACLMacedonianAdditionAdvisory.wox new file mode 100644 index 000000000..41baed3d6 --- /dev/null +++ b/UI/Templates/SOGoACLMacedonianAdditionAdvisory.wox @@ -0,0 +1,28 @@ + + + + + + ве додаде + + + + ве додаде на листата за пристап кон неговите папка. + + + + diff --git a/UI/Templates/SOGoACLMacedonianModificationAdvisory.wox b/UI/Templates/SOGoACLMacedonianModificationAdvisory.wox new file mode 100644 index 000000000..73b1785b4 --- /dev/null +++ b/UI/Templates/SOGoACLMacedonianModificationAdvisory.wox @@ -0,0 +1,28 @@ + + + + + + ги промени вашите права за пристап + + + + ги промени вашите права за пристап на неговата папка. + + + + diff --git a/UI/Templates/SOGoACLMacedonianRemovalAdvisory.wox b/UI/Templates/SOGoACLMacedonianRemovalAdvisory.wox new file mode 100644 index 000000000..6ff9a81bb --- /dev/null +++ b/UI/Templates/SOGoACLMacedonianRemovalAdvisory.wox @@ -0,0 +1,28 @@ + + + + + + ве отстрани од + + + + ве отстрани од пристапот на неговите папка. + + + + diff --git a/UI/Templates/SOGoFolderMacedonianAdditionAdvisory.wox b/UI/Templates/SOGoFolderMacedonianAdditionAdvisory.wox new file mode 100644 index 000000000..e2cf62514 --- /dev/null +++ b/UI/Templates/SOGoFolderMacedonianAdditionAdvisory.wox @@ -0,0 +1,23 @@ + + + + + + е креирана + + + +The папката е креирана. + + + + diff --git a/UI/Templates/SOGoFolderMacedonianRemovalAdvisory.wox b/UI/Templates/SOGoFolderMacedonianRemovalAdvisory.wox new file mode 100644 index 000000000..932986c8b --- /dev/null +++ b/UI/Templates/SOGoFolderMacedonianRemovalAdvisory.wox @@ -0,0 +1,23 @@ + + + + + + е избришана + + + +The папката е избришана. + + + + From 106f8ec3145cbe06340b164cb8f8c83093249814 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Wed, 11 Nov 2015 09:46:21 -0500 Subject: [PATCH 48/69] Improve Portuguese translation --- UI/PreferencesUI/Arabic.lproj/Localizable.strings | 2 +- UI/PreferencesUI/Basque.lproj/Localizable.strings | 2 +- UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings | 2 +- UI/PreferencesUI/Catalan.lproj/Localizable.strings | 2 +- UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings | 2 +- UI/PreferencesUI/Czech.lproj/Localizable.strings | 2 +- UI/PreferencesUI/Dutch.lproj/Localizable.strings | 2 +- UI/PreferencesUI/English.lproj/Localizable.strings | 2 +- UI/PreferencesUI/Finnish.lproj/Localizable.strings | 2 +- UI/PreferencesUI/French.lproj/Localizable.strings | 2 +- UI/PreferencesUI/German.lproj/Localizable.strings | 2 +- UI/PreferencesUI/Hungarian.lproj/Localizable.strings | 2 +- UI/PreferencesUI/Icelandic.lproj/Localizable.strings | 2 +- UI/PreferencesUI/Italian.lproj/Localizable.strings | 2 +- UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings | 2 +- UI/PreferencesUI/Polish.lproj/Localizable.strings | 2 +- UI/PreferencesUI/Russian.lproj/Localizable.strings | 2 +- UI/PreferencesUI/Slovak.lproj/Localizable.strings | 2 +- UI/PreferencesUI/Slovenian.lproj/Localizable.strings | 2 +- UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings | 2 +- UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings | 2 +- UI/PreferencesUI/Swedish.lproj/Localizable.strings | 2 +- UI/PreferencesUI/Ukrainian.lproj/Localizable.strings | 2 +- UI/PreferencesUI/Welsh.lproj/Localizable.strings | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/UI/PreferencesUI/Arabic.lproj/Localizable.strings b/UI/PreferencesUI/Arabic.lproj/Localizable.strings index 096d7709b..505168d99 100644 --- a/UI/PreferencesUI/Arabic.lproj/Localizable.strings +++ b/UI/PreferencesUI/Arabic.lproj/Localizable.strings @@ -221,7 +221,7 @@ "NorwegianBokmal" = "Norsk bokmål"; "NorwegianNynorsk" = "Norsk nynorsk"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "BrazilianPortuguese" = "Português brasileiro"; "Russian" = "Русский"; "Slovak" = "Slovensky"; diff --git a/UI/PreferencesUI/Basque.lproj/Localizable.strings b/UI/PreferencesUI/Basque.lproj/Localizable.strings index e56953ca1..776a85242 100644 --- a/UI/PreferencesUI/Basque.lproj/Localizable.strings +++ b/UI/PreferencesUI/Basque.lproj/Localizable.strings @@ -234,7 +234,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings b/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings index 252b79d0a..b328bade8 100644 --- a/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings +++ b/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings @@ -234,7 +234,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Eslovaco"; "SpanishSpain" = "Español (España)"; diff --git a/UI/PreferencesUI/Catalan.lproj/Localizable.strings b/UI/PreferencesUI/Catalan.lproj/Localizable.strings index 5391d4daf..2cdc6a3d4 100644 --- a/UI/PreferencesUI/Catalan.lproj/Localizable.strings +++ b/UI/PreferencesUI/Catalan.lproj/Localizable.strings @@ -235,7 +235,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings b/UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings index acba1bd7d..760a25d29 100644 --- a/UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings +++ b/UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings @@ -232,7 +232,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "SpanishSpain" = "Español (España)"; diff --git a/UI/PreferencesUI/Czech.lproj/Localizable.strings b/UI/PreferencesUI/Czech.lproj/Localizable.strings index 437687cdc..8c452ccf3 100644 --- a/UI/PreferencesUI/Czech.lproj/Localizable.strings +++ b/UI/PreferencesUI/Czech.lproj/Localizable.strings @@ -234,7 +234,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Portugues brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/Dutch.lproj/Localizable.strings b/UI/PreferencesUI/Dutch.lproj/Localizable.strings index 048b8d2d2..33a6c7a87 100644 --- a/UI/PreferencesUI/Dutch.lproj/Localizable.strings +++ b/UI/PreferencesUI/Dutch.lproj/Localizable.strings @@ -234,7 +234,7 @@ "NorwegianNynorsk" = "Norsk Nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/English.lproj/Localizable.strings b/UI/PreferencesUI/English.lproj/Localizable.strings index 83c192e60..9e7f0badc 100644 --- a/UI/PreferencesUI/English.lproj/Localizable.strings +++ b/UI/PreferencesUI/English.lproj/Localizable.strings @@ -234,7 +234,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/Finnish.lproj/Localizable.strings b/UI/PreferencesUI/Finnish.lproj/Localizable.strings index 84f40b310..3e709ef01 100644 --- a/UI/PreferencesUI/Finnish.lproj/Localizable.strings +++ b/UI/PreferencesUI/Finnish.lproj/Localizable.strings @@ -234,7 +234,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/French.lproj/Localizable.strings b/UI/PreferencesUI/French.lproj/Localizable.strings index 9096b24d6..9c24a4f0a 100644 --- a/UI/PreferencesUI/French.lproj/Localizable.strings +++ b/UI/PreferencesUI/French.lproj/Localizable.strings @@ -234,7 +234,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/German.lproj/Localizable.strings b/UI/PreferencesUI/German.lproj/Localizable.strings index 8e16c847f..6cacfd7da 100644 --- a/UI/PreferencesUI/German.lproj/Localizable.strings +++ b/UI/PreferencesUI/German.lproj/Localizable.strings @@ -234,7 +234,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/Hungarian.lproj/Localizable.strings b/UI/PreferencesUI/Hungarian.lproj/Localizable.strings index 97543698e..cedbde37f 100644 --- a/UI/PreferencesUI/Hungarian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Hungarian.lproj/Localizable.strings @@ -234,7 +234,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Szlovén"; "SpanishSpain" = "Español (España)"; diff --git a/UI/PreferencesUI/Icelandic.lproj/Localizable.strings b/UI/PreferencesUI/Icelandic.lproj/Localizable.strings index 7fb3f0707..dba576d3a 100644 --- a/UI/PreferencesUI/Icelandic.lproj/Localizable.strings +++ b/UI/PreferencesUI/Icelandic.lproj/Localizable.strings @@ -192,7 +192,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/Italian.lproj/Localizable.strings b/UI/PreferencesUI/Italian.lproj/Localizable.strings index bdb669850..039429f91 100644 --- a/UI/PreferencesUI/Italian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Italian.lproj/Localizable.strings @@ -213,7 +213,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings b/UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings index 4ae8ba8b6..35808f568 100644 --- a/UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings +++ b/UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings @@ -199,7 +199,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/Polish.lproj/Localizable.strings b/UI/PreferencesUI/Polish.lproj/Localizable.strings index 0399bdaae..492ae470d 100644 --- a/UI/PreferencesUI/Polish.lproj/Localizable.strings +++ b/UI/PreferencesUI/Polish.lproj/Localizable.strings @@ -234,7 +234,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovenská"; "SpanishSpain" = "Español (España)"; diff --git a/UI/PreferencesUI/Russian.lproj/Localizable.strings b/UI/PreferencesUI/Russian.lproj/Localizable.strings index d9234f38d..9be953f32 100644 --- a/UI/PreferencesUI/Russian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Russian.lproj/Localizable.strings @@ -234,7 +234,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/Slovak.lproj/Localizable.strings b/UI/PreferencesUI/Slovak.lproj/Localizable.strings index 68c9d23a9..7a040f23c 100644 --- a/UI/PreferencesUI/Slovak.lproj/Localizable.strings +++ b/UI/PreferencesUI/Slovak.lproj/Localizable.strings @@ -231,7 +231,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/Slovenian.lproj/Localizable.strings b/UI/PreferencesUI/Slovenian.lproj/Localizable.strings index 2eab9cb17..a503fbe8e 100644 --- a/UI/PreferencesUI/Slovenian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Slovenian.lproj/Localizable.strings @@ -233,7 +233,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings b/UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings index 346329d3b..cbc4cec5e 100644 --- a/UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings +++ b/UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings @@ -227,7 +227,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings b/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings index 6710cdb4f..d37ced1a3 100644 --- a/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings +++ b/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings @@ -234,7 +234,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Eslovaquia"; "SpanishSpain" = "Español (España)"; diff --git a/UI/PreferencesUI/Swedish.lproj/Localizable.strings b/UI/PreferencesUI/Swedish.lproj/Localizable.strings index 675e30f48..e74a1800a 100644 --- a/UI/PreferencesUI/Swedish.lproj/Localizable.strings +++ b/UI/PreferencesUI/Swedish.lproj/Localizable.strings @@ -201,7 +201,7 @@ Servernamn:"; "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings b/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings index 80682c6fb..197b1f1a6 100644 --- a/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings @@ -211,7 +211,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; diff --git a/UI/PreferencesUI/Welsh.lproj/Localizable.strings b/UI/PreferencesUI/Welsh.lproj/Localizable.strings index 525c1c80b..9d23d5abc 100644 --- a/UI/PreferencesUI/Welsh.lproj/Localizable.strings +++ b/UI/PreferencesUI/Welsh.lproj/Localizable.strings @@ -199,7 +199,7 @@ "NorwegianNynorsk" = "Norsk nynorsk"; "BrazilianPortuguese" = "Português brasileiro"; "Polish" = "Polski"; -"Portuguese" = "Portuguese"; +"Portuguese" = "Português"; "Russian" = "Русский"; "Slovak" = "Slovensky"; "Slovenian" = "Slovenščina"; From f2347e13d732af4e7ddf1f10b3195b673034ef7a Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Wed, 11 Nov 2015 11:49:36 -0500 Subject: [PATCH 49/69] Fix localizable strings in contact editor --- UI/Templates/ContactsUI/UIxContactEditor.wox | 28 ++++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/UI/Templates/ContactsUI/UIxContactEditor.wox b/UI/Templates/ContactsUI/UIxContactEditor.wox index 893b94ddd..06b4e759f 100644 --- a/UI/Templates/ContactsUI/UIxContactEditor.wox +++ b/UI/Templates/ContactsUI/UIxContactEditor.wox @@ -57,7 +57,7 @@
-
-
-
-