Compare commits

...

17 commits
live ... master

Author SHA1 Message Date
steckbrief
5e01215867 fixes Attempt to read a non-existent global 'xmlns' 2025-01-05 21:41:52 +01:00
52da5f834b disable check of content type
implementation to determine content type of files in android/java differ from php
.amr files do not work
example: http://up.picn.de/files/56e2d7b9-9847-4596-803c-e5b37f9b089f/good%20bad%20ugly.amr
2025-01-04 22:31:03 +01:00
b7752e744d add flag "escape backreferences" for rewrite 2025-01-04 22:10:45 +01:00
trzcode
a8c88ebb37 implements xep-0363 v1.1.0 2024-07-22 21:19:08 +02:00
steckbrief
4ff3b79b99 adds possibility to sort ascending or descending by timestamp 2018-10-21 00:11:53 +02:00
steckbrief
fbd9e9bd30 adds .vscode to gitignore 2018-10-20 19:03:01 +02:00
steckbrief
4dca5c1160 adds basic rsm 'https://xmpp.org/extensions/xep-0059.html' functionality for file-listing 2018-10-20 19:01:24 +02:00
steckbrief
cf3467ce1f adds support for requesting spec and implementation version of http-fileservice-module 2018-10-17 22:28:37 +02:00
steckbrief
3973a5f737 adds API method to get spec and implementation version of the storage backend 2018-10-17 22:10:42 +02:00
steckbrief
bc439cd988 adds common and http protocol functionalities 2018-10-17 22:08:12 +02:00
steckbrief
840bd494f9 fixes error in getJidDomain(JID) in case of a JID with resource identifier 2018-10-14 12:52:17 +02:00
steckbrief
35ad320c69 introduces support for 'urn:xmpp:filetransfer:http' and 'urn:xmpp:http:upload' in parallel 2018-10-06 16:10:03 +02:00
steckbrief
b89763652f moves version functionality into its own function 2018-10-06 10:52:25 +02:00
steckbrief
30afe3e279 Merge branch 'master' of ssh://repos.thedevstack.de/httpupload 2018-10-05 13:14:53 +02:00
steckbrief
82c3719e0d moves upload functionality into its own function 2018-10-05 13:13:04 +02:00
steckbrief
abe4d46462 fixes a problem in checking for file existence when filename contains '%20' as white space 2018-10-04 08:32:08 +02:00
steckbrief
507d2fd564 added request for version of xmpp-fileservice-module 2018-02-24 00:20:33 +01:00
10 changed files with 475 additions and 112 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
config.php
.vscode

View file

