/** * Location implementation classes for Mail Manager services */ import type { ServiceLocation, ServiceLocationUri, ServiceLocationSocketSole, ServiceLocationSocketSplit, ServiceLocationFile } from '@/types/service'; /** * Base Location class */ export abstract class Location { abstract toJson(): ServiceLocation; static fromJson(data: ServiceLocation): Location { switch (data.type) { case 'URI': return LocationUri.fromJson(data); case 'SOCKET_SOLE': return LocationSocketSole.fromJson(data); case 'SOCKET_SPLIT': return LocationSocketSplit.fromJson(data); case 'FILE': return LocationFile.fromJson(data); default: throw new Error(`Unknown location type: ${(data as any).type}`); } } } /** * URI-based service location for API and web services * Used by: JMAP, Gmail API, etc. */ export class LocationUri extends Location { readonly type = 'URI' as const; scheme: string; host: string; port: number; path?: string; verifyPeer: boolean; verifyHost: boolean; constructor( scheme: string = 'https', host: string = '', port: number = 443, path?: string, verifyPeer: boolean = true, verifyHost: boolean = true ) { super(); this.scheme = scheme; this.host = host; this.port = port; this.path = path; this.verifyPeer = verifyPeer; this.verifyHost = verifyHost; } static fromJson(data: ServiceLocationUri): LocationUri { return new LocationUri( data.scheme, data.host, data.port, data.path, data.verifyPeer ?? true, data.verifyHost ?? true ); } toJson(): ServiceLocationUri { return { type: this.type, scheme: this.scheme, host: this.host, port: this.port, ...(this.path && { path: this.path }), ...(this.verifyPeer !== undefined && { verifyPeer: this.verifyPeer }), ...(this.verifyHost !== undefined && { verifyHost: this.verifyHost }) }; } getUrl(): string { const path = this.path || ''; return `${this.scheme}://${this.host}:${this.port}${path}`; } } /** * Single socket-based service location * Used by: services using a single host/port combination */ export class LocationSocketSole extends Location { readonly type = 'SOCKET_SOLE' as const; host: string; port: number; encryption: 'none' | 'ssl' | 'tls' | 'starttls'; verifyPeer: boolean; verifyHost: boolean; constructor( host: string = '', port: number = 993, encryption: 'none' | 'ssl' | 'tls' | 'starttls' = 'ssl', verifyPeer: boolean = true, verifyHost: boolean = true ) { super(); this.host = host; this.port = port; this.encryption = encryption; this.verifyPeer = verifyPeer; this.verifyHost = verifyHost; } static fromJson(data: ServiceLocationSocketSole): LocationSocketSole { return new LocationSocketSole( data.host, data.port, data.encryption, data.verifyPeer ?? true, data.verifyHost ?? true ); } toJson(): ServiceLocationSocketSole { return { type: this.type, host: this.host, port: this.port, encryption: this.encryption, ...(this.verifyPeer !== undefined && { verifyPeer: this.verifyPeer }), ...(this.verifyHost !== undefined && { verifyHost: this.verifyHost }) }; } } /** * Split socket-based service location * Used by: traditional IMAP/SMTP configurations */ export class LocationSocketSplit extends Location { readonly type = 'SOCKET_SPLIT' as const; inboundHost: string; inboundPort: number; inboundEncryption: 'none' | 'ssl' | 'tls' | 'starttls'; outboundHost: string; outboundPort: number; outboundEncryption: 'none' | 'ssl' | 'tls' | 'starttls'; inboundVerifyPeer: boolean; inboundVerifyHost: boolean; outboundVerifyPeer: boolean; outboundVerifyHost: boolean; constructor( inboundHost: string = '', inboundPort: number = 993, inboundEncryption: 'none' | 'ssl' | 'tls' | 'starttls' = 'ssl', outboundHost: string = '', outboundPort: number = 465, outboundEncryption: 'none' | 'ssl' | 'tls' | 'starttls' = 'ssl', inboundVerifyPeer: boolean = true, inboundVerifyHost: boolean = true, outboundVerifyPeer: boolean = true, outboundVerifyHost: boolean = true ) { super(); this.inboundHost = inboundHost; this.inboundPort = inboundPort; this.inboundEncryption = inboundEncryption; this.outboundHost = outboundHost; this.outboundPort = outboundPort; this.outboundEncryption = outboundEncryption; this.inboundVerifyPeer = inboundVerifyPeer; this.inboundVerifyHost = inboundVerifyHost; this.outboundVerifyPeer = outboundVerifyPeer; this.outboundVerifyHost = outboundVerifyHost; } static fromJson(data: ServiceLocationSocketSplit): LocationSocketSplit { return new LocationSocketSplit( data.inboundHost, data.inboundPort, data.inboundEncryption, data.outboundHost, data.outboundPort, data.outboundEncryption, data.inboundVerifyPeer ?? true, data.inboundVerifyHost ?? true, data.outboundVerifyPeer ?? true, data.outboundVerifyHost ?? true ); } toJson(): ServiceLocationSocketSplit { return { type: this.type, inboundHost: this.inboundHost, inboundPort: this.inboundPort, inboundEncryption: this.inboundEncryption, outboundHost: this.outboundHost, outboundPort: this.outboundPort, outboundEncryption: this.outboundEncryption, ...(this.inboundVerifyPeer !== undefined && { inboundVerifyPeer: this.inboundVerifyPeer }), ...(this.inboundVerifyHost !== undefined && { inboundVerifyHost: this.inboundVerifyHost }), ...(this.outboundVerifyPeer !== undefined && { outboundVerifyPeer: this.outboundVerifyPeer }), ...(this.outboundVerifyHost !== undefined && { outboundVerifyHost: this.outboundVerifyHost }) }; } } /** * File-based service location * Used by: local file system providers */ export class LocationFile extends Location { readonly type = 'FILE' as const; path: string; constructor(path: string = '') { super(); this.path = path; } static fromJson(data: ServiceLocationFile): LocationFile { return new LocationFile(data.path); } toJson(): ServiceLocationFile { return { type: this.type, path: this.path }; } }