mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-11-10 03:07:11 +00:00
Merge to 2.3.20
This commit is contained in:
commit
2c8f56fb67
217 files changed
+5077
-734
No files matched your search
@@ -0,0 +1,60 @@
|
||||
"Inviting the following persons is prohibited:" = "לא ניתן להזמין את אנשי הקשר הבאים: ";
|
||||
"Personal Calendar" = "לוח שנה אישי";
|
||||
vevent_class0 = "(אירוע פומבי)";
|
||||
vevent_class1 = "(אירוע פרטי)";
|
||||
vevent_class2 = "(אירוע סודי)";
|
||||
|
||||
vtodo_class0 = "(משימה פומבית)";
|
||||
vtodo_class1 = "(משימה פרטית)";
|
||||
vtodo_class2 = "(משימה סודית)";
|
||||
/* Receipts */
|
||||
"The event \"%{Summary}\" was created" = "האירוע \"{Summary}%\" נוצר";
|
||||
"The event \"%{Summary}\" was deleted" = "האירוע \"{Summary}%\" נמחק";
|
||||
"The event \"%{Summary}\" was updated" = "האירוע \"{Summary}%\" עודכן";
|
||||
"The following attendees(s) were notified" = "המשתתפים הבאים קיבלו התראה";
|
||||
"The following attendees(s) were added" = "המשתתפים הבאים הוספו";
|
||||
"The following attendees(s) were removed" = "המשתתפים הבאים הוסרו";
|
||||
/* IMIP messages */
|
||||
"calendar_label" = "לוח שנה";
|
||||
"startDate_label" = "התחלה";
|
||||
"endDate_label" = "סיום";
|
||||
"due_label" = "תאריך להגשה";
|
||||
"location_label" = "מיקום";
|
||||
"summary_label" = "תקציר";
|
||||
"comment_label" = "הערה";
|
||||
/* Invitation */
|
||||
"Event Invitation: \"%{Summary}\"" = "הזמנה לאירוע: \"{Summary}%\"";
|
||||
"(sent by %{SentBy}) " = "(מאת {SentBy}%)";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "{Organizer}% {SentByText}% הוזמנתם ל {Summary}%.\n\nהתחלה: {StartDate}%\nסיום: {EndDate}%\nתאור: {Description}%";
|
||||
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "{Organizer}% {SentByText}% הוזמנתם ל {Summary}%.\n\nהתחלה: {StartDate}% ב {StartTime}%\nסיום: {EndDate}% ב {EndTime}%\nתאור: {Description}%";
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "אירוע מבוטל: \"{Summary}%\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
|
||||
= "{Organizer}% {SentByText}% ביטלו את האירוע: {Summary}%.\n\nהתחלה: {StartDate}%\nסיום: {EndDate}%\nתאור: {Description}%";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "{Organizer}% {SentByText}% ביטלו את האירוע: {Summary}%.\n\nהתחלה: {StartDate}% ב {StartTime}%\nסיום: {EndDate}% ב {EndTime}%\nתאור: {Description}%";
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
|
||||
= "הפגישה \"{Summary}%\" עבור {OldStartDate}% שונתה";
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
= "הפגישה \"{Summary}%\" עבור {OldStartDate}% ב {OldStartTime}% השתנתה";
|
||||
"The following parameters have changed in the \"%{Summary}\" meeting:"
|
||||
= "הפרמטרים הבאים השתנו בפגישה \"{Summary}%\":";
|
||||
"Please accept or decline those changes."
|
||||
= "נא לאשר או לבטל את השינויים";
|
||||
/* Reply */
|
||||
"Accepted invitation: \"%{Summary}\"" = "הזמנה אושרה: \"{Summary}%\"";
|
||||
"Declined invitation: \"%{Summary}\"" = "הזמנה נדחתה: \"{Summary}%\"";
|
||||
"Delegated invitation: \"%{Summary}\"" = "הזמנה הואצלה: \"{Summary}%\"";
|
||||
"Not yet decided on invitation: \"%{Summary}\"" = "טרם הוחלט לגבי הזמנה: \"{Summary}%\"";
|
||||
"%{Attendee} %{SentByText}has accepted your event invitation."
|
||||
= "{Attendee}% {SentByText}% אישר את הזמנתך.";
|
||||
"%{Attendee} %{SentByText}has declined your event invitation."
|
||||
= "{Attendee}% {SentByText}% דחה את הזמנתך.";
|
||||
"%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}."
|
||||
= "{Attendee}% {SentByText}% ההזמנה הואצלה ל {Delegate}%.";
|
||||
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
|
||||
= "{Attendee}% {SentByText}% טרם הוחלט לגבי הזמנתך.";
|
||||
/* Resources */
|
||||
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "לא ניתן לגשת למשאב: \"{Cn}% {SystemEmail}%\"";
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "כמות מקסימלית של הזמנות בו זמנית ({NumberOfSimultaneousBookings}%) עבור המשאב \"{Cn}% {SystemEmail}%\". קיימת התנגשות באירוע \"{EventTitle}%\", המתחיל ב {StartDate}%.";
|
||||
@@ -1305,17 +1305,13 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
{
|
||||
NSMutableArray *ma;
|
||||
NSDictionary *row;
|
||||
NSCalendarDate *rangeEndDate;
|
||||
unsigned int count, max;
|
||||
|
||||
max = [_records count];
|
||||
ma = [NSMutableArray arrayWithCapacity: max];
|
||||
|
||||
// Adjust the range so it ends at midnight. This is necessary when calculating
|
||||
// recurrences of all-day events.
|
||||
rangeEndDate = [[_r endDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 seconds:1];
|
||||
_r = [NGCalendarDateRange calendarDateRangeWithStartDate: [_r startDate]
|
||||
endDate: rangeEndDate];
|
||||
endDate: [_r endDate]];
|
||||
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
|
||||
@@ -1776,6 +1776,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
NSArray *allEvents;
|
||||
iCalEvent *event;
|
||||
NSUInteger i;
|
||||
int j;
|
||||
|
||||
allEvents = [rqCalendar events];
|
||||
|
||||
@@ -1808,12 +1809,27 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
if (uid)
|
||||
{
|
||||
NSDictionary *defaultIdentity;
|
||||
NSArray *allAttendees;
|
||||
iCalPerson *attendee;
|
||||
SOGoUser *organizer;
|
||||
|
||||
|
||||
organizer = [SOGoUser userWithLogin: uid];
|
||||
defaultIdentity = [organizer defaultIdentity];
|
||||
[[event organizer] setCn: [defaultIdentity objectForKey: @"fullName"]];
|
||||
[[event organizer] setEmail: [defaultIdentity objectForKey: @"email"]];
|
||||
|
||||
// We now check if one of the attendee is also the organizer. If so,
|
||||
// we remove it. See bug #3905 (https://sogo.nu/bugs/view.php?id=3905)
|
||||
// for more details. This is a Calendar app bug on Apple Yosemite.
|
||||
allAttendees = [event attendees];
|
||||
|
||||
for (j = [allAttendees count]-1; j >= 0; j--)
|
||||
{
|
||||
attendee = [allAttendees objectAtIndex: j];
|
||||
if ([organizer hasEmail: [attendee rfc822Email]])
|
||||
[event removeFromAttendees: attendee];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1981,9 +1997,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
[self _decomposeGroupsInRequestCalendar: calendar];
|
||||
|
||||
if ([[ownerUser domainDefaults] iPhoneForceAllDayTransparency] && [rq isIPhone])
|
||||
{
|
||||
[self _adjustTransparencyInRequestCalendar: calendar];
|
||||
}
|
||||
[self _adjustTransparencyInRequestCalendar: calendar];
|
||||
|
||||
[self _adjustEventsInRequestCalendar: calendar];
|
||||
[self adjustClassificationInRequestCalendar: calendar];
|
||||
|
||||
@@ -618,7 +618,10 @@
|
||||
if (content)
|
||||
ASSIGN (originalCalendar, [iCalCalendar parseSingleFromSource: content]);
|
||||
else
|
||||
[self warnWithFormat: @"content not available, we will crash"];
|
||||
{
|
||||
[self warnWithFormat: @"content not available, we don't update the event"];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
oldMaster = (iCalRepeatableEntityObject *)
|
||||
@@ -935,6 +938,9 @@
|
||||
if ([[context objectForKey: @"DeviceType"] isEqualToString: @"WindowsOutlook15"])
|
||||
return;
|
||||
|
||||
// remove all alarms to avoid bug #3925
|
||||
[event removeAllAlarms];
|
||||
|
||||
dd = [from domainDefaults];
|
||||
if ([dd appointmentSendEMailNotifications] && [event isStillRelevant])
|
||||
{
|
||||
@@ -1231,12 +1237,31 @@
|
||||
|
||||
- (NSException *) moveToFolder: (SOGoGCSFolder *) newFolder
|
||||
{
|
||||
SOGoCalendarComponent *newComponent;
|
||||
NSException *ex;
|
||||
id o;
|
||||
|
||||
ex = [self copyToFolder: newFolder];
|
||||
// Lookup to see if the event exists in the target calendar. During a MOVE, we do
|
||||
// keep the ID of the event intact.
|
||||
o = [newFolder lookupName: [self nameInContainer]
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
|
||||
if (!ex)
|
||||
ex = [self delete];
|
||||
if ([o isKindOfClass: [NSException class]])
|
||||
{
|
||||
newComponent = [[self class] objectWithName: [self nameInContainer]
|
||||
inContainer: newFolder];
|
||||
|
||||
ex = [newComponent saveCalendar: [self calendar: NO secure: NO]];
|
||||
|
||||
if (!ex)
|
||||
ex = [self delete];
|
||||
}
|
||||
else
|
||||
{
|
||||
ex = [NSException exceptionWithHTTPStatus: 409
|
||||
reason: @"Target exists - MOVE disallowed."];
|
||||
}
|
||||
|
||||
return ex;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ vtodo_class2 = "(Gizli görev)";
|
||||
"calendar_label" = "Takvim";
|
||||
"startDate_label" = "Başlangıç";
|
||||
"endDate_label" = "Bitiş";
|
||||
"due_label" = "Son Tarih";
|
||||
"due_label" = "Kapanış Tarihi";
|
||||
"location_label" = "Konum";
|
||||
"summary_label" = "Özet";
|
||||
"comment_label" = "Açıklama";
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
"Personal Address Book" = "אנשי קשר";
|
||||
"Collected Address Book" = "אנשי קשר אשר נוצרו משליחת הודעה";
|
||||
@@ -69,6 +69,8 @@ Mailer_RESOURCE_FILES += \
|
||||
SOGoMailCroatianReply.wo \
|
||||
SOGoMailGermanForward.wo \
|
||||
SOGoMailGermanReply.wo \
|
||||
SOGoMailHebrewForward.wo \
|
||||
SOGoMailHebrewReply.wo \
|
||||
SOGoMailHungarianForward.wo \
|
||||
SOGoMailHungarianReply.wo \
|
||||
SOGoMailIcelandicForward.wo \
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
"OtherUsersFolderName" = "משתשמשים אחרים";
|
||||
"SharedFoldersName" = "תיקיות משותפות";
|
||||
@@ -1928,15 +1928,11 @@ static NSString *userAgent = nil;
|
||||
//
|
||||
- (NSException *) sendMailAndCopyToSent: (BOOL) copyToSent
|
||||
{
|
||||
NSMutableData *cleaned_message;
|
||||
SOGoMailFolder *sentFolder;
|
||||
SOGoDomainDefaults *dd;
|
||||
NSURL *sourceIMAP4URL;
|
||||
NSException *error;
|
||||
NSData *message;
|
||||
NSRange r1;
|
||||
|
||||
unsigned int limit;
|
||||
|
||||
// We strip the BCC fields prior sending any mails
|
||||
NGMimeMessageGenerator *generator;
|
||||
@@ -1944,43 +1940,9 @@ static NSString *userAgent = nil;
|
||||
generator = [[[NGMimeMessageGenerator alloc] init] autorelease];
|
||||
message = [generator generateMimeFromPart: [self mimeMessage]];
|
||||
|
||||
//
|
||||
// We now look for the Bcc: header. If it is present, we remove it.
|
||||
// Some servers, like qmail, do not remove it automatically.
|
||||
//
|
||||
#warning FIXME - we should fix the case issue when we switch to Pantomime
|
||||
cleaned_message = [NSMutableData dataWithData: message];
|
||||
|
||||
// We search only in the headers so we start at 0 until
|
||||
// we find \r\n\r\n, which is the headers delimiter
|
||||
r1 = [cleaned_message rangeOfCString: "\r\n\r\n"];
|
||||
limit = r1.location-1;
|
||||
r1 = [cleaned_message rangeOfCString: "\r\nbcc: "
|
||||
options: 0
|
||||
range: NSMakeRange(0,limit)];
|
||||
|
||||
if (r1.location != NSNotFound)
|
||||
{
|
||||
// We search for the first \r\n AFTER the Bcc: header and
|
||||
// replace the whole thing with \r\n.
|
||||
unsigned int i;
|
||||
|
||||
for (i = r1.location+7; i < limit; i++)
|
||||
{
|
||||
if ([cleaned_message characterAtIndex: i] == '\r' &&
|
||||
(i+1 < limit && [cleaned_message characterAtIndex: i+1] == '\n') &&
|
||||
(i+2 < limit && !isspace([cleaned_message characterAtIndex: i+2])))
|
||||
break;
|
||||
}
|
||||
|
||||
[cleaned_message replaceBytesInRange: NSMakeRange(r1.location, i-r1.location)
|
||||
withBytes: NULL
|
||||
length: 0];
|
||||
}
|
||||
|
||||
dd = [[context activeUser] domainDefaults];
|
||||
error = [[SOGoMailer mailerWithDomainDefaults: dd]
|
||||
sendMailData: cleaned_message
|
||||
sendMailData: message
|
||||
toRecipients: [self allBareRecipients]
|
||||
sender: [self sender]
|
||||
withAuthenticator: [self authenticatorInContext: context]
|
||||
|
||||
@@ -76,6 +76,9 @@
|
||||
@interface SOGoMailGermanForward : SOGoMailForward
|
||||
@end
|
||||
|
||||
@interface SOGoMailHebrewForward : SOGoMailForward
|
||||
@end
|
||||
|
||||
@interface SOGoMailHungarianForward : SOGoMailForward
|
||||
@end
|
||||
|
||||
|
||||
@@ -287,6 +287,9 @@
|
||||
@implementation SOGoMailGermanForward
|
||||
@end
|
||||
|
||||
@implementation SOGoMailHebrewForward
|
||||
@end
|
||||
|
||||
@implementation SOGoMailHungarianForward
|
||||
@end
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<#newLine/>
|
||||
<#newLine/>
|
||||
<#signaturePlacementOnTop><#newLine/>
|
||||
<#signature/><#newLine/></#signaturePlacementOnTop>
|
||||
-------- הודעה מקורית --------<#newLine/>
|
||||
נושא: <#subject/><#newLine/>
|
||||
תאריך: <#date/><#newLine/>
|
||||
מאת: <#from/><#newLine/>
|
||||
<#hasReplyTo>השב: <#replyTo/></#hasReplyTo><#hasOrganization>אירגון: <#organization/></#hasOrganization>אל: <#to/><#newLine/>
|
||||
<#hasCc>מכותב: <#cc/></#hasCc><#hasNewsGroups>קבוצות חדשות: <#newsgroups/></#hasNewsGroups><#hasReferences>תזכור: <#references/></#hasReferences><#newLine/>
|
||||
<#newLine/>
|
||||
<#messageBody/><#newLine/>
|
||||
<#signaturePlacementOnBottom><#newLine/>
|
||||
<#newLine/>
|
||||
<#signature/></#signaturePlacementOnBottom>
|
||||
<#newLine/>
|
||||
@@ -0,0 +1,88 @@
|
||||
subject: WOString {
|
||||
value = subject;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
date: WOString {
|
||||
value = date;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
from: WOString {
|
||||
value = from;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
newLine: WOString {
|
||||
value = newLine;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasReplyTo: WOConditional {
|
||||
condition = hasReplyTo;
|
||||
}
|
||||
|
||||
replyTo: WOString {
|
||||
value = replyTo;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasOrganization: WOConditional {
|
||||
condition = hasOrganization;
|
||||
}
|
||||
|
||||
organization: WOString {
|
||||
value = organization;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
to: WOString {
|
||||
value = to;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasCc: WOConditional {
|
||||
condition = hasCc;
|
||||
}
|
||||
|
||||
cc: WOString {
|
||||
value = cc;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasNewsGroups: WOConditional {
|
||||
condition = hasNewsGroups;
|
||||
}
|
||||
|
||||
newsgroups: WOString {
|
||||
value = newsgroups;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasReferences: WOConditional {
|
||||
condition = hasReferences;
|
||||
}
|
||||
|
||||
references: WOString {
|
||||
value = references;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
messageBody: WOString {
|
||||
value = messageBody;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
signature: WOString {
|
||||
value = signature;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
signaturePlacementOnTop: WOConditional {
|
||||
condition = signaturePlacementOnTop;
|
||||
}
|
||||
|
||||
signaturePlacementOnBottom: WOConditional {
|
||||
condition = signaturePlacementOnTop;
|
||||
negate = YES;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<#replyPlacementOnTop><#newLine/>
|
||||
<#newLine/>
|
||||
</#replyPlacementOnTop><#signaturePlacementOnTop><#newLine/>
|
||||
<#signature/><#newLine/>
|
||||
</#signaturePlacementOnTop><#outlookMode>-------- הודעה מקורית --------<#newLine/>
|
||||
נושא: <#subject/><#newLine/>
|
||||
תאריך: <#date/><#newLine/>
|
||||
מאת: <#from/><#newLine/>
|
||||
<#hasReplyTo>השב: <#replyTo/></#hasReplyTo><#hasOrganization>אירגון: <#organization/></#hasOrganization>אל: <#to/><#newLine/>
|
||||
<#hasCc>מכותב: <#cc/></#hasCc><#hasNewsGroups>קבוצות חדשות: <#newsgroups/></#hasNewsGroups><#hasReferences>תזכור: <#references/></#hasReferences></#outlookMode><#newLine/>
|
||||
<#standardMode>פועל <#date/>, <#from/> כתב:</#standardMode><#newLine/>
|
||||
<#newLine/>
|
||||
<#messageBody/><#newLine/>
|
||||
<#replyPlacementOnBottom><#newLine/>
|
||||
<#newLine/>
|
||||
</#replyPlacementOnBottom><#signaturePlacementOnBottom><#signature/></#signaturePlacementOnBottom><#newLine/>
|
||||
@@ -0,0 +1,106 @@
|
||||
outlookMode: WOConditional {
|
||||
condition = outlookMode;
|
||||
}
|
||||
|
||||
standardMode: WOConditional {
|
||||
condition = outlookMode;
|
||||
negate = YES;
|
||||
}
|
||||
|
||||
subject: WOString {
|
||||
value = subject;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
date: WOString {
|
||||
value = date;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
from: WOString {
|
||||
value = from;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
newLine: WOString {
|
||||
value = newLine;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasReplyTo: WOConditional {
|
||||
condition = hasReplyTo;
|
||||
}
|
||||
|
||||
replyTo: WOString {
|
||||
value = replyTo;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasOrganization: WOConditional {
|
||||
condition = hasOrganization;
|
||||
}
|
||||
|
||||
organization: WOString {
|
||||
value = organization;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
to: WOString {
|
||||
value = to;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasCc: WOConditional {
|
||||
condition = hasCc;
|
||||
}
|
||||
|
||||
cc: WOString {
|
||||
value = cc;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasNewsGroups: WOConditional {
|
||||
condition = hasNewsGroups;
|
||||
}
|
||||
|
||||
newsgroups: WOString {
|
||||
value = newsgroups;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
hasReferences: WOConditional {
|
||||
condition = hasReferences;
|
||||
}
|
||||
|
||||
references: WOString {
|
||||
value = references;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
messageBody: WOString {
|
||||
value = messageBody;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
signature: WOString {
|
||||
value = signature;
|
||||
escapeHTML = NO;
|
||||
}
|
||||
|
||||
replyPlacementOnTop: WOConditional {
|
||||
condition = replyPlacementOnTop;
|
||||
}
|
||||
|
||||
replyPlacementOnBottom: WOConditional {
|
||||
condition = replyPlacementOnTop;
|
||||
negate = YES;
|
||||
}
|
||||
|
||||
signaturePlacementOnTop: WOConditional {
|
||||
condition = signaturePlacementOnTop;
|
||||
}
|
||||
|
||||
signaturePlacementOnBottom: WOConditional {
|
||||
condition = signaturePlacementOnTop;
|
||||
negate = YES;
|
||||
}
|
||||
@@ -75,6 +75,9 @@
|
||||
@interface SOGoMailGermanReply : SOGoMailReply
|
||||
@end
|
||||
|
||||
@interface SOGoMailHebrewReply : SOGoMailReply
|
||||
@end
|
||||
|
||||
@interface SOGoMailHungarianReply : SOGoMailReply
|
||||
@end
|
||||
|
||||
|
||||
@@ -123,6 +123,9 @@
|
||||
@implementation SOGoMailGermanReply
|
||||
@end
|
||||
|
||||
@implementation SOGoMailHebrewReply
|
||||
@end
|
||||
|
||||
@implementation SOGoMailHungarianReply
|
||||
@end
|
||||
|
||||
|
||||
@@ -114,7 +114,9 @@ static memcached_st *handle = NULL;
|
||||
if (handle)
|
||||
{
|
||||
memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1);
|
||||
|
||||
memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_TCP_NODELAY, 1);
|
||||
memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_RETRY_TIMEOUT, 5);
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
|
||||
// We define the default value for cleaning up cached users'
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
"Finnish",
|
||||
"French",
|
||||
"German",
|
||||
"Hebrew",
|
||||
"Hungarian",
|
||||
"Icelandic",
|
||||
"Italian",
|
||||
|
||||
@@ -439,7 +439,23 @@ static NSArray *childRecordFields = nil;
|
||||
|
||||
- (GCSFolder *) ocsFolderForPath: (NSString *) _path
|
||||
{
|
||||
return [[self folderManager] folderAtPath: _path];
|
||||
NSDictionary *record;
|
||||
SOGoCache *cache;
|
||||
|
||||
cache = [SOGoCache sharedCache];
|
||||
record = [[cache valueForKey: _path] objectFromJSONString];
|
||||
|
||||
if (!record)
|
||||
{
|
||||
record = [[self folderManager] recordAtPath: _path];
|
||||
|
||||
if (!record)
|
||||
return nil;
|
||||
|
||||
[cache setValue: [record jsonRepresentation] forKey: _path];
|
||||
}
|
||||
|
||||
return [[self folderManager] folderForRecord: record];
|
||||
}
|
||||
|
||||
- (BOOL) folderIsMandatory
|
||||
@@ -590,6 +606,7 @@ static NSArray *childRecordFields = nil;
|
||||
[self removeFolderSettings: moduleSettings
|
||||
withReference: [self folderReference]];
|
||||
[us synchronize];
|
||||
[[SOGoCache sharedCache] removeValueForKey: ocsPath];
|
||||
|
||||
if ([[context request] handledByDefaultHandler])
|
||||
[self sendFolderAdvisoryTemplate: @"Removal"];
|
||||
|
||||
@@ -40,6 +40,111 @@
|
||||
|
||||
#import "SOGoMailer.h"
|
||||
|
||||
//
|
||||
// Useful extension that comes from Pantomime which is also
|
||||
// released under the LGPL. We should eventually merge
|
||||
// this with the same category found in SOPE's NGSmtpClient.m
|
||||
// or simply drop sope-mime in favor of Pantomime
|
||||
//
|
||||
@interface NSMutableData (DataCleanupExtension)
|
||||
|
||||
- (unichar) characterAtIndex: (int) theIndex;
|
||||
- (NSRange) rangeOfCString: (const char *) theCString;
|
||||
- (NSRange) rangeOfCString: (const char *) theCString
|
||||
options: (unsigned int) theOptions
|
||||
range: (NSRange) theRange;
|
||||
@end
|
||||
|
||||
@implementation NSMutableData (DataCleanupExtension)
|
||||
|
||||
- (unichar) characterAtIndex: (int) theIndex
|
||||
{
|
||||
const char *bytes;
|
||||
int i, len;
|
||||
|
||||
len = [self length];
|
||||
|
||||
if (len == 0 || theIndex >= len)
|
||||
{
|
||||
[[NSException exceptionWithName: NSRangeException
|
||||
reason: @"Index out of range."
|
||||
userInfo: nil] raise];
|
||||
|
||||
return (unichar)0;
|
||||
}
|
||||
|
||||
bytes = [self bytes];
|
||||
|
||||
for (i = 0; i < theIndex; i++)
|
||||
{
|
||||
bytes++;
|
||||
}
|
||||
|
||||
return (unichar)*bytes;
|
||||
}
|
||||
|
||||
- (NSRange) rangeOfCString: (const char *) theCString
|
||||
{
|
||||
return [self rangeOfCString: theCString
|
||||
options: 0
|
||||
range: NSMakeRange(0,[self length])];
|
||||
}
|
||||
|
||||
-(NSRange) rangeOfCString: (const char *) theCString
|
||||
options: (unsigned int) theOptions
|
||||
range: (NSRange) theRange
|
||||
{
|
||||
const char *b, *bytes;
|
||||
int i, len, slen;
|
||||
|
||||
if (!theCString)
|
||||
{
|
||||
return NSMakeRange(NSNotFound,0);
|
||||
}
|
||||
|
||||
bytes = [self bytes];
|
||||
len = [self length];
|
||||
slen = strlen(theCString);
|
||||
|
||||
b = bytes;
|
||||
|
||||
if (len > theRange.location + theRange.length)
|
||||
{
|
||||
len = theRange.location + theRange.length;
|
||||
}
|
||||
|
||||
if (theOptions == NSCaseInsensitiveSearch)
|
||||
{
|
||||
i = theRange.location;
|
||||
b += i;
|
||||
|
||||
for (; i <= len-slen; i++, b++)
|
||||
{
|
||||
if (!strncasecmp(theCString,b,slen))
|
||||
{
|
||||
return NSMakeRange(i,slen);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
i = theRange.location;
|
||||
b += i;
|
||||
|
||||
for (; i <= len-slen; i++, b++)
|
||||
{
|
||||
if (!memcmp(theCString,b,slen))
|
||||
{
|
||||
return NSMakeRange(i,slen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NSMakeRange(NSNotFound,0);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation SOGoMailer
|
||||
|
||||
+ (SOGoMailer *) mailerWithDomainDefaults: (SOGoDomainDefaults *) dd
|
||||
@@ -241,6 +346,52 @@
|
||||
reason: @"cannot send message: no sender set"];
|
||||
else
|
||||
{
|
||||
NSMutableData *cleaned_message;
|
||||
NSRange r1;
|
||||
unsigned int limit;
|
||||
|
||||
//
|
||||
// We now look for the Bcc: header. If it is present, we remove it.
|
||||
// Some servers, like qmail, do not remove it automatically.
|
||||
//
|
||||
#warning FIXME - we should fix the case issue when we switch to Pantomime
|
||||
cleaned_message = [NSMutableData dataWithData: data];
|
||||
|
||||
// We search only in the headers so we start at 0 until
|
||||
// we find \r\n\r\n, which is the headers delimiter
|
||||
r1 = [cleaned_message rangeOfCString: "\r\n\r\n"];
|
||||
limit = r1.location-1;
|
||||
|
||||
// We check if the mail actually *starts* with the Bcc: header
|
||||
r1 = [cleaned_message rangeOfCString: "Bcc: "
|
||||
options: 0
|
||||
range: NSMakeRange(0,5)];
|
||||
|
||||
// It does not, let's search in the entire headers
|
||||
if (r1.location == NSNotFound)
|
||||
r1 = [cleaned_message rangeOfCString: "\r\nBcc: "
|
||||
options: 0
|
||||
range: NSMakeRange(0,limit)];
|
||||
|
||||
if (r1.location != NSNotFound)
|
||||
{
|
||||
// We search for the first \r\n AFTER the Bcc: header and
|
||||
// replace the whole thing with \r\n.
|
||||
unsigned int i;
|
||||
|
||||
for (i = r1.location+7; i < limit; i++)
|
||||
{
|
||||
if ([cleaned_message characterAtIndex: i] == '\r' &&
|
||||
(i+1 < limit && [cleaned_message characterAtIndex: i+1] == '\n') &&
|
||||
(i+2 < limit && !isspace([cleaned_message characterAtIndex: i+2])))
|
||||
break;
|
||||
}
|
||||
|
||||
[cleaned_message replaceBytesInRange: NSMakeRange(r1.location, i-r1.location+2)
|
||||
withBytes: NULL
|
||||
length: 0];
|
||||
}
|
||||
|
||||
if ([mailingMechanism isEqualToString: @"sendmail"])
|
||||
result = [self _sendmailSendData: data
|
||||
toRecipients: recipients
|
||||
|
||||
@@ -297,14 +297,8 @@ static SoSecurityManager *sm = nil;
|
||||
// This is important because user A could delete folder X, and user B has subscribed to it.
|
||||
// If the "default roles" are enabled for calendars/address books, -validatePersmission:.. will
|
||||
// work (grabbing the default role) and the deleted resource will be incorrectly returned.
|
||||
//
|
||||
// FIXME - 2015/01/29 - this fix (24c6c8c91d421594bd51f07904d4cd3911cd187c) was reverted. Normally, we should add
|
||||
// [subscribedFolder ocsFolderForPath: [subscribedFolder ocsPath]] to check the existence of the folder but it causes
|
||||
// massive SQL traffic.
|
||||
//
|
||||
// The proper fix would be to check upon login and only upon login if GCS folders that we're subscribed to are still existent.
|
||||
//
|
||||
if (subscribedFolder
|
||||
&& [subscribedFolder ocsFolderForPath: [subscribedFolder ocsPath]]
|
||||
&& ![sm validatePermission: SOGoPerm_AccessObject
|
||||
onObject: subscribedFolder
|
||||
inContext: context])
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SOGoSieveManager.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2010-2015 Inverse inc.
|
||||
* Copyright (C) 2010-2017 Inverse inc.
|
||||
*
|
||||
* Author: Inverse <info@inverse.ca>
|
||||
*
|
||||
@@ -20,9 +20,7 @@
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
@@ -366,7 +364,7 @@ static NSString *sieveScriptName = @"sogo";
|
||||
}
|
||||
else
|
||||
scriptError = @"Rule lacks a 'value' parameter";
|
||||
|
||||
|
||||
return (scriptError == nil);
|
||||
}
|
||||
|
||||
@@ -784,7 +782,8 @@ static NSString *sieveScriptName = @"sogo";
|
||||
SOGoDomainDefaults *dd;
|
||||
NGSieveClient *client;
|
||||
NSString *filterScript, *v;
|
||||
BOOL b;
|
||||
BOOL b, dateCapability;
|
||||
unsigned int now;
|
||||
|
||||
dd = [user domainDefaults];
|
||||
if (!([dd sieveScriptsEnabled] || [dd vacationEnabled] || [dd forwardEnabled]))
|
||||
@@ -797,7 +796,7 @@ static NSString *sieveScriptName = @"sogo";
|
||||
if (!client)
|
||||
return NO;
|
||||
|
||||
// We adjust the "methodRequirements" based on the server's
|
||||
// We adjust the "methodRequirements" based on the server's
|
||||
// capabilities. Cyrus exposes "imapflags" while Dovecot (and
|
||||
// potentially others) expose "imap4flags" as specified in RFC5332
|
||||
if ([client hasCapability: @"imap4flags"])
|
||||
@@ -806,7 +805,9 @@ static NSString *sieveScriptName = @"sogo";
|
||||
[methodRequirements setObject: @"imap4flags" forKey: @"removeflag"];
|
||||
[methodRequirements setObject: @"imap4flags" forKey: @"flag"];
|
||||
}
|
||||
|
||||
|
||||
dateCapability = [client hasCapability: @"date"] && [client hasCapability: @"relational"];
|
||||
|
||||
//
|
||||
// Now let's generate the script
|
||||
//
|
||||
@@ -832,16 +833,24 @@ static NSString *sieveScriptName = @"sogo";
|
||||
// We handle vacation messages.
|
||||
// See http://ietfreport.isoc.org/idref/draft-ietf-sieve-vacation/
|
||||
values = [ud vacationOptions];
|
||||
now = [[NSCalendarDate calendarDate] timeIntervalSince1970];
|
||||
|
||||
if (values && [[values objectForKey: @"enabled"] boolValue])
|
||||
if (values && [[values objectForKey: @"enabled"] boolValue] &&
|
||||
(![[values objectForKey: @"startDateEnabled"] boolValue] ||
|
||||
dateCapability || [[values objectForKey: @"startDate"] intValue] < now) &&
|
||||
(![[values objectForKey: @"endDateEnabled"] boolValue] ||
|
||||
dateCapability || [[values objectForKey: @"endDate"] intValue] > now))
|
||||
{
|
||||
NSCalendarDate *startDate, *endDate;
|
||||
NSMutableArray *allConditions;
|
||||
NSMutableString *vacation_script;
|
||||
NSArray *addresses;
|
||||
NSString *text;
|
||||
|
||||
|
||||
BOOL ignore, alwaysSend;
|
||||
int days, i;
|
||||
|
||||
|
||||
allConditions = [NSMutableArray array];
|
||||
days = [[values objectForKey: @"daysBetweenResponse"] intValue];
|
||||
addresses = [values objectForKey: @"autoReplyEmailAddresses"];
|
||||
alwaysSend = [[values objectForKey: @"alwaysSend"] boolValue];
|
||||
@@ -853,32 +862,64 @@ static NSString *sieveScriptName = @"sogo";
|
||||
days = 7;
|
||||
|
||||
vacation_script = [NSMutableString string];
|
||||
|
||||
|
||||
[req addObjectUniquely: @"vacation"];
|
||||
|
||||
// Skip mailing lists
|
||||
if (ignore)
|
||||
[vacation_script appendString: @"if allof ( not exists [\"list-help\", \"list-unsubscribe\", \"list-subscribe\", \"list-owner\", \"list-post\", \"list-archive\", \"list-id\", \"Mailing-List\"], not header :comparator \"i;ascii-casemap\" :is \"Precedence\" [\"list\", \"bulk\", \"junk\"], not header :comparator \"i;ascii-casemap\" :matches \"To\" \"Multiple recipients of*\" ) {"];
|
||||
|
||||
{
|
||||
[allConditions addObject: @"not exists [\"list-help\", \"list-unsubscribe\", \"list-subscribe\", \"list-owner\", \"list-post\", \"list-archive\", \"list-id\", \"Mailing-List\"]"];
|
||||
[allConditions addObject: @"not header :comparator \"i;ascii-casemap\" :is \"Precedence\" [\"list\", \"bulk\", \"junk\"]"];
|
||||
[allConditions addObject: @"not header :comparator \"i;ascii-casemap\" :matches \"To\" \"Multiple recipients of*\""];
|
||||
}
|
||||
|
||||
// Start date of auto-reply
|
||||
if ([[values objectForKey: @"startDateEnabled"] boolValue] && dateCapability)
|
||||
{
|
||||
[req addObjectUniquely: @"date"];
|
||||
[req addObjectUniquely: @"relational"];
|
||||
startDate = [NSCalendarDate dateWithTimeIntervalSince1970:
|
||||
[[values objectForKey: @"startDate"] intValue]];
|
||||
[allConditions addObject: [NSString stringWithFormat: @"currentdate :value \"ge\" \"date\" \"%@\"",
|
||||
[startDate descriptionWithCalendarFormat: @"%Y-%m-%d"]]];
|
||||
}
|
||||
|
||||
// End date of auto-reply
|
||||
if ([[values objectForKey: @"endDateEnabled"] boolValue] && dateCapability)
|
||||
{
|
||||
[req addObjectUniquely: @"date"];
|
||||
[req addObjectUniquely: @"relational"];
|
||||
endDate = [NSCalendarDate dateWithTimeIntervalSince1970:
|
||||
[[values objectForKey: @"endDate"] intValue]];
|
||||
[allConditions addObject: [NSString stringWithFormat: @"currentdate :value \"le\" \"date\" \"%@\"",
|
||||
[endDate descriptionWithCalendarFormat: @"%Y-%m-%d"]]];
|
||||
}
|
||||
|
||||
// Apply conditions
|
||||
if ([allConditions count])
|
||||
[vacation_script appendFormat: @"if allof ( %@ ) { ",
|
||||
[allConditions componentsJoinedByString: @", "]];
|
||||
|
||||
[vacation_script appendFormat: @"vacation :days %d :addresses [", days];
|
||||
|
||||
for (i = 0; i < [addresses count]; i++)
|
||||
{
|
||||
[vacation_script appendFormat: @"\"%@\"", [addresses objectAtIndex: i]];
|
||||
|
||||
|
||||
if (i == [addresses count]-1)
|
||||
[vacation_script appendString: @"] "];
|
||||
else
|
||||
[vacation_script appendString: @", "];
|
||||
}
|
||||
|
||||
|
||||
[vacation_script appendFormat: @"text:\r\n%@\r\n.\r\n;\r\n", text];
|
||||
|
||||
if (ignore)
|
||||
|
||||
// Closing bracket of conditions
|
||||
if ([allConditions count])
|
||||
[vacation_script appendString: @"}\r\n"];
|
||||
|
||||
//
|
||||
// See http://sogo.nu/bugs/view.php?id=2332 for details
|
||||
// See https://sogo.nu/bugs/view.php?id=2332 for details
|
||||
//
|
||||
if (alwaysSend)
|
||||
[script insertString: vacation_script atIndex: 0];
|
||||
@@ -896,7 +937,7 @@ static NSString *sieveScriptName = @"sogo";
|
||||
int i;
|
||||
|
||||
b = YES;
|
||||
|
||||
|
||||
addresses = [values objectForKey: @"forwardAddress"];
|
||||
if ([addresses isKindOfClass: [NSString class]])
|
||||
addresses = [NSArray arrayWithObject: addresses];
|
||||
@@ -907,11 +948,11 @@ static NSString *sieveScriptName = @"sogo";
|
||||
if (v && [v length] > 0)
|
||||
[script appendFormat: @"redirect \"%@\";\r\n", v];
|
||||
}
|
||||
|
||||
|
||||
if ([[values objectForKey: @"keepCopy"] boolValue])
|
||||
[script appendString: @"keep;\r\n"];
|
||||
}
|
||||
|
||||
|
||||
if ([req count])
|
||||
{
|
||||
header = [NSString stringWithFormat: @"require [\"%@\"];\r\n",
|
||||
@@ -925,7 +966,7 @@ static NSString *sieveScriptName = @"sogo";
|
||||
result = [client setActiveScript: @""];
|
||||
// We delete the existing Sieve script
|
||||
result = [client deleteScript: sieveScriptName];
|
||||
|
||||
|
||||
if (![[result valueForKey:@"result"] boolValue]) {
|
||||
[self logWithFormat: @"WARNING: Could not delete Sieve script - continuing...: %@", result];
|
||||
}
|
||||
@@ -935,13 +976,13 @@ static NSString *sieveScriptName = @"sogo";
|
||||
if (b && [script length])
|
||||
{
|
||||
result = [client putScript: sieveScriptName script: script];
|
||||
|
||||
|
||||
if (![[result valueForKey:@"result"] boolValue]) {
|
||||
[self logWithFormat: @"Could not upload Sieve script: %@", result];
|
||||
[client closeConnection];
|
||||
[client closeConnection];
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
result = [client setActiveScript: sieveScriptName];
|
||||
if (![[result valueForKey:@"result"] boolValue]) {
|
||||
[self logWithFormat: @"Could not enable Sieve script: %@", result];
|
||||
|
||||
Reference in new issue
Block a user