@ -15,32 +15,81 @@ local xmpp_server_key = module:get_option("http_upload_external_server_key");
local filetransfer_manager_ui_url = module:get_option("filetransfer_manager_ui_url");
-- imports
require"https";
prosody.unlock_globals();
--require"https";
local st = require"util.stanza";
local http = (string.len(external_url) >= 5 and string.sub(external_url,1,5) == "https") and require"ssl.https" or require"socket.http";
local json = require"util.json";
local dataform = require "util.dataforms".new;
local ltn12 = require"ltn12";
local rsm = require"util.rsm";
prosody.lock_globals();
-- depends
module:depends("disco");
-- namespace
local xmlns_http_upload = "urn:xmpp:filetransfer:http";
local xmlns_filetransfer_http = "urn:xmpp:filetransfer:http";
local xmlns_http_upload = "urn:xmpp:http:upload";
local xmlns_http_upload_0 = "urn:xmpp:http:upload:0";
-- versions
spec_version = "v0.4";
impl_version = "v0.4-dev";
module:add_feature(xmlns_filetransfer_http);
module:add_feature(xmlns_http_upload);
module:add_feature(xmlns_http_upload_0);
if filetransfer_manager_ui_url then
-- add additional disco info to advertise managing UI
module:add_extension(dataform {
{ name = "FORM_TYPE", type = "hidden", value = xmlns_http_upload },
{ name = "FORM_TYPE", type = "hidden", value = xmlns_filetransfer_http },
{ name = "filetransfer-manager-ui-url", type = "text-single" },
}:form({ ["filetransfer-manager-ui-url"] = filetransfer_manager_ui_url }, "result"));
end
local function buildRequestBody(reqparams)
module:log("debug", "param count " .. #reqparams);
local params = {};
for k,v in pairs(reqparams) do
if v ~= nil then
params[#params + 1] = k .. "=" .. tostring(v);
end
end
return table.concat(params, "&");
end
local function listfiles(origin, orig_from, stanza, request)
local rsmSet = rsm.get(request);
local limit = rsmSet and rsmSet.max or -1;
local descending = rsmSet and rsmSet.before or nil;
local index = rsmSet and rsmSet.index or 0;
--local before, after = rsmSet and rsmSet.before, rsmSet and rsmSet.after;
--if type(before) ~= "string" then before = nil; end
local filter = request.attr.filter or nil;
local from = request.attr.from or nil;
local to = request.attr.to or nil;
local with = request.attr.with or nil;
-- build the body
local reqbody = "xmpp_server_key=" .. xmpp_server_key .. "&slot_type=list&user_jid=" .. orig_from;
local reqparams = {
["xmpp_server_key"] = xmpp_server_key,
["slot_type"] = "list",
["user_jid"] = orig_from,
["offset"] = index,
["limit"] = limit,
["descending"] = descending,
["filter"] = filter,
["from"] = from,
["to"] = to,
["with"] = with
};
--local reqbody = "xmpp_server_key=" .. xmpp_server_key .. "&slot_type=list&user_jid=" .. orig_from ..
-- "&offset=" .. index .. "&limit=" .. limit .. "&descending=" .. tostring(descending);
--reqbody = reqbody .. "&filter=" .. filter .. "&from=" .. from .. "&to=" .. to .. "&with=" .. with;
local reqbody = buildRequestBody(reqparams);
module:log("debug", "Request body: " .. reqbody);
-- the request
local respbody, statuscode = http.request(external_url, reqbody);
-- respbody is nil in case the server is not reachable
@ -48,8 +97,10 @@ local function listfiles(origin, orig_from, stanza, request)
respbody = string.gsub(respbody, "\\/", "/");
end
local filelistempty = true;
local list;
local count = 0;
local first = {};
first.index = index;
-- check the response
if statuscode == 500 then
@ -76,8 +127,10 @@ local function listfiles(origin, orig_from, stanza, request)
return true;
else
-- process json response
list = respobj.list;
filelistempty = list == nil or next(list) == nil;
if respobj.list then
list = respobj.list.files;
count = respobj.list.count;
end
end
else
origin.send(st.error_reply(stanza, "cancel", "undefined-condition", "status code: " .. statuscode .. " response: " ..respbody));
@ -86,8 +139,8 @@ local function listfiles(origin, orig_from, stanza, request)
local addedfiles = 0;
local reply = st.reply(stanza);
reply:tag("list", {xmlns=xmlns_http_upload});
if filelistempty == false then
reply:tag("list", {xmlns=xmlns_filetransfer_http});
if count > 0 then
for i, file in ipairs(list) do
local url = file.url;
if url == "" then
@ -112,9 +165,10 @@ local function listfiles(origin, orig_from, stanza, request)
end
end
end
if filelistempty or addedfiles == 0 then
if count == 0 or addedfiles == 0 then
reply:tag("empty"):up();
end
reply:add_child(rsm.generate{first = first, count = count})
origin.send(reply);
return true;
end
@ -126,8 +180,6 @@ local function deletefile(origin, orig_from, stanza, request)
origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid fileurl"));
return true;
end
-- build the body
--local reqbody = "xmpp_server_key=" .. xmpp_server_key .. "&slot_type=delete&file_url=" .. fileurl .. "&user_jid=" .. orig_from;
-- the request
local resp = {};
local client, statuscode = http.request{url=fileurl,
@ -158,7 +210,7 @@ local function deletefile(origin, orig_from, stanza, request)
elseif errobj["err_code"] ~= nil and errobj["msg"] ~= nil then
if errobj.err_code == 4 then
origin.send(st.error_reply(stanza, "cancel", "internal-server-error", errobj.msg)
:tag("missing-parameter", {xmlns=xmlns_http_upload})
:tag("missing-parameter", {xmlns=xmlns_filetransfer_http})
:text(errobj.parameters.missing_parameter));
return true;
else
@ -172,7 +224,7 @@ local function deletefile(origin, orig_from, stanza, request)
end
elseif statuscode == 204 or statuscode == 404 then
local reply = st.reply(stanza);
reply:tag("deleted", { xmlns = xmlns_http_upload });
reply:tag("deleted", { xmlns = xmlns_filetransfer_http });
origin.send(reply);
return true;
elseif respbody ~= nil then
@ -185,50 +237,56 @@ local function deletefile(origin, orig_from, stanza, request)
end
end
-- hooks
module:hook("iq/host/"..xmlns_http_upload..":request", function (event)
local stanza, origin = event.stanza, event.origin;
local orig_from = stanza.attr.from;
local request = stanza.tags[1];
-- local clients only
if origin.type ~= "c2s" then
origin.send(st.error_reply(stanza, "cancel", "not-authorized"));
local function version(origin, stanza)
local reply = st.reply(stanza);
reply:tag("version", { xmlns = xmlns_filetransfer_http });
reply:tag("xmpp-fileservice-module", { spec = spec_version, implementation = impl_version }):up();
-- the request
local respbody, statuscode = http.request(external_url .. "?action=version");
-- respbody is nil in case the server is not reachable
if respbody ~= nil then
respbody = string.gsub(respbody, "\\/", "/");
end
local http_spec_version = nil;
local http_impl_version = nil;
-- check the response
if statuscode == 200 then
local respobj, pos, err = json.decode(respbody);
if err then
-- do nothing for the moment
else
if respobj["spec"] ~= nil and respobj["impl"] ~= nil then
http_spec_version = respobj.spec;
http_impl_version = respobj.impl;
reply:tag("http-fileservice-module", { spec = http_spec_version, implementation = http_impl_version }):up();
end
end
end
origin.send(reply);
return true;
end
local function create_upload_slot_with_childs(origin, orig_from, stanza, request, namespace, recipient)
-- validate
local filename = request:get_child_text("filename");
if not filename then
origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid filename"));
return true;
end
-- check configuration
if not external_url or not xmpp_server_key then
module:log("debug", "missing configuration options: http_upload_external_url and/or http_upload_external_server_key");
origin.send(st.error_reply(stanza, "cancel", "internal-server-error"));
local filesize = tonumber(request:get_child_text("size"));
if not filesize then
origin.send(st.error_reply(stanza, "modify", "bad-request", "Missing or invalid file size"));
return true;
end
local slot_type = request.attr.type;
if slot_type then
module:log("debug", "incoming request is of type " .. slot_type);
else
module:log("debug", "incoming request has no type - using default type 'upload'");
end
if not slot_type or slot_type == "upload" then
-- validate
local filename = request:get_child_text("filename");
if not filename then
origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid filename"));
return true;
end
local filesize = tonumber(request:get_child_text("size"));
if not filesize then
origin.send(st.error_reply(stanza, "modify", "bad-request", "Missing or invalid file size"));
return true;
end
local recipient = request.attr.recipient;
if not recipient then
origin.send(st.error_reply(stanza, "modify", "bad-request", "Missing or invalid recipient"));
return true;
end
local content_type = request:get_child_text("content-type");
return create_upload_slot(origin, orig_from, stanza, namespace, recipient, filename, filesize, content_type);
end
local content_type = request:get_child_text("content-type");
local function create_upload_slot(origin, orig_from, stanza, namespace, recipient, filename, filesize, content_type)
-- build the body
local reqbody = "xmpp_server_key=" .. xmpp_server_key .. "&slot_type=upload&size=" .. filesize .. "&filename=" .. filename .. "&user_jid=" .. orig_from .. "&recipient_jid=" .. recipient;
if content_type then
@ -261,17 +319,17 @@ module:hook("iq/host/"..xmlns_http_upload..":request", function (event)
return true;
elseif errobj.err_code == 2 then
origin.send(st.error_reply(stanza, "modify", "not-acceptable", errobj.msg)
:tag("file-too-large", {xmlns=xmlns_http_upload})
:tag("file-too-large", {xmlns=namespace})
:tag("max-size"):text(errobj.parameters.max_file_size));
return true;
elseif errobj.err_code == 3 then
origin.send(st.error_reply(stanza, "modify", "not-acceptable", errobj.msg)
:tag("invalid-character", {xmlns=xmlns_http_upload})
:tag("invalid-character", {xmlns=namespace})
:text(errobj.parameters.invalid_character));
return true;
elseif errobj.err_code == 4 then
origin.send(st.error_reply(stanza, "cancel", "internal-server-error", errobj.msg)
:tag("missing-parameter", {xmlns=xmlns_http_upload})
:tag("missing-parameter", {xmlns=namespace})
:text(errobj.parameters.missing_parameter));
return true;
else
@ -310,17 +368,120 @@ module:hook("iq/host/"..xmlns_http_upload..":request", function (event)
end
local reply = st.reply(stanza);
reply:tag("slot", { xmlns = xmlns_http_upload });
reply:tag("get"):text(get_url):up();
reply:tag("put"):text(put_url):up();
reply:tag("slot", { xmlns = namespace });
if namespace == xmlns_http_upload_0 then
reply:tag("put", { url = put_url }):up();
reply:tag("get", { url = get_url }):up();
else
reply:tag("get"):text(get_url):up();
reply:tag("put"):text(put_url):up();
end
origin.send(reply);
return true;
end
-- hooks
module:hook("iq/host/"..xmlns_filetransfer_http..":request", function (event)
local stanza, origin = event.stanza, event.origin;
local orig_from = stanza.attr.from;
local request = stanza.tags[1];
-- local clients only
if origin.type ~= "c2s" then
origin.send(st.error_reply(stanza, "cancel", "not-authorized"));
return true;
end
-- check configuration
if not external_url or not xmpp_server_key then
module:log("debug", "missing configuration options: http_upload_external_url and/or http_upload_external_server_key");
origin.send(st.error_reply(stanza, "cancel", "internal-server-error"));
return true;
end
local slot_type = request.attr.type;
if slot_type then
module:log("debug", "incoming request is of type " .. slot_type);
else
module:log("debug", "incoming request has no type - using default type 'upload'");
end
if not slot_type or slot_type == "upload" then
local recipient = request.attr.recipient;
if not recipient then
origin.send(st.error_reply(stanza, "modify", "bad-request", "Missing or invalid recipient"));
return true;
end
return create_upload_slot(origin, orig_from, stanza, request, xmlns_filetransfer_http, recipient);
elseif slot_type == "delete" then
return deletefile(origin, orig_from, stanza, request);
elseif slot_type == "list" then
return listfiles(origin, orig_from, stanza, request);
elseif slot_type == "version" then
return version(origin, stanza);
else
origin.send(st.error_reply(stanza, "cancel", "undefined-condition", "status code: " .. statuscode .. " response: " ..respbody));
return true;
end
end);
module:hook("iq/host/"..xmlns_http_upload..":request", function (event)
local stanza, origin = event.stanza, event.origin;
local orig_from = stanza.attr.from;
local request = stanza.tags[1];
-- local clients only
if origin.type ~= "c2s" then
origin.send(st.error_reply(stanza, "cancel", "not-authorized"));
return true;
end
-- check configuration
if not external_url or not xmpp_server_key then
module:log("debug", "missing configuration options: http_upload_external_url and/or http_upload_external_server_key");
origin.send(st.error_reply(stanza, "cancel", "internal-server-error"));
return true;
end
local slot_type = request.attr.type;
if slot_type then
module:log("debug", "incoming request is of type " .. slot_type);
else
module:log("debug", "incoming request has no type - using default type 'upload'");
end
return create_upload_slot_with_childs(origin, orig_from, stanza, request, xmlns_http_upload, "Unknown");
end);
module:hook("iq/host/"..xmlns_http_upload_0..":request", function (event)
local stanza, origin = event.stanza, event.origin;
local orig_from = stanza.attr.from;
local request = stanza.tags[1];
-- local clients only
if origin.type ~= "c2s" then
origin.send(st.error_reply(stanza, "cancel", "not-authorized"));
return true;
end
-- check configuration
if not external_url or not xmpp_server_key then
module:log("debug", "missing configuration options: http_upload_external_url and/or http_upload_external_server_key");
origin.send(st.error_reply(stanza, "cancel", "internal-server-error"));
return true;
end
local slot_type = request.attr.type;
if slot_type then
module:log("debug", "incoming request is of type " .. slot_type);
else
module:log("debug", "incoming request has no type - using default type 'upload'");
end
local filename = request.attr.filename;
if not filename then
origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid filename"));
return true;
end
local filesize = tonumber(request.attr.size);
if not filesize then
origin.send(st.error_reply(stanza, "modify", "bad-request", "Missing or invalid file size"));
return true;
end
local content_type = request.attr["content-type"] or "application/octet-stream";
return create_upload_slot(origin, orig_from, stanza, xmlns_http_upload_0, "Unknown", filename, filesize, content_type);
end);

View file

@ -1,3 +1,3 @@
RewriteEngine on
RewriteCond %{REQUEST_METHOD} (DELETE|PUT)
RewriteRule ^(.*)$ index.php?uri=$1 [L,QSA]
RewriteRule ^(.*)$ index.php?uri=$1 [B,L,QSA]

View file

@ -14,5 +14,7 @@ return [
'delete_token_validity' => 5 * 60,
// Flag to whether deletion is only allowed by creator or anybody
'delete_only_by_creator' => true,
// The default count of items returned when a file list is requested
'list_default_limit' => 30,
];
?>

View file

@ -67,8 +67,8 @@ switch ($method) {
case 'POST':
// parse post parameters
// check if all parameters are present - return 400 (bad request) if a parameter is missing / empty
$xmppServerKey = getMandatoryPostParameter('xmpp_server_key');
$userJid = getMandatoryPostParameter('user_jid');
$xmppServerKey = getMandatoryPost('xmpp_server_key');
$userJid = getMandatoryPost('user_jid');
$slotType = getOptionalPostParameter('slot_type', 'upload');
// Check if xmppServerKey is allowed to request slots
@ -78,14 +78,17 @@ switch ($method) {
switch ($slotType) {
case 'list':
$slots = readSlots($userJid);
$result = ['list' => $slots];
$limit = getOptionalPostParameter('limit', $config['list_default_limit']);
$offset = getOptionalPostParameter('offset', 0);
$descending = getOptionalPostParameter('descending', 'false') === 'true';
$files = listFiles($userJid, $limit, $offset, $descending);
$result = ['list' => $files];
break;
case 'upload':
default:
// Check if all parameters needed for an upload are present - return 400 (bad request) if a parameter is missing / empty
$filename = rawurlencode(getMandatoryPostParameter('filename'));
$filesize = getMandatoryPostParameter('size');
$filename = rawurlencode(getMandatoryPost('filename'));
$filesize = getMandatoryPost('size');
$mimeType = getOptionalPostParameter('content_type');
$recipientJid = getOptionalPostParameter('recipient_jid', 'Unknown'); // Optional for backwards compatibility (xep-0363 v0.1)
@ -143,7 +146,8 @@ switch ($method) {
sendHttpReturnCodeAndJson(403, "Uploaded file size differs from requested slot size.");
}
// check actual mime type with registered mime type
if (!is_null($slotParameters['content_type']) && !empty($slotParameters['content_type']) && mime_content_type($uploadFilePath) != $slotParameters['content_type']) {
$uploadedContentType = mime_content_type($uploadFilePath);
if (false && !is_null($slotParameters['content_type']) && !empty($slotParameters['content_type']) && $uploadedContentType != $slotParameters['content_type']) {
unlink($uploadFilePath);
sendHttpReturnCodeAndJson(403, "Uploaded file content type differs from requested slot content type.");
}
@ -193,6 +197,17 @@ switch ($method) {
sendHttpReturnCodeAndJson(500, "Could not delete file.");
}
break;
case 'GET':
$actionParameter = getMandatoryGet('action');
switch ($actionParameter) {
case 'version':
echo json_encode(require_once(__DIR__.'/lib/version.inc.php'));
break;
default:
sendHttpReturnCodeAndJson(403, "Access not allowed.");
}
break;
default:
sendHttpReturnCodeAndJson(403, "Access not allowed.");
break;
@ -212,12 +227,12 @@ function checkFilenameParameter($filename, $slotParameters) {
return $slotParameters['filename'] == $filename;
}
function getMandatoryPostParameter($parameterName) {
$parameter = $_POST[$parameterName];
if (!isset($parameter) || is_null($parameter) || empty($parameter)) {
sendHttpReturnCodeAndJson(400, ['msg' => 'Missing parameter.', 'err_code' => 4, 'parameters' => ['missing_parameter' => $parameterName]]);
}
return $parameter;
function getMandatoryPost($parameterName) {
return getMandatoryPostParameter($parameterName, ['msg' => 'Missing parameter.', 'err_code' => 4, 'parameters' => ['missing_parameter' => $parameterName]], true);
}
function getMandatoryGet($parameterName) {
return getMandatoryGetParameter($parameterName, ['msg' => 'Missing parameter.', 'err_code' => 4, 'parameters' => ['missing_parameter' => $parameterName]], true);
}
function getUUIDFromUri($uri) {

View file

@ -15,4 +15,50 @@ function generate_uuid() {
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
);
}
?>
function getFromArray($key, $array) {
if (array_key_exists($key, $array)
&& isset($array[$key])
&& !empty($array[$key])) {
return $array[$key];
} else {
return NULL;
}
}
function format_size($size, $precision = 2) {
$sizes = ['bytes', 'Kb', 'Mb', 'Gb', 'Tb'];
$i = 0;
while (1023 < $size && $i < count($sizes) - 1) {
$size /= 1023;
++$i;
}
return number_format($size, $precision).' '.$sizes[$i];
}
function startsWith($haystack, $needle) {
$length = strlen($needle);
return (substr($haystack, 0, $length) === $needle);
}
function endsWith($haystack, $needle) {
$length = strlen($needle);
return $length === 0 || (substr($haystack, -$length) === $needle);
}
function generatePath($parts, $basePath = __DIR__) {
$path = $basePath;
if (!is_array($parts)) {
$parts = [$parts];
}
foreach ($parts as $part) {
$path .= DIRECTORY_SEPARATOR.generatePathName($part);
}
return $path;
}
function generatePathName($name) {
return urlencode($name);
}

View file

@ -24,7 +24,42 @@ function loadSlotParameters($slotUUID, $config) {
return $slotParameters;
}
function readSlots($jid) {
function listFiles($jid, $limit = -1, $offset = 0, $descending = false) {
// Read complete set of existing slots per jid (unsorted)
$slots = readSlots($jid, $limit, $offset);
if ($descending) {
// Sort descending by timestamp
usort($slots, function($a, $b) {
return $b['sent_time'] - $a['sent_time'];
});
} else {
// Sort ascending by timestamp
usort($slots, function($a, $b) {
return $a['sent_time'] - $b['sent_time'];
});
}
// Select requested slot subset
$offsetCounter = 0;
$resultSet = array();
foreach ($slots as $slot) {
if (0 < $offset && $offsetCounter < $offset) {
$offsetCounter++;
continue;
}
$resultSet[] = $slot;
if (0 < $limit && $limit == count($resultSet)) {
break;
}
}
return ['count' => count($slots),
'hasMore' => $offset + count($resultSet) < count($slots),
'files' => $resultSet];
}
function readSlots($jid, $limit = -1, $offset = 0) {
global $config;
$jid = getBareJid($jid);
@ -40,7 +75,7 @@ function readSlots($jid) {
if ($senderBareJid == $jid || $recipientBareJid == $jid) {
$filePath = getUploadFilePath($slotUUID, $config, $params['filename']);
$file = [];
$fileExists = file_exists($filePath);
$fileExists = file_exists(rawurldecode($filePath));
$file['url'] = "";
$file['sent_time'] = $params['creation_time'];
if ($fileExists) {
@ -60,5 +95,6 @@ function readSlots($jid) {
}
}
}
return $slots;
}

View file

@ -6,59 +6,156 @@
*
*/
require_once('functions.common.inc.php');
/**
* Inspired by https://github.com/owncloud/core/blob/master/lib/private/appframework/http/request.php#L523
*/
function getServerProtocol() {
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
if (strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], ',') !== false) {
$parts = explode(',', $_SERVER['HTTP_X_FORWARDED_PROTO']);
$proto = strtolower(trim($parts[0]));
} else {
$proto = strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']);
}
// Verify that the protocol is always HTTP or HTTPS
// default to http if an invalid value is provided
return $proto === 'https' ? 'https' : 'http';
}
if (isset($_SERVER['HTTPS'])
&& $_SERVER['HTTPS'] !== null
&& $_SERVER['HTTPS'] !== 'off'
&& $_SERVER['HTTPS'] !== '') {
return 'https';
}
return 'http';
$protocol = getHeaderExtensionValue('FORWARDED_PROTO');
if (isset($protocol)) {
if (strpos($protocol, ',') !== false) {
$parts = explode(',', $protocol);
$proto = strtolower(trim($parts[0]));
} else {
$proto = strtolower($protocol);
}
// Verify that the protocol is always HTTP or HTTPS
// default to http if an invalid value is provided
return $proto === 'https' ? 'https' : 'http';
}
if (isset($_SERVER['HTTPS'])
&& $_SERVER['HTTPS'] !== null
&& $_SERVER['HTTPS'] !== 'off'
&& $_SERVER['HTTPS'] !== '') {
return 'https';
}
return 'http';
}
function getRequestHostname() {
if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
return strtolower($_SERVER['HTTP_X_FORWARDED_HOST']);
}
return strtolower($_SERVER['HTTP_HOST']);
$forwardedHost = getHeaderExtensionValue('FORWARDED_HOST');
if (isset($forwardedHost)) {
return strtolower($forwardedHost);
}
return strtolower(getHeaderValue('HOST'));
}
function getRequestUriWithoutFilename() {
return strtolower(substr($_SERVER['REQUEST_URI'], 0, strrpos($_SERVER['REQUEST_URI'], '/') + 1));
return strtolower(substr($_SERVER['REQUEST_URI'], 0, strrpos($_SERVER['REQUEST_URI'], '/') + 1));
}
function sendHttpReturnCodeAndJson($code, $data) {
if (!is_array($data)) {
$data = ['msg' => $data];
}
header('Content-Type: application/json');
sendHttpReturnCodeAndMessage($code, json_encode($data));
if (!is_array($data)) {
$data = ['msg' => $data];
}
setContentType('application/json');
sendHttpReturnCodeAndMessage($code, json_encode($data));
}
function sendHttpReturnCodeAndMessage($code, $text = '') {
http_response_code($code);
exit($text);
http_response_code($code);
exit($text);
}
function setContentType($contentType) {
header('Content-Type: '.$contentType);
}
function getHeaderExtensionValue($headerName) {
$headerName = strtoupper($headerName);
if (!startsWith($headerName, 'HTTP_X_')) {
if (!startsWith($headerName, 'HTTP_')) {
$headerName = str_replace('HTTP_', 'HTTP_X_', $headerName);
} else {
$headerName = 'HTTP_X_'.$headerName;
}
}
return getHeaderValue($headerName);
}
function getHeaderValue($headerName) {
$headerName = strtoupper($headerName);
if (!startsWith($headerName, 'HTTP_')) {
$headerName = 'HTTP_'.$headerName;
}
return getFromArray($headerName, $_SERVER);
}
function getFileParameter($parameterName) {
return getFromArray($parameterName, $_FILES);
}
function getOptionalFileParameter($parameterName, $default = NULL) {
$parameter = getFileParameter($parameterName);
return handleOptionalParameter($parameter, $default);
}
function getMandatoryFileParameter($parameterName, $message = '', $json = false) {
$parameter = getFileParameter($parameterName);
return handleMandatoryParameter($parameterName, $parameter, $message, $json);
}
function getPostParameter($parameterName) {
return getFromArray($parameterName, $_POST);
}
function getOptionalPostParameter($parameterName, $default = NULL) {
$parameter = $_POST[$parameterName];
if (!isset($parameter) || is_null($parameter) || empty($parameter)) {
$parameter = $default;
}
return $parameter;
$parameter = getPostParameter($parameterName);
return handleOptionalParameter($parameter, $default);
}
?>
function getMandatoryPostParameter($parameterName, $message = '', $json = false) {
$parameter = getPostParameter($parameterName);
return handleMandatoryParameter($parameterName, $parameter, $message, $json);
}
function getGetParameter($parameterName) {
return getFromArray($parameterName, $_GET);
}
function getOptionalGetParameter($parameterName, $default = NULL) {
$parameter = getGetParameter($parameterName);
return handleOptionalParameter($parameter, $default);
}
function getMandatoryGetParameter($parameterName, $message = '', $json = false) {
$parameter = getGetParameter($parameterName);
return handleMandatoryParameter($parameterName, $parameter, $message, $json);
}
function handleOptionalParameter($parameter, $default) {
if (!isset($parameter) || is_null($parameter) || empty($parameter)) {
$parameter = $default;
}
return $parameter;
}
function handleMandatoryParameter($parameterName, $parameter, $message, $json) {
if (!isset($parameter) || is_null($parameter) || empty($parameter)) {
if (empty($message) || is_null($message)) {
if ($json) {
$message = ['msg' => 'Missing parameter.', 'parameters' => ['missing_parameter' => $parameterName]];
} else {
$message = 'Missing mandatory parameter "'.$parameterName.'".';
}
}
if (!$json) {
sendHttpReturnCodeAndMessage(400, $message);
} else {
sendHttpReturnCodeAndJson(400, $message);
}
}
return $parameter;
}
?>

View file

@ -0,0 +1,5 @@
<?php
return [
'spec' => '0.3',
'impl' => '0.3-dev'
];

View file

@ -13,7 +13,7 @@ function getJidDomain($jid) {
if ($slashIndex !== false) {
if ($slashIndex > $atIndex) {// 'local@domain.foo/resource' and 'local@domain.foo/res@otherres' case
return substr($jid, $atIndex + 1, $slashIndex - $atIndex + 1);
return substr($jid, $atIndex + 1, $slashIndex - $atIndex - 1);
} else {// 'domain.foo/res@otherres' case
return substr($jid, 0, $slashIndex);
}