# 家书 FamilyBook · Apache 重写规则
#
# ⚠️ 这个文件**不随包上线**（familybook_push.sh 的打包白名单里没有它）。
#    原因：线上站点根那份 .htaccess 是**宝塔面板生成的**（PHP 版本段、防盗链、
#    缓存段、伪静态…），整份覆盖会把面板的东西清空，而且面板下次重写还会改回来，
#    变成"时好时坏"。
#    /dav/ 与 /carddav/ 的重写规则由 `familybook_deploy.sh` 第 5c 段**追加**到线上那份里
#    （带 `# >>> FamilyBook CalDAV` 标记，幂等）。
#
#    那这份文件是干什么用的？
#      · 新建站点（没有宝塔那层）时，可直接把它作为站点根的 .htaccess；
#      · 它是那段规则的**唯一书面来源**，push 脚本会断言部署脚本里的规则串
#        与它完全一致（防两处漂移）。
#
# 只做一件事：把 /dav/ 与 /carddav/ 开头的 URL 分别交给 dav.php / carddav.php。
# 其余一律不动 —— index.php 那套 ?r=xxx 路由不依赖重写，
# 少写规则就少一处"上线才发现 404"的地方。

<IfModule mod_rewrite.c>
  RewriteEngine On

  # 🔴 v31：`/.well-known/*` 是 RFC 6764 规定的 DAV 发现入口，客户端**第一句**就问这里。
#    ⚠️ 它必须排在 `^dav` / `^carddav` 之前 —— 这两条是前缀通配（`^dav(/.*)?$`），
#       而 `.well-known/caldav` 并不以 dav 开头，所以严格说互不干扰；
#       但放在最前面是"发现链优先于资源访问"的意图表达，后人加规则时不易搞错。
#    交给 dav.php / carddav.php 回 301（它们在**鉴权之前**处理这条路径：
#    .well-known 是免鉴权入口，在这里 401 会让客户端以为"不支持 DAV"）。
RewriteRule ^\.well-known/caldav$ dav.php [L,QSA]
RewriteRule ^\.well-known/carddav$ carddav.php [L,QSA]

# CalDAV：/dav/、/dav/3.ics/、/dav/3.ics/12.ics 全部交给 dav.php
  RewriteRule ^dav(/.*)?$ dav.php [L,QSA]

  # CardDAV（v24 第 22 批）：/carddav/、/carddav/1.vcf/、/carddav/1.vcf/12.vcf 交给 carddav.php
  # ⚠️ 两条规则**顺序无关但都必须有**：它们的前缀（dav vs carddav）互不包含，
  #    不会互相抢；写成 `^dav` 在前是因为历史顺序，真要调也不能写成 `^dav(/|$)` 之外的
  #    宽松形式 —— 那会把 /dav2/ 之类也吞进来。
  RewriteRule ^carddav(/.*)?$ carddav.php [L,QSA]

  # 🔴 v31：`/.well-known/caldav` 与 `/.well-known/carddav`（RFC 6764）。
  #    标准客户端（DAVx⁵ / iOS / Evolution）添加账号时**第一句**就试这里 ——
  #    原来这里 404，客户端直接判定"域名下没有 DAV 服务"，连口令框都不弹。
  #    症状正是用户报的「同步不出任何数据」（账号根本建不起来）。
  #    ⚠️ 这两条必须**排在 `^dav` / `^carddav` 之前**吗？不需要 ——
  #       `.well-known/...` 不以 dav/carddav 开头，两条规则互不干扰。
  #       但**必须**在站点根的"真实文件"判断之前：Apache 对 .htaccess 里的
  #       RewriteRule 天然就是先于文件查找生效的，所以这里无需额外处理。
  #    交给 dav.php / carddav.php 自己回 301（它们在鉴权**之前**处理，
  #    因为 .well-known 是免鉴权入口 —— 在这里 401 会让客户端以为"不支持"）。
  RewriteRule ^\.well-known/caldav$ dav.php [L,QSA]
  RewriteRule ^\.well-known/carddav$ carddav.php [L,QSA]

  # 导航展示页（v24 第 24 批 用户第 4 条）：/nav/?<短语> —— 全站唯一免登录页面。
  # ⚠️ suggest 那条**必须排在前面**：`^nav(/.*)?$` 会把 /nav/suggest.php 也吞进去，
  #    顺序反了就变成"每次联想都去渲染展示页"（返回一页 HTML、前端拿到非 JSON）。
  #    这也是本批新踩的一条：之前只有 dav/carddav 两条互不包含的前缀，
  #    到这里第一次出现"一个前缀包含另一个"的情况，顺序**变成有语义的**了。
  RewriteRule ^nav/suggest\.php$ nav_suggest.php [L,QSA]
  RewriteRule ^nav/?$ nav_show.php [L,QSA]
</IfModule>

# ⚠️ 导航图标与展示页头像**不需要重写规则**（v24 第 25 批）：
#    nav_icon.php / nav_avatar.php 是站点根下的**真实文件**，Apache 直接就能执行。
#    展示页里的地址由 Nav::abs() 拼成 `<base_url>/nav_icon.php?f=…`，落在站点根，
#    不经过 /nav/ 这个重写前缀 —— 所以上面那两条规则一个字都不用动。
#    （这正是选"站点根下的两个独立入口"而不是"塞进 /nav/ 子路径"的原因：
#      塞进去就得再加两条重写规则，而重写规则的顺序语义已经踩过一次坑了。）

# ⚠️⚠️ DAV 的 Basic 鉴权**必须**靠这一行才能在 Apache 上工作：
#    Apache 默认**不把 `Authorization` 头交给 PHP-FPM** → `$_SERVER['PHP_AUTH_USER']`
#    和 `HTTP_AUTHORIZATION` 都是 null → /dav/ 与 /carddav/ 无论口令填得多正确都 401。
#    线上实测（Apache 2.4.53，直接监听 80/443）：
#      没有这行 : {"PHP_AUTH_USER":null,"HTTP_AUTHORIZATION":null}
#      加上这行 : {"PHP_AUTH_USER":"abc","HTTP_AUTHORIZATION":"(有)"}
#    `CGIPassAuth` 是 Apache **2.4.13+ 的核心指令**，不需要额外模块。
#    （本地 `php -S` 会自己填好 PHP_AUTH_USER，所以 e2e 全绿而线上全 401 ——
#      典型的"本地好好的、线上不行"。部署脚本也会把这一段追加到线上那份里。）
<IfModule mod_authz_core.c>
  CGIPassAuth On
</IfModule>

# 备份/临时文件永远不给出去（这条线上很要紧：*.php.bak 曾经可被直接下载）
<FilesMatch "\.(bak|old|orig|save|swp|swo|tmp)$">
  Require all denied
</FilesMatch>
<FilesMatch "^\.|~$">
  Require all denied
</FilesMatch>
