package main import ( "encoding/json" "net/mail" "strconv" "github.com/abhinavxd/libredesk/internal/envelope" "github.com/abhinavxd/libredesk/internal/inbox" "github.com/abhinavxd/libredesk/internal/inbox/channel/email/oauth" imodels "github.com/abhinavxd/libredesk/internal/inbox/models" "github.com/valyala/fasthttp" "github.com/zerodha/fastglue" ) // handleGetInboxes returns all inboxes func handleGetInboxes(r *fastglue.Request) error { var app = r.Context.(*App) inboxes, err := app.inbox.GetAll() if err != nil { return sendErrorEnvelope(r, err) } for i := range inboxes { if err := inboxes[i].ClearPasswords(); err != nil { app.lo.Error("error clearing inbox passwords from response", "error", err) return envelope.NewError(envelope.GeneralError, app.i18n.Ts("globals.messages.errorFetching", "name", "{globals.terms.inbox}"), nil) } } return r.SendEnvelope(inboxes) } // handleGetInbox returns an inbox by ID func handleGetInbox(r *fastglue.Request) error { var ( app = r.Context.(*App) id, _ = strconv.Atoi(r.RequestCtx.UserValue("id").(string)) ) inbox, err := app.inbox.GetDBRecord(id) if err != nil { return sendErrorEnvelope(r, err) } if err := inbox.ClearPasswords(); err != nil { app.lo.Error("error clearing inbox passwords from response", "error", err) return envelope.NewError(envelope.GeneralError, app.i18n.Ts("globals.messages.errorFetching", "name", "{globals.terms.inbox}"), nil) } return r.SendEnvelope(inbox) } // handleCreateInbox creates a new inbox func handleCreateInbox(r *fastglue.Request) error { var ( app = r.Context.(*App) inbox = imodels.Inbox{} ) if err := r.Decode(&inbox, "json"); err != nil { return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.errorParsing", "name", "{globals.terms.request}"), err.Error(), envelope.InputError) } createdInbox, err := app.inbox.Create(inbox) if err != nil { return sendErrorEnvelope(r, err) } if err := validateInbox(app, createdInbox); err != nil { return sendErrorEnvelope(r, err) } if err := reloadInboxes(app); err != nil { return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, app.i18n.Ts("globals.messages.couldNotReload", "name", "{globals.terms.inbox}"), nil, envelope.GeneralError) } // Clear passwords before returning. if err := createdInbox.ClearPasswords(); err != nil { app.lo.Error("error clearing inbox passwords from response", "error", err) return envelope.NewError(envelope.GeneralError, app.i18n.Ts("globals.messages.errorCreating", "name", "{globals.terms.inbox}"), nil) } return r.SendEnvelope(createdInbox) } // handleUpdateInbox updates an inbox func handleUpdateInbox(r *fastglue.Request) error { var ( app = r.Context.(*App) inbox = imodels.Inbox{} ) id, err := strconv.Atoi(r.RequestCtx.UserValue("id").(string)) if err != nil || id == 0 { return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.invalid", "name", "`id`"), nil, envelope.InputError) } if err := r.Decode(&inbox, "json"); err != nil { return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.errorParsing", "name", "{globals.terms.request}"), err.Error(), envelope.InputError) } if err := validateInbox(app, inbox); err != nil { return sendErrorEnvelope(r, err) } updatedInbox, err := app.inbox.Update(id, inbox) if err != nil { return sendErrorEnvelope(r, err) } if err := reloadInboxes(app); err != nil { return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, app.i18n.Ts("globals.messages.couldNotReload", "name", "{globals.terms.inbox}"), nil, envelope.GeneralError) } // Clear passwords before returning. if err := updatedInbox.ClearPasswords(); err != nil { app.lo.Error("error clearing inbox passwords from response", "error", err) return envelope.NewError(envelope.GeneralError, app.i18n.Ts("globals.messages.errorUpdating", "name", "{globals.terms.inbox}"), nil) } return r.SendEnvelope(updatedInbox) } // handleToggleInbox toggles an inbox func handleToggleInbox(r *fastglue.Request) error { var ( app = r.Context.(*App) ) id, err := strconv.Atoi(r.RequestCtx.UserValue("id").(string)) if err != nil || id == 0 { return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.invalid", "name", "`id`"), nil, envelope.InputError) } toggledInbox, err := app.inbox.Toggle(id) if err != nil { return err } if err := reloadInboxes(app); err != nil { return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, app.i18n.Ts("globals.messages.couldNotReload", "name", "{globals.terms.inbox}"), nil, envelope.GeneralError) } // Clear passwords before returning if err := toggledInbox.ClearPasswords(); err != nil { app.lo.Error("error clearing inbox passwords from response", "error", err) return envelope.NewError(envelope.GeneralError, app.i18n.Ts("globals.messages.errorFetching", "name", "{globals.terms.inbox}"), nil) } return r.SendEnvelope(toggledInbox) } // handleDeleteInbox deletes an inbox func handleDeleteInbox(r *fastglue.Request) error { var ( app = r.Context.(*App) id, _ = strconv.Atoi(r.RequestCtx.UserValue("id").(string)) ) err := app.inbox.SoftDelete(id) if err != nil { return sendErrorEnvelope(r, err) } if err := reloadInboxes(app); err != nil { return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, app.i18n.Ts("globals.messages.couldNotReload", "name", "{globals.terms.inbox}"), nil, envelope.GeneralError) } return r.SendEnvelope(true) } // validateInbox validates the inbox func validateInbox(app *App, inb imodels.Inbox) error { // Validate from address. if _, err := mail.ParseAddress(inb.From); err != nil { return envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.invalidFromAddress"), nil) } if len(inb.Config) == 0 { return envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.empty", "name", "config"), nil) } if inb.Name == "" { return envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.empty", "name", "name"), nil) } if inb.Channel == "" { return envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.empty", "name", "channel"), nil) } // Validate email channel config. if inb.Channel == inbox.ChannelEmail { if err := validateEmailConfig(app, inb.Config); err != nil { return err } } return nil } // validateEmailConfig validates the email inbox configuration. func validateEmailConfig(app *App, configJSON json.RawMessage) error { var cfg imodels.Config if err := json.Unmarshal(configJSON, &cfg); err != nil { return envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.invalid", "name", "config"), nil) } // Validate auth_type. if cfg.AuthType != "" && cfg.AuthType != imodels.AuthTypePassword && cfg.AuthType != imodels.AuthTypeOAuth2 { return envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.invalid", "name", "auth_type"), nil) } // Validate OAuth config if auth_type is oauth2. if cfg.AuthType == imodels.AuthTypeOAuth2 { if cfg.OAuth == nil { return envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.empty", "name", "oauth"), nil) } if cfg.OAuth.Provider != string(oauth.ProviderGoogle) && cfg.OAuth.Provider != string(oauth.ProviderMicrosoft) { return envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.invalid", "name", "oauth.provider"), nil) } if cfg.OAuth.ClientID == "" { return envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.empty", "name", "oauth.client_id"), nil) } } // Validate SMTP configs. for i, smtp := range cfg.SMTP { if smtp.Host == "" { return envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.empty", "name", "smtp.host"), nil) } if smtp.Port <= 0 { return envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.invalid", "name", "smtp.port"), nil) } // Validate auth_protocol for password auth. if cfg.AuthType != imodels.AuthTypeOAuth2 { validAuthProtocols := map[string]bool{"": true, "none": true, "plain": true, "login": true, "cram": true} if !validAuthProtocols[cfg.SMTP[i].AuthProtocol] { return envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.invalid", "name", "smtp.auth_protocol"), nil) } } } // Validate IMAP configs. for _, imap := range cfg.IMAP { if imap.Host == "" { return envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.empty", "name", "imap.host"), nil) } if imap.Port <= 0 { return envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.invalid", "name", "imap.port"), nil) } if imap.Mailbox == "" { return envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.empty", "name", "imap.mailbox"), nil) } // Validate tls_type. validTLSTypes := map[string]bool{"none": true, "starttls": true, "tls": true} if !validTLSTypes[imap.TLSType] { return envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.invalid", "name", "imap.tls_type"), nil) } } return nil }