diff --git a/pkg/caldav/parsing.go b/pkg/caldav/parsing.go index 4d4e0572b..113b80774 100644 --- a/pkg/caldav/parsing.go +++ b/pkg/caldav/parsing.go @@ -265,6 +265,7 @@ func getHexColorFromCaldavColor(caldavColor string) string { return hexColor } +//nolint:gocyclo func ParseTaskFromVTODO(content string) (vTask *models.Task, err error) { parsed, err := ics.ParseCalendar(strings.NewReader(content)) if err != nil { @@ -301,11 +302,23 @@ func ParseTaskFromVTODO(content string) (vTask *models.Task, err error) { } } + // Get UID and SUMMARY (log warning if missing, but don't fail for backwards compatibility) + uid, hasUID := task["UID"] + if !hasUID { + log.Warningf("[CALDAV] VTODO missing UID field") + } + + summary, hasSummary := task["SUMMARY"] + if !hasSummary { + log.Warningf("[CALDAV] VTODO missing SUMMARY field") + } + // Parse the priority var priority int64 if _, ok := task["PRIORITY"]; ok { priorityParsed, err := strconv.ParseInt(task["PRIORITY"].Value, 10, 64) if err != nil { + log.Errorf("[CALDAV] Failed to parse PRIORITY: %v", err) return nil, err } @@ -313,10 +326,16 @@ func ParseTaskFromVTODO(content string) (vTask *models.Task, err error) { } // Parse the enddate - duration, _ := time.ParseDuration(task["DURATION"].Value) + var duration time.Duration + if durationProp, ok := task["DURATION"]; ok { + duration, _ = time.ParseDuration(durationProp.Value) + } - description := strings.ReplaceAll(task["DESCRIPTION"].Value, "\\,", ",") - description = strings.ReplaceAll(description, "\\n", "\n") + description := "" + if descProp, ok := task["DESCRIPTION"]; ok { + description = strings.ReplaceAll(descProp.Value, "\\,", ",") + description = strings.ReplaceAll(description, "\\n", "\n") + } var labels []*models.Label if val, ok := task["CATEGORIES"]; ok { @@ -329,9 +348,18 @@ func ParseTaskFromVTODO(content string) (vTask *models.Task, err error) { } } + // Safely extract values + var uidValue, titleValue string + if hasUID { + uidValue = uid.Value + } + if hasSummary { + titleValue = summary.Value + } + vTask = &models.Task{ - UID: task["UID"].Value, - Title: task["SUMMARY"].Value, + UID: uidValue, + Title: titleValue, Description: description, Priority: priority, Labels: labels, @@ -371,7 +399,7 @@ func ParseTaskFromVTODO(content string) (vTask *models.Task, err error) { }) } - if task["STATUS"].Value == "COMPLETED" { + if status, ok := task["STATUS"]; ok && status.Value == "COMPLETED" { vTask.Done = true } diff --git a/pkg/routes/caldav/listStorageProvider.go b/pkg/routes/caldav/listStorageProvider.go index c5b5e5b41..6d6984600 100644 --- a/pkg/routes/caldav/listStorageProvider.go +++ b/pkg/routes/caldav/listStorageProvider.go @@ -288,6 +288,8 @@ func (vcls *VikunjaCaldavProjectStorage) CreateResource(rpath, content string) ( vTask, err := caldav.ParseTaskFromVTODO(content) if err != nil { + log.Errorf("[CALDAV] Failed to parse VTODO in CreateResource: %v", err) + log.Debugf("[CALDAV] VTODO content that failed to parse: %s", content) return nil, err } @@ -296,15 +298,18 @@ func (vcls *VikunjaCaldavProjectStorage) CreateResource(rpath, content string) ( // Check the permissions canCreate, err := vTask.CanCreate(s, vcls.user) if err != nil { + log.Errorf("[CALDAV] Permission check failed in CreateResource for user %s, project %d: %v", vcls.user.Username, vcls.project.ID, err) return nil, err } if !canCreate { + log.Warningf("[CALDAV] User %s does not have permission to create task in project %d", vcls.user.Username, vcls.project.ID) return nil, errs.ForbiddenError } // Create the task err = vTask.Create(s, vcls.user) if err != nil { + log.Errorf("[CALDAV] Failed to create task in CreateResource: %v, task: %+v", err, vTask) _ = s.Rollback() return nil, err } @@ -312,6 +317,7 @@ func (vcls *VikunjaCaldavProjectStorage) CreateResource(rpath, content string) ( vcls.task.ID = vTask.ID err = persistLabels(s, vcls.user, vcls.task, vTask.Labels) if err != nil { + log.Errorf("[CALDAV] Failed to persist labels in CreateResource: %v, labels: %+v", err, vTask.Labels) _ = s.Rollback() return nil, err } @@ -319,11 +325,13 @@ func (vcls *VikunjaCaldavProjectStorage) CreateResource(rpath, content string) ( vcls.task.ProjectID = vcls.project.ID err = persistRelations(s, vcls.user, vcls.task, vTask.RelatedTasks) if err != nil { + log.Errorf("[CALDAV] Failed to persist relations in CreateResource: %v, relations: %+v", err, vTask.RelatedTasks) _ = s.Rollback() return nil, err } if err := s.Commit(); err != nil { + log.Errorf("[CALDAV] Failed to commit transaction in CreateResource: %v", err) return nil, err } @@ -341,6 +349,8 @@ func (vcls *VikunjaCaldavProjectStorage) UpdateResource(rpath, content string) ( vTask, err := caldav.ParseTaskFromVTODO(content) if err != nil { + log.Errorf("[CALDAV] Failed to parse VTODO in UpdateResource: %v", err) + log.Debugf("[CALDAV] VTODO content that failed to parse: %s", content) return nil, err } @@ -357,10 +367,12 @@ func (vcls *VikunjaCaldavProjectStorage) UpdateResource(rpath, content string) ( // Check the permissions canUpdate, err := vTask.CanUpdate(s, vcls.user) if err != nil { + log.Errorf("[CALDAV] Permission check failed in UpdateResource for user %s, task %d: %v", vcls.user.Username, vcls.task.ID, err) _ = s.Rollback() return nil, err } if !canUpdate { + log.Warningf("[CALDAV] User %s does not have permission to update task %d", vcls.user.Username, vcls.task.ID) _ = s.Rollback() return nil, errs.ForbiddenError } @@ -368,23 +380,27 @@ func (vcls *VikunjaCaldavProjectStorage) UpdateResource(rpath, content string) ( // Update the task err = vTask.Update(s, vcls.user) if err != nil { + log.Errorf("[CALDAV] Failed to update task in UpdateResource: %v, task: %+v", err, vTask) _ = s.Rollback() return nil, err } err = persistLabels(s, vcls.user, vcls.task, vTask.Labels) if err != nil { + log.Errorf("[CALDAV] Failed to persist labels in UpdateResource: %v, labels: %+v", err, vTask.Labels) _ = s.Rollback() return nil, err } err = persistRelations(s, vcls.user, vcls.task, vTask.RelatedTasks) if err != nil { + log.Errorf("[CALDAV] Failed to persist relations in UpdateResource: %v, relations: %+v", err, vTask.RelatedTasks) _ = s.Rollback() return nil, err } if err := s.Commit(); err != nil { + log.Errorf("[CALDAV] Failed to commit transaction in UpdateResource: %v", err) return nil, err }