?? mms_uaprof.c
字號:
/*
* Mbuni - Open Source MMS Gateway
*
* User-Agent profiles handling, content adaptation.
*
* Copyright (C) 2003 - 2005, Digital Solutions Ltd. - http://www.dsmagic.com
*
* Paul Bagyenda <bagyenda@dsmagic.com>
*
* This program is free software, distributed under the terms of
* the GNU General Public License, with a few exceptions granted (see LICENSE)
*/
#include <ctype.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <unistd.h>
#include "mms_uaprof.h"
#include "mms_util.h"
struct MmsUaProfile {
List *versions;
unsigned long maxmsgsize;
struct {
long x, y;
} maxres;
struct {
unsigned char all;
unsigned char presentation;
List *content, *_hash; /* List of accepted content formats (+ hash keys for fast look-up). */
List *charset, *_chash; /* List of accepted charsets. */
List *lang; /* List of accepted languages. */
List *enc; /* List of accepted encodings. */
} ccppaccept;
};
static Dict *profile_dict; /* Of MmsUaProfile *. */
static Octstr *profile_dir; /* Directory for storing data. */
/* Hash function -- case insensitive. */
static unsigned long hash_key(Octstr *s)
{
unsigned long h = 0;
int i, n;
char *x;
if (!s) return 0;
for (x = octstr_get_cstr(s), i = 0, n = octstr_len(s); i<n; i++)
h += (unsigned long)tolower(x[i]);
return h;
}
static void destroy_uaprof(MmsUaProfile *prof)
{
if (prof->versions)
gwlist_destroy(prof->versions,
(gwlist_item_destructor_t *)octstr_destroy);
if (prof->ccppaccept.content) {
gwlist_destroy(prof->ccppaccept.content, (gwlist_item_destructor_t *)octstr_destroy);
gwlist_destroy(prof->ccppaccept._hash, NULL);
}
if (prof->ccppaccept.charset) {
gwlist_destroy(prof->ccppaccept.charset, (gwlist_item_destructor_t *)octstr_destroy);
gwlist_destroy(prof->ccppaccept._chash, NULL);
} if (prof->ccppaccept.lang)
gwlist_destroy(prof->ccppaccept.lang, (gwlist_item_destructor_t *)octstr_destroy);
if (prof->ccppaccept.enc)
gwlist_destroy(prof->ccppaccept.enc, (gwlist_item_destructor_t *)octstr_destroy);
gw_free(prof);
}
static void dump_profile(MmsUaProfile *prof, Octstr *name)
{
int i;
Octstr *s;
debug("mms.uaprof", 0, "Dumping profile for %s", octstr_get_cstr(name));
debug("mms.uaprof", 0, "MaxMsgSize: %ld", prof->maxmsgsize);
debug("mms.uaprof", 0, "MaxRes: %ldx%ld", prof->maxres.x,prof->maxres.y);
s = octstr_create("");
if (prof->ccppaccept.content)
for (i=0; i<gwlist_len(prof->ccppaccept.content); i++)
octstr_format_append(s, "%S, ", gwlist_get(prof->ccppaccept.content,i));
debug("mms.uaprof", 0, "Accept content: %s", octstr_get_cstr(s));
octstr_destroy(s);
s = octstr_create("");
if (prof->ccppaccept.enc)
for (i=0; i<gwlist_len(prof->ccppaccept.enc); i++)
octstr_format_append(s, "%S, ", gwlist_get(prof->ccppaccept.enc,i));
debug("mms.uaprof", 0, "Accept encodings: %s", octstr_get_cstr(s));
octstr_destroy(s);
s = octstr_create("");
if (prof->ccppaccept.lang)
for (i=0; i<gwlist_len(prof->ccppaccept.lang); i++)
octstr_format_append(s, "%S, ", gwlist_get(prof->ccppaccept.lang,i));
debug("mms.uaprof", 0, "Accept language: %s", octstr_get_cstr(s));
octstr_destroy(s);
s = octstr_create("");
if (prof->ccppaccept.charset)
for (i=0; i<gwlist_len(prof->ccppaccept.charset); i++)
octstr_format_append(s, "%S, ", gwlist_get(prof->ccppaccept.charset,i));
debug("mms.uaprof", 0, "Accept charset: %s", octstr_get_cstr(s));
octstr_destroy(s);
s = octstr_create("");
if (prof->versions)
for (i=0; i<gwlist_len(prof->versions); i++)
octstr_format_append(s, "%S, ", gwlist_get(prof->versions,i));
debug("mms.uaprof", 0, "Mms Version: %s", octstr_get_cstr(s));
octstr_destroy(s);
}
/* Helper function: find a node. Uses breadth first search */
static xmlNodePtr find_node(xmlNodePtr start, char *name, char *id, int level, int maxlevel)
{
xmlNodePtr node, x, list;
if (level >= maxlevel) return NULL;
/* First search at top level. */
for (list=start; list; list=list->next)
if (list->type == XML_COMMENT_NODE)
continue;
else if (xmlStrcasecmp(list->name, (const xmlChar *)name) == 0) {
if (!id)
return list;
else {
unsigned char *s;
if ((s= xmlGetProp(list,(unsigned char *)"ID")) != NULL &&
xmlStrcasecmp(s,(unsigned char *)id) == 0) {
xmlFree(s);
return list;
}
if (s) xmlFree(s);
}
}
/* Then recurse...*/
for (list = start; list; list=list->next)
for (node = list->xmlChildrenNode; node; node = node->next)
if (xmlStrcasecmp(node->name, (const xmlChar *)name) == 0) {
if (!id)
return node;
else {
unsigned char *s;
if ((s = xmlGetProp(node,(unsigned char *)"ID")) != NULL &&
xmlStrcasecmp(s,(unsigned char *)id) == 0) {
xmlFree(s);
return node;
}
if (s) xmlFree(s);
}
} else if (node->type != XML_COMMENT_NODE &&
(x = find_node(node, name,id, level+1,maxlevel)) != NULL)
return x;
return NULL;
}
MmsUaProfile *mms_make_ua_profile(List *req_headers)
{
MmsUaProfile *prof = NULL;
Octstr *s, *ua;
List *l;
int i, n;
static int uacounter;
/* Check cache first, if not, then construct. */
if ((ua = http_header_value(req_headers, octstr_imm("User-Agent"))) == NULL)
ua = octstr_format("dummy-ua-%d", uacounter++);
if ((prof = dict_get(profile_dict, ua)) != NULL)
goto done;
prof = gw_malloc(sizeof *prof);
memset(prof, 0, sizeof *prof);
/* Put in some defaults. then read then check accepts. */
prof->maxres.x = 640;
prof->maxres.y = 480;
prof->maxmsgsize = 100*1024;
prof->versions = gwlist_create();
gwlist_append(prof->versions, octstr_imm("1.0")); /* Assume 1.0 for now. */
/* Get accepted charsets. */
s = http_header_value(req_headers, octstr_imm("Accept-Charset"));
if (s && (l = http_header_split_value(s)) != NULL) {
prof->ccppaccept.charset = l;
prof->ccppaccept._chash = gwlist_create();
for (i = 0, n = gwlist_len(l); i<n; i++)
gwlist_append(prof->ccppaccept._chash, (void *)hash_key(gwlist_get(l, i)));
}
if (s) octstr_destroy(s);
/* Get accepted encodings. */
s = http_header_value(req_headers, octstr_imm("Accept-Encoding"));
if (s && (l = http_header_split_value(s)) != NULL)
prof->ccppaccept.enc = l;
if (s) octstr_destroy(s);
/* Get accepted language. */
s = http_header_value(req_headers, octstr_imm("Accept-Language"));
if (s && (l = http_header_split_value(s)) != NULL)
prof->ccppaccept.lang = l;
if (s) octstr_destroy(s);
s = http_header_value(req_headers, octstr_imm("Accept"));
if (s && (l = http_header_split_value(s)) != NULL) {
prof->ccppaccept.content = l;
prof->ccppaccept._hash = gwlist_create();
for (i = 0, n = l ? gwlist_len(l) : 0; i<n; i++) {
Octstr *x = gwlist_get(l, i);
if (octstr_str_compare(x, "*/*") == 0)
prof->ccppaccept.all = 1;
else if (octstr_case_compare(x, octstr_imm(PRES_TYPE)) == 0)
prof->ccppaccept.presentation = 1;
gwlist_append(prof->ccppaccept._hash, (void *)hash_key(x));
}
}
if (s) octstr_destroy(s);
/* Put it in with the UA string as the key. */
if (dict_put_once(profile_dict, ua, prof) != 1)
warning(0, "mms_uaprof: Duplicate cache entry(%s)?\n",
octstr_get_cstr(ua));
/* Done. Dump it while debugging. */
done:
#if 1
dump_profile(prof, ua ? ua : octstr_imm("<from http headers>"));
#endif
if (ua)
octstr_destroy(ua);
return prof;
}
static MmsUaProfile *parse_uaprofile(Octstr *xml)
{
char *s = octstr_get_cstr(xml);
xmlDocPtr doc = xmlParseMemory(s, octstr_len(xml));
xmlNodePtr node, xnode;
MmsUaProfile *prof = NULL;
if (!doc || !doc->xmlChildrenNode)
goto done;
node = find_node(doc->xmlChildrenNode, "Description", "MmsCharacteristics",0,3);
prof = gw_malloc(sizeof *prof);
memset(prof, 0, sizeof *prof);
/* Put in some defaults. then read the file. */
prof->versions = NULL;
prof->maxres.x = 640;
prof->maxres.y = 480;
prof->maxmsgsize = 100*1024;
prof->versions = NULL;
if (!node)
goto done;
for (xnode = node->xmlChildrenNode; xnode; xnode = xnode->next) {
xmlNodePtr child = xnode->xmlChildrenNode, lnode, rdfnode;
const unsigned char *xname = xnode->name;
unsigned char *childtext = xmlNodeListGetString(doc, child, 1);
List *l;
/* If there is a Bag, get the list. */
if ((rdfnode = find_node(xnode->xmlChildrenNode, "Bag", NULL,0,1)) != NULL) {
l = gwlist_create();
for (lnode = rdfnode->xmlChildrenNode; lnode; lnode = lnode->next)
if (xmlStrcasecmp(lnode->name, (const xmlChar *)"li") == 0) {
unsigned char *t = xmlNodeListGetString(doc, lnode->xmlChildrenNode,1);
if (t) {
gwlist_append(l, octstr_create((char *)t));
xmlFree(t);
}
}
} else
l = NULL;
if (xmlStrcasecmp(xname, (const xmlChar *)"MmsMaxMessageSize") == 0)
sscanf((char *)childtext, "%ld", &prof->maxmsgsize);
else if (xmlStrcasecmp(xname, (const xmlChar *)"MmsMaxImageResolution") == 0)
sscanf((char *)childtext, "%ldx%ld", &prof->maxres.x, &prof->maxres.y);
else if (xmlStrcasecmp(xname, (const xmlChar *)"MmsCcppAcceptCharSet") == 0 ||
xmlStrcasecmp(xname, (const xmlChar *)"MmsCcppAccept-CharSet") == 0) {/* Cranky old ones! */
int i, n;
prof->ccppaccept.charset = l;
prof->ccppaccept._chash = gwlist_create();
for (i = 0, n = gwlist_len(l); i<n; i++)
gwlist_append(prof->ccppaccept._chash, (void *)hash_key(gwlist_get(l, i)));
} else if (xmlStrcasecmp(xname, (const xmlChar *)"MmsCcppAcceptLanguage") == 0)
prof->ccppaccept.lang = l;
else if (xmlStrcasecmp(xname, (const xmlChar *)"MmsCcppAcceptEncoding") == 0)
prof->ccppaccept.enc = l;
else if (xmlStrcasecmp(xname, (const xmlChar *)"MmsVersion") == 0) {
if (!l && childtext) { /* SonyEriccson uses old format! */
l = gwlist_create();
gwlist_append(l, octstr_create((char *)childtext));
}
prof->versions = l;
} else if (xmlStrcasecmp(xname, (const xmlChar *)"MmsCcppAccept") == 0) {
int i, n;
prof->ccppaccept.content = l;
prof->ccppaccept._hash = gwlist_create();
for (i = 0, n = l ? gwlist_len(l) : 0; i<n; i++) {
Octstr *x = gwlist_get(l, i);
if (octstr_str_compare(x, "*/*") == 0)
prof->ccppaccept.all = 1;
else if (octstr_case_compare(x, octstr_imm(PRES_TYPE)) == 0)
prof->ccppaccept.presentation = 1;
gwlist_append(prof->ccppaccept._hash, (void *)hash_key(x));
}
}
if (childtext) xmlFree(childtext);
}
done:
if (doc) xmlFreeDoc(doc);
return prof;
}
static int replace_slash(int ch)
{
return (ch == '/') ? '$' : ch;
}
static int unreplace_slash(int ch)
{
return (ch == '$') ? '/' : ch;
}
static int mms_load_ua_profile_cache(char *dir)
{
DIR *dirp;
struct dirent *dp;
dirp = opendir(dir);
if (!dirp) {
error(0, "mms_uaprof: Failed to open UA prof cache directory %s",
dir);
return -1;
}
while ((dp = readdir(dirp)) != NULL) {
Octstr *fname;
Octstr *xml = NULL;
MmsUaProfile *prof = NULL;
Octstr *key = NULL;
if (strcmp(dp->d_name, ".") == 0 ||
strcmp(dp->d_name, "..") == 0) /* A directory, skip. */
continue;
fname = octstr_format("%.255s/%.254s", dir, dp->d_name);
xml = octstr_read_file(octstr_get_cstr(fname));
octstr_destroy(fname);
if (!xml) {
error(0, "mms_uaprof: Failed to read UA prof doc %s in %s (%s)\n",
dp->d_name, dir, strerror(errno));
continue;
}
prof = parse_uaprofile(xml);
if (!prof) {
error(0, "mms_uaprof: Failed to parse UA prof doc %s in %s\n", dp->d_name, dir);
goto loop;
}
key = octstr_create(dp->d_name);
octstr_convert_range(key, 0, octstr_len(key), unreplace_slash);
if (dict_put_once(profile_dict, key, prof) != 1)
warning(0, "mms_uaprof: Duplicate cache entry(%s)?\n",
octstr_get_cstr(key));
#if 1
dump_profile(prof, key);
#endif
loop:
if (xml) octstr_destroy(xml);
if (key) octstr_destroy(key);
}
closedir(dirp);
return 0;
}
static MmsUaProfile *profile_fetch(Octstr *profile_url)
{
Octstr *body = NULL;
List *h, *rh = NULL;
int status;
MmsUaProfile *prof;
gw_assert(profile_dict);
debug("mms.uaprof", 0, "Entered fetcher");
if ((prof = dict_get(profile_dict, profile_url)) != NULL)
return prof;
h = http_create_empty_headers();
http_header_add(h, "User-Agent", MM_NAME "/" MMSC_VERSION);
status = mms_url_fetch_content(HTTP_METHOD_GET, profile_url, h, octstr_imm(""), &rh, &body);
if (http_status_class(status) == HTTP_STATUS_SUCCESSFUL) {
prof = parse_uaprofile(body);
debug("mms.uaprof", 0, "Fetcher got %s", octstr_get_cstr(profile_url));
if (prof) {
if (dict_put_once(profile_dict, profile_url, prof) != 1)
warning(0, "mms_uaprof: Duplicate ua profile fetched? (%s)?\n",
octstr_get_cstr(profile_url));
else {
Octstr *fname;
FILE *f;
octstr_convert_range(profile_url, 0, octstr_len(profile_url), replace_slash);
fname = octstr_format("%.255s/%.254s", octstr_get_cstr(profile_dir),
octstr_get_cstr(profile_url));
f = fopen(octstr_get_cstr(fname), "w");
if (f) {
octstr_print(f, body);
fclose(f);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -