Merge pull request #164 from Zentyal/jgarcia/revert-merge-inverse
Revert merge from inverse
This commit is contained in:
commit
1404dbdb88
|
@ -66,33 +66,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
//
|
//
|
||||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||||
{
|
{
|
||||||
NSString *tmp, *s;
|
return [[self stringByEncodingBase64] stringByReplacingString: @"\n" withString: @""];
|
||||||
unichar *buf, *start, c;
|
|
||||||
int len, i, j;
|
|
||||||
|
|
||||||
tmp = [self stringByEncodingBase64] ;
|
|
||||||
|
|
||||||
len = [tmp length];
|
|
||||||
|
|
||||||
start = buf = (unichar *)malloc(len*sizeof(unichar));
|
|
||||||
[tmp getCharacters: buf range: NSMakeRange(0, len)];
|
|
||||||
|
|
||||||
for (i = 0, j = 0; i < len; i++)
|
|
||||||
{
|
|
||||||
c = *buf;
|
|
||||||
|
|
||||||
if (!(c == 0xA))
|
|
||||||
{
|
|
||||||
*(start+j) = c;
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf++;
|
|
||||||
}
|
|
||||||
|
|
||||||
s = [[NSString alloc] initWithCharactersNoCopy: start length: j freeWhenDone: YES];
|
|
||||||
|
|
||||||
return AUTORELEASE(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSData *) wbxml2xml
|
- (NSData *) wbxml2xml
|
||||||
|
|
|
@ -46,99 +46,6 @@ static NSArray *easCommandParameters = nil;
|
||||||
|
|
||||||
@implementation NSString (ActiveSync)
|
@implementation NSString (ActiveSync)
|
||||||
|
|
||||||
//
|
|
||||||
// This is a copy from NSString+XMLEscaping.m from SOPE.
|
|
||||||
// The difference here is that we use wchar_t instead of unichar.
|
|
||||||
// This is needed to get the rigth numeric character reference.
|
|
||||||
// e.g. SMILING FACE WITH OPEN MOUTH
|
|
||||||
// ok: wchar_t -> 😃 wrong: unichar -> � �
|
|
||||||
//
|
|
||||||
// We avoir naming it like the one in SOPE since if the ActiveSync
|
|
||||||
// bundle is loaded, it'll overwrite the one provided by SOPE.
|
|
||||||
//
|
|
||||||
- (NSString *) _stringByEscapingXMLStringUsingCharacters {
|
|
||||||
register unsigned i, len, j;
|
|
||||||
register wchar_t *buf;
|
|
||||||
const wchar_t *chars;
|
|
||||||
unsigned escapeCount;
|
|
||||||
|
|
||||||
if ([self length] == 0) return @"";
|
|
||||||
|
|
||||||
NSData *data = [self dataUsingEncoding:NSUTF32StringEncoding];
|
|
||||||
chars = [data bytes];
|
|
||||||
len = [data length]/4;
|
|
||||||
|
|
||||||
/* check for characters to escape ... */
|
|
||||||
for (i = 0, escapeCount = 0; i < len; i++) {
|
|
||||||
switch (chars[i]) {
|
|
||||||
case '&': case '"': case '<': case '>': case '\r':
|
|
||||||
escapeCount++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (chars[i] > 127)
|
|
||||||
escapeCount++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (escapeCount == 0 ) {
|
|
||||||
/* nothing to escape ... */
|
|
||||||
return [[self copy] autorelease];
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = calloc((len + 5) + (escapeCount * 16), sizeof(wchar_t));
|
|
||||||
for (i = 0, j = 0; i < len; i++) {
|
|
||||||
switch (chars[i]) {
|
|
||||||
/* escape special chars */
|
|
||||||
case '\r':
|
|
||||||
buf[j] = '&'; j++; buf[j] = '#'; j++; buf[j] = '1'; j++;
|
|
||||||
buf[j] = '3'; j++; buf[j] = ';'; j++;
|
|
||||||
break;
|
|
||||||
case '&':
|
|
||||||
buf[j] = '&'; j++; buf[j] = 'a'; j++; buf[j] = 'm'; j++;
|
|
||||||
buf[j] = 'p'; j++; buf[j] = ';'; j++;
|
|
||||||
break;
|
|
||||||
case '"':
|
|
||||||
buf[j] = '&'; j++; buf[j] = 'q'; j++; buf[j] = 'u'; j++;
|
|
||||||
buf[j] = 'o'; j++; buf[j] = 't'; j++; buf[j] = ';'; j++;
|
|
||||||
break;
|
|
||||||
case '<':
|
|
||||||
buf[j] = '&'; j++; buf[j] = 'l'; j++; buf[j] = 't'; j++;
|
|
||||||
buf[j] = ';'; j++;
|
|
||||||
break;
|
|
||||||
case '>':
|
|
||||||
buf[j] = '&'; j++; buf[j] = 'g'; j++; buf[j] = 't'; j++;
|
|
||||||
buf[j] = ';'; j++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* escape big chars */
|
|
||||||
if (chars[i] > 127) {
|
|
||||||
unsigned char nbuf[32];
|
|
||||||
unsigned int k;
|
|
||||||
|
|
||||||
sprintf((char *)nbuf, "&#%i;", (int)chars[i]);
|
|
||||||
for (k = 0; nbuf[k] != '\0'; k++) {
|
|
||||||
buf[j] = nbuf[k];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (chars[i] == 0x9 || chars[i] == 0xA || chars[i] == 0xD || chars[i] >= 0x20) { // ignore any unsupported control character
|
|
||||||
/* nothing to escape */
|
|
||||||
buf[j] = chars[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self = [[NSString alloc] initWithBytesNoCopy: buf
|
|
||||||
length: (j*sizeof(wchar_t))
|
|
||||||
encoding: NSUTF32StringEncoding
|
|
||||||
freeWhenDone: YES];
|
|
||||||
|
|
||||||
return [self autorelease];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *) sanitizedServerIdWithType: (SOGoMicrosoftActiveSyncFolderType) folderType
|
- (NSString *) sanitizedServerIdWithType: (SOGoMicrosoftActiveSyncFolderType) folderType
|
||||||
{
|
{
|
||||||
if (folderType == ActiveSyncEventFolder)
|
if (folderType == ActiveSyncEventFolder)
|
||||||
|
@ -158,7 +65,11 @@ static NSArray *easCommandParameters = nil;
|
||||||
|
|
||||||
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
|
||||||
{
|
{
|
||||||
return [self _stringByEscapingXMLStringUsingCharacters];
|
NSString *s;
|
||||||
|
|
||||||
|
s = [self safeString];
|
||||||
|
|
||||||
|
return [s stringByEscapingHTMLString];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (int) activeSyncFolderType
|
- (int) activeSyncFolderType
|
||||||
|
|
|
@ -453,27 +453,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
||||||
inBuffer: (NSMutableString *) theBuffer
|
inBuffer: (NSMutableString *) theBuffer
|
||||||
{
|
{
|
||||||
|
|
||||||
id aDelete, sogoObject, value;
|
|
||||||
NSArray *deletions;
|
NSArray *deletions;
|
||||||
NSString *serverId;
|
NSString *serverId;
|
||||||
|
|
||||||
BOOL deletesAsMoves, useTrash;
|
id aDelete, sogoObject;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
deletions = (id)[theDocumentElement getElementsByTagName: @"Delete"];
|
deletions = (id)[theDocumentElement getElementsByTagName: @"Delete"];
|
||||||
|
|
||||||
if ([deletions count])
|
if ([deletions count])
|
||||||
{
|
{
|
||||||
// From the documention, if DeletesAsMoves is missing, we must assume it's a YES.
|
|
||||||
// See https://msdn.microsoft.com/en-us/library/gg675480(v=exchg.80).aspx for all details.
|
|
||||||
value = [theDocumentElement getElementsByTagName: @"DeletesAsMoves"];
|
|
||||||
deletesAsMoves = YES;
|
|
||||||
useTrash = YES;
|
|
||||||
|
|
||||||
if ([value count] && [[[value lastObject] textValue] length])
|
|
||||||
deletesAsMoves = [[[value lastObject] textValue] boolValue];
|
|
||||||
|
|
||||||
for (i = 0; i < [deletions count]; i++)
|
for (i = 0; i < [deletions count]; i++)
|
||||||
{
|
{
|
||||||
aDelete = [deletions objectAtIndex: i];
|
aDelete = [deletions objectAtIndex: i];
|
||||||
|
@ -485,13 +474,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
acquire: NO];
|
acquire: NO];
|
||||||
|
|
||||||
if (![sogoObject isKindOfClass: [NSException class]])
|
if (![sogoObject isKindOfClass: [NSException class]])
|
||||||
{
|
[sogoObject delete];
|
||||||
// FIXME: handle errors here
|
|
||||||
if (deletesAsMoves)
|
|
||||||
[(SOGoMailFolder *)[sogoObject container] deleteUIDs: [NSArray arrayWithObjects: serverId, nil] useTrashFolder: &useTrash inContext: context];
|
|
||||||
else
|
|
||||||
[sogoObject delete];
|
|
||||||
}
|
|
||||||
|
|
||||||
[theBuffer appendString: @"<Delete>"];
|
[theBuffer appendString: @"<Delete>"];
|
||||||
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", serverId];
|
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", serverId];
|
||||||
|
|
|
@ -453,7 +453,7 @@ static BOOL debugOn = NO;
|
||||||
[s appendString: @"<FolderCreate xmlns=\"FolderHierarchy:\">"];
|
[s appendString: @"<FolderCreate xmlns=\"FolderHierarchy:\">"];
|
||||||
[s appendFormat: @"<Status>%d</Status>", 1];
|
[s appendFormat: @"<Status>%d</Status>", 1];
|
||||||
[s appendFormat: @"<SyncKey>%@</SyncKey>", syncKey];
|
[s appendFormat: @"<SyncKey>%@</SyncKey>", syncKey];
|
||||||
[s appendFormat: @"<ServerId>%@</ServerId>", [nameInContainer stringByEscapingURL]];
|
[s appendFormat: @"<ServerId>%@</ServerId>", nameInContainer];
|
||||||
[s appendString: @"</FolderCreate>"];
|
[s appendString: @"</FolderCreate>"];
|
||||||
|
|
||||||
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
||||||
|
@ -1084,8 +1084,6 @@ static BOOL debugOn = NO;
|
||||||
SOGoMailAccounts *accountsFolder;
|
SOGoMailAccounts *accountsFolder;
|
||||||
SOGoUserFolder *userFolder;
|
SOGoUserFolder *userFolder;
|
||||||
SOGoMailObject *mailObject;
|
SOGoMailObject *mailObject;
|
||||||
NSArray *partKeys;
|
|
||||||
int p;
|
|
||||||
|
|
||||||
NSRange r1, r2;
|
NSRange r1, r2;
|
||||||
|
|
||||||
|
@ -1105,14 +1103,7 @@ static BOOL debugOn = NO;
|
||||||
acquire: NO];
|
acquire: NO];
|
||||||
|
|
||||||
mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO];
|
mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO];
|
||||||
|
currentBodyPart = [mailObject lookupImap4BodyPartKey: pathToPart inContext: context];
|
||||||
partKeys = [pathToPart componentsSeparatedByString: @"."];
|
|
||||||
|
|
||||||
currentBodyPart = [mailObject lookupImap4BodyPartKey: [partKeys objectAtIndex:0] inContext: context];
|
|
||||||
for (p = 1; p < [partKeys count]; p++)
|
|
||||||
{
|
|
||||||
currentBodyPart = [currentBodyPart lookupImap4BodyPartKey: [partKeys objectAtIndex:p] inContext: context];
|
|
||||||
}
|
|
||||||
|
|
||||||
[theResponse setHeader: [NSString stringWithFormat: @"%@/%@", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]]
|
[theResponse setHeader: [NSString stringWithFormat: @"%@/%@", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]]
|
||||||
forKey: @"Content-Type"];
|
forKey: @"Content-Type"];
|
||||||
|
@ -1169,9 +1160,9 @@ static BOOL debugOn = NO;
|
||||||
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
|
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
|
||||||
|
|
||||||
if (folderType == ActiveSyncMailFolder)
|
if (folderType == ActiveSyncMailFolder)
|
||||||
nameInCache = [NSString stringWithFormat: @"folder%@", realCollectionId];
|
nameInCache = [NSString stringWithFormat: @"folder%@", realCollectionId];
|
||||||
else
|
else
|
||||||
nameInCache = collectionId;
|
nameInCache = collectionId;
|
||||||
|
|
||||||
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
||||||
|
|
||||||
|
@ -1243,11 +1234,10 @@ static BOOL debugOn = NO;
|
||||||
- (void) processItemOperations: (id <DOMElement>) theDocumentElement
|
- (void) processItemOperations: (id <DOMElement>) theDocumentElement
|
||||||
inResponse: (WOResponse *) theResponse
|
inResponse: (WOResponse *) theResponse
|
||||||
{
|
{
|
||||||
NSString *fileReference, *realCollectionId, *serverId, *bodyPreferenceType, *collectionId;
|
NSString *fileReference, *realCollectionId;
|
||||||
NSMutableString *s;
|
NSMutableString *s;
|
||||||
NSArray *fetchRequests;
|
NSArray *fetchRequests;
|
||||||
id aFetch;
|
id aFetch;
|
||||||
NSData *d;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
SOGoMicrosoftActiveSyncFolderType folderType;
|
SOGoMicrosoftActiveSyncFolderType folderType;
|
||||||
|
@ -1257,6 +1247,8 @@ static BOOL debugOn = NO;
|
||||||
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||||
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||||
[s appendString: @"<ItemOperations xmlns=\"ItemOperations:\">"];
|
[s appendString: @"<ItemOperations xmlns=\"ItemOperations:\">"];
|
||||||
|
[s appendString: @"<Status>1</Status>"];
|
||||||
|
[s appendString: @"<Response>"];
|
||||||
|
|
||||||
fetchRequests = (id)[theDocumentElement getElementsByTagName: @"Fetch"];
|
fetchRequests = (id)[theDocumentElement getElementsByTagName: @"Fetch"];
|
||||||
|
|
||||||
|
@ -1264,25 +1256,17 @@ static BOOL debugOn = NO;
|
||||||
{
|
{
|
||||||
NSMutableData *bytes, *parts;
|
NSMutableData *bytes, *parts;
|
||||||
NSMutableArray *partLength;
|
NSMutableArray *partLength;
|
||||||
|
NSData *d;
|
||||||
|
|
||||||
bytes = [NSMutableData data];
|
bytes = [NSMutableData data];
|
||||||
parts = [NSMutableData data];
|
parts = [NSMutableData data];
|
||||||
partLength = [NSMutableArray array];
|
partLength = [NSMutableArray array];
|
||||||
|
|
||||||
[s appendString: @"<Status>1</Status>"];
|
|
||||||
[s appendString: @"<Response>"];
|
|
||||||
|
|
||||||
for (i = 0; i < [fetchRequests count]; i++)
|
for (i = 0; i < [fetchRequests count]; i++)
|
||||||
{
|
{
|
||||||
aFetch = [fetchRequests objectAtIndex: i];
|
aFetch = [fetchRequests objectAtIndex: i];
|
||||||
fileReference = [[[(id)[aFetch getElementsByTagName: @"FileReference"] lastObject] textValue] stringByUnescapingURL];
|
fileReference = [[[(id)[aFetch getElementsByTagName: @"FileReference"] lastObject] textValue] stringByUnescapingURL];
|
||||||
collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
|
realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType];
|
||||||
|
|
||||||
// its either a itemOperation to fetch an attachment or an email
|
|
||||||
if ([fileReference length])
|
|
||||||
realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType];
|
|
||||||
else
|
|
||||||
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
|
|
||||||
|
|
||||||
if (folderType == ActiveSyncMailFolder)
|
if (folderType == ActiveSyncMailFolder)
|
||||||
{
|
{
|
||||||
|
@ -1292,80 +1276,43 @@ static BOOL debugOn = NO;
|
||||||
SOGoUserFolder *userFolder;
|
SOGoUserFolder *userFolder;
|
||||||
SOGoMailObject *mailObject;
|
SOGoMailObject *mailObject;
|
||||||
|
|
||||||
if ([fileReference length])
|
NSRange r1, r2;
|
||||||
|
|
||||||
|
r1 = [realCollectionId rangeOfString: @"/"];
|
||||||
|
r2 = [realCollectionId rangeOfString: @"/" options: 0 range: NSMakeRange(NSMaxRange(r1)+1, [realCollectionId length]-NSMaxRange(r1)-1)];
|
||||||
|
|
||||||
|
folderName = [realCollectionId substringToIndex: r1.location];
|
||||||
|
messageName = [realCollectionId substringWithRange: NSMakeRange(NSMaxRange(r1), r2.location-r1.location-1)];
|
||||||
|
pathToPart = [realCollectionId substringFromIndex: r2.location+1];
|
||||||
|
|
||||||
|
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||||
|
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||||
|
currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
||||||
|
|
||||||
|
currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", folderName]
|
||||||
|
inContext: context
|
||||||
|
acquire: NO];
|
||||||
|
|
||||||
|
mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO];
|
||||||
|
currentBodyPart = [mailObject lookupImap4BodyPartKey: pathToPart inContext: context];
|
||||||
|
|
||||||
|
[s appendString: @"<Fetch>"];
|
||||||
|
[s appendString: @"<Status>1</Status>"];
|
||||||
|
[s appendFormat: @"<FileReference xmlns=\"AirSyncBase:\">%@</FileReference>", [fileReference stringByEscapingURL]];
|
||||||
|
[s appendString: @"<Properties>"];
|
||||||
|
|
||||||
|
[s appendFormat: @"<ContentType xmlns=\"AirSyncBase:\">%@/%@</ContentType>", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]];
|
||||||
|
|
||||||
|
if ([[theResponse headerForKey: @"Content-Type"] isEqualToString:@"application/vnd.ms-sync.multipart"])
|
||||||
{
|
{
|
||||||
// fetch attachment
|
[s appendFormat: @"<Part>%d</Part>", i+1];
|
||||||
NSRange r1, r2;
|
[partLength addObject: [NSNumber numberWithInteger: [[currentBodyPart fetchBLOB] length]]];
|
||||||
NSArray *partKeys;
|
[parts appendData:[currentBodyPart fetchBLOB]];
|
||||||
int p;
|
|
||||||
|
|
||||||
r1 = [realCollectionId rangeOfString: @"/"];
|
|
||||||
r2 = [realCollectionId rangeOfString: @"/" options: 0 range: NSMakeRange(NSMaxRange(r1)+1, [realCollectionId length]-NSMaxRange(r1)-1)];
|
|
||||||
|
|
||||||
folderName = [realCollectionId substringToIndex: r1.location];
|
|
||||||
messageName = [realCollectionId substringWithRange: NSMakeRange(NSMaxRange(r1), r2.location-r1.location-1)];
|
|
||||||
pathToPart = [realCollectionId substringFromIndex: r2.location+1];
|
|
||||||
|
|
||||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
|
||||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
|
||||||
currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
|
||||||
|
|
||||||
currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", folderName]
|
|
||||||
inContext: context
|
|
||||||
acquire: NO];
|
|
||||||
|
|
||||||
mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO];
|
|
||||||
|
|
||||||
partKeys = [pathToPart componentsSeparatedByString: @"."];
|
|
||||||
|
|
||||||
currentBodyPart = [mailObject lookupImap4BodyPartKey: [partKeys objectAtIndex:0] inContext: context];
|
|
||||||
for (p = 1; p < [partKeys count]; p++)
|
|
||||||
{
|
|
||||||
currentBodyPart = [currentBodyPart lookupImap4BodyPartKey: [partKeys objectAtIndex:p] inContext: context];
|
|
||||||
}
|
|
||||||
|
|
||||||
[s appendString: @"<Fetch>"];
|
|
||||||
[s appendString: @"<Status>1</Status>"];
|
|
||||||
[s appendFormat: @"<FileReference xmlns=\"AirSyncBase:\">%@</FileReference>", [fileReference stringByEscapingURL]];
|
|
||||||
[s appendString: @"<Properties>"];
|
|
||||||
|
|
||||||
[s appendFormat: @"<ContentType xmlns=\"AirSyncBase:\">%@/%@</ContentType>", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]];
|
|
||||||
|
|
||||||
if ([[theResponse headerForKey: @"Content-Type"] isEqualToString:@"application/vnd.ms-sync.multipart"])
|
|
||||||
{
|
|
||||||
NSData *d;
|
|
||||||
d = [currentBodyPart fetchBLOB];
|
|
||||||
|
|
||||||
[s appendFormat: @"<Part>%d</Part>", i+1];
|
|
||||||
[partLength addObject: [NSNumber numberWithInteger: [d length]]];
|
|
||||||
[parts appendData: d];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NSString *a;
|
|
||||||
a = [[currentBodyPart fetchBLOB] activeSyncRepresentationInContext: context];
|
|
||||||
|
|
||||||
[s appendFormat: @"<Range>0-%d</Range>", [a length]-1];
|
|
||||||
[s appendFormat: @"<Data>%@</Data>", a];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// fetch mail
|
[s appendFormat: @"<Range>0-%d</Range>", [[[currentBodyPart fetchBLOB] activeSyncRepresentationInContext: context] length]-1];
|
||||||
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
[s appendFormat: @"<Data>%@</Data>", [[currentBodyPart fetchBLOB] activeSyncRepresentationInContext: context]];
|
||||||
serverId = [[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue];
|
|
||||||
bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue];
|
|
||||||
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
|
|
||||||
|
|
||||||
currentCollection = [self collectionFromId: realCollectionId type: folderType];
|
|
||||||
|
|
||||||
mailObject = [currentCollection lookupName: serverId inContext: context acquire: NO];
|
|
||||||
[s appendString: @"<Fetch>"];
|
|
||||||
[s appendString: @"<Status>1</Status>"];
|
|
||||||
[s appendFormat: @"<CollectionId xmlns=\"AirSyncBase:\">%@</CollectionId>", collectionId];
|
|
||||||
[s appendFormat: @"<ServerId xmlns=\"AirSyncBase:\">%@</ServerId>", serverId];
|
|
||||||
[s appendString: @"<Properties>"];
|
|
||||||
[s appendString: [mailObject activeSyncRepresentationInContext: context]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[s appendString: @"</Properties>"];
|
[s appendString: @"</Properties>"];
|
||||||
|
@ -1420,62 +1367,6 @@ static BOOL debugOn = NO;
|
||||||
[theResponse setContent: d];
|
[theResponse setContent: d];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ([theDocumentElement getElementsByTagName: @"EmptyFolderContents"])
|
|
||||||
{
|
|
||||||
NGImap4Connection *connection;
|
|
||||||
NSEnumerator *subfolders;
|
|
||||||
NSException *error;
|
|
||||||
NSURL *currentURL;
|
|
||||||
id co;
|
|
||||||
|
|
||||||
collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue];
|
|
||||||
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
|
|
||||||
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
|
||||||
|
|
||||||
if (folderType == ActiveSyncMailFolder)
|
|
||||||
{
|
|
||||||
co = [self collectionFromId: realCollectionId type: folderType];
|
|
||||||
error = [co addFlagsToAllMessages: @"deleted"];
|
|
||||||
|
|
||||||
if (!error)
|
|
||||||
error = [(SOGoMailFolder *)co expunge];
|
|
||||||
|
|
||||||
if (!error)
|
|
||||||
{
|
|
||||||
[co flushMailCaches];
|
|
||||||
|
|
||||||
if ([theDocumentElement getElementsByTagName: @"DeleteSubFolders"])
|
|
||||||
{
|
|
||||||
// Delete sub-folders
|
|
||||||
connection = [co imap4Connection];
|
|
||||||
subfolders = [[co allFolderURLs] objectEnumerator];
|
|
||||||
|
|
||||||
while ((currentURL = [subfolders nextObject]))
|
|
||||||
{
|
|
||||||
[[connection client] unsubscribe: [currentURL path]];
|
|
||||||
[connection deleteMailboxAtURL: currentURL];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[s appendString: @"<Status>1</Status>"];
|
|
||||||
[s appendString: @"</ItemOperations>"];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
[s appendString: @"<Status>3</Status>"];
|
|
||||||
[s appendString: @"</ItemOperations>"];
|
|
||||||
}
|
|
||||||
|
|
||||||
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
|
|
||||||
[theResponse setContent: d];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[theResponse setStatus: 500];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -381,6 +381,11 @@ struct GlobalObjectId {
|
||||||
|
|
||||||
if (s)
|
if (s)
|
||||||
{
|
{
|
||||||
|
// We sanitize the content immediately, in case we have non-UNICODE safe
|
||||||
|
// characters that would be re-encoded later in HTML entities and thus,
|
||||||
|
// ignore afterwards.
|
||||||
|
s = [s safeString];
|
||||||
|
|
||||||
body = [s dataUsingEncoding: NSUTF8StringEncoding];
|
body = [s dataUsingEncoding: NSUTF8StringEncoding];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -861,41 +866,10 @@ struct GlobalObjectId {
|
||||||
|
|
||||||
if (d)
|
if (d)
|
||||||
{
|
{
|
||||||
NSMutableData *sanitizedData;
|
|
||||||
NSString *content;
|
NSString *content;
|
||||||
int len, truncated;
|
int len, truncated;
|
||||||
|
|
||||||
// Outlook fails to decode quoted-printable (see #3082) if lines are not termined by CRLF.
|
content = [[NSString alloc] initWithData: d encoding: NSUTF8StringEncoding];
|
||||||
const char *bytes;
|
|
||||||
char *mbytes;
|
|
||||||
int mlen;
|
|
||||||
|
|
||||||
len = [d length];
|
|
||||||
mlen = 0;
|
|
||||||
|
|
||||||
sanitizedData = [NSMutableData dataWithLength: len*2];
|
|
||||||
|
|
||||||
bytes = [d bytes];
|
|
||||||
mbytes = [sanitizedData mutableBytes];
|
|
||||||
|
|
||||||
while (len > 0)
|
|
||||||
{
|
|
||||||
if (*bytes == '\n' && *(bytes-1) != '\r' && mlen > 0)
|
|
||||||
{
|
|
||||||
*mbytes = '\r';
|
|
||||||
mbytes++;
|
|
||||||
mlen++;
|
|
||||||
}
|
|
||||||
|
|
||||||
*mbytes = *bytes;
|
|
||||||
mbytes++; bytes++;
|
|
||||||
len--;
|
|
||||||
mlen++;
|
|
||||||
}
|
|
||||||
|
|
||||||
[sanitizedData setLength: mlen];
|
|
||||||
|
|
||||||
content = [[NSString alloc] initWithData: sanitizedData encoding: NSUTF8StringEncoding];
|
|
||||||
|
|
||||||
// FIXME: This is a hack. We should normally avoid doing this as we might get
|
// FIXME: This is a hack. We should normally avoid doing this as we might get
|
||||||
// broken encodings. We should rather tell that the data was truncated and expect
|
// broken encodings. We should rather tell that the data was truncated and expect
|
||||||
|
@ -905,7 +879,7 @@ struct GlobalObjectId {
|
||||||
// for an "interesting" discussion around this.
|
// for an "interesting" discussion around this.
|
||||||
//
|
//
|
||||||
if (!content)
|
if (!content)
|
||||||
content = [[NSString alloc] initWithData: sanitizedData encoding: NSISOLatin1StringEncoding];
|
content = [[NSString alloc] initWithData: d encoding: NSISOLatin1StringEncoding];
|
||||||
|
|
||||||
AUTORELEASE(content);
|
AUTORELEASE(content);
|
||||||
|
|
||||||
|
|
|
@ -506,14 +506,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[self setOrganizer: person];
|
[self setOrganizer: person];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// iOS is plain stupid here. It seends event invitations with no Organizer.
|
||||||
|
// We check this corner-case and if MeetingStatus == 1 (see http://msdn.microsoft.com/en-us/library/ee219342(v=exchg.80).aspx or details)
|
||||||
|
// and there's no organizer, we fake one.
|
||||||
|
//
|
||||||
if ((o = [theValues objectForKey: @"MeetingStatus"]))
|
if ((o = [theValues objectForKey: @"MeetingStatus"]))
|
||||||
{
|
{
|
||||||
//
|
if ([o intValue] == 1 && ![theValues objectForKey: @"Organizer_Email"])
|
||||||
// iOS is plain stupid here. It seends event invitations with no Organizer.
|
|
||||||
// We check this corner-case and if MeetingStatus == 1 (see http://msdn.microsoft.com/en-us/library/ee219342(v=exchg.80).aspx or details)
|
|
||||||
// and there's no organizer, we fake one.
|
|
||||||
//
|
|
||||||
if ([o intValue] == 1 && ![theValues objectForKey: @"Organizer_Email"] && ![[[self organizer] rfc822Email] length])
|
|
||||||
{
|
{
|
||||||
iCalPerson *person;
|
iCalPerson *person;
|
||||||
|
|
||||||
|
@ -523,20 +523,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[person setPartStat: @"ACCEPTED"];
|
[person setPartStat: @"ACCEPTED"];
|
||||||
[self setOrganizer: person];
|
[self setOrganizer: person];
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// When MeetingResponse fails Outlook still sends a new calendar entry with MeetingStatus=3.
|
|
||||||
// Use the organizer from the request if the event has no organizer.
|
|
||||||
//
|
|
||||||
if ([o intValue] == 3 && [theValues objectForKey: @"Organizer_Email"] && ![[[self organizer] rfc822Email] length])
|
|
||||||
{
|
|
||||||
iCalPerson *person;
|
|
||||||
person = [iCalPerson elementWithTag: @"organizer"];
|
|
||||||
[person setEmail: [theValues objectForKey: @"Organizer_Email"]];
|
|
||||||
[person setCn: [theValues objectForKey: @"Organizer_Name"]];
|
|
||||||
[person setPartStat: @"ACCEPTED"];
|
|
||||||
[self setOrganizer: person];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1920,11 +1920,10 @@ events. This parameter is an array of arbitrary strings.
|
||||||
|
|
||||||
Defaults to a list that depends on the language.
|
Defaults to a list that depends on the language.
|
||||||
|
|
||||||
|U |SOGoCalendarCategoriesColors
|
|U |SOGoCalendarDefaultCategoryColor
|
||||||
|Parameter used to define the colour of categories. This parameter
|
|Parameter used to define the default colour of categories.
|
||||||
is a dictionary of category name/color.
|
|
||||||
|
|
||||||
Defaults to `#F0F0F0` for all categories when unset.
|
Defaults to `#F0F0F0` when unset.
|
||||||
|
|
||||||
|U |SOGoCalendarEventsDefaultClassification
|
|U |SOGoCalendarEventsDefaultClassification
|
||||||
|Parameter used to defined the default classification for new events.
|
|Parameter used to defined the default classification for new events.
|
||||||
|
@ -2137,8 +2136,7 @@ Multi-domains Configuration
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
If you want your installation to isolate two groups of users, you must
|
If you want your installation to isolate two groups of users, you must
|
||||||
define a distinct authentication source for each _domain_. Your domain keys
|
define a distinct authentication source for each _domain_. Following is
|
||||||
must have the same value as your email domain you want to add. Following is
|
|
||||||
the same configuration that now includes two domains (acme.com and
|
the same configuration that now includes two domains (acme.com and
|
||||||
coyote.com):
|
coyote.com):
|
||||||
|
|
||||||
|
@ -2146,7 +2144,7 @@ coyote.com):
|
||||||
{
|
{
|
||||||
...
|
...
|
||||||
domains = {
|
domains = {
|
||||||
acme.com = {
|
acme = {
|
||||||
SOGoMailDomain = acme.com;
|
SOGoMailDomain = acme.com;
|
||||||
SOGoDraftsFolderName = Drafts;
|
SOGoDraftsFolderName = Drafts;
|
||||||
SOGoUserSources = (
|
SOGoUserSources = (
|
||||||
|
@ -2167,7 +2165,7 @@ coyote.com):
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
coyote.com = {
|
coyote = {
|
||||||
SOGoMailDomain = coyote.com;
|
SOGoMailDomain = coyote.com;
|
||||||
SOGoIMAPServer = imap.coyote.com;
|
SOGoIMAPServer = imap.coyote.com;
|
||||||
SOGoUserSources = (
|
SOGoUserSources = (
|
||||||
|
@ -2198,7 +2196,7 @@ domains.
|
||||||
[cols="3,47,50a"]
|
[cols="3,47,50a"]
|
||||||
|=======================================================================
|
|=======================================================================
|
||||||
|S |SOGoEnableDomainBasedUID
|
|S |SOGoEnableDomainBasedUID
|
||||||
|Parameter used to enable user identification by domain. Users will be
|
|Parameter used to activate user identification by domain. Users will be
|
||||||
able (without being required) to login using the form `username@domain`,
|
able (without being required) to login using the form `username@domain`,
|
||||||
meaning that values of _UIDFieldName_ no longer have to be unique among
|
meaning that values of _UIDFieldName_ no longer have to be unique among
|
||||||
all domains but only within the same domain. Internally, users will
|
all domains but only within the same domain. Internally, users will
|
||||||
|
@ -2713,11 +2711,6 @@ current version of SOGo from the previous release.
|
||||||
|
|
||||||
[cols="100a"]
|
[cols="100a"]
|
||||||
|=======================================================================
|
|=======================================================================
|
||||||
h|2.3.1
|
|
||||||
|The SOGoCalendarDefaultCategoryColor default has been removed. If you
|
|
||||||
want to customize the color of calendar categories, use the
|
|
||||||
SOGoCalendarCategories and SOGoCalendarCategoriesColors defaults.
|
|
||||||
|
|
||||||
h|2.3.0
|
h|2.3.0
|
||||||
|Run the shell script `sql-update-2.2.17_to_2.3.0.sh` or
|
|Run the shell script `sql-update-2.2.17_to_2.3.0.sh` or
|
||||||
`sql-update-2.2.17_to_2.3.0-mysql.sh` (if you use MySQL).
|
`sql-update-2.2.17_to_2.3.0-mysql.sh` (if you use MySQL).
|
||||||
|
@ -2725,9 +2718,6 @@ h|2.3.0
|
||||||
This will grow the "participant states" field of calendar quick tables to a larger
|
This will grow the "participant states" field of calendar quick tables to a larger
|
||||||
size and add the the "c_description" column to calendar quick tables.
|
size and add the the "c_description" column to calendar quick tables.
|
||||||
|
|
||||||
Moreover, if you are using a multi-domain configuration, make sure the keys for
|
|
||||||
your domains match the email domains you have defined.
|
|
||||||
|
|
||||||
h|2.2.8
|
h|2.2.8
|
||||||
|The configuration configuration parameters were renamed:
|
|The configuration configuration parameters were renamed:
|
||||||
|
|
||||||
|
|
20
NEWS
20
NEWS
|
@ -1,23 +1,3 @@
|
||||||
2.3.1 (2015-06-XX)
|
|
||||||
------------------
|
|
||||||
|
|
||||||
Enhancements
|
|
||||||
- improved EAS speed, especially when fetching big attachments
|
|
||||||
- now always enforce the organizer's default identity in appointments
|
|
||||||
- improved the handling of default calendar categories/colors (#3200)
|
|
||||||
- added support for DeletesAsMoves over EAS
|
|
||||||
|
|
||||||
Bug fixes
|
|
||||||
- EAS's GetItemEstimate/ItemOperations now support fetching mails and empty folders
|
|
||||||
- fixed some rare cornercases in multidomain configurations
|
|
||||||
- properly escape folder after creation using EAS (#3237)
|
|
||||||
- fixed potential organizer highjacking when using EAS (#3131)
|
|
||||||
- properly support big characters in EAS and fix encoding QP EAS error for Outlook (#3082)
|
|
||||||
- properly encode id of DOM elements in Address Book module (#3239, #3245)
|
|
||||||
- fixed multi-domain support for sogo-tool backup/restore (#2600)
|
|
||||||
- fixed data ordering in events list of Calendar module (#3261)
|
|
||||||
- fixed data ordering in tasks list of Calendar module (#3267)
|
|
||||||
|
|
||||||
2.3.0 (2015-06-01)
|
2.3.0 (2015-06-01)
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
# SOGo needs directory in /var/run
|
|
||||||
d /var/run/sogo 0755 sogo sogo
|
|
|
@ -49,7 +49,6 @@ function adjustSchema() {
|
||||||
|
|
||||||
echo "This script will ask for the sql password twice" >&2
|
echo "This script will ask for the sql password twice" >&2
|
||||||
echo "Converting c_partstates from VARCHAR(255) to mediumtext in calendar quick tables" >&2
|
echo "Converting c_partstates from VARCHAR(255) to mediumtext in calendar quick tables" >&2
|
||||||
echo "Adding c_description column as mediumtext in calendar quick tables" >&2
|
|
||||||
tables=`mysql -p -s -u $username -h $hostname $database -e "select SUBSTRING_INDEX(c_quick_location, '/', -1) from $indextable where c_path3 = 'Calendar';"`
|
tables=`mysql -p -s -u $username -h $hostname $database -e "select SUBSTRING_INDEX(c_quick_location, '/', -1) from $indextable where c_path3 = 'Calendar';"`
|
||||||
|
|
||||||
for table in $tables;
|
for table in $tables;
|
|
@ -45,8 +45,7 @@ function adjustSchema() {
|
||||||
IFS="$oldIFS"
|
IFS="$oldIFS"
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "Converting c_partstates from VARCHAR(255) to mediumtext in calendar quick tables" >&2
|
echo "Converting c_cycleinfo from VARCHAR(255) to TEXT in calendar quick tables" >&2
|
||||||
echo "Adding c_description column as mediumtext in calendar quick tables" >&2
|
|
||||||
tables=`psql -t -U $username -h $hostname $database -c "select split_part(c_quick_location, '/', 5) from $indextable where c_path3 = 'Calendar';"`
|
tables=`psql -t -U $username -h $hostname $database -c "select split_part(c_quick_location, '/', 5) from $indextable where c_path3 = 'Calendar';"`
|
||||||
|
|
||||||
for table in $tables;
|
for table in $tables;
|
|
@ -3157,8 +3157,6 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||||
inContainer: self];
|
inContainer: self];
|
||||||
[object setIsNew: YES];
|
[object setIsNew: YES];
|
||||||
content = [NSMutableString stringWithString: @"BEGIN:VCALENDAR\n"];
|
content = [NSMutableString stringWithString: @"BEGIN:VCALENDAR\n"];
|
||||||
[content appendFormat: @"PRODID:-//Inverse inc./SOGo %@//EN\n", SOGoVersion];
|
|
||||||
|
|
||||||
if (timezone)
|
if (timezone)
|
||||||
[content appendFormat: @"%@\n", [timezone versitString]];
|
[content appendFormat: @"%@\n", [timezone versitString]];
|
||||||
[content appendFormat: @"%@\nEND:VCALENDAR", [event versitString]];
|
[content appendFormat: @"%@\nEND:VCALENDAR", [event versitString]];
|
||||||
|
|
|
@ -1820,28 +1820,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
[self warnWithFormat: @"Invalid event: no end date; setting duration to %@", [event duration]];
|
[self warnWithFormat: @"Invalid event: no end date; setting duration to %@", [event duration]];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([event organizer])
|
if ([event organizer] && ![[[event organizer] cn] length])
|
||||||
{
|
{
|
||||||
NSString *uid;
|
[[event organizer] setCn: [[event organizer] rfc822Email]];
|
||||||
|
|
||||||
if (![[[event organizer] cn] length])
|
|
||||||
{
|
|
||||||
[[event organizer] setCn: [[event organizer] rfc822Email]];
|
|
||||||
}
|
|
||||||
|
|
||||||
// We now make sure that the organizer, if managed by SOGo, is using
|
|
||||||
// its default email when creating events and inviting attendees.
|
|
||||||
uid = [[SOGoUserManager sharedUserManager] getUIDForEmail: [[event organizer] rfc822Email]];
|
|
||||||
if (uid)
|
|
||||||
{
|
|
||||||
NSDictionary *defaultIdentity;
|
|
||||||
SOGoUser *organizer;
|
|
||||||
|
|
||||||
organizer = [SOGoUser userWithLogin: uid];
|
|
||||||
defaultIdentity = [organizer defaultIdentity];
|
|
||||||
[[event organizer] setCn: [defaultIdentity objectForKey: @"fullName"]];
|
|
||||||
[[event organizer] setEmail: [defaultIdentity objectForKey: @"email"]];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* iCalAlarm+SOGo.h - this file is part of SOGo
|
/* iCalAlarm+SOGo.h - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2015 Inverse inc.
|
* Copyright (C) 2014 Inverse inc.
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,11 +20,11 @@
|
||||||
|
|
||||||
#import <NGCards/iCalAlarm.h>
|
#import <NGCards/iCalAlarm.h>
|
||||||
|
|
||||||
@class iCalEntityObject;
|
@class iCalRepeatableEntityObject;
|
||||||
|
|
||||||
@interface iCalAlarm (SOGoExtensions)
|
@interface iCalAlarm (SOGoExtensions)
|
||||||
|
|
||||||
+ (id) alarmForEvent: (iCalEntityObject *) theEntity
|
+ (id) alarmForEvent: (iCalRepeatableEntityObject *) theEntity
|
||||||
owner: (NSString *) theOwner
|
owner: (NSString *) theOwner
|
||||||
action: (NSString *) reminderAction
|
action: (NSString *) reminderAction
|
||||||
unit: (NSString *) reminderUnit
|
unit: (NSString *) reminderUnit
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* iCalAlarm+SOGo.m - this file is part of SOGo
|
/* iCalAlarm+SOGo.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2015 Inverse inc.
|
* Copyright (C) 2014 Inverse inc.
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -28,7 +28,6 @@
|
||||||
|
|
||||||
#import <NGCards/iCalPerson.h>
|
#import <NGCards/iCalPerson.h>
|
||||||
#import <NGCards/iCalTrigger.h>
|
#import <NGCards/iCalTrigger.h>
|
||||||
#import <NGCards/iCalEntityObject.h>
|
|
||||||
|
|
||||||
@implementation iCalAlarm (SOGoExtensions)
|
@implementation iCalAlarm (SOGoExtensions)
|
||||||
|
|
||||||
|
@ -66,7 +65,7 @@
|
||||||
[alarm addChild: aAttendee];
|
[alarm addChild: aAttendee];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id) alarmForEvent: (iCalEntityObject *) theEntity
|
+ (id) alarmForEvent: (iCalRepeatableEntityObject *) theEntity
|
||||||
owner: (NSString *) theOwner
|
owner: (NSString *) theOwner
|
||||||
action: (NSString *) reminderAction
|
action: (NSString *) reminderAction
|
||||||
unit: (NSString *) reminderUnit
|
unit: (NSString *) reminderUnit
|
||||||
|
|
|
@ -777,6 +777,8 @@ static BOOL debugSoParts = NO;
|
||||||
filename = [NSString stringWithFormat: @"unknown_%@", path];
|
filename = [NSString stringWithFormat: @"unknown_%@", path];
|
||||||
else if ([mimeType isEqualToString: @"message/rfc822"])
|
else if ([mimeType isEqualToString: @"message/rfc822"])
|
||||||
filename = [NSString stringWithFormat: @"email_%@.eml", path];
|
filename = [NSString stringWithFormat: @"email_%@.eml", path];
|
||||||
|
else if ([mimeType isEqualToString: @"text/calendar"])
|
||||||
|
filename = [NSString stringWithFormat: @"calendar_%@.ics", path];
|
||||||
|
|
||||||
|
|
||||||
if (filename)
|
if (filename)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* NSString+Utilities.h - this file is part of SOGo
|
/* NSString+Utilities.h - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2006-2015 Inverse inc.
|
* Copyright (C) 2006-2014 Inverse inc.
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -46,9 +46,6 @@
|
||||||
- (NSString *) asCSSIdentifier;
|
- (NSString *) asCSSIdentifier;
|
||||||
- (NSString *) fromCSSIdentifier;
|
- (NSString *) fromCSSIdentifier;
|
||||||
|
|
||||||
/* JavaScript safety */
|
|
||||||
- (NSString *) asSafeJSString;
|
|
||||||
|
|
||||||
/* SQL safety */
|
/* SQL safety */
|
||||||
- (NSString *) asSafeSQLString;
|
- (NSString *) asSafeSQLString;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* NSString+Utilities.m - this file is part of SOGo
|
/* NSString+Utilities.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2006-2015 Inverse inc.
|
* Copyright (C) 2006-2014 Inverse inc.
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -257,7 +257,7 @@ static int cssEscapingCount;
|
||||||
return selfCopy;
|
return selfCopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *) asSafeJSString
|
- (NSString *) doubleQuotedString
|
||||||
{
|
{
|
||||||
NSMutableString *representation;
|
NSMutableString *representation;
|
||||||
|
|
||||||
|
@ -270,12 +270,7 @@ static int cssEscapingCount;
|
||||||
[representation replaceString: @"\r" withString: @"\\r"];
|
[representation replaceString: @"\r" withString: @"\\r"];
|
||||||
[representation replaceString: @"\t" withString: @"\\t"];
|
[representation replaceString: @"\t" withString: @"\\t"];
|
||||||
|
|
||||||
return representation;
|
return [NSString stringWithFormat: @"\"%@\"", representation];
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *) doubleQuotedString
|
|
||||||
{
|
|
||||||
return [NSString stringWithFormat: @"\"%@\"", [self asSafeJSString]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -338,18 +333,12 @@ static int cssEscapingCount;
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
strings = [NSArray arrayWithObjects: @"_U_", @"_D_", @"_H_", @"_A_", @"_S_",
|
strings = [NSArray arrayWithObjects: @"_U_", @"_D_", @"_H_", @"_A_", @"_S_",
|
||||||
@"_C_", @"_SC_",
|
@"_C_", @"_CO_", @"_SP_", @"_SQ_", @"_AM_", @"_P_", @"_DS_", nil];
|
||||||
@"_CO_", @"_SP_", @"_SQ_", @"_DQ_",
|
|
||||||
@"_LP_", @"_RP_", @"_LS_", @"_RS_", @"_LC_", @"_RC_",
|
|
||||||
@"_AM_", @"_P_", @"_DS_", nil];
|
|
||||||
[strings retain];
|
[strings retain];
|
||||||
cssEscapingStrings = [strings asPointersOfObjects];
|
cssEscapingStrings = [strings asPointersOfObjects];
|
||||||
|
|
||||||
characters = [NSArray arrayWithObjects: @"_", @".", @"#", @"@", @"*",
|
characters = [NSArray arrayWithObjects: @"_", @".", @"#", @"@", @"*", @":",
|
||||||
@":", @";",
|
@",", @" ", @"'", @"&", @"+", @"$", nil];
|
||||||
@",", @" ", @"'", @"\"",
|
|
||||||
@"(", @")", @"[", @"]", @"{", @"}",
|
|
||||||
@"&", @"+", @"$", nil];
|
|
||||||
cssEscapingCount = [strings count];
|
cssEscapingCount = [strings count];
|
||||||
cssEscapingCharacters = NSZoneMalloc (NULL,
|
cssEscapingCharacters = NSZoneMalloc (NULL,
|
||||||
(cssEscapingCount + 1)
|
(cssEscapingCount + 1)
|
||||||
|
|
|
@ -70,6 +70,7 @@
|
||||||
|
|
||||||
SOGoMailAutoSave = "5";
|
SOGoMailAutoSave = "5";
|
||||||
|
|
||||||
|
SOGoCalendarDefaultCategoryColor = "#aaa";
|
||||||
SOGoCalendarShouldDisplayWeekend = YES;
|
SOGoCalendarShouldDisplayWeekend = YES;
|
||||||
SOGoCalendarEventsDefaultClassification = "PUBLIC";
|
SOGoCalendarEventsDefaultClassification = "PUBLIC";
|
||||||
SOGoCalendarTasksDefaultClassification = "PUBLIC";
|
SOGoCalendarTasksDefaultClassification = "PUBLIC";
|
||||||
|
@ -87,9 +88,5 @@
|
||||||
$label5 = ("Later", "#993399");
|
$label5 = ("Later", "#993399");
|
||||||
};
|
};
|
||||||
|
|
||||||
SOGoCalendarCategories = ("Customer", "Calls", "Favorites", "Meeting", "Ideas", "Miscellaneous", "Birthday", "Anniversary", "Vacation", "Travel", "Projects", "Suppliers", "Gifts", "Clients", "Issues", "Business", "Holidays", "Personal", "Status", "Competition", "Follow up", "Public Holiday");
|
|
||||||
|
|
||||||
SOGoCalendarCategoriesColors = { "Customer" = "#F0F0F0"; "Calls" = "#F0F0F0"; "Favorites" = "#F0F0F0"; "Meeting" = "#F0F0F0"; "Ideas" = "#F0F0F0"; "Miscellaneous" = "#F0F0F0"; "Birthday" = "#F0F0F0"; "Anniversary" = "#F0F0F0"; "Vacation" = "#F0F0F0"; "Travel" = "#F0F0F0"; "Projects" = "#F0F0F0"; "Suppliers" = "#F0F0F0"; "Gifts" = "#F0F0F0"; "Clients" = "#F0F0F0"; "Issues" = "#F0F0F0"; "Business" = "#F0F0F0"; "Holidays" = "#F0F0F0"; "Personal" = "#F0F0F0"; "Status" = "#F0F0F0"; "Competition" = "#F0F0F0"; "Follow up" = "#F0F0F0"; "Public Holiday" = "#F0F0F0"; };
|
|
||||||
|
|
||||||
SOGoSubscriptionFolderFormat = "%{FolderName} (%{UserName} <%{Email}>)";
|
SOGoSubscriptionFolderFormat = "%{FolderName} (%{UserName} <%{Email}>)";
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,8 @@
|
||||||
- (NSArray *) refreshViewIntervals;
|
- (NSArray *) refreshViewIntervals;
|
||||||
- (NSString *) subscriptionFolderFormat;
|
- (NSString *) subscriptionFolderFormat;
|
||||||
|
|
||||||
|
- (NSString *) calendarDefaultCategoryColor;
|
||||||
|
|
||||||
- (NSArray *) freeBusyDefaultInterval;
|
- (NSArray *) freeBusyDefaultInterval;
|
||||||
- (int) davCalendarStartTimeLimit;
|
- (int) davCalendarStartTimeLimit;
|
||||||
|
|
||||||
|
|
|
@ -294,6 +294,11 @@
|
||||||
return [self stringForKey: @"SOGoLDAPContactInfoAttribute"];
|
return [self stringForKey: @"SOGoLDAPContactInfoAttribute"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSString *) calendarDefaultCategoryColor
|
||||||
|
{
|
||||||
|
return [self stringForKey: @"SOGoCalendarDefaultCategoryColor"];
|
||||||
|
}
|
||||||
|
|
||||||
- (NSArray *) freeBusyDefaultInterval
|
- (NSArray *) freeBusyDefaultInterval
|
||||||
{
|
{
|
||||||
return [self arrayForKey: @"SOGoFreeBusyDefaultInterval"];
|
return [self arrayForKey: @"SOGoFreeBusyDefaultInterval"];
|
||||||
|
|
|
@ -165,9 +165,10 @@
|
||||||
// The domain is probably appended to the username;
|
// The domain is probably appended to the username;
|
||||||
// make sure it is defined as a domain in the configuration.
|
// make sure it is defined as a domain in the configuration.
|
||||||
domain = [newLogin substringFromIndex: (r.location + r.length)];
|
domain = [newLogin substringFromIndex: (r.location + r.length)];
|
||||||
if ([[sd domainIds] containsObject: domain] &&
|
if ([[sd domainIds] containsObject: domain])
|
||||||
![sd enableDomainBasedUID])
|
|
||||||
newLogin = [newLogin substringToIndex: r.location];
|
newLogin = [newLogin substringToIndex: r.location];
|
||||||
|
else
|
||||||
|
domain = nil;
|
||||||
|
|
||||||
if (domain != nil && ![sd enableDomainBasedUID])
|
if (domain != nil && ![sd enableDomainBasedUID])
|
||||||
// Login domains are enabled (SOGoLoginDomains) but not
|
// Login domains are enabled (SOGoLoginDomains) but not
|
||||||
|
@ -196,25 +197,8 @@
|
||||||
// When the user is associated to a domain, the [SOGoUser login]
|
// When the user is associated to a domain, the [SOGoUser login]
|
||||||
// method returns the combination login@domain while
|
// method returns the combination login@domain while
|
||||||
// [SOGoUser loginInDomain] only returns the login.
|
// [SOGoUser loginInDomain] only returns the login.
|
||||||
r = [realUID rangeOfString: domain options: NSBackwardsSearch|NSCaseInsensitiveSearch];
|
uid = [NSString stringWithString: realUID];
|
||||||
|
realUID = [NSString stringWithFormat: @"%@@%@", realUID, domain];
|
||||||
// Do NOT strip @domain.com if SOGoEnableDomainBasedUID is enabled since
|
|
||||||
// the real login most likely is the email address.
|
|
||||||
if (r.location != NSNotFound && ![sd enableDomainBasedUID])
|
|
||||||
uid = [realUID substringToIndex: r.location-1];
|
|
||||||
// If we don't have the domain in the UID but SOGoEnableDomainBasedUID is
|
|
||||||
// enabled, let's add it internally so so it becomes unique across
|
|
||||||
// all potential domains.
|
|
||||||
else if (r.location == NSNotFound && [sd enableDomainBasedUID])
|
|
||||||
{
|
|
||||||
uid = [NSString stringWithString: realUID];
|
|
||||||
realUID = [NSString stringWithFormat: @"%@@%@", realUID, domain];
|
|
||||||
}
|
|
||||||
// We found the domain and SOGoEnableDomainBasedUID is enabled,
|
|
||||||
// we keep realUID.. This would happen for example if the user
|
|
||||||
// authenticates with foo@bar.com and the UIDFieldName is also foo@bar.com
|
|
||||||
else if ([sd enableDomainBasedUID])
|
|
||||||
uid = [NSString stringWithString: realUID];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -702,7 +702,7 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
|
||||||
|
|
||||||
- (NSDictionary *) calendarCategoriesColors
|
- (NSDictionary *) calendarCategoriesColors
|
||||||
{
|
{
|
||||||
return [self objectForKey: @"SOGoCalendarCategoriesColors"];
|
return [self dictionaryForKey: @"SOGoCalendarCategoriesColors"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) setCalendarShouldDisplayWeekend: (BOOL) newValue
|
- (void) setCalendarShouldDisplayWeekend: (BOOL) newValue
|
||||||
|
|
|
@ -494,10 +494,10 @@ static Class NSNullK;
|
||||||
NSMutableDictionary *currentUser;
|
NSMutableDictionary *currentUser;
|
||||||
NSDictionary *failedCount;
|
NSDictionary *failedCount;
|
||||||
NSString *dictPassword, *username, *jsonUser;
|
NSString *dictPassword, *username, *jsonUser;
|
||||||
SOGoSystemDefaults *sd;
|
SOGoSystemDefaults *dd;
|
||||||
BOOL checkOK;
|
BOOL checkOK;
|
||||||
|
|
||||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
dd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||||
|
|
||||||
username = _login;
|
username = _login;
|
||||||
|
|
||||||
|
@ -517,9 +517,21 @@ static Class NSNullK;
|
||||||
|
|
||||||
if (r.location != NSNotFound)
|
if (r.location != NSNotFound)
|
||||||
{
|
{
|
||||||
|
NSArray *allDomains;
|
||||||
|
int i;
|
||||||
|
|
||||||
*_domain = [username substringFromIndex: r.location+1];
|
*_domain = [username substringFromIndex: r.location+1];
|
||||||
|
|
||||||
if (![[[SOGoSystemDefaults sharedSystemDefaults] domainIds] containsObject: *_domain])
|
allDomains = [[dd dictionaryForKey: @"domains"] allValues];
|
||||||
|
|
||||||
|
for (i = 0; i < [allDomains count]; i++)
|
||||||
|
{
|
||||||
|
if ([*_domain isEqualToString: [[allDomains objectAtIndex: i] objectForKey: @"SOGoMailDomain"]])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We haven't found one
|
||||||
|
if (i == [allDomains count])
|
||||||
*_domain = nil;
|
*_domain = nil;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -536,10 +548,10 @@ static Class NSNullK;
|
||||||
start_time = [[failedCount objectForKey: @"InitialDate"] unsignedIntValue];
|
start_time = [[failedCount objectForKey: @"InitialDate"] unsignedIntValue];
|
||||||
delta = current_time - start_time;
|
delta = current_time - start_time;
|
||||||
|
|
||||||
block_time = [sd failedLoginBlockInterval];
|
block_time = [dd failedLoginBlockInterval];
|
||||||
|
|
||||||
if ([[failedCount objectForKey: @"FailedCount"] intValue] >= [sd maximumFailedLoginCount] &&
|
if ([[failedCount objectForKey: @"FailedCount"] intValue] >= [dd maximumFailedLoginCount] &&
|
||||||
delta >= [sd maximumFailedLoginInterval] &&
|
delta >= [dd maximumFailedLoginInterval] &&
|
||||||
delta <= block_time )
|
delta <= block_time )
|
||||||
{
|
{
|
||||||
*_perr = PolicyAccountLocked;
|
*_perr = PolicyAccountLocked;
|
||||||
|
@ -558,28 +570,6 @@ static Class NSNullK;
|
||||||
// authentication source and try to validate there, then cache it.
|
// authentication source and try to validate there, then cache it.
|
||||||
jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: username];
|
jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: username];
|
||||||
currentUser = [jsonUser objectFromJSONString];
|
currentUser = [jsonUser objectFromJSONString];
|
||||||
|
|
||||||
//
|
|
||||||
// If we are using multidomain and the UIDFieldName is not part of the email address
|
|
||||||
// we must bind without the domain part since internally, SOGo will use
|
|
||||||
// UIDFieldName @ domain as its unique identifier if the UIDFieldName is used to
|
|
||||||
// authenticate. This can happen for example of one has in LDAP:
|
|
||||||
//
|
|
||||||
// dn: uid=foo,dc=example,dc=com
|
|
||||||
// uid: foo
|
|
||||||
// mail: broccoli@example.com
|
|
||||||
//
|
|
||||||
// and authenticates with "foo", using bindFields = (uid, mail) and SOGoEnableDomainBasedUID = YES;
|
|
||||||
// Otherwise, -_sourceCheckLogin:... would have failed because SOGo would try to bind using: foo@example.com
|
|
||||||
//
|
|
||||||
if ([[currentUser objectForKey: @"DomainLessLogin"] boolValue])
|
|
||||||
{
|
|
||||||
NSRange r;
|
|
||||||
|
|
||||||
r = [_login rangeOfString: [NSString stringWithFormat: @"@%@", *_domain]];
|
|
||||||
_login = [_login substringToIndex: r.location];
|
|
||||||
}
|
|
||||||
|
|
||||||
dictPassword = [currentUser objectForKey: @"password"];
|
dictPassword = [currentUser objectForKey: @"password"];
|
||||||
if (useCache && currentUser && dictPassword)
|
if (useCache && currentUser && dictPassword)
|
||||||
{
|
{
|
||||||
|
@ -599,18 +589,6 @@ static Class NSNullK;
|
||||||
currentUser = [NSMutableDictionary dictionary];
|
currentUser = [NSMutableDictionary dictionary];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Before caching user attributes, we must check if SOGoEnableDomainBasedUID is enabled
|
|
||||||
// but we don't have a domain. That would happen for example if the user authenticates
|
|
||||||
// without the domain part. We must also cache that information, since SOGo will try
|
|
||||||
// afterward to bind with UIDFieldName@domain, and it could potentially not exist
|
|
||||||
// in the authentication source. See the rationale in _sourceCheckLogin: ...
|
|
||||||
if ([sd enableDomainBasedUID] &&
|
|
||||||
[username rangeOfString: @"@"].location == NSNotFound)
|
|
||||||
{
|
|
||||||
username = [NSString stringWithFormat: @"%@@%@", username, *_domain];
|
|
||||||
[currentUser setObject: [NSNumber numberWithBool: YES] forKey: @"DomainLessLogin"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// It's important to cache the password here as we might have cached the
|
// It's important to cache the password here as we might have cached the
|
||||||
// user's entry in -contactInfosForUserWithUIDorEmail: and if we don't
|
// user's entry in -contactInfosForUserWithUIDorEmail: and if we don't
|
||||||
// set the password and recache the entry, the password would never be
|
// set the password and recache the entry, the password would never be
|
||||||
|
@ -624,7 +602,7 @@ static Class NSNullK;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If failed login "rate-limiting" is enabled, we adjust the stats
|
// If failed login "rate-limiting" is enabled, we adjust the stats
|
||||||
if ([sd maximumFailedLoginCount])
|
if ([dd maximumFailedLoginCount])
|
||||||
{
|
{
|
||||||
[[SOGoCache sharedCache] setFailedCount: ([[failedCount objectForKey: @"FailedCount"] intValue] + 1)
|
[[SOGoCache sharedCache] setFailedCount: ([[failedCount objectForKey: @"FailedCount"] intValue] + 1)
|
||||||
forLogin: username];
|
forLogin: username];
|
||||||
|
@ -732,9 +710,9 @@ static Class NSNullK;
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
- (void) _fillContactInfosForUser: (NSMutableDictionary *) theCurrentUser
|
- (void) _fillContactInfosForUser: (NSMutableDictionary *) currentUser
|
||||||
withUIDorEmail: (NSString *) theUID
|
withUIDorEmail: (NSString *) uid
|
||||||
inDomain: (NSString *) theDomain
|
inDomain: (NSString *) domain
|
||||||
{
|
{
|
||||||
NSString *sourceID, *cn, *c_domain, *c_uid, *c_imaphostname, *c_imaplogin, *c_sievehostname;
|
NSString *sourceID, *cn, *c_domain, *c_uid, *c_imaphostname, *c_imaplogin, *c_sievehostname;
|
||||||
NSObject <SOGoSource> *currentSource;
|
NSObject <SOGoSource> *currentSource;
|
||||||
|
@ -761,28 +739,19 @@ static Class NSNullK;
|
||||||
|
|
||||||
enumerator = [access_types_list objectEnumerator];
|
enumerator = [access_types_list objectEnumerator];
|
||||||
while ((access_type = [enumerator nextObject]) != nil)
|
while ((access_type = [enumerator nextObject]) != nil)
|
||||||
[theCurrentUser setObject: [NSNumber numberWithBool: YES]
|
[currentUser setObject: [NSNumber numberWithBool: YES]
|
||||||
forKey: access_type];
|
forKey: access_type];
|
||||||
|
|
||||||
if ([[theCurrentUser objectForKey: @"DomainLessLogin"] boolValue])
|
sogoSources = [[self authenticationSourceIDsInDomain: domain] objectEnumerator];
|
||||||
{
|
|
||||||
NSRange r;
|
|
||||||
|
|
||||||
r = [theUID rangeOfString: [NSString stringWithFormat: @"@%@", theDomain]];
|
|
||||||
theUID = [theUID substringToIndex: r.location];
|
|
||||||
}
|
|
||||||
|
|
||||||
sogoSources = [[self authenticationSourceIDsInDomain: theDomain] objectEnumerator];
|
|
||||||
userEntry = nil;
|
userEntry = nil;
|
||||||
while (!userEntry && (sourceID = [sogoSources nextObject]))
|
while (!userEntry && (sourceID = [sogoSources nextObject]))
|
||||||
{
|
{
|
||||||
currentSource = [_sources objectForKey: sourceID];
|
currentSource = [_sources objectForKey: sourceID];
|
||||||
|
userEntry = [currentSource lookupContactEntryWithUIDorEmail: uid
|
||||||
userEntry = [currentSource lookupContactEntryWithUIDorEmail: theUID
|
inDomain: domain];
|
||||||
inDomain: theDomain];
|
|
||||||
if (userEntry)
|
if (userEntry)
|
||||||
{
|
{
|
||||||
[theCurrentUser setObject: sourceID forKey: @"SOGoSource"];
|
[currentUser setObject: sourceID forKey: @"SOGoSource"];
|
||||||
if (!cn)
|
if (!cn)
|
||||||
cn = [userEntry objectForKey: @"c_cn"];
|
cn = [userEntry objectForKey: @"c_cn"];
|
||||||
if (!c_uid)
|
if (!c_uid)
|
||||||
|
@ -804,27 +773,27 @@ static Class NSNullK;
|
||||||
{
|
{
|
||||||
access = [[userEntry objectForKey: access_type] boolValue];
|
access = [[userEntry objectForKey: access_type] boolValue];
|
||||||
if (!access)
|
if (!access)
|
||||||
[theCurrentUser setObject: [NSNumber numberWithBool: NO]
|
[currentUser setObject: [NSNumber numberWithBool: NO]
|
||||||
forKey: access_type];
|
forKey: access_type];
|
||||||
}
|
}
|
||||||
|
|
||||||
// We check if it's a group
|
// We check if it's a group
|
||||||
isGroup = [userEntry objectForKey: @"isGroup"];
|
isGroup = [userEntry objectForKey: @"isGroup"];
|
||||||
if (isGroup)
|
if (isGroup)
|
||||||
[theCurrentUser setObject: isGroup forKey: @"isGroup"];
|
[currentUser setObject: isGroup forKey: @"isGroup"];
|
||||||
|
|
||||||
// We also fill the resource attributes, if any
|
// We also fill the resource attributes, if any
|
||||||
if ([userEntry objectForKey: @"isResource"])
|
if ([userEntry objectForKey: @"isResource"])
|
||||||
[theCurrentUser setObject: [userEntry objectForKey: @"isResource"]
|
[currentUser setObject: [userEntry objectForKey: @"isResource"]
|
||||||
forKey: @"isResource"];
|
forKey: @"isResource"];
|
||||||
if ([userEntry objectForKey: @"numberOfSimultaneousBookings"])
|
if ([userEntry objectForKey: @"numberOfSimultaneousBookings"])
|
||||||
[theCurrentUser setObject: [userEntry objectForKey: @"numberOfSimultaneousBookings"]
|
[currentUser setObject: [userEntry objectForKey: @"numberOfSimultaneousBookings"]
|
||||||
forKey: @"numberOfSimultaneousBookings"];
|
forKey: @"numberOfSimultaneousBookings"];
|
||||||
|
|
||||||
// This is Active Directory specific attribute (needed on OpenChange/* layer)
|
// This is Active Directory specific attribute (needed on OpenChange/* layer)
|
||||||
if ([userEntry objectForKey: @"samaccountname"])
|
if ([userEntry objectForKey: @"samaccountname"])
|
||||||
[theCurrentUser setObject: [userEntry objectForKey: @"samaccountname"]
|
[currentUser setObject: [userEntry objectForKey: @"samaccountname"]
|
||||||
forKey: @"sAMAccountName"];
|
forKey: @"sAMAccountName"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -836,20 +805,20 @@ static Class NSNullK;
|
||||||
c_domain = @"";
|
c_domain = @"";
|
||||||
|
|
||||||
if (c_imaphostname)
|
if (c_imaphostname)
|
||||||
[theCurrentUser setObject: c_imaphostname forKey: @"c_imaphostname"];
|
[currentUser setObject: c_imaphostname forKey: @"c_imaphostname"];
|
||||||
if (c_imaplogin)
|
if (c_imaplogin)
|
||||||
[theCurrentUser setObject: c_imaplogin forKey: @"c_imaplogin"];
|
[currentUser setObject: c_imaplogin forKey: @"c_imaplogin"];
|
||||||
if (c_sievehostname)
|
if (c_sievehostname)
|
||||||
[theCurrentUser setObject: c_sievehostname forKey: @"c_sievehostname"];
|
[currentUser setObject: c_sievehostname forKey: @"c_sievehostname"];
|
||||||
|
|
||||||
[theCurrentUser setObject: emails forKey: @"emails"];
|
[currentUser setObject: emails forKey: @"emails"];
|
||||||
[theCurrentUser setObject: cn forKey: @"cn"];
|
[currentUser setObject: cn forKey: @"cn"];
|
||||||
[theCurrentUser setObject: c_uid forKey: @"c_uid"];
|
[currentUser setObject: c_uid forKey: @"c_uid"];
|
||||||
[theCurrentUser setObject: c_domain forKey: @"c_domain"];
|
[currentUser setObject: c_domain forKey: @"c_domain"];
|
||||||
|
|
||||||
// If our LDAP queries gave us nothing, we add at least one default
|
// If our LDAP queries gave us nothing, we add at least one default
|
||||||
// email address based on the default domain.
|
// email address based on the default domain.
|
||||||
[self _fillContactMailRecords: theCurrentUser];
|
[self _fillContactMailRecords: currentUser];
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -943,9 +912,8 @@ static Class NSNullK;
|
||||||
- (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid
|
- (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid
|
||||||
inDomain: (NSString *) domain
|
inDomain: (NSString *) domain
|
||||||
{
|
{
|
||||||
NSString *aUID, *cacheUid, *jsonUser;
|
|
||||||
NSMutableDictionary *currentUser;
|
NSMutableDictionary *currentUser;
|
||||||
|
NSString *aUID, *cacheUid, *jsonUser;
|
||||||
BOOL newUser;
|
BOOL newUser;
|
||||||
|
|
||||||
if ([uid isEqualToString: @"anonymous"])
|
if ([uid isEqualToString: @"anonymous"])
|
||||||
|
@ -954,14 +922,12 @@ static Class NSNullK;
|
||||||
{
|
{
|
||||||
// Remove the "@" prefix used to identified groups in the ACL tables.
|
// Remove the "@" prefix used to identified groups in the ACL tables.
|
||||||
aUID = [uid hasPrefix: @"@"] ? [uid substringFromIndex: 1] : uid;
|
aUID = [uid hasPrefix: @"@"] ? [uid substringFromIndex: 1] : uid;
|
||||||
if (domain && [aUID rangeOfString: @"@"].location == NSNotFound)
|
if (domain)
|
||||||
cacheUid = [NSString stringWithFormat: @"%@@%@", aUID, domain];
|
cacheUid = [NSString stringWithFormat: @"%@@%@", aUID, domain];
|
||||||
else
|
else
|
||||||
cacheUid = aUID;
|
cacheUid = aUID;
|
||||||
|
|
||||||
jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: cacheUid];
|
jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: cacheUid];
|
||||||
currentUser = [jsonUser objectFromJSONString];
|
currentUser = [jsonUser objectFromJSONString];
|
||||||
|
|
||||||
if ([currentUser isKindOfClass: NSNullK])
|
if ([currentUser isKindOfClass: NSNullK])
|
||||||
currentUser = nil;
|
currentUser = nil;
|
||||||
else if (!([currentUser objectForKey: @"emails"]
|
else if (!([currentUser objectForKey: @"emails"]
|
||||||
|
@ -971,10 +937,8 @@ static Class NSNullK;
|
||||||
// that we have an occurence with only a cached password. In the
|
// that we have an occurence with only a cached password. In the
|
||||||
// latter case, we update the entry with the remaining information
|
// latter case, we update the entry with the remaining information
|
||||||
// and recache the value.
|
// and recache the value.
|
||||||
if (!currentUser ||
|
if (!currentUser || ([currentUser count] == 1 && [currentUser objectForKey: @"password"]))
|
||||||
([currentUser count] == 1 && [currentUser objectForKey: @"password"]) ||
|
{
|
||||||
([currentUser count] == 2 && [currentUser objectForKey: @"password"] && [currentUser objectForKey: @"DomainLessLogin"]))
|
|
||||||
{
|
|
||||||
newUser = YES;
|
newUser = YES;
|
||||||
|
|
||||||
if (!currentUser)
|
if (!currentUser)
|
||||||
|
@ -994,22 +958,9 @@ static Class NSNullK;
|
||||||
currentUser = nil;
|
currentUser = nil;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
[self _retainUser: currentUser
|
||||||
SOGoSystemDefaults *sd;
|
withLogin: cacheUid];
|
||||||
|
}
|
||||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
|
||||||
|
|
||||||
// SOGoEnableDomainBasedUID is set to YES but we don't have a domain part. This happens in
|
|
||||||
// multi-domain environments authenticating only with the UIDFieldName
|
|
||||||
if ([sd enableDomainBasedUID] && !domain)
|
|
||||||
{
|
|
||||||
cacheUid = [NSString stringWithFormat: @"%@@%@", cacheUid, [currentUser objectForKey: @"c_domain"]];
|
|
||||||
[currentUser setObject: [NSNumber numberWithBool: YES] forKey: @"DomainLessLogin"];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self _retainUser: currentUser withLogin: cacheUid];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
/* SOGoToolBackup.m - this file is part of SOGo
|
/* SOGoToolBackup.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2009-2015 Inverse inc.
|
* Copyright (C) 2009-2011 Inverse inc.
|
||||||
|
*
|
||||||
|
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
|
* Francis Lachapelle <flachapelle@inverse.ca>
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -39,7 +42,6 @@
|
||||||
#import <SOGo/SOGoUserDefaults.h>
|
#import <SOGo/SOGoUserDefaults.h>
|
||||||
#import <SOGo/SOGoUserProfile.h>
|
#import <SOGo/SOGoUserProfile.h>
|
||||||
#import <SOGo/SOGoUserSettings.h>
|
#import <SOGo/SOGoUserSettings.h>
|
||||||
#import <SOGo/SOGoSystemDefaults.h>
|
|
||||||
#import <Contacts/NSDictionary+LDIF.h>
|
#import <Contacts/NSDictionary+LDIF.h>
|
||||||
|
|
||||||
#import "SOGoTool.h"
|
#import "SOGoTool.h"
|
||||||
|
@ -53,7 +55,7 @@
|
||||||
@interface SOGoToolBackup : SOGoTool
|
@interface SOGoToolBackup : SOGoTool
|
||||||
{
|
{
|
||||||
NSString *directory;
|
NSString *directory;
|
||||||
NSArray *usersToBackup;
|
NSArray *userIDs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -81,7 +83,7 @@
|
||||||
if ((self = [super init]))
|
if ((self = [super init]))
|
||||||
{
|
{
|
||||||
directory = nil;
|
directory = nil;
|
||||||
usersToBackup = nil;
|
userIDs = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
@ -90,7 +92,7 @@
|
||||||
- (void) dealloc
|
- (void) dealloc
|
||||||
{
|
{
|
||||||
[directory release];
|
[directory release];
|
||||||
[usersToBackup release];
|
[userIDs release];
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +144,6 @@
|
||||||
lm = [SOGoUserManager sharedUserManager];
|
lm = [SOGoUserManager sharedUserManager];
|
||||||
pool = [[NSAutoreleasePool alloc] init];
|
pool = [[NSAutoreleasePool alloc] init];
|
||||||
|
|
||||||
|
|
||||||
max = [users count];
|
max = [users count];
|
||||||
user = [users objectAtIndex: 0];
|
user = [users objectAtIndex: 0];
|
||||||
if (max == 1 && [user isEqualToString: @"ALL"])
|
if (max == 1 && [user isEqualToString: @"ALL"])
|
||||||
|
@ -198,10 +199,10 @@
|
||||||
}
|
}
|
||||||
[allUsers autorelease];
|
[allUsers autorelease];
|
||||||
|
|
||||||
ASSIGN (usersToBackup, allUsers);
|
ASSIGN (userIDs, [allUsers objectsForKey: @"c_uid" notFoundMarker: nil]);
|
||||||
DESTROY(pool);
|
DESTROY(pool);
|
||||||
|
|
||||||
return ([usersToBackup count] > 0);
|
return ([userIDs count] > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL) parseArguments
|
- (BOOL) parseArguments
|
||||||
|
@ -409,29 +410,19 @@
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL) exportUser: (NSDictionary *) theUser
|
- (BOOL) exportUser: (NSString *) uid
|
||||||
{
|
{
|
||||||
NSString *exportPath, *gcsUID, *ldapUID;
|
|
||||||
NSMutableDictionary *userRecord;
|
NSMutableDictionary *userRecord;
|
||||||
SOGoSystemDefaults *sd;
|
NSString *exportPath;
|
||||||
|
|
||||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
|
||||||
userRecord = [NSMutableDictionary dictionary];
|
userRecord = [NSMutableDictionary dictionary];
|
||||||
|
exportPath = [directory stringByAppendingPathComponent: uid];
|
||||||
|
|
||||||
ldapUID = [theUser objectForKey: @"c_uid"];
|
return ([self extractUserFolders: uid
|
||||||
exportPath = [directory stringByAppendingPathComponent: ldapUID];
|
|
||||||
|
|
||||||
gcsUID = [theUser objectForKey: @"c_uid"];
|
|
||||||
|
|
||||||
if ([sd enableDomainBasedUID] && [gcsUID rangeOfString: @"@"].location == NSNotFound)
|
|
||||||
gcsUID = [NSString stringWithFormat: @"%@@%@", gcsUID, [theUser objectForKey: @"c_domain"]];
|
|
||||||
|
|
||||||
|
|
||||||
return ([self extractUserFolders: gcsUID
|
|
||||||
intoRecord: userRecord]
|
intoRecord: userRecord]
|
||||||
&& [self extractUserLDIFRecord: ldapUID
|
&& [self extractUserLDIFRecord: uid
|
||||||
intoRecord: userRecord]
|
intoRecord: userRecord]
|
||||||
&& [self extractUserPreferences: gcsUID
|
&& [self extractUserPreferences: uid
|
||||||
intoRecord: userRecord]
|
intoRecord: userRecord]
|
||||||
&& [userRecord writeToFile: exportPath
|
&& [userRecord writeToFile: exportPath
|
||||||
atomically: NO]);
|
atomically: NO]);
|
||||||
|
@ -447,10 +438,10 @@
|
||||||
|
|
||||||
pool = [NSAutoreleasePool new];
|
pool = [NSAutoreleasePool new];
|
||||||
|
|
||||||
max = [usersToBackup count];
|
max = [userIDs count];
|
||||||
for (count = 0; rc && count < max; count++)
|
for (count = 0; rc && count < max; count++)
|
||||||
{
|
{
|
||||||
rc = [self exportUser: [usersToBackup objectAtIndex: count]];
|
rc = [self exportUser: [userIDs objectAtIndex: count]];
|
||||||
if ((count % 10) == 0)
|
if ((count % 10) == 0)
|
||||||
[pool emptyPool];
|
[pool emptyPool];
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@ typedef enum SOGoToolRestoreMode {
|
||||||
{
|
{
|
||||||
NSString *directory;
|
NSString *directory;
|
||||||
NSString *userID;
|
NSString *userID;
|
||||||
NSString *filename;
|
|
||||||
NSString *restoreFolder;
|
NSString *restoreFolder;
|
||||||
BOOL destructive; /* destructive mode not handled */
|
BOOL destructive; /* destructive mode not handled */
|
||||||
SOGoToolRestoreMode restoreMode;
|
SOGoToolRestoreMode restoreMode;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SOGoToolRestore.m - this file is part of SOGo
|
/* SOGoToolRestore.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2009-2015 Inverse inc.
|
* Copyright (C) 2009-2014 Inverse inc.
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -73,7 +73,6 @@
|
||||||
{
|
{
|
||||||
directory = nil;
|
directory = nil;
|
||||||
userID = nil;
|
userID = nil;
|
||||||
filename = nil;
|
|
||||||
restoreFolder = nil;
|
restoreFolder = nil;
|
||||||
destructive = NO;
|
destructive = NO;
|
||||||
}
|
}
|
||||||
|
@ -85,7 +84,6 @@
|
||||||
{
|
{
|
||||||
[directory release];
|
[directory release];
|
||||||
[userID release];
|
[userID release];
|
||||||
[filename release];
|
|
||||||
[restoreFolder release];
|
[restoreFolder release];
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
@ -154,35 +152,25 @@
|
||||||
|
|
||||||
- (BOOL) fetchUserID: (NSString *) identifier
|
- (BOOL) fetchUserID: (NSString *) identifier
|
||||||
{
|
{
|
||||||
SOGoSystemDefaults *sd;
|
BOOL rc;
|
||||||
SOGoUserManager *lm;
|
SOGoUserManager *lm;
|
||||||
NSDictionary *infos;
|
NSDictionary *infos;
|
||||||
|
SOGoSystemDefaults *sd;
|
||||||
NSString *uid = nil;
|
NSString *uid = nil;
|
||||||
|
|
||||||
BOOL rc;
|
|
||||||
|
|
||||||
lm = [SOGoUserManager sharedUserManager];
|
lm = [SOGoUserManager sharedUserManager];
|
||||||
infos = [lm contactInfosForUserWithUIDorEmail: identifier];
|
infos = [lm contactInfosForUserWithUIDorEmail: identifier];
|
||||||
uid = nil;
|
|
||||||
|
|
||||||
if (infos)
|
if (infos)
|
||||||
{
|
{
|
||||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||||
uid = [infos objectForKey: @"c_uid"];
|
if ([sd enableDomainBasedUID])
|
||||||
|
|
||||||
if ([sd enableDomainBasedUID] && [uid rangeOfString: @"@"].location == NSNotFound)
|
|
||||||
uid = [NSString stringWithFormat: @"%@@%@",
|
uid = [NSString stringWithFormat: @"%@@%@",
|
||||||
[infos objectForKey: @"c_uid"],
|
[infos objectForKey: @"c_uid"],
|
||||||
[infos objectForKey: @"c_domain"]];
|
[infos objectForKey: @"c_domain"]];
|
||||||
|
|
||||||
if ([[infos objectForKey: @"DomainLessLogin"] boolValue])
|
|
||||||
ASSIGN(filename, [infos objectForKey: @"c_uid"]);
|
|
||||||
else
|
else
|
||||||
ASSIGN(filename, uid);
|
uid = [infos objectForKey: @"c_uid"];
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSIGN (userID, uid);
|
ASSIGN (userID, uid);
|
||||||
|
|
||||||
if (userID)
|
if (userID)
|
||||||
rc = YES;
|
rc = YES;
|
||||||
else
|
else
|
||||||
|
@ -620,7 +608,7 @@
|
||||||
NSString *importPath;
|
NSString *importPath;
|
||||||
BOOL rc;
|
BOOL rc;
|
||||||
|
|
||||||
importPath = [directory stringByAppendingPathComponent: filename];
|
importPath = [directory stringByAppendingPathComponent: userID];
|
||||||
userRecord = [NSDictionary dictionaryWithContentsOfFile: importPath];
|
userRecord = [NSDictionary dictionaryWithContentsOfFile: importPath];
|
||||||
if (userRecord)
|
if (userRecord)
|
||||||
{
|
{
|
||||||
|
@ -638,7 +626,7 @@
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
rc = NO;
|
rc = NO;
|
||||||
NSLog(@"user backup (%@) file could not be loaded", importPath);
|
NSLog (@"user backup file could not be loaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
/* sogo-tool.m - this file is part of SOGo
|
/* sogo-tool.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2009-2015 Inverse inc.
|
* Copyright (C) 2009 Inverse inc.
|
||||||
|
*
|
||||||
|
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||||
Copyright (C) 2005-2015 Inverse inc.
|
Copyright (C) 2005-2010 Inverse inc.
|
||||||
|
|
||||||
This file is part of SOGo
|
This file is part of SOGo
|
||||||
|
|
||||||
|
@ -378,8 +378,9 @@ static Class SOGoContactGCSEntryK = Nil;
|
||||||
result = [self redirectToLocation: [self modulePath]];
|
result = [self redirectToLocation: [self modulePath]];
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
jsRefreshMethod = [NSString stringWithFormat: @"refreshContacts('%@')",
|
jsRefreshMethod
|
||||||
[contact nameInContainer]];
|
= [NSString stringWithFormat: @"refreshContacts(\"%@\")",
|
||||||
|
[contact nameInContainer]];
|
||||||
result = [self jsCloseWithRefreshMethod: jsRefreshMethod];
|
result = [self jsCloseWithRefreshMethod: jsRefreshMethod];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2004 SKYRIX Software AG
|
Copyright (C) 2004 SKYRIX Software AG
|
||||||
Copyright (C) 2005-2015 Inverse inc.
|
Copyright (C) 2005-2014 Inverse inc.
|
||||||
|
|
||||||
This file is part of SOGo.
|
This file is part of SOGo.
|
||||||
|
|
||||||
|
@ -138,13 +138,9 @@
|
||||||
if ([email length] > 0)
|
if ([email length] > 0)
|
||||||
{
|
{
|
||||||
fn = [card fn];
|
fn = [card fn];
|
||||||
if ([fn length] > 0)
|
fn = [fn stringByReplacingString: @"\"" withString: @""];
|
||||||
attrs = [NSString stringWithFormat: @"%@ <%@>", fn, email];
|
fn = [fn stringByReplacingString: @"'" withString: @"\\\'"];
|
||||||
else
|
attrs = [NSString stringWithFormat: @"onclick=\"return openMailTo('%@ <%@>');\"", fn, email];
|
||||||
attrs = email;
|
|
||||||
attrs = [attrs stringByReplacingString: @"'" withString: @"\\'"];
|
|
||||||
attrs = [attrs stringByReplacingString: @"\"" withString: @"\\\""];
|
|
||||||
attrs = [NSString stringWithFormat: @"onclick=\"return openMailTo('%@');\"", attrs];
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -185,23 +181,16 @@
|
||||||
for (i = 0; i < [emails count]; i++)
|
for (i = 0; i < [emails count]; i++)
|
||||||
{
|
{
|
||||||
email = [[emails objectAtIndex: i] flattenedValuesForKey: @""];
|
email = [[emails objectAtIndex: i] flattenedValuesForKey: @""];
|
||||||
if ([email length])
|
fn = [card fn];
|
||||||
{
|
fn = [fn stringByReplacingString: @"\"" withString: @""];
|
||||||
fn = [card fn];
|
fn = [fn stringByReplacingString: @"'" withString: @"\\\'"];
|
||||||
if ([fn length])
|
attrs = [NSString stringWithFormat: @"onclick=\"return openMailTo('%@ <%@>');\"", fn, email];
|
||||||
attrs = [NSString stringWithFormat: @"%@ <%@>", fn, email];
|
|
||||||
else
|
|
||||||
attrs = email;
|
|
||||||
attrs = [attrs stringByReplacingString: @"'" withString: @"\\'"];
|
|
||||||
attrs = [attrs stringByReplacingString: @"\"" withString: @"\\\""];
|
|
||||||
attrs = [NSString stringWithFormat: @"onclick=\"return openMailTo('%@');\"", attrs];
|
|
||||||
|
|
||||||
[secondaryEmails addObject: [self _cardStringWithLabel: nil
|
[secondaryEmails addObject: [self _cardStringWithLabel: nil
|
||||||
value: email
|
value: email
|
||||||
byEscapingHTMLString: YES
|
byEscapingHTMLString: YES
|
||||||
asLinkScheme: @"mailto:"
|
asLinkScheme: @"mailto:"
|
||||||
withLinkAttributes: attrs]];
|
withLinkAttributes: attrs]];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* UIxListEditor.m - this file is part of SOGo
|
/* UIxListEditor.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2008-2015 Inverse inc.
|
* Copyright (C) 2008-2014 Inverse inc.
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -29,8 +29,6 @@
|
||||||
#import <NGCards/NGVCardReference.h>
|
#import <NGCards/NGVCardReference.h>
|
||||||
#import <NGCards/NGVList.h>
|
#import <NGCards/NGVList.h>
|
||||||
|
|
||||||
#import <SOGo/NSString+Utilities.h>
|
|
||||||
|
|
||||||
#import <Contacts/SOGoContactGCSEntry.h>
|
#import <Contacts/SOGoContactGCSEntry.h>
|
||||||
#import <Contacts/SOGoContactGCSFolder.h>
|
#import <Contacts/SOGoContactGCSFolder.h>
|
||||||
#import <Contacts/SOGoContactGCSList.h>
|
#import <Contacts/SOGoContactGCSList.h>
|
||||||
|
@ -291,8 +289,9 @@
|
||||||
result = [self redirectToLocation: [self modulePath]];
|
result = [self redirectToLocation: [self modulePath]];
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
jsRefreshMethod = [NSString stringWithFormat: @"refreshContacts('%@')",
|
jsRefreshMethod
|
||||||
[co nameInContainer]];
|
= [NSString stringWithFormat: @"refreshContacts(\"%@\")",
|
||||||
|
[co nameInContainer]];
|
||||||
result = [self jsCloseWithRefreshMethod: jsRefreshMethod];
|
result = [self jsCloseWithRefreshMethod: jsRefreshMethod];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -463,7 +463,7 @@
|
||||||
if (!activeUserIsInDomain || ![uid isEqualToString: login])
|
if (!activeUserIsInDomain || ![uid isEqualToString: login])
|
||||||
{
|
{
|
||||||
jsonLine = [NSMutableArray arrayWithCapacity: 4];
|
jsonLine = [NSMutableArray arrayWithCapacity: 4];
|
||||||
if ([domain length] && [uid rangeOfString: @"@"].location == NSNotFound)
|
if ([domain length])
|
||||||
uid = [NSString stringWithFormat: @"%@@%@", uid, domain];
|
uid = [NSString stringWithFormat: @"%@@%@", uid, domain];
|
||||||
[jsonLine addObject: uid];
|
[jsonLine addObject: uid];
|
||||||
[jsonLine addObject: [contact objectForKey: @"cn"]];
|
[jsonLine addObject: [contact objectForKey: @"cn"]];
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
NSDictionary *calendarCategoriesColors;
|
NSDictionary *calendarCategoriesColors;
|
||||||
|
|
||||||
NSArray *contactsCategories;
|
NSArray *contactsCategories;
|
||||||
|
NSString *defaultCategoryColor;
|
||||||
NSCalendarDate *today;
|
NSCalendarDate *today;
|
||||||
|
|
||||||
// Mail labels/tags
|
// Mail labels/tags
|
||||||
|
|
|
@ -128,6 +128,7 @@ static NSArray *reminderValues = nil;
|
||||||
|
|
||||||
calendarCategories = nil;
|
calendarCategories = nil;
|
||||||
calendarCategoriesColors = nil;
|
calendarCategoriesColors = nil;
|
||||||
|
defaultCategoryColor = nil;
|
||||||
category = nil;
|
category = nil;
|
||||||
|
|
||||||
label = nil;
|
label = nil;
|
||||||
|
@ -174,6 +175,7 @@ static NSArray *reminderValues = nil;
|
||||||
[vacationOptions release];
|
[vacationOptions release];
|
||||||
[calendarCategories release];
|
[calendarCategories release];
|
||||||
[calendarCategoriesColors release];
|
[calendarCategoriesColors release];
|
||||||
|
[defaultCategoryColor release];
|
||||||
[category release];
|
[category release];
|
||||||
[label release];
|
[label release];
|
||||||
[mailLabels release];
|
[mailLabels release];
|
||||||
|
@ -1519,6 +1521,15 @@ static NSArray *reminderValues = nil;
|
||||||
ASSIGN (calendarCategoriesColors, [userDefaults calendarCategoriesColors]);
|
ASSIGN (calendarCategoriesColors, [userDefaults calendarCategoriesColors]);
|
||||||
|
|
||||||
categoryColor = [calendarCategoriesColors objectForKey: category];
|
categoryColor = [calendarCategoriesColors objectForKey: category];
|
||||||
|
if (!categoryColor)
|
||||||
|
{
|
||||||
|
if (!defaultCategoryColor)
|
||||||
|
{
|
||||||
|
dd = [[context activeUser] domainDefaults];
|
||||||
|
ASSIGN (defaultCategoryColor, [dd calendarDefaultCategoryColor]);
|
||||||
|
}
|
||||||
|
categoryColor = defaultCategoryColor;
|
||||||
|
}
|
||||||
|
|
||||||
return categoryColor;
|
return categoryColor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2007-2015 Inverse inc.
|
Copyright (C) 2007-2012 Inverse inc.
|
||||||
Copyright (C) 2004 SKYRIX Software AG
|
Copyright (C) 2004 SKYRIX Software AG
|
||||||
|
|
||||||
This file is part of SOGo
|
This file is part of SOGo
|
||||||
|
@ -452,7 +452,7 @@ static SoProduct *commonProduct = nil;
|
||||||
|
|
||||||
jsClose = [UIxJSClose new];
|
jsClose = [UIxJSClose new];
|
||||||
[jsClose autorelease];
|
[jsClose autorelease];
|
||||||
[jsClose setRefreshMethod: [methodName doubleQuotedString]];
|
[jsClose setRefreshMethod: methodName];
|
||||||
|
|
||||||
return jsClose;
|
return jsClose;
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,11 +149,11 @@
|
||||||
|
|
||||||
<var:foreach list="personalContactInfos" item="currentContact">
|
<var:foreach list="personalContactInfos" item="currentContact">
|
||||||
<tr var:class="currentContactClasses"
|
<tr var:class="currentContactClasses"
|
||||||
var:categories="currentContact.c_categories.asSafeJSString"
|
var:categories="currentContact.c_categories"
|
||||||
var:id="currentContact.c_name.asCSSIdentifier"
|
var:id="currentContact.c_name"
|
||||||
var:contactname="currentContact.c_cn.asSafeJSString">
|
var:contactname="currentContact.c_cn">
|
||||||
<td class="displayName" var:title="currentContact.c_cn.asSafeJSString"><var:string value="currentContact.c_cn" const:escapeHTML="YES" /></td>
|
<td class="displayName" var:title="currentContact.c_cn"><var:string value="currentContact.c_cn" const:escapeHTML="YES" /></td>
|
||||||
<td var:title="currentContact.c_mail.asSafeJSString"><var:string value="currentContact.c_mail"/></td>
|
<td var:title="currentContact.c_mail"><var:string value="currentContact.c_mail"/></td>
|
||||||
<td><var:string value="currentContact.c_screenname"/></td>
|
<td><var:string value="currentContact.c_screenname"/></td>
|
||||||
<td><var:string value="currentContact.c_o"/></td>
|
<td><var:string value="currentContact.c_o"/></td>
|
||||||
<td><var:string value="currentContact.c_telephonenumber"/></td>
|
<td><var:string value="currentContact.c_telephonenumber"/></td>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
><script type="text/javascript"
|
><script type="text/javascript"
|
||||||
><var:if condition="hasRefreshMethod">
|
><var:if condition="hasRefreshMethod">
|
||||||
var p = <var:if condition="singleWindowModeEnabled">parent</var:if><var:if condition="singleWindowModeEnabled" const:negate="YES">window.opener</var:if>;
|
var p = <var:if condition="singleWindowModeEnabled">parent</var:if><var:if condition="singleWindowModeEnabled" const:negate="YES">window.opener</var:if>;
|
||||||
if (p) p.setTimeout(<var:string value="refreshMethod" const:escapeHTML="NO" />, 50);
|
if (p) p.setTimeout('<var:string value="refreshMethod" const:escapeHTML="NO" />;', 50);
|
||||||
</var:if>
|
</var:if>
|
||||||
onCloseButtonClick();
|
onCloseButtonClick();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -31,7 +31,7 @@ function openContactsFolder(contactsFolder, reload, idx) {
|
||||||
|
|
||||||
var selection;
|
var selection;
|
||||||
if (idx) {
|
if (idx) {
|
||||||
selection = [idx.asCSSIdentifier()];
|
selection = [idx];
|
||||||
}
|
}
|
||||||
else if (contactsFolder == Contact.currentAddressBook) {
|
else if (contactsFolder == Contact.currentAddressBook) {
|
||||||
var contactsList = $("contactsList");
|
var contactsList = $("contactsList");
|
||||||
|
@ -74,7 +74,7 @@ function contactsListCallback(http) {
|
||||||
var contact = data[i];
|
var contact = data[i];
|
||||||
var row = rows[i];
|
var row = rows[i];
|
||||||
row.className = contact["c_component"];
|
row.className = contact["c_component"];
|
||||||
row.setAttribute("id", contact["c_name"].asCSSIdentifier());
|
row.setAttribute("id", contact["c_name"]);
|
||||||
row.setAttribute("categories", contact["c_categories"]);
|
row.setAttribute("categories", contact["c_categories"]);
|
||||||
row.setAttribute("contactname", contact["c_cn"]);
|
row.setAttribute("contactname", contact["c_cn"]);
|
||||||
var cells = row.getElementsByTagName("TD");
|
var cells = row.getElementsByTagName("TD");
|
||||||
|
@ -111,7 +111,7 @@ function contactsListCallback(http) {
|
||||||
for (var j = i; j < data.length; j++) {
|
for (var j = i; j < data.length; j++) {
|
||||||
var contact = data[j];
|
var contact = data[j];
|
||||||
var row = createElement("tr",
|
var row = createElement("tr",
|
||||||
contact["c_name"].asCSSIdentifier(),
|
contact["c_name"],
|
||||||
contact["c_component"],
|
contact["c_component"],
|
||||||
null,
|
null,
|
||||||
{ categories: contact["c_categories"],
|
{ categories: contact["c_categories"],
|
||||||
|
@ -272,7 +272,7 @@ function _onContactMenuAction(folderItem, action, refresh) {
|
||||||
if (Object.isArray(document.menuTarget) && selectedFolders.length > 0) {
|
if (Object.isArray(document.menuTarget) && selectedFolders.length > 0) {
|
||||||
var selectedFolderId = $(selectedFolders[0]).readAttribute("id");
|
var selectedFolderId = $(selectedFolders[0]).readAttribute("id");
|
||||||
var contactIds = $(document.menuTarget).collect(function(row) {
|
var contactIds = $(document.menuTarget).collect(function(row) {
|
||||||
return row.getAttribute("id").fromCSSIdentifier();
|
return row.getAttribute("id");
|
||||||
});
|
});
|
||||||
|
|
||||||
for (var i = 0; i < contactIds.length; i++) {
|
for (var i = 0; i < contactIds.length; i++) {
|
||||||
|
@ -283,7 +283,9 @@ function _onContactMenuAction(folderItem, action, refresh) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var url = ApplicationBaseURL + "/" + selectedFolderId + "/" + action;
|
var url = ApplicationBaseURL + "/" + selectedFolderId + "/" + action;
|
||||||
var uids = contactIds.collect(encodeURIComponent).join('&uid=');
|
var uids = contactIds.collect(function (s) {
|
||||||
|
return encodeURIComponent(s.unescapeHTML());
|
||||||
|
}).join('&uid=');
|
||||||
if (refresh)
|
if (refresh)
|
||||||
triggerAjaxRequest(url, actionContactCallback, selectedFolderId,
|
triggerAjaxRequest(url, actionContactCallback, selectedFolderId,
|
||||||
('folder='+ folderId + '&uid=' + uids),
|
('folder='+ folderId + '&uid=' + uids),
|
||||||
|
@ -310,22 +312,22 @@ function onMenuExportContact (event) {
|
||||||
if (canExport) {
|
if (canExport) {
|
||||||
var selectedFolderId = $(selectedFolders[0]).readAttribute("id");
|
var selectedFolderId = $(selectedFolders[0]).readAttribute("id");
|
||||||
var contactIds = document.menuTarget.collect(function(row) {
|
var contactIds = document.menuTarget.collect(function(row) {
|
||||||
return row.readAttribute("id").fromCSSIdentifier();
|
return row.readAttribute("id");
|
||||||
});
|
});
|
||||||
var url = ApplicationBaseURL + "/" + selectedFolderId + "/export"
|
var url = ApplicationBaseURL + "/" + selectedFolderId + "/export"
|
||||||
+ "?uid=" + contactIds.collect(encodeURIComponent).join("&uid=");
|
+ "?uid=" + contactIds.join("&uid=");
|
||||||
window.location.href = url;
|
window.location.href = url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMenuRawContact (event) {
|
function onMenuRawContact (event) {
|
||||||
var cname = document.menuTarget.collect(function(row) {
|
var cname = document.menuTarget.collect(function(row) {
|
||||||
return row.readAttribute("id").fromCSSIdentifier();
|
return row.readAttribute("id");
|
||||||
});
|
});
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
openGenericWindow(URLForFolderID(Contact.currentAddressBook)
|
openGenericWindow(URLForFolderID(Contact.currentAddressBook)
|
||||||
+ "/" + encodeURIComponent(cname) + "/raw");
|
+ "/" + cname + "/raw");
|
||||||
}).delay(0.1);
|
}).delay(0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,22 +350,22 @@ function actionContactCallback(http) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadContact(cname) {
|
function loadContact(idx) {
|
||||||
if (document.contactAjaxRequest) {
|
if (document.contactAjaxRequest) {
|
||||||
document.contactAjaxRequest.aborted = true;
|
document.contactAjaxRequest.aborted = true;
|
||||||
document.contactAjaxRequest.abort();
|
document.contactAjaxRequest.abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cachedContacts[Contact.currentAddressBook + "/" + cname]) {
|
if (cachedContacts[Contact.currentAddressBook + "/" + idx]) {
|
||||||
var div = $('contactView');
|
var div = $('contactView');
|
||||||
Contact.currentContactId = cname;
|
Contact.currentContactId = idx;
|
||||||
div.innerHTML = cachedContacts[Contact.currentAddressBook + "/" + cname];
|
div.innerHTML = cachedContacts[Contact.currentAddressBook + "/" + idx];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var url = (URLForFolderID(Contact.currentAddressBook)
|
var url = (URLForFolderID(Contact.currentAddressBook)
|
||||||
+ "/" + encodeURIComponent(cname) + "/view?noframe=1");
|
+ "/" + encodeURIComponent(idx.unescapeHTML()) + "/view?noframe=1");
|
||||||
document.contactAjaxRequest
|
document.contactAjaxRequest
|
||||||
= triggerAjaxRequest(url, contactLoadCallback, cname);
|
= triggerAjaxRequest(url, contactLoadCallback, idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,9 +418,8 @@ function moveTo(uri) {
|
||||||
/* contact menu entries */
|
/* contact menu entries */
|
||||||
function onContactRowDblClick(event) {
|
function onContactRowDblClick(event) {
|
||||||
var t = getTarget(event);
|
var t = getTarget(event);
|
||||||
var cname = t.parentNode.getAttribute('id').fromCSSIdentifier();
|
var cname = t.parentNode.getAttribute('id');
|
||||||
|
|
||||||
cname = encodeURIComponent(cname);
|
|
||||||
openContactWindow(URLForFolderID(Contact.currentAddressBook)
|
openContactWindow(URLForFolderID(Contact.currentAddressBook)
|
||||||
+ "/" + cname + "/edit", cname);
|
+ "/" + cname + "/edit", cname);
|
||||||
|
|
||||||
|
@ -437,7 +438,7 @@ function onContactSelectionChange(event) {
|
||||||
|
|
||||||
if (rows.length == 1) {
|
if (rows.length == 1) {
|
||||||
var node = $(rows[0]);
|
var node = $(rows[0]);
|
||||||
loadContact(node.getAttribute('id').fromCSSIdentifier());
|
loadContact(node.getAttribute('id'));
|
||||||
}
|
}
|
||||||
else if (rows.length > 1) {
|
else if (rows.length > 1) {
|
||||||
$('contactView').update();
|
$('contactView').update();
|
||||||
|
@ -478,9 +479,8 @@ function onToolbarEditSelectedContacts(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < rows.length; i++) {
|
for (var i = 0; i < rows.length; i++) {
|
||||||
var id = encodeURIComponent(rows[i].fromCSSIdentifier());
|
|
||||||
openContactWindow(URLForFolderID(Contact.currentAddressBook)
|
openContactWindow(URLForFolderID(Contact.currentAddressBook)
|
||||||
+ "/" + id + "/edit", rows[i]);
|
+ "/" + rows[i] + "/edit", rows[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -488,17 +488,16 @@ function onToolbarEditSelectedContacts(event) {
|
||||||
|
|
||||||
function onToolbarWriteToSelectedContacts(event) {
|
function onToolbarWriteToSelectedContacts(event) {
|
||||||
var contactsList = $('contactsList');
|
var contactsList = $('contactsList');
|
||||||
var rowIds = contactsList.getSelectedRowsId();
|
var rows = contactsList.getSelectedRowsId();
|
||||||
|
var rowsWithEmail = 0;
|
||||||
|
|
||||||
if (rowIds.length == 0) {
|
if (rows.length == 0) {
|
||||||
showAlertDialog(_("Please select a contact."));
|
showAlertDialog(_("Please select a contact."));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
openMailComposeWindow(ApplicationBaseURL + "/../Mail/compose"
|
openMailComposeWindow(ApplicationBaseURL + "/../Mail/compose"
|
||||||
+ "?folder=" + Contact.currentAddressBook.substring(1)
|
+ "?folder=" + Contact.currentAddressBook.substring(1)
|
||||||
+ "&uid=" + rowIds.collect(function(id) {
|
+ "&uid=" + rows.join("&uid="));
|
||||||
return encodeURIComponent(id.fromCSSIdentifier());
|
|
||||||
}).join("&uid="));
|
|
||||||
if (document.body.hasClassName("popup"))
|
if (document.body.hasClassName("popup"))
|
||||||
window.close();
|
window.close();
|
||||||
}
|
}
|
||||||
|
@ -525,28 +524,26 @@ function onToolbarDeleteSelectedContactsConfirm(dialogId) {
|
||||||
var contactsList = $('contactsList');
|
var contactsList = $('contactsList');
|
||||||
var rowIds = contactsList.getSelectedRowsId();
|
var rowIds = contactsList.getSelectedRowsId();
|
||||||
var urlstr = (URLForFolderID(Contact.currentAddressBook) + "/batchDelete");
|
var urlstr = (URLForFolderID(Contact.currentAddressBook) + "/batchDelete");
|
||||||
|
|
||||||
for (var i = 0; i < rowIds.length; i++)
|
for (var i = 0; i < rowIds.length; i++)
|
||||||
$(rowIds[i]).hide();
|
$(rowIds[i]).hide();
|
||||||
triggerAjaxRequest(urlstr, onContactDeleteEventCallback, rowIds,
|
triggerAjaxRequest(urlstr, onContactDeleteEventCallback, rowIds,
|
||||||
('ids=' + rowIds.collect(function(id) {
|
('ids=' + rowIds.collect(function (s) {
|
||||||
return encodeURIComponent(id.fromCSSIdentifier());
|
return encodeURIComponent(s.unescapeHTML());
|
||||||
}).join(",")),
|
}).join(",")),
|
||||||
{ "Content-type": "application/x-www-form-urlencoded" });
|
{ "Content-type": "application/x-www-form-urlencoded" });
|
||||||
}
|
}
|
||||||
|
|
||||||
function onContactDeleteEventCallback(http) {
|
function onContactDeleteEventCallback(http) {
|
||||||
|
var rowIds = http.callbackData;
|
||||||
if (http.readyState == 4) {
|
if (http.readyState == 4) {
|
||||||
if (isHttpStatus204(http.status)) {
|
if (isHttpStatus204(http.status)) {
|
||||||
var rowIds = http.callbackData;
|
|
||||||
var row;
|
var row;
|
||||||
var nextRow = null;
|
var nextRow = null;
|
||||||
for (var i = 0; i < rowIds.length; i++) {
|
for (var i = 0; i < rowIds.length; i++) {
|
||||||
var id = rowIds[i].fromCSSIdentifier();
|
delete cachedContacts[Contact.currentAddressBook + "/" + rowIds[i]];
|
||||||
delete cachedContacts[Contact.currentAddressBook + "/" + id];
|
|
||||||
row = $(rowIds[i]);
|
row = $(rowIds[i]);
|
||||||
var displayName = row.readAttribute("contactname");
|
var displayName = row.readAttribute("contactname");
|
||||||
if (Contact.currentContactId == id) {
|
if (Contact.currentContactId == row) {
|
||||||
Contact.currentContactId = null;
|
Contact.currentContactId = null;
|
||||||
}
|
}
|
||||||
var nextRow = row.next("tr");
|
var nextRow = row.next("tr");
|
||||||
|
@ -558,7 +555,7 @@ function onContactDeleteEventCallback(http) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (nextRow) {
|
if (nextRow) {
|
||||||
Contact.currentContactId = nextRow.getAttribute("id").fromCSSIdentifier();
|
Contact.currentContactId = nextRow.getAttribute("id");
|
||||||
nextRow.selectElement();
|
nextRow.selectElement();
|
||||||
loadContact(Contact.currentContactId);
|
loadContact(Contact.currentContactId);
|
||||||
}
|
}
|
||||||
|
@ -673,7 +670,7 @@ function onConfirmContactSelection(event) {
|
||||||
var contactsList = $("contactsList");
|
var contactsList = $("contactsList");
|
||||||
var rows = contactsList.getSelectedRows();
|
var rows = contactsList.getSelectedRows();
|
||||||
for (i = 0; i < rows.length; i++) {
|
for (i = 0; i < rows.length; i++) {
|
||||||
var cid = rows[i].getAttribute("id").fromCSSIdentifier();
|
var cid = rows[i].getAttribute("id");
|
||||||
if (cid.endsWith(".vlf")) {
|
if (cid.endsWith(".vlf")) {
|
||||||
addListToOpener(tag, Contact.currentAddressBook, currentAddressBookName, cid);
|
addListToOpener(tag, Contact.currentAddressBook, currentAddressBookName, cid);
|
||||||
}
|
}
|
||||||
|
@ -1298,7 +1295,7 @@ function onDocumentKeydown(event) {
|
||||||
else if (keyCode == Event.KEY_DOWN ||
|
else if (keyCode == Event.KEY_DOWN ||
|
||||||
keyCode == Event.KEY_UP) {
|
keyCode == Event.KEY_UP) {
|
||||||
if (Contact.currentContactId) {
|
if (Contact.currentContactId) {
|
||||||
var row = $(Contact.currentContactId.asCSSIdentifier());
|
var row = $(Contact.currentContactId);
|
||||||
var nextRow;
|
var nextRow;
|
||||||
if (keyCode == Event.KEY_DOWN)
|
if (keyCode == Event.KEY_DOWN)
|
||||||
nextRow = row.next("tr");
|
nextRow = row.next("tr");
|
||||||
|
@ -1322,7 +1319,7 @@ function onDocumentKeydown(event) {
|
||||||
|
|
||||||
// Select and load the next message
|
// Select and load the next message
|
||||||
nextRow.selectElement();
|
nextRow.selectElement();
|
||||||
loadContact(nextRow.readAttribute("id").fromCSSIdentifier());
|
loadContact(nextRow.readAttribute("id"));
|
||||||
}
|
}
|
||||||
Event.stop(event);
|
Event.stop(event);
|
||||||
}
|
}
|
||||||
|
@ -1468,12 +1465,11 @@ function onCategoriesMenuItemClick() {
|
||||||
var rowIds = contactsList.getSelectedRowsId();
|
var rowIds = contactsList.getSelectedRowsId();
|
||||||
if (rowIds.length > 0) {
|
if (rowIds.length > 0) {
|
||||||
for (var i = 0; i < rowIds.length; i++) {
|
for (var i = 0; i < rowIds.length; i++) {
|
||||||
var id = rowIds[i].fromCSSIdentifier();
|
|
||||||
var url = (URLForFolderID(Contact.currentAddressBook)
|
var url = (URLForFolderID(Contact.currentAddressBook)
|
||||||
+ "/" + encodeURIComponent(id) + "/" + method);
|
+ "/" + rowIds[i] + "/" + method);
|
||||||
url += "?category=" + encodeURIComponent(this.category);
|
url += "?category=" + encodeURIComponent(this.category);
|
||||||
triggerAjaxRequest(url, onCategoriesMenuItemCallback,
|
triggerAjaxRequest(url, onCategoriesMenuItemCallback,
|
||||||
{ 'addressBook' : Contact.currentAddressBook, 'id' : id });
|
{ 'addressBook' : Contact.currentAddressBook, 'id' : rowIds[i] });
|
||||||
if (set) {
|
if (set) {
|
||||||
setCategoryOnNode($(rowIds[i]), this.category);
|
setCategoryOnNode($(rowIds[i]), this.category);
|
||||||
}
|
}
|
||||||
|
@ -1501,7 +1497,7 @@ function onCategoriesMenuItemCallback(http) {
|
||||||
|
|
||||||
function setCategoryOnNode(contactNode, category) {
|
function setCategoryOnNode(contactNode, category) {
|
||||||
var catList = contactNode.getAttribute("categories");
|
var catList = contactNode.getAttribute("categories");
|
||||||
var catsArray = catList? catList.split(",") : [];
|
var catsArray = catList.split(",");
|
||||||
if (catsArray.indexOf(category) == -1) {
|
if (catsArray.indexOf(category) == -1) {
|
||||||
catsArray.push(category);
|
catsArray.push(category);
|
||||||
contactNode.setAttribute("categories", catsArray.join(","));
|
contactNode.setAttribute("categories", catsArray.join(","));
|
||||||
|
@ -1611,9 +1607,9 @@ function dropSelectedContacts(action, toId) {
|
||||||
if ((!currentFolderIsRemote() || action != "move")
|
if ((!currentFolderIsRemote() || action != "move")
|
||||||
&& fromId.substring(1) != toId) {
|
&& fromId.substring(1) != toId) {
|
||||||
|
|
||||||
var url = ApplicationBaseURL + fromId + "/" + action;
|
var url = ApplicationBaseURL + "/" + fromId + "/" + action;
|
||||||
var uids = contactIds.collect(function(id) {
|
var uids = contactIds.collect(function (s) {
|
||||||
return encodeURIComponent(id.fromCSSIdentifier());
|
return encodeURIComponent(s.unescapeHTML());
|
||||||
}).join('&uid=');
|
}).join('&uid=');
|
||||||
triggerAjaxRequest(url, actionContactCallback, fromId,
|
triggerAjaxRequest(url, actionContactCallback, fromId,
|
||||||
('folder='+ toId + '&uid=' + uids),
|
('folder='+ toId + '&uid=' + uids),
|
||||||
|
|
|
@ -73,10 +73,6 @@ String.prototype.decodeEntities = function() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.unescapeHTMLEntities = function() {
|
|
||||||
return this.unescapeHTML().replace(/"/g,'"');
|
|
||||||
};
|
|
||||||
|
|
||||||
String.prototype.asDate = function () {
|
String.prototype.asDate = function () {
|
||||||
var newDate;
|
var newDate;
|
||||||
var date = this.split("/");
|
var date = this.split("/");
|
||||||
|
@ -98,39 +94,20 @@ String.prototype.asDate = function () {
|
||||||
return newDate;
|
return newDate;
|
||||||
};
|
};
|
||||||
|
|
||||||
RegExp.escape = function(text) {
|
|
||||||
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
||||||
}
|
|
||||||
|
|
||||||
var css_invalid_characters = [ '_' , '.', '#' , '@' , '*', ':' , ';' , ',' , ' ',
|
|
||||||
'(', ')', '[', ']', '{', '}',
|
|
||||||
"'", '"', '&', '+' ];
|
|
||||||
var css_escape_characters = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_SC_', '_CO_', '_SP_',
|
|
||||||
'_LP_', '_RP_', '_LS_', '_RQ_', '_LC_', '_RC_',
|
|
||||||
'_SQ_', '_DQ_', '_AM_', '_P_' ];
|
|
||||||
|
|
||||||
String.prototype.asCSSIdentifier = function() {
|
String.prototype.asCSSIdentifier = function() {
|
||||||
|
var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ',' , ' '
|
||||||
|
, "'", '&', '\\+' ];
|
||||||
|
var escapeds = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_CO_',
|
||||||
|
'_SP_', '_SQ_', '_AM_', '_P_' ];
|
||||||
|
|
||||||
var newString = this;
|
var newString = this;
|
||||||
for (var i = 0; i < css_invalid_characters.length; i++) {
|
for (var i = 0; i < characters.length; i++) {
|
||||||
var re = new RegExp(RegExp.escape(css_invalid_characters[i]), 'g');
|
var re = new RegExp(characters[i], 'g');
|
||||||
newString = newString.replace(re, css_escape_characters[i]);
|
newString = newString.replace(re, escapeds[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (/^\d/.test(newString))
|
if (/^\d+/.test(newString)) {
|
||||||
newString = '_' + newString;
|
newString = '_' + newString;
|
||||||
|
|
||||||
return newString;
|
|
||||||
};
|
|
||||||
|
|
||||||
String.prototype.fromCSSIdentifier = function() {
|
|
||||||
var newString = this;
|
|
||||||
|
|
||||||
if (/^_\d/.test(newString))
|
|
||||||
newString = newString.substring(1);
|
|
||||||
|
|
||||||
for (var i = 0; i < css_escape_characters.length; i++) {
|
|
||||||
var re = new RegExp(css_escape_characters[i], 'g');
|
|
||||||
newString = newString.replace(re, css_invalid_characters[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return newString;
|
return newString;
|
||||||
|
|
|
@ -1015,32 +1015,6 @@ function eventsListCallback(http) {
|
||||||
|
|
||||||
if (http.responseText.length > 0) {
|
if (http.responseText.length > 0) {
|
||||||
var data = http.responseText.evalJSON(true);
|
var data = http.responseText.evalJSON(true);
|
||||||
|
|
||||||
// [0] Event ID
|
|
||||||
// [1] Calendar ID
|
|
||||||
// [2] Calendar name
|
|
||||||
// [3] Status
|
|
||||||
// [4] Title
|
|
||||||
// [5] Start date
|
|
||||||
// [6] End date
|
|
||||||
// [7] Location
|
|
||||||
// [8] Is all day?
|
|
||||||
// [9] Classification (0 = public, 1, = private, 2 = confidential)
|
|
||||||
// [10] Category
|
|
||||||
// [11] Participants email addresses
|
|
||||||
// [12] Participants states
|
|
||||||
// [13] Owner
|
|
||||||
// [14] Is cyclic?
|
|
||||||
// [15] Next alarm
|
|
||||||
// [16] recurrence-id
|
|
||||||
// [17] isException
|
|
||||||
// [18] Editable?
|
|
||||||
// [19] Erasable?
|
|
||||||
// [20] Owner is organizer?
|
|
||||||
// [21] Description
|
|
||||||
// [22] Formatted start date
|
|
||||||
// [23] Formatted end date
|
|
||||||
|
|
||||||
for (var i = 0; i < data.length; i++) {
|
for (var i = 0; i < data.length; i++) {
|
||||||
var row = createElement("tr");
|
var row = createElement("tr");
|
||||||
table.tBodies[0].appendChild(row);
|
table.tBodies[0].appendChild(row);
|
||||||
|
@ -1082,12 +1056,12 @@ function eventsListCallback(http) {
|
||||||
td = createElement("td");
|
td = createElement("td");
|
||||||
row.appendChild(td);
|
row.appendChild(td);
|
||||||
td.observe("mousedown", listRowMouseDownHandler, true);
|
td.observe("mousedown", listRowMouseDownHandler, true);
|
||||||
td.update(data[i][22]); // start date
|
td.update(data[i][21]); // start date
|
||||||
|
|
||||||
td = createElement("td");
|
td = createElement("td");
|
||||||
row.appendChild(td);
|
row.appendChild(td);
|
||||||
td.observe("mousedown", listRowMouseDownHandler, true);
|
td.observe("mousedown", listRowMouseDownHandler, true);
|
||||||
td.update(data[i][23]); // end date
|
td.update(data[i][22]); // end date
|
||||||
|
|
||||||
td = createElement("td");
|
td = createElement("td");
|
||||||
row.appendChild(td);
|
row.appendChild(td);
|
||||||
|
@ -1191,9 +1165,8 @@ function tasksListCallback(http) {
|
||||||
// [12] Owner
|
// [12] Owner
|
||||||
// [13] recurrence-id
|
// [13] recurrence-id
|
||||||
// [14] isException
|
// [14] isException
|
||||||
// [15] Description
|
// [15] Status CSS class (duelater, completed, etc)
|
||||||
// [16] Status CSS class (duelater, completed, etc)
|
// [16] Due date (formatted)
|
||||||
// [17] Due date (formatted)
|
|
||||||
|
|
||||||
for (var i = 0; i < data.length; i++) {
|
for (var i = 0; i < data.length; i++) {
|
||||||
var row = createElement("tr");
|
var row = createElement("tr");
|
||||||
|
@ -1209,12 +1182,16 @@ function tasksListCallback(http) {
|
||||||
if (rTime)
|
if (rTime)
|
||||||
id += "-" + escape(rTime);
|
id += "-" + escape(rTime);
|
||||||
row.setAttribute("id", id);
|
row.setAttribute("id", id);
|
||||||
|
//row.cname = escape(data[i][0]);
|
||||||
|
//row.calendar = calendar;
|
||||||
if (rTime)
|
if (rTime)
|
||||||
row.recurrenceTime = escape(rTime);
|
row.recurrenceTime = escape(rTime);
|
||||||
row.isException = data[i][14];
|
row.isException = data[i][14];
|
||||||
|
|
||||||
|
|
||||||
|
//row.setAttribute("id", calendar + "-" + cname);
|
||||||
//listItem.addClassName(data[i][5]); // Classification
|
//listItem.addClassName(data[i][5]); // Classification
|
||||||
row.addClassName(data[i][16]); // status
|
//row.addClassName(data[i][14]); // status
|
||||||
row.addClassName("taskRow");
|
row.addClassName("taskRow");
|
||||||
row.calendar = calendar;
|
row.calendar = calendar;
|
||||||
row.cname = cname;
|
row.cname = cname;
|
||||||
|
@ -1259,8 +1236,8 @@ function tasksListCallback(http) {
|
||||||
|
|
||||||
cell = createElement("td");
|
cell = createElement("td");
|
||||||
row.appendChild(cell);
|
row.appendChild(cell);
|
||||||
if (data[i][17])
|
if (data[i][16])
|
||||||
cell.update(data[i][17]); // end date
|
cell.update(data[i][16]); // end date
|
||||||
|
|
||||||
cell = createElement("td");
|
cell = createElement("td");
|
||||||
row.appendChild(cell);
|
row.appendChild(cell);
|
||||||
|
|
|
@ -253,9 +253,6 @@ cp Scripts/logrotate ${RPM_BUILD_ROOT}/etc/logrotate.d/sogo
|
||||||
%if 0%{?_with_systemd}
|
%if 0%{?_with_systemd}
|
||||||
cp Scripts/sogo-systemd-redhat ${RPM_BUILD_ROOT}/usr/lib/systemd/system/sogod.service
|
cp Scripts/sogo-systemd-redhat ${RPM_BUILD_ROOT}/usr/lib/systemd/system/sogod.service
|
||||||
chmod 644 ${RPM_BUILD_ROOT}/usr/lib/systemd/system/sogod.service
|
chmod 644 ${RPM_BUILD_ROOT}/usr/lib/systemd/system/sogod.service
|
||||||
mkdir ${RPM_BUILD_ROOT}/etc/tmpfiles.d
|
|
||||||
cp Scripts/sogo-systemd.conf ${RPM_BUILD_ROOT}/etc/tmpfiles.d/sogo.conf
|
|
||||||
chmod 644 ${RPM_BUILD_ROOT}/etc/tmpfiles.d/sogo.conf
|
|
||||||
%else
|
%else
|
||||||
cp Scripts/sogo-init.d-redhat ${RPM_BUILD_ROOT}/etc/init.d/sogod
|
cp Scripts/sogo-init.d-redhat ${RPM_BUILD_ROOT}/etc/init.d/sogod
|
||||||
chmod 755 ${RPM_BUILD_ROOT}/etc/init.d/sogod
|
chmod 755 ${RPM_BUILD_ROOT}/etc/init.d/sogod
|
||||||
|
@ -292,7 +289,6 @@ rm -fr ${RPM_BUILD_ROOT}
|
||||||
|
|
||||||
%if 0%{?_with_systemd}
|
%if 0%{?_with_systemd}
|
||||||
/usr/lib/systemd/system/sogod.service
|
/usr/lib/systemd/system/sogod.service
|
||||||
/etc/tmpfiles.d/sogo.conf
|
|
||||||
%else
|
%else
|
||||||
/etc/init.d/sogod
|
/etc/init.d/sogod
|
||||||
%endif
|
%endif
|
||||||
|
|
Loading…
Reference in a new issue