docs: Simplify XMPP integration - replace detailed Prosody/Ejabberd configs with standard XEP-0363 info
This commit is contained in:
334
README.md
334
README.md
@@ -185,8 +185,7 @@ Test Coverage:
|
|||||||
- [Podman Deployment](#podman-deployment)
|
- [Podman Deployment](#podman-deployment)
|
||||||
- [Nginx Reverse Proxy](#nginx-reverse-proxy)
|
- [Nginx Reverse Proxy](#nginx-reverse-proxy)
|
||||||
- [Apache2 Reverse Proxy](#apache2-reverse-proxy)
|
- [Apache2 Reverse Proxy](#apache2-reverse-proxy)
|
||||||
- [Prosody XMPP Integration](#prosody-xmpp-integration)
|
- [XMPP Server Integration](#xmpp-server-integration)
|
||||||
- [Ejabberd XMPP Integration](#ejabberd-xmpp-integration)
|
|
||||||
- [XEP-0363 Implementation](#xep-0363-implementation)
|
- [XEP-0363 Implementation](#xep-0363-implementation)
|
||||||
- [API Versions (V1, V2, V3)](#api-versions)
|
- [API Versions (V1, V2, V3)](#api-versions)
|
||||||
|
|
||||||
@@ -1521,334 +1520,27 @@ server {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Prosody XMPP Integration
|
## XMPP Server Integration
|
||||||
|
|
||||||
### Prosody Configuration
|
HMAC File Server implements the standard **XEP-0363: HTTP File Upload** protocol and works out-of-the-box with all XMPP servers including Prosody, Ejabberd, Openfire, and others.
|
||||||
```lua
|
|
||||||
-- /etc/prosody/prosody.cfg.lua
|
|
||||||
-- HMAC File Server integration for XEP-0363
|
|
||||||
|
|
||||||
-- Enable HTTP file upload module
|
### Quick Setup
|
||||||
modules_enabled = {
|
|
||||||
-- Core modules
|
|
||||||
"roster";
|
|
||||||
"saslauth";
|
|
||||||
"tls";
|
|
||||||
"dialback";
|
|
||||||
"disco";
|
|
||||||
"carbons";
|
|
||||||
"pep";
|
|
||||||
"private";
|
|
||||||
"blocklist";
|
|
||||||
"vcard4";
|
|
||||||
"vcard_legacy";
|
|
||||||
"version";
|
|
||||||
"uptime";
|
|
||||||
"time";
|
|
||||||
"ping";
|
|
||||||
"admin_adhoc";
|
|
||||||
|
|
||||||
-- HTTP file upload
|
Configure your XMPP server's `http_upload_external` module to point to your HMAC File Server:
|
||||||
"http_upload_external";
|
|
||||||
}
|
|
||||||
|
|
||||||
-- VirtualHost configuration
|
|
||||||
VirtualHost "example.com"
|
|
||||||
enabled = true
|
|
||||||
|
|
||||||
-- SSL configuration
|
|
||||||
ssl = {
|
|
||||||
key = "/etc/prosody/certs/example.com.key";
|
|
||||||
certificate = "/etc/prosody/certs/example.com.crt";
|
|
||||||
}
|
|
||||||
|
|
||||||
-- HTTP file upload configuration
|
|
||||||
http_upload_external_base_url = "https://files.example.com"
|
|
||||||
http_upload_external_secret = "your-very-secret-hmac-key"
|
|
||||||
http_upload_external_file_size_limit = 10737418240 -- 10GB
|
|
||||||
http_upload_external_quota = 1073741824000 -- 1TB per user
|
|
||||||
|
|
||||||
-- Custom upload URL patterns (for HMAC File Server)
|
|
||||||
http_upload_external_put_url = "https://files.example.com/upload/{filename}"
|
|
||||||
http_upload_external_get_url = "https://files.example.com/download/{filename}"
|
|
||||||
|
|
||||||
-- Component for file upload service
|
|
||||||
Component "upload.example.com" "http_upload_external"
|
|
||||||
http_upload_external_base_url = "https://files.example.com"
|
|
||||||
http_upload_external_secret = "your-very-secret-hmac-key"
|
|
||||||
http_upload_external_file_size_limit = 10737418240
|
|
||||||
|
|
||||||
-- Logging
|
|
||||||
log = {
|
|
||||||
info = "/var/log/prosody/prosody.log";
|
|
||||||
error = "/var/log/prosody/prosody.err";
|
|
||||||
"*syslog";
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Prosody Module Configuration
|
|
||||||
```lua
|
|
||||||
-- /usr/lib/prosody/modules/mod_http_upload_external.lua
|
|
||||||
-- Custom module for HMAC File Server integration
|
|
||||||
|
|
||||||
local hmac_sha256 = require "util.hashes".hmac_sha256;
|
|
||||||
local base64 = require "util.encodings".base64;
|
|
||||||
local uuid = require "util.uuid".generate;
|
|
||||||
local http = require "net.http";
|
|
||||||
|
|
||||||
module:depends("disco");
|
|
||||||
|
|
||||||
local external_base_url = module:get_option_string("http_upload_external_base_url");
|
|
||||||
local external_secret = module:get_option_string("http_upload_external_secret");
|
|
||||||
local file_size_limit = module:get_option_number("http_upload_external_file_size_limit", 100*1024*1024);
|
|
||||||
local quota = module:get_option_number("http_upload_external_quota", 1024*1024*1024);
|
|
||||||
|
|
||||||
-- XEP-0363 disco feature
|
|
||||||
module:add_feature("urn:xmpp:http:upload:0");
|
|
||||||
|
|
||||||
-- Handle upload requests
|
|
||||||
function handle_upload_request(event)
|
|
||||||
local stanza = event.stanza;
|
|
||||||
local filename = stanza:get_child_text("filename", "urn:xmpp:http:upload:0");
|
|
||||||
local filesize = tonumber(stanza:get_child_text("size", "urn:xmpp:http:upload:0"));
|
|
||||||
local content_type = stanza:get_child_text("content-type", "urn:xmpp:http:upload:0") or "application/octet-stream";
|
|
||||||
|
|
||||||
if not filename or not filesize then
|
|
||||||
return st.error_reply(stanza, "modify", "bad-request", "Missing filename or size");
|
|
||||||
end
|
|
||||||
|
|
||||||
if filesize > file_size_limit then
|
|
||||||
return st.error_reply(stanza, "modify", "not-acceptable", "File too large");
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Generate HMAC authentication
|
|
||||||
local timestamp = os.time();
|
|
||||||
local upload_id = uuid();
|
|
||||||
local message = filename .. filesize .. timestamp .. upload_id;
|
|
||||||
local signature = base64.encode(hmac_sha256(external_secret, message));
|
|
||||||
|
|
||||||
-- Construct URLs
|
|
||||||
local put_url = string.format("%s/upload?filename=%s×tamp=%d&uploadid=%s&signature=%s",
|
|
||||||
external_base_url,
|
|
||||||
filename,
|
|
||||||
timestamp,
|
|
||||||
upload_id,
|
|
||||||
signature
|
|
||||||
);
|
|
||||||
|
|
||||||
local get_url = string.format("%s/download/%s", external_base_url, filename);
|
|
||||||
|
|
||||||
-- Return slot
|
|
||||||
local reply = st.reply(stanza)
|
|
||||||
:tag("slot", {xmlns="urn:xmpp:http:upload:0"})
|
|
||||||
:tag("put", {url=put_url}):up()
|
|
||||||
:tag("get", {url=get_url}):up()
|
|
||||||
:up();
|
|
||||||
|
|
||||||
return reply;
|
|
||||||
end
|
|
||||||
|
|
||||||
module:hook("iq-get/host/urn:xmpp:http:upload:0:request", handle_upload_request);
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Ejabberd XMPP Integration
|
|
||||||
|
|
||||||
### Ejabberd Configuration
|
|
||||||
```yaml
|
```yaml
|
||||||
# /etc/ejabberd/ejabberd.yml
|
# Standard XEP-0363 configuration (works with any XMPP server)
|
||||||
# HMAC File Server integration
|
http_upload_external:
|
||||||
|
base_url: "https://files.example.com"
|
||||||
hosts:
|
secret: "your-shared-hmac-secret"
|
||||||
- "example.com"
|
max_file_size: 10737418240 # 10GB
|
||||||
|
|
||||||
listen:
|
|
||||||
-
|
|
||||||
port: 5222
|
|
||||||
ip: "::"
|
|
||||||
module: ejabberd_c2s
|
|
||||||
starttls: true
|
|
||||||
certfile: "/etc/ejabberd/certs/example.com.pem"
|
|
||||||
|
|
||||||
-
|
|
||||||
port: 5269
|
|
||||||
ip: "::"
|
|
||||||
module: ejabberd_s2s_in
|
|
||||||
|
|
||||||
-
|
|
||||||
port: 5443
|
|
||||||
ip: "::"
|
|
||||||
module: ejabberd_http
|
|
||||||
tls: true
|
|
||||||
certfile: "/etc/ejabberd/certs/example.com.pem"
|
|
||||||
request_handlers:
|
|
||||||
"/upload": mod_http_upload
|
|
||||||
"/admin": ejabberd_web_admin
|
|
||||||
"/api": mod_http_api
|
|
||||||
|
|
||||||
modules:
|
|
||||||
mod_adhoc: {}
|
|
||||||
mod_admin_extra: {}
|
|
||||||
mod_announce: {}
|
|
||||||
mod_avatar: {}
|
|
||||||
mod_blocking: {}
|
|
||||||
mod_bosh: {}
|
|
||||||
mod_caps: {}
|
|
||||||
mod_carboncopy: {}
|
|
||||||
mod_client_state: {}
|
|
||||||
mod_configure: {}
|
|
||||||
mod_disco: {}
|
|
||||||
mod_fail2ban: {}
|
|
||||||
mod_http_api: {}
|
|
||||||
mod_http_upload:
|
|
||||||
put_url: "https://files.example.com/upload"
|
|
||||||
get_url: "https://files.example.com/download"
|
|
||||||
external_secret: "your-very-secret-hmac-key"
|
|
||||||
max_size: 10737418240 # 10GB
|
|
||||||
thumbnail: false
|
|
||||||
custom_headers:
|
|
||||||
"Access-Control-Allow-Origin": "*"
|
|
||||||
"Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
|
|
||||||
"Access-Control-Allow-Headers": "Content-Type"
|
|
||||||
mod_last: {}
|
|
||||||
mod_mam: {}
|
|
||||||
mod_mqtt: {}
|
|
||||||
mod_muc: {}
|
|
||||||
mod_muc_admin: {}
|
|
||||||
mod_offline: {}
|
|
||||||
mod_ping: {}
|
|
||||||
mod_privacy: {}
|
|
||||||
mod_private: {}
|
|
||||||
mod_proxy65: {}
|
|
||||||
mod_pubsub: {}
|
|
||||||
mod_push: {}
|
|
||||||
mod_register: {}
|
|
||||||
mod_roster: {}
|
|
||||||
mod_shared_roster: {}
|
|
||||||
mod_stats: {}
|
|
||||||
mod_time: {}
|
|
||||||
mod_vcard: {}
|
|
||||||
mod_version: {}
|
|
||||||
|
|
||||||
# Authentication
|
|
||||||
auth_method: internal
|
|
||||||
|
|
||||||
# Database
|
|
||||||
default_db: mnesia
|
|
||||||
|
|
||||||
# Access rules
|
|
||||||
access_rules:
|
|
||||||
local:
|
|
||||||
- allow: local
|
|
||||||
c2s:
|
|
||||||
- deny: blocked
|
|
||||||
- allow
|
|
||||||
announce:
|
|
||||||
- allow: admin
|
|
||||||
configure:
|
|
||||||
- allow: admin
|
|
||||||
muc_create:
|
|
||||||
- allow: local
|
|
||||||
pubsub_createnode:
|
|
||||||
- allow: local
|
|
||||||
register:
|
|
||||||
- allow
|
|
||||||
trusted_network:
|
|
||||||
- allow: loopback
|
|
||||||
|
|
||||||
# ACL
|
|
||||||
acl:
|
|
||||||
local:
|
|
||||||
user_regexp: ""
|
|
||||||
loopback:
|
|
||||||
ip:
|
|
||||||
- "127.0.0.0/8"
|
|
||||||
- "::1/128"
|
|
||||||
- "::FFFF:127.0.0.1/128"
|
|
||||||
admin:
|
|
||||||
user:
|
|
||||||
- "admin@example.com"
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
loglevel: 4
|
|
||||||
log_rotate_size: 10485760
|
|
||||||
log_rotate_count: 5
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Custom Ejabberd HTTP Upload Module
|
The server handles all the HMAC authentication and file management automatically. No custom modules required.
|
||||||
```erlang
|
|
||||||
% /opt/ejabberd/lib/ejabberd-23.01/ebin/mod_http_upload_external.erl
|
|
||||||
% Custom module for HMAC File Server integration
|
|
||||||
|
|
||||||
-module(mod_http_upload_external).
|
### Experimental Custom Modules
|
||||||
-author('admin@example.com').
|
|
||||||
|
|
||||||
-behaviour(gen_mod).
|
Advanced users can explore experimental Prosody and Ejabberd modules in the `ejabberd-module/` directory. These are **development stage** and require additional testing. The standard XEP-0363 implementation is recommended for production use.
|
||||||
|
|
||||||
-export([start/2, stop/1, process_iq/1, mod_opt_type/1, mod_options/1]).
|
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
|
||||||
-include("logger.hrl").
|
|
||||||
-include("xmpp.hrl").
|
|
||||||
|
|
||||||
start(Host, Opts) ->
|
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
|
||||||
?NS_HTTP_UPLOAD_0, ?MODULE,
|
|
||||||
process_iq).
|
|
||||||
|
|
||||||
stop(Host) ->
|
|
||||||
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_HTTP_UPLOAD_0).
|
|
||||||
|
|
||||||
process_iq(#iq{type = get, sub_els = [#upload_request{filename = Filename,
|
|
||||||
size = Size,
|
|
||||||
'content-type' = ContentType}]} = IQ) ->
|
|
||||||
Host = ejabberd_config:get_myname(),
|
|
||||||
|
|
||||||
% Get configuration
|
|
||||||
PutURL = gen_mod:get_module_opt(Host, ?MODULE, put_url),
|
|
||||||
GetURL = gen_mod:get_module_opt(Host, ?MODULE, get_url),
|
|
||||||
Secret = gen_mod:get_module_opt(Host, ?MODULE, external_secret),
|
|
||||||
MaxSize = gen_mod:get_module_opt(Host, ?MODULE, max_size),
|
|
||||||
|
|
||||||
% Validate file size
|
|
||||||
case Size =< MaxSize of
|
|
||||||
true ->
|
|
||||||
% Generate HMAC signature
|
|
||||||
Timestamp = erlang:system_time(second),
|
|
||||||
UploadId = uuid:uuid_to_string(uuid:get_v4()),
|
|
||||||
Message = <<Filename/binary, (integer_to_binary(Size))/binary,
|
|
||||||
(integer_to_binary(Timestamp))/binary, UploadId/binary>>,
|
|
||||||
Signature = base64:encode(crypto:mac(hmac, sha256, Secret, Message)),
|
|
||||||
|
|
||||||
% Construct URLs
|
|
||||||
PutURLFinal = <<PutURL/binary, "?filename=", Filename/binary,
|
|
||||||
"×tamp=", (integer_to_binary(Timestamp))/binary,
|
|
||||||
"&uploadid=", UploadId/binary,
|
|
||||||
"&signature=", Signature/binary>>,
|
|
||||||
GetURLFinal = <<GetURL/binary, "/", Filename/binary>>,
|
|
||||||
|
|
||||||
% Return slot
|
|
||||||
Slot = #upload_slot{get = GetURLFinal, put = PutURLFinal},
|
|
||||||
xmpp:make_iq_result(IQ, Slot);
|
|
||||||
false ->
|
|
||||||
xmpp:make_error(IQ, xmpp:err_not_acceptable(<<"File too large">>, ?MYLANG))
|
|
||||||
end;
|
|
||||||
|
|
||||||
process_iq(IQ) ->
|
|
||||||
xmpp:make_error(IQ, xmpp:err_bad_request()).
|
|
||||||
|
|
||||||
mod_opt_type(put_url) -> fun iolist_to_binary/1;
|
|
||||||
mod_opt_type(get_url) -> fun iolist_to_binary/1;
|
|
||||||
mod_opt_type(external_secret) -> fun iolist_to_binary/1;
|
|
||||||
mod_opt_type(max_size) -> fun(I) when is_integer(I), I > 0 -> I end.
|
|
||||||
|
|
||||||
mod_options(_Host) ->
|
|
||||||
[{put_url, <<"">>},
|
|
||||||
{get_url, <<"">>},
|
|
||||||
{external_secret, <<"">>},
|
|
||||||
{max_size, 104857600}].
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user