mirror of
https://github.com/ghostersk/gowebmail.git
synced 2026-04-17 08:36:01 +01:00
fixed sending of email(sending email considered it as spam)
This commit is contained in:
@@ -104,6 +104,7 @@ func main() {
|
||||
api.HandleFunc("/accounts", h.API.ListAccounts).Methods("GET")
|
||||
api.HandleFunc("/accounts", h.API.AddAccount).Methods("POST")
|
||||
api.HandleFunc("/accounts/test", h.API.TestConnection).Methods("POST")
|
||||
api.HandleFunc("/accounts/detect", h.API.DetectMailSettings).Methods("POST")
|
||||
api.HandleFunc("/accounts/{id:[0-9]+}", h.API.GetAccount).Methods("GET")
|
||||
api.HandleFunc("/accounts/{id:[0-9]+}", h.API.UpdateAccount).Methods("PUT")
|
||||
api.HandleFunc("/accounts/{id:[0-9]+}", h.API.DeleteAccount).Methods("DELETE")
|
||||
|
||||
@@ -446,6 +446,7 @@ func (d *DB) ListAuditLogs(page, pageSize int, eventFilter string) (*models.Audi
|
||||
}, rows.Err()
|
||||
}
|
||||
|
||||
|
||||
// ---- Email Accounts ----
|
||||
|
||||
func (d *DB) CreateAccount(a *models.EmailAccount) error {
|
||||
@@ -716,8 +717,7 @@ func (d *DB) GetFolderByPath(accountID int64, fullPath string) (*models.Folder,
|
||||
COALESCE(is_hidden,0), COALESCE(sync_enabled,1)
|
||||
FROM folders WHERE account_id=? AND full_path=?`, accountID, fullPath,
|
||||
).Scan(&f.ID, &f.AccountID, &f.Name, &f.FullPath, &f.FolderType, &f.UnreadCount, &f.TotalCount, &isHidden, &syncEnabled)
|
||||
f.IsHidden = isHidden == 1
|
||||
f.SyncEnabled = syncEnabled == 1
|
||||
f.IsHidden = isHidden == 1; f.SyncEnabled = syncEnabled == 1
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1072,12 +1072,8 @@ func (d *DB) IsRemoteContentAllowed(userID int64, sender string) (bool, error) {
|
||||
// SetFolderVisibility sets is_hidden and sync_enabled for a folder owned by the user.
|
||||
func (d *DB) SetFolderVisibility(folderID, userID int64, isHidden, syncEnabled bool) error {
|
||||
ih, se := 0, 0
|
||||
if isHidden {
|
||||
ih = 1
|
||||
}
|
||||
if syncEnabled {
|
||||
se = 1
|
||||
}
|
||||
if isHidden { ih = 1 }
|
||||
if syncEnabled { se = 1 }
|
||||
_, err := d.sql.Exec(`
|
||||
UPDATE folders SET is_hidden=?, sync_enabled=?
|
||||
WHERE id=? AND account_id IN (SELECT id FROM email_accounts WHERE user_id=?)`,
|
||||
@@ -1089,11 +1085,11 @@ func (d *DB) GetFolderByID(folderID int64) (*models.Folder, error) {
|
||||
f := &models.Folder{}
|
||||
var isHidden, syncEnabled int
|
||||
err := d.sql.QueryRow(
|
||||
`SELECT id, account_id, name, full_path, folder_type, unread_count, total_count
|
||||
`SELECT id, account_id, name, full_path, folder_type, unread_count, total_count,
|
||||
COALESCE(is_hidden,0), COALESCE(sync_enabled,1)
|
||||
FROM folders WHERE id=?`, folderID,
|
||||
).Scan(&f.ID, &f.AccountID, &f.Name, &f.FullPath, &f.FolderType, &f.UnreadCount, &f.TotalCount, &isHidden, &syncEnabled)
|
||||
f.IsHidden = isHidden == 1
|
||||
f.SyncEnabled = syncEnabled == 1
|
||||
f.IsHidden = isHidden == 1; f.SyncEnabled = syncEnabled == 1
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -639,7 +639,12 @@ func SendMessageFull(ctx context.Context, account *gomailModels.EmailAccount, re
|
||||
func buildMIMEMessage(buf *bytes.Buffer, account *gomailModels.EmailAccount, req *gomailModels.ComposeRequest) string {
|
||||
from := netmail.Address{Name: account.DisplayName, Address: account.EmailAddress}
|
||||
boundary := fmt.Sprintf("gomail_%x", time.Now().UnixNano())
|
||||
msgID := fmt.Sprintf("<%d.%s@gomail>", time.Now().UnixNano(), strings.ReplaceAll(account.EmailAddress, "@", "."))
|
||||
// Use the sender's actual domain for Message-ID so it passes spam filters
|
||||
domain := account.EmailAddress
|
||||
if at := strings.Index(domain, "@"); at >= 0 {
|
||||
domain = domain[at+1:]
|
||||
}
|
||||
msgID := fmt.Sprintf("<%d.%s@%s>", time.Now().UnixNano(), strings.ReplaceAll(account.EmailAddress, "@", "."), domain)
|
||||
|
||||
buf.WriteString("Message-ID: " + msgID + "\r\n")
|
||||
buf.WriteString("From: " + from.String() + "\r\n")
|
||||
|
||||
@@ -5,9 +5,11 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/yourusername/gomail/config"
|
||||
@@ -240,6 +242,83 @@ func (h *APIHandler) TestConnection(w http.ResponseWriter, r *http.Request) {
|
||||
h.writeJSON(w, map[string]bool{"ok": true})
|
||||
}
|
||||
|
||||
// DetectMailSettings tries common IMAP/SMTP combinations for a domain and returns
|
||||
// the first working combination, or sensible defaults if nothing connects.
|
||||
func (h *APIHandler) DetectMailSettings(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
Email string `json:"email"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Email == "" {
|
||||
h.writeError(w, http.StatusBadRequest, "email required")
|
||||
return
|
||||
}
|
||||
at := strings.Index(req.Email, "@")
|
||||
if at < 0 {
|
||||
h.writeError(w, http.StatusBadRequest, "invalid email")
|
||||
return
|
||||
}
|
||||
domain := req.Email[at+1:]
|
||||
|
||||
type candidate struct {
|
||||
host string
|
||||
port int
|
||||
}
|
||||
imapCandidates := []candidate{
|
||||
{"imap." + domain, 993},
|
||||
{"mail." + domain, 993},
|
||||
{"imap." + domain, 143},
|
||||
{"mail." + domain, 143},
|
||||
}
|
||||
smtpCandidates := []candidate{
|
||||
{"smtp." + domain, 587},
|
||||
{"mail." + domain, 587},
|
||||
{"smtp." + domain, 465},
|
||||
{"mail." + domain, 465},
|
||||
{"smtp." + domain, 25},
|
||||
}
|
||||
|
||||
type result struct {
|
||||
IMAPHost string `json:"imap_host"`
|
||||
IMAPPort int `json:"imap_port"`
|
||||
SMTPHost string `json:"smtp_host"`
|
||||
SMTPPort int `json:"smtp_port"`
|
||||
Detected bool `json:"detected"`
|
||||
}
|
||||
|
||||
res := result{
|
||||
IMAPHost: "imap." + domain,
|
||||
IMAPPort: 993,
|
||||
SMTPHost: "smtp." + domain,
|
||||
SMTPPort: 587,
|
||||
}
|
||||
|
||||
// Try IMAP candidates (TCP dial only, no auth needed to detect)
|
||||
for _, c := range imapCandidates {
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", c.host, c.port), 4*time.Second)
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
res.IMAPHost = c.host
|
||||
res.IMAPPort = c.port
|
||||
res.Detected = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Try SMTP candidates
|
||||
for _, c := range smtpCandidates {
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", c.host, c.port), 4*time.Second)
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
res.SMTPHost = c.host
|
||||
res.SMTPPort = c.port
|
||||
res.Detected = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
h.writeJSON(w, res)
|
||||
}
|
||||
|
||||
func (h *APIHandler) DeleteAccount(w http.ResponseWriter, r *http.Request) {
|
||||
userID := middleware.GetUserID(r)
|
||||
accountID := pathInt64(r, "id")
|
||||
|
||||
@@ -425,4 +425,4 @@ body.admin-page{overflow:auto;background:var(--bg)}
|
||||
/* ── Icon sync button ────────────────────────────────────────── */
|
||||
.icon-sync-btn{background:none;border:none;color:var(--muted);cursor:pointer;
|
||||
padding:2px;border-radius:4px;line-height:1;flex-shrink:0;transition:color .15s}
|
||||
.icon-sync-btn:hover{color:var(--text)}
|
||||
.icon-sync-btn:hover{color:var(--text)}
|
||||
|
||||
@@ -139,8 +139,29 @@ async function addIMAPAccount() {
|
||||
btn.disabled=true;btn.textContent='Connecting...';
|
||||
const r=await api('POST','/accounts',body);
|
||||
btn.disabled=false;btn.textContent='Connect';
|
||||
if (r?.ok){toast('Account added!','success');closeModal('add-account-modal');loadAccounts();loadFolders();loadMessages();}
|
||||
else toast(r?.error||'Failed to add account','error');
|
||||
if (r?.ok){
|
||||
toast('Account added — syncing…','success');
|
||||
closeModal('add-account-modal');
|
||||
await loadAccounts();
|
||||
// Background sync takes a moment — reload folders/messages after a short wait
|
||||
setTimeout(async ()=>{ await loadFolders(); await loadMessages(); toast('Sync complete','success'); }, 3000);
|
||||
} else toast(r?.error||'Failed to add account','error');
|
||||
}
|
||||
|
||||
async function detectMailSettings() {
|
||||
const email=document.getElementById('imap-email').value.trim();
|
||||
if (!email||!email.includes('@')){toast('Enter your email address first','error');return;}
|
||||
const btn=document.getElementById('detect-btn');
|
||||
btn.innerHTML='<span class="spinner-inline"></span>Detecting…';btn.disabled=true;
|
||||
const r=await api('POST','/accounts/detect',{email});
|
||||
btn.textContent='Auto-detect';btn.disabled=false;
|
||||
if (!r){toast('Detection failed','error');return;}
|
||||
document.getElementById('imap-host').value=r.imap_host||'';
|
||||
document.getElementById('imap-port').value=r.imap_port||993;
|
||||
document.getElementById('smtp-host').value=r.smtp_host||'';
|
||||
document.getElementById('smtp-port').value=r.smtp_port||587;
|
||||
if(r.detected) toast(`Detected ${r.imap_host} / ${r.smtp_host}`,'success');
|
||||
else toast('No servers found — filled with defaults based on domain','info');
|
||||
}
|
||||
|
||||
async function syncNow(id, e) {
|
||||
@@ -885,4 +906,4 @@ document.addEventListener('DOMContentLoaded', ()=>{
|
||||
initTagField('compose-cc-tags');
|
||||
initTagField('compose-bcc-tags');
|
||||
init();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -170,9 +170,18 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-divider"><span>or add IMAP account</span></div>
|
||||
<div class="modal-field"><label>Email Address</label><input type="email" id="imap-email" placeholder="you@example.com"></div>
|
||||
<div class="modal-field"><label>Email Address</label>
|
||||
<div style="display:flex;gap:8px;flex:1">
|
||||
<input type="email" id="imap-email" placeholder="you@example.com" style="flex:1">
|
||||
<button class="btn-secondary" id="detect-btn" onclick="detectMailSettings()" style="white-space:nowrap;font-size:12px">Auto-detect</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-field"><label>Display Name</label><input type="text" id="imap-name" placeholder="Your Name"></div>
|
||||
<div class="modal-field"><label>Password / App Password</label><input type="password" id="imap-password"></div>
|
||||
<div style="font-size:11px;color:var(--muted);padding:0 0 8px;line-height:1.6">
|
||||
Common ports — IMAP: <strong>993</strong> TLS/SSL, <strong>143</strong> STARTTLS/Plain ·
|
||||
SMTP: <strong>587</strong> STARTTLS, <strong>465</strong> TLS/SSL, <strong>25</strong> Plain
|
||||
</div>
|
||||
<div class="modal-row">
|
||||
<div class="modal-field"><label>IMAP Host</label><input type="text" id="imap-host" placeholder="imap.example.com"></div>
|
||||
<div class="modal-field"><label>IMAP Port</label><input type="number" id="imap-port" value="993"></div>
|
||||
@@ -275,4 +284,4 @@
|
||||
|
||||
{{define "scripts"}}
|
||||
<script src="/static/js/app.js"></script>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
Reference in New Issue
Block a